├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── cec2017 ├── __init__.py ├── basic.py ├── composition.py ├── data.pkl ├── functions.py ├── hybrid.py ├── simple.py ├── transforms.py └── utils.py ├── example.py ├── extra └── plots.jpg ├── setup.py └── tests ├── __init__.py └── test_functions.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | __pycache__/ 3 | *.pyc 4 | .venv 5 | venv 6 | build/ 7 | dist/ 8 | CEC2017.egg-info/ 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Duncan Tilley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include cec2017/data.pkl 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CEC 2017 Python 2 | 3 | Python 3 module containing a native implementation of the CEC 2017 benchmark functions (single objective optimization). The implementation is adapted from Awad's original C implementation, available on [Suganthan's GitHub repo](https://github.com/P-N-Suganthan/CEC2017-BoundContrained), along with the problem definitions [1]. 4 | 5 | Although there are wrappers for the C code, this module is easier to use, natively supports numpy arrays and is (_much_) more readable than the C implementation. 6 | 7 | During the implementation of this module, a few differences between the problem definitions and the original code were picked up on. In these cases, the implementation always follows whatever the original code did to remain compatible, and are marked with `Note:` comments. 8 | 9 | As per the problem definitions, functions are defined for 10, 30, 50 and 100 dimensions, with functions `f1` to `f10` and `f21` to `f28` also being defined for 2 and 20 dimensions. If you provide custom rotation matrices (and shuffles, where applicable) you can however use arbitrary dimensions. Below are some surface plots for functions `f1` to `f10` over 2 dimensions. 10 | 11 | ![Function Surface Plots](extra/plots.jpg) 12 | 13 | > \[1\] _Awad, N. H., Ali, M. Z., Suganthan, P. N., Liang, J. J., & Qu, B. Y. (2016). Problem Definitions and Evaluation Criteria for the CEC 2017 Special Session and Competition on Single Objective Bound Constrained Real-Parameter Numerical Optimization._ 14 | 15 | ## Features 16 | 17 | - Native implementation of all CEC 2017 single objective functions optimized for multiple simultaneous evaluations 18 | - Pre-defined rotations, shifts, and shuffles for 2, 10, 20, 30, 50 and 100 dimensions 19 | - Allows custom rotations, shifts, and shuffles 20 | - Convenient surface plot utility 21 | - Easy access to basic functions f1 to f19 (e.g. Ackley, Discus, etc.) 22 | 23 | ## Installation 24 | 25 | ``` 26 | git clone https://github.com/tilleyd/cec2017-py 27 | cd cec2017-py 28 | python3 setup.py install 29 | ``` 30 | 31 | ## Usage 32 | 33 | Below is a simple example of executing either a single function or all functions. Note that each function takes a 2D array and returns a 1D array. See [example.py](example.py) for more advanced use cases. 34 | 35 | ```py 36 | # Using only f5: 37 | from cec2017.functions import f5 38 | samples = 3 39 | dimension = 50 40 | x = np.random.uniform(-100, 100, size=(samples, dimension)) 41 | val = f5(x) 42 | for i in range(samples): 43 | print(f"f5(x_{i}) = {val[i]:.6f}") 44 | 45 | # Using all functions: 46 | from cec2017.functions import all_functions 47 | for f in all_functions: 48 | x = np.random.uniform(-100, 100, size=(samples, dimension)) 49 | val = f(x) 50 | for i in range(samples): 51 | print(f"{f.__name__}(x_{i}) = {val[i]:.6f}") 52 | ``` 53 | 54 | ## Changelog 55 | 56 | ### 23 Nov 2022 57 | - All functions have been reimplemented using numpy vector operations instead of native python loops. 58 | - **Breaking change:** Functions now expect a 2D input array of shape (m, D) where D is the dimensionality, and m is an arbitrary sample size. 59 | 60 | The above updates have allowed a substantial increase in performance when evaluating multiple parameter samples simultaneously, which previously would have had to be done as consecutive calls. See [PR 5](https://github.com/tilleyd/cec2017-py/pull/5) for more details. 61 | 62 | ## License 63 | 64 | Copyright © 2022 Duncan Tilley 65 | See the [license notice](LICENSE.txt) for full details. 66 | 67 | ## Issues 68 | 69 | If you see any issues or possible improvements, please open an issue or feel free to make a pull request. 70 | -------------------------------------------------------------------------------- /cec2017/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tilleyd/cec2017-py/424a9fa2757914c3e4cfdd8f59a268b1aeb3197f/cec2017/__init__.py -------------------------------------------------------------------------------- /cec2017/basic.py: -------------------------------------------------------------------------------- 1 | # cec2017.basic 2 | # Author: Duncan Tilley 3 | # Basic function definitions 4 | 5 | from typing import Optional 6 | import numpy as np 7 | 8 | 9 | def bent_cigar(x: np.ndarray) -> np.ndarray: 10 | sm = np.sum(x[:, 1:] * x[:, 1:], axis=1) 11 | sm = sm * 10e6 12 | return x[:, 0]*x[:, 0] + sm 13 | 14 | 15 | def sum_diff_pow(x: np.ndarray) -> np.ndarray: 16 | i = np.expand_dims(np.arange(x.shape[1]) + 1, 0) 17 | x_pow = np.power(np.abs(x), i) 18 | return np.sum(x_pow, axis=1) 19 | 20 | 21 | def zakharov(x: np.ndarray) -> np.ndarray: 22 | # NOTE: the i+1 term is not in the CEC function definitions, but is in the 23 | # code and in any definition you find online 24 | i = np.expand_dims(np.arange(x.shape[1]) + 1, 0) 25 | sm = np.sum(i * x, axis=1) 26 | sms = np.sum(x * x, axis=1) 27 | sm = 0.5 * sm 28 | sm = sm * sm 29 | return sms + sm + (sm * sm) 30 | 31 | 32 | def rosenbrock(x: np.ndarray) -> np.ndarray: 33 | x = 0.02048 * x + 1.0 34 | t1 = x[:, :-1] * x[:, :-1] - x[:, 1:] 35 | t1 = 100 * t1 * t1 36 | t2 = x[:, :-1] - 1 37 | t2 = t2 * t2 38 | return np.sum(t1 + t2, axis=1) 39 | 40 | 41 | def rastrigin(x: np.ndarray) -> np.ndarray: 42 | # NOTE: the 0.0512 shrinking is omitted in the problem definitions but is 43 | # present in the provided code 44 | x = 0.0512 * x 45 | cs = np.cos(2 * np.pi * x) 46 | xs = x*x - 10*cs + 10 47 | return np.sum(xs, axis=1) 48 | 49 | 50 | def expanded_schaffers_f6(x: np.ndarray) -> np.ndarray: 51 | t = x[:, :-1]*x[:, :-1] + x[:, 1:]*x[:, 1:] 52 | t1 = np.sin(np.sqrt(t)) 53 | t1 = t1*t1 - 0.5 54 | t2 = 1 + 0.001*t 55 | t2 = t2*t2 56 | return np.sum(0.5 + t1/t2, axis=1) 57 | 58 | 59 | def lunacek_bi_rastrigin( 60 | x: np.ndarray, 61 | shift: Optional[np.ndarray] = None, 62 | rotation: Optional[np.ndarray] = None, 63 | ) -> np.ndarray: 64 | # a special case; we need the shift vector and rotation matrix 65 | nx = x.shape[1] 66 | if shift is None: 67 | shift = np.zeros((1, nx)) 68 | else: 69 | shift = np.expand_dims(shift, 0) 70 | 71 | # calculate the coefficients 72 | mu0 = 2.5 73 | s = 1 - 1 / (2 * ((nx+20)**0.5) - 8.2) 74 | mu1 = -((mu0*mu0-1)/s)**0.5 75 | 76 | # shift and scale 77 | y = 0.1 * (x - shift) 78 | 79 | tmpx = 2 * y 80 | tmpx[:, shift[0] < 0] *= -1 81 | 82 | z = tmpx.copy() 83 | tmpx = tmpx + mu0 84 | 85 | t1 = tmpx - mu0 86 | t1 = t1 * t1 87 | t1 = np.sum(t1, axis=1) 88 | t2 = tmpx - mu1 89 | t2 = s * t2 * t2 90 | t2 = np.sum(t2, axis=1) + nx 91 | 92 | if rotation is None: 93 | y = z 94 | else: 95 | y = np.matmul( 96 | np.expand_dims(rotation, 0), 97 | np.expand_dims(z, -1), 98 | )[:, :, 0] 99 | 100 | y = np.cos(2.0*np.pi*y) 101 | t = np.sum(y, axis=1) 102 | 103 | r = t1 104 | r[t1 >= t2] = t2[t1 >= t2] 105 | return r + 10.0*(nx-t) 106 | 107 | 108 | def non_cont_rastrigin( 109 | x: np.ndarray, 110 | shift: Optional[np.ndarray] = None, 111 | rotation: Optional[np.ndarray] = None, 112 | ) -> np.ndarray: 113 | # a special case; we need the shift vector and rotation matrix 114 | nx = x.shape[1] 115 | if shift is None: 116 | shift = np.zeros((1, nx)) 117 | else: 118 | shift = np.expand_dims(shift, 0) 119 | shifted = x - shift 120 | 121 | sm = 0.0 122 | x = x.copy() 123 | mask = np.abs(shifted) > 0.5 124 | x[mask] = (shift + np.floor(2*shifted+0.5) * 0.5)[mask] 125 | 126 | # for i in range(0, nx): 127 | # if abs(x[i]-shift[i]) > 0.5: 128 | # x[i] = shift[i] + np.floor(2*(x[i]-shift[i])+0.5)/2 129 | 130 | z = 0.0512 * shifted 131 | if rotation is not None: 132 | z = np.matmul( 133 | np.expand_dims(rotation, 0), 134 | np.expand_dims(z, -1), 135 | )[:, :, 0] 136 | 137 | sm = z*z - 10*np.cos(2*np.pi*z) + 10 138 | sm = np.sum(sm, axis=1) 139 | # for i in range(0, nx): 140 | # sm += (z[i]*z[i] - 10.0*np.cos(2.0*np.pi*z[i]) + 10.0) 141 | return sm 142 | 143 | 144 | def levy(x: np.ndarray) -> np.ndarray: 145 | # NOTE: the function definitions state to scale by 5.12/100, but the code 146 | # doesn't do this, and the example graph in the definitions correspond to 147 | # the version without scaling 148 | # x = 0.0512 * x 149 | w = 1.0 + 0.25*(x - 1.0) 150 | 151 | term1 = (np.sin(np.pi*w[:, 0]))**2 152 | term3 = ((w[:, -1] - 1)**2) * (1 + ((np.sin(2*np.pi*w[:, -1]))**2)) 153 | 154 | sm = 0.0 155 | 156 | wi = w[:, :-1] 157 | newv = ((wi - 1)**2) * (1 + 10*((np.sin(np.pi*wi+1))**2)) 158 | sm = np.sum(newv, axis=1) 159 | 160 | return term1 + sm + term3 161 | 162 | 163 | def modified_schwefel(x: np.ndarray) -> np.ndarray: 164 | nx = x.shape[1] 165 | x = 10.0 * x # scale to search range 166 | 167 | z = x + 420.9687462275036 168 | mask1 = z < -500 169 | mask2 = z > 500 170 | sm = z * np.sin(np.sqrt(np.abs(z))) 171 | 172 | zm = np.mod(np.abs(z), 500) 173 | zm[mask1] = (zm[mask1] - 500) 174 | zm[mask2] = (500 - zm[mask2]) 175 | t = z + 500 176 | t[mask2] = z[mask2] - 500 177 | t = t*t 178 | 179 | mask1_or_2 = np.logical_or(mask1, mask2) 180 | sm[mask1_or_2] = (zm * np.sin(np.sqrt(np.abs(zm))) - t / (10_000*nx))[mask1_or_2] 181 | return 418.9829*nx - np.sum(sm, axis=1) 182 | 183 | 184 | def high_conditioned_elliptic(x: np.ndarray) -> np.ndarray: 185 | factor = 6 / (x.shape[1] - 1) 186 | i = np.expand_dims(np.arange(x.shape[1]), 0) 187 | sm = x*x * 10**(i * factor) 188 | return np.sum(sm, axis=1) 189 | 190 | 191 | def discus(x: np.ndarray) -> np.ndarray: 192 | sm0 = 1e+6*x[:, 0]*x[:, 0] 193 | sm = np.sum(x[:, 1:]*x[:, 1:], axis=1) 194 | return sm0 + sm 195 | 196 | 197 | def ackley(x: np.ndarray) -> np.ndarray: 198 | smsq = np.sum(x*x, axis=1) 199 | smcs = np.sum(np.cos((2*np.pi)*x), axis=1) 200 | inx = 1/x.shape[1] 201 | return -20*np.exp(-0.2*np.sqrt(inx*smsq)) - np.exp(inx*smcs) + 20 + np.e 202 | 203 | 204 | def weierstrass(x: np.ndarray) -> np.ndarray: 205 | x = 0.005 * x 206 | k = np.arange(start=0, stop=21, step=1) 207 | k = np.expand_dims(np.expand_dims(k, 0), 0) 208 | ak = 0.5**k 209 | bk = np.pi * (3**k) 210 | 211 | kcs = ak * np.cos(2*(np.expand_dims(x, -1) + 0.5)*bk) # shape (M, nx, 21) 212 | ksm = np.sum(kcs, axis=2) 213 | sm = np.sum(ksm, axis=1) 214 | 215 | kcs = ak * np.cos(bk) 216 | ksm = np.sum(kcs) 217 | return sm - x.shape[1]*ksm 218 | 219 | 220 | def griewank(x: np.ndarray) -> np.ndarray: 221 | nx = x.shape[1] 222 | x = 6.0 * x 223 | factor = 1/4000 224 | d = np.expand_dims(np.arange(start=1, stop=nx + 1), 0) 225 | cs = np.cos(x / d) 226 | sm = np.sum(factor*x*x, axis=1) 227 | pd = np.prod(np.cos(x / d), axis=1) 228 | return sm - pd + 1 229 | 230 | 231 | def katsuura(x: np.ndarray) -> np.ndarray: 232 | x = 0.05 * x 233 | nx = x.shape[1] 234 | pw = 10/(nx**1.2) 235 | prd = 1.0 236 | tj = 2**np.arange(start=1, stop=33, step=1, dtype=np.int64) 237 | tj = np.expand_dims(np.expand_dims(tj, 0), 0) 238 | tjx = tj*np.expand_dims(x, -1) # shape (M, nx, 32) 239 | t = np.abs(tjx - np.round(tjx)) / tj 240 | tsm = np.sum(t, axis=2) 241 | 242 | i = np.arange(nx) + 1 243 | prd = np.prod((1 + i*tsm)**pw, axis=1) 244 | df = 10/(nx*nx) 245 | return df*prd - df 246 | 247 | 248 | def happy_cat(x: np.ndarray) -> np.ndarray: 249 | x = (0.05 * x) - 1 250 | nx = x.shape[1] 251 | sm = np.sum(x, axis=1) 252 | smsq = np.sum(x*x, axis=1) 253 | return (np.abs(smsq - nx))**0.25 + (0.5*smsq + sm)/nx + 0.5 254 | 255 | 256 | def h_g_bat(x: np.ndarray) -> np.ndarray: 257 | x = (0.05 * x) - 1 258 | nx = x.shape[1] 259 | sm = np.sum(x, axis=1) 260 | smsq = np.sum(x*x, axis=1) 261 | return (np.abs(smsq*smsq - sm*sm))**0.5 + (0.5*smsq + sm)/nx + 0.5 262 | 263 | 264 | def expanded_griewanks_plus_rosenbrock(x: np.ndarray) -> np.ndarray: 265 | x = (0.05 * x) + 1 266 | 267 | tmp1 = x[:, :-1]*x[:, :-1] - x[:, 1:] 268 | tmp2 = x[:, :-1] - 1.0 269 | temp = 100*tmp1*tmp1 + tmp2*tmp2 270 | sm = (temp*temp)/4000 - np.cos(temp) + 1 271 | 272 | tmp1 = x[:, -1:]*x[:, -1:] - x[:, 0:1] 273 | tmp2 = x[:, -1:] - 1 274 | temp = 100*tmp1*tmp1 + tmp2*tmp2 275 | sm = sm + (temp*temp)/4000 - np.cos(temp) + 1 276 | 277 | return np.sum(sm, axis=1) 278 | 279 | 280 | def schaffers_f7(x: np.ndarray) -> np.ndarray: 281 | nx = x.shape[1] 282 | # NOTE: the function definitions state to scale by 0.5/100, but the code 283 | # doesn't do this, and the example graph in the definitions correspond to 284 | # the version without scaling 285 | # x = 0.005 * x 286 | sm = 0.0 287 | si = np.sqrt(x[:, :-1]*x[:, :-1] + x[:, 1:]*x[:, 1:]) 288 | tmp = np.sin(50*(np.power(si, 0.2))) 289 | # NOTE: the original code has this error here (tmp shouldn't be squared) 290 | # that I'm keeping for consistency. 291 | sm = np.sqrt(si) * (tmp*tmp + 1) 292 | sm = np.sum(sm, axis=1) 293 | sm = (sm*sm) / (nx*nx - 2*nx + 1) 294 | return sm 295 | 296 | 297 | all_functions = [ 298 | bent_cigar, 299 | sum_diff_pow, 300 | zakharov, 301 | rosenbrock, 302 | rastrigin, 303 | expanded_schaffers_f6, 304 | lunacek_bi_rastrigin, 305 | non_cont_rastrigin, 306 | levy, 307 | modified_schwefel, 308 | high_conditioned_elliptic, 309 | discus, 310 | ackley, 311 | weierstrass, 312 | griewank, 313 | katsuura, 314 | happy_cat, 315 | h_g_bat, 316 | expanded_griewanks_plus_rosenbrock, 317 | schaffers_f7 318 | ] 319 | -------------------------------------------------------------------------------- /cec2017/composition.py: -------------------------------------------------------------------------------- 1 | # cec2017.composition 2 | # Author: Duncan Tilley 3 | # Composition function definitions, f21 to f30 4 | 5 | from . import basic 6 | from . import transforms 7 | from . import hybrid 8 | 9 | import numpy as np 10 | 11 | 12 | def _calc_w(x, sigma): 13 | nx = x.shape[1] 14 | w = np.sum(x*x, axis=1) 15 | nzmask = w != 0 16 | w[nzmask] = ((1.0/w)**0.5)[nzmask] * np.exp(-w / (2.0*nx*sigma*sigma))[nzmask] 17 | w[~nzmask] = float('inf') 18 | return w 19 | 20 | 21 | def _composition(x, rotations, shifts, funcs, sigmas, lambdas, biases): 22 | nv = x.shape[0] 23 | nx = x.shape[1] 24 | 25 | N = len(funcs) 26 | vals = np.zeros((nv, N)) 27 | w = np.zeros((nv, N)) 28 | for i in range(0, N): 29 | x_shifted = x - np.expand_dims(shifts[i][:nx], 0) 30 | x_t = transforms.shift_rotate(x, shifts[i][:nx], rotations[i]) 31 | vals[:, i] = funcs[i](x_t) 32 | w[:, i] = _calc_w(x_shifted, sigmas[i]) 33 | w_sm = np.sum(w, axis=1) 34 | 35 | nz_mask = w_sm != 0.0 36 | w[nz_mask, :] /= w_sm[nz_mask, None] 37 | w[~nz_mask, :] = 1/N 38 | 39 | return np.sum(w * (lambdas*vals + biases), axis=1) 40 | 41 | 42 | def _compose_hybrids(x, rotations, shifts, shuffles, funcs, sigmas, offsets, biases): 43 | nv = x.shape[0] 44 | nx = x.shape[1] 45 | 46 | N = len(funcs) 47 | vals = np.zeros((nv, N)) 48 | w = np.zeros((nv, N)) 49 | for i in range(0, N): 50 | x_shifted = x - np.expand_dims(shifts[i][:nx], 0) 51 | vals[:, i] = funcs[i](x, rotation=rotations[i], shift=shifts[i][:nx], shuffle=shuffles[i]) - offsets[i] 52 | w[:, i] = _calc_w(x_shifted, sigmas[i]) 53 | w_sm = np.sum(w, axis=1) 54 | 55 | nz_mask = w_sm != 0.0 56 | w[nz_mask, :] /= w_sm[nz_mask, None] 57 | w[~nz_mask, :] = 1/N 58 | 59 | return np.sum(w * (vals + biases), axis=1) 60 | 61 | 62 | def f21(x, rotations=None, shifts=None): 63 | """ 64 | Composition Function 1 (N=3) 65 | 66 | Args: 67 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 68 | rotations (matrix): Optional rotation matrices (NxDxD). If None 69 | (default), the official matrices from the benchmark suite will be 70 | used. 71 | shifts (array): Optional shift vectors (NxD). If None (default), the 72 | official vectors from the benchmark suite will be used. 73 | """ 74 | x = np.array(x) 75 | nx = x.shape[1] 76 | 77 | if rotations is None: 78 | rotations = transforms.rotations_cf[nx][0] 79 | if shifts is None: 80 | shifts = transforms.shifts_cf[0] 81 | 82 | funcs = [basic.rosenbrock, basic.high_conditioned_elliptic, basic.rastrigin] 83 | sigmas = np.array([10.0, 20.0, 30.0]) 84 | lambdas = np.array([1.0, 1.0e-6, 1.0]) 85 | biases = np.array([0.0, 100.0, 200.0]) 86 | return _composition(x, rotations, shifts, funcs, sigmas, lambdas, biases) + 2100 87 | 88 | 89 | def f22(x, rotations=None, shifts=None): 90 | """ 91 | Composition Function 2 (N=3) 92 | 93 | Args: 94 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 95 | rotations (matrix): Optional rotation matrices (NxDxD). If None 96 | (default), the official matrices from the benchmark suite will be 97 | used. 98 | shifts (array): Optional shift vectors (NxD). If None (default), the 99 | official vectors from the benchmark suite will be used. 100 | """ 101 | x = np.array(x) 102 | nx = x.shape[1] 103 | 104 | if rotations is None: 105 | rotations = transforms.rotations_cf[nx][1] 106 | if shifts is None: 107 | shifts = transforms.shifts_cf[1] 108 | 109 | funcs = [basic.rastrigin, basic.griewank, basic.modified_schwefel] 110 | sigmas = np.array([10.0, 20.0, 30.0]) 111 | lambdas = np.array([1.0, 10.0, 1.0]) 112 | biases = np.array([0.0, 100.0, 200.0]) 113 | 114 | return _composition(x, rotations, shifts, funcs, sigmas, lambdas, biases) + 2200 115 | 116 | 117 | def f23(x, rotations=None, shifts=None): 118 | """ 119 | Composition Function 3 (N=4) 120 | 121 | Args: 122 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 123 | rotations (matrix): Optional rotation matrices (NxDxD). If None 124 | (default), the official matrices from the benchmark suite will be 125 | used. 126 | shifts (array): Optional shift vectors (NxD). If None (default), the 127 | official vectors from the benchmark suite will be used. 128 | """ 129 | x = np.array(x) 130 | nx = x.shape[1] 131 | 132 | if rotations is None: 133 | rotations = transforms.rotations_cf[nx][2] 134 | if shifts is None: 135 | shifts = transforms.shifts_cf[2] 136 | 137 | funcs = [basic.rosenbrock, basic.ackley, basic.modified_schwefel, basic.rastrigin] 138 | sigmas = np.array([10.0, 20.0, 30.0, 40.0]) 139 | lambdas = np.array([1.0, 10.0, 1.0, 1.0]) 140 | biases = np.array([0.0, 100.0, 200.0, 300.0]) 141 | return _composition(x, rotations, shifts, funcs, sigmas, lambdas, biases) + 2300 142 | 143 | 144 | def f24(x, rotations=None, shifts=None): 145 | """ 146 | Composition Function 4 (N=4) 147 | 148 | Args: 149 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 150 | rotations (matrix): Optional rotation matrices (NxDxD). If None 151 | (default), the official matrices from the benchmark suite will be 152 | used. 153 | shifts (array): Optional shift vectors (NxD). If None (default), the 154 | official vectors from the benchmark suite will be used. 155 | """ 156 | x = np.array(x) 157 | nx = x.shape[1] 158 | 159 | if rotations is None: 160 | rotations = transforms.rotations_cf[nx][3] 161 | if shifts is None: 162 | shifts = transforms.shifts_cf[3] 163 | 164 | funcs = [basic.ackley, basic.high_conditioned_elliptic, basic.griewank, basic.rastrigin] 165 | sigmas = np.array([10.0, 20.0, 30.0, 40.0]) 166 | lambdas = np.array([1.0, 1.0e-6, 10.0, 1.0]) 167 | biases = np.array([0.0, 100.0, 200.0, 300.0]) 168 | return _composition(x, rotations, shifts, funcs, sigmas, lambdas, biases) + 2400 169 | 170 | 171 | def f25(x, rotations=None, shifts=None): 172 | """ 173 | Composition Function 5 (N=5) 174 | 175 | Args: 176 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 177 | rotations (matrix): Optional rotation matrices (NxDxD). If None 178 | (default), the official matrices from the benchmark suite will be 179 | used. 180 | shifts (array): Optional shift vectors (NxD). If None (default), the 181 | official vectors from the benchmark suite will be used. 182 | """ 183 | x = np.array(x) 184 | nx = x.shape[1] 185 | 186 | if rotations is None: 187 | rotations = transforms.rotations_cf[nx][4] 188 | if shifts is None: 189 | shifts = transforms.shifts_cf[4] 190 | 191 | funcs = [basic.rastrigin, basic.happy_cat, basic.ackley, basic.discus, basic.rosenbrock] 192 | sigmas = np.array([10.0, 20.0, 30.0, 40.0, 50.0]) 193 | lambdas = np.array([10.0, 1.0, 10.0, 1.0e-6, 1.0]) 194 | biases = np.array([0.0, 100.0, 200.0, 300.0, 400.0]) 195 | return _composition(x, rotations, shifts, funcs, sigmas, lambdas, biases) + 2500 196 | 197 | 198 | def f26(x, rotations=None, shifts=None): 199 | """ 200 | Composition Function 6 (N=5) 201 | 202 | Args: 203 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 204 | rotations (matrix): Optional rotation matrices (NxDxD). If None 205 | (default), the official matrices from the benchmark suite will be 206 | used. 207 | shifts (array): Optional shift vectors (NxD). If None (default), the 208 | official vectors from the benchmark suite will be used. 209 | """ 210 | x = np.array(x) 211 | nx = x.shape[1] 212 | 213 | if rotations is None: 214 | rotations = transforms.rotations_cf[nx][5] 215 | if shifts is None: 216 | shifts = transforms.shifts_cf[5] 217 | 218 | funcs = [basic.expanded_schaffers_f6, basic.modified_schwefel, basic.griewank, basic.rosenbrock, basic.rastrigin] 219 | sigmas = np.array([10.0, 20.0, 20.0, 30.0, 40.0]) 220 | # NOTE: the lambdas specified in the problem definitions (below) differ from 221 | # what is used in the code 222 | #lambdas = np.array([1.0e-26, 10.0, 1.0e-6, 10.0, 5.0e-4]) 223 | lambdas = np.array([5.0e-4, 1.0, 10.0, 1.0, 10.0]) 224 | biases = np.array([0.0, 100.0, 200.0, 300.0, 400.0]) 225 | return _composition(x, rotations, shifts, funcs, sigmas, lambdas, biases) + 2600 226 | 227 | 228 | def f27(x, rotations=None, shifts=None): 229 | """ 230 | Composition Function 7 (N=6) 231 | 232 | Args: 233 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 234 | rotations (matrix): Optional rotation matrices (NxDxD). If None 235 | (default), the official matrices from the benchmark suite will be 236 | used. 237 | shifts (array): Optional shift vectors (NxD). If None (default), the 238 | official vectors from the benchmark suite will be used. 239 | """ 240 | x = np.array(x) 241 | nx = x.shape[1] 242 | 243 | if rotations is None: 244 | rotations = transforms.rotations_cf[nx][6] 245 | if shifts is None: 246 | shifts = transforms.shifts_cf[6] 247 | 248 | funcs = [ 249 | basic.h_g_bat, 250 | basic.rastrigin, 251 | basic.modified_schwefel, 252 | basic.bent_cigar, 253 | basic.high_conditioned_elliptic, 254 | basic.expanded_schaffers_f6, 255 | ] 256 | sigmas = np.array([10.0, 20.0, 30.0, 40.0, 50.0, 60.0]) 257 | lambdas = np.array([10.0, 10.0, 2.5, 1.0e-26, 1.0e-6, 5.0e-4]) 258 | biases = np.array([0.0, 100.0, 200.0, 300.0, 400.0, 500.0]) 259 | return _composition(x, rotations, shifts, funcs, sigmas, lambdas, biases) + 2700 260 | 261 | 262 | def f28(x, rotations=None, shifts=None): 263 | """ 264 | Composition Function 8 (N=6) 265 | 266 | Args: 267 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 268 | rotations (matrix): Optional rotation matrices (NxDxD). If None 269 | (default), the official matrices from the benchmark suite will be 270 | used. 271 | shifts (array): Optional shift vectors (NxD). If None (default), the 272 | official vectors from the benchmark suite will be used. 273 | """ 274 | x = np.array(x) 275 | nx = x.shape[1] 276 | 277 | if rotations is None: 278 | rotations = transforms.rotations_cf[nx][7] 279 | if shifts is None: 280 | shifts = transforms.shifts_cf[7] 281 | 282 | funcs = [ 283 | basic.ackley, 284 | basic.griewank, 285 | basic.discus, 286 | basic.rosenbrock, 287 | basic.happy_cat, 288 | basic.expanded_schaffers_f6, 289 | ] 290 | sigmas = np.array([10.0, 20.0, 30.0, 40.0, 50.0, 60.0]) 291 | lambdas = np.array([10.0, 10.0, 1.0e-6, 1.0, 1.0, 5.0e-4]) 292 | biases = np.array([0.0, 100.0, 200.0, 300.0, 400.0, 500.0]) 293 | return _composition(x, rotations, shifts, funcs, sigmas, lambdas, biases) + 2800 294 | 295 | 296 | def f29(x, rotations=None, shifts=None, shuffles=None): 297 | """ 298 | Composition Function 9 (N=3) 299 | 300 | Args: 301 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 302 | rotations (matrix): Optional rotation matrices (NxDxD). If None 303 | (default), the official matrices from the benchmark suite will be 304 | used. 305 | shifts (array): Optional shift vectors (NxD). If None (default), the 306 | official vectors from the benchmark suite will be used. 307 | shuffles (array): Optional shuffle vectors (NxD). If None (default), the 308 | official permutation vectors from the benchmark suite will be used. 309 | """ 310 | x = np.array(x) 311 | nx = x.shape[1] 312 | 313 | if rotations is None: 314 | rotations = transforms.rotations_cf[nx][8] 315 | if shifts is None: 316 | shifts = transforms.shifts_cf[8] 317 | if shuffles is None: 318 | shuffles = transforms.shuffles_cf[nx][0] 319 | 320 | funcs = [hybrid.f15, hybrid.f16, hybrid.f17] 321 | sigmas = np.array([10.0, 30.0, 50.0]) 322 | biases = np.array([0.0, 100.0, 200.0]) 323 | offsets = np.array([1500, 1600, 1700]) # subtract F* added at the end of the functions 324 | 325 | return _compose_hybrids(x, rotations, shifts, shuffles, funcs, sigmas, offsets, biases) + 2900 326 | 327 | 328 | def f30(x, rotations=None, shifts=None, shuffles=None): 329 | """ 330 | Composition Function 10 (N=3) 331 | 332 | Args: 333 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 334 | rotations (matrix): Optional rotation matrices (NxDxD). If None 335 | (default), the official matrices from the benchmark suite will be 336 | used. 337 | shifts (array): Optional shift vectors (NxD). If None (default), the 338 | official vectors from the benchmark suite will be used. 339 | shuffles (array): Optional shuffle vectors (NxD). If None (default), the 340 | official permutation vectors from the benchmark suite will be used. 341 | """ 342 | x = np.array(x) 343 | nx = x.shape[1] 344 | 345 | if rotations is None: 346 | rotations = transforms.rotations_cf[nx][9] 347 | if shifts is None: 348 | shifts = transforms.shifts_cf[9] 349 | if shuffles is None: 350 | shuffles = transforms.shuffles_cf[nx][1] 351 | 352 | funcs = [hybrid.f15, hybrid.f18, hybrid.f19] 353 | sigmas = np.array([10.0, 30.0, 50.0]) 354 | biases = np.array([0.0, 100.0, 200.0]) 355 | offsets = np.array([1500, 1800, 1900]) # subtract F* added at the end of the functions 356 | return _compose_hybrids(x, rotations, shifts, shuffles, funcs, sigmas, offsets, biases) + 3000 357 | 358 | 359 | all_functions = [ 360 | f21, 361 | f22, 362 | f23, 363 | f24, 364 | f25, 365 | f26, 366 | f27, 367 | f28, 368 | f29, 369 | f30, 370 | ] 371 | -------------------------------------------------------------------------------- /cec2017/data.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tilleyd/cec2017-py/424a9fa2757914c3e4cfdd8f59a268b1aeb3197f/cec2017/data.pkl -------------------------------------------------------------------------------- /cec2017/functions.py: -------------------------------------------------------------------------------- 1 | # cec2017.functions 2 | # Author: Duncan Tilley 3 | # Combines simple, hybrid and composition functions (f1 - f30) into a single 4 | # module 5 | 6 | from .simple import * 7 | from .hybrid import * 8 | from .composition import * 9 | 10 | all_functions = [ 11 | f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, 12 | f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, 13 | f21, f22, f23, f24, f25, f26, f27, f28, f29, f30 14 | ] 15 | -------------------------------------------------------------------------------- /cec2017/hybrid.py: -------------------------------------------------------------------------------- 1 | # cec2017.hybrid 2 | # Author: Duncan Tilley 3 | # Hybrid function definitions, f11 to f20 4 | 5 | from . import basic 6 | from . import transforms 7 | 8 | import numpy as np 9 | 10 | def _shuffle_and_partition(x, shuffle, partitions): 11 | """ 12 | First applies the given permutation, then splits x into partitions given 13 | the percentages. 14 | 15 | Args: 16 | x (array): Input vector. 17 | shuffle (array): Shuffle vector. 18 | partitions (list): List of percentages. Assumed to add up to 1.0. 19 | 20 | Returns: 21 | (list of arrays): The partitions of x after shuffling. 22 | """ 23 | nx = len(x) 24 | # shuffle 25 | xs = np.zeros(x.shape) 26 | for i in range(0, nx): 27 | xs[i] = x[shuffle[i]] 28 | # and partition 29 | parts = [] 30 | start, end = 0, 0 31 | for p in partitions[:-1]: 32 | end = start + int(np.ceil(p * nx)) 33 | parts.append(xs[start:end]) 34 | start = end 35 | parts.append(xs[end:]) 36 | return parts 37 | 38 | 39 | def f11(x, rotation=None, shift=None, shuffle=None): 40 | """ 41 | Hybrid Function 1 (N=3) 42 | 43 | Args: 44 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 45 | rotation (matrix): Optional rotation matrix. If None (default), the 46 | official matrix from the benchmark suite will be used. 47 | shift (array): Optional shift vector. If None (default), the official 48 | vector from the benchmark suite will be used. 49 | shuffle (array): Optionbal shuffle vector. If None (default), the 50 | official permutation vector from the benchmark suite will be used. 51 | """ 52 | x = np.array(x) 53 | nx = x.shape[1] 54 | 55 | if rotation is None: 56 | rotation = transforms.rotations[nx][10] 57 | if shift is None: 58 | shift = transforms.shifts[10][:nx] 59 | if shuffle is None: 60 | shuffle = transforms.shuffles[nx][0] 61 | 62 | x_transformed = transforms.shift_rotate(x, shift, rotation) 63 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.2, 0.4, 0.4]) 64 | 65 | y = basic.zakharov(x_parts[0]) 66 | y += basic.rosenbrock(x_parts[1]) 67 | y += basic.rastrigin(x_parts[2]) 68 | return y + 1100.0 69 | 70 | 71 | def f12(x, rotation=None, shift=None, shuffle=None): 72 | """ 73 | Hybrid Function 2 (N=3) 74 | 75 | Args: 76 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 77 | rotation (matrix): Optional rotation matrix. If None (default), the 78 | official matrix from the benchmark suite will be used. 79 | shift (array): Optional shift vector. If None (default), the official 80 | vector from the benchmark suite will be used. 81 | shuffle (array): Optionbal shuffle vector. If None (default), the 82 | official permutation vector from the benchmark suite will be used. 83 | """ 84 | x = np.array(x) 85 | nx = x.shape[1] 86 | 87 | if rotation is None: 88 | rotation = transforms.rotations[nx][11] 89 | if shift is None: 90 | shift = transforms.shifts[11][:nx] 91 | if shuffle is None: 92 | shuffle = transforms.shuffles[nx][1] 93 | 94 | x_transformed = transforms.shift_rotate(x, shift, rotation) 95 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.3, 0.3, 0.4]) 96 | 97 | y = basic.high_conditioned_elliptic(x_parts[0]) 98 | y += basic.modified_schwefel(x_parts[1]) 99 | y += basic.bent_cigar(x_parts[2]) 100 | return y + 1200.0 101 | 102 | 103 | def f13(x, rotation=None, shift=None, shuffle=None): 104 | """ 105 | Hybrid Function 3 (N=3) 106 | 107 | Args: 108 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 109 | rotation (matrix): Optional rotation matrix. If None (default), the 110 | official matrix from the benchmark suite will be used. 111 | shift (array): Optional shift vector. If None (default), the official 112 | vector from the benchmark suite will be used. 113 | shuffle (array): Optionbal shuffle vector. If None (default), the 114 | official permutation vector from the benchmark suite will be used. 115 | """ 116 | x = np.array(x) 117 | nx = x.shape[1] 118 | 119 | if rotation is None: 120 | rotation = transforms.rotations[nx][12] 121 | if shift is None: 122 | shift = transforms.shifts[12][:nx] 123 | if shuffle is None: 124 | shuffle = transforms.shuffles[nx][2] 125 | 126 | x_transformed = transforms.shift_rotate(x, shift, rotation) 127 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.3, 0.3, 0.4]) 128 | 129 | y = basic.bent_cigar(x_parts[0]) 130 | y += basic.rosenbrock(x_parts[1]) 131 | y += basic.lunacek_bi_rastrigin(x_parts[2]) 132 | return y + 1300.0 133 | 134 | 135 | def f14(x, rotation=None, shift=None, shuffle=None): 136 | """ 137 | Hybrid Function 4 (N=4) 138 | 139 | Args: 140 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 141 | rotation (matrix): Optional rotation matrix. If None (default), the 142 | official matrix from the benchmark suite will be used. 143 | shift (array): Optional shift vector. If None (default), the official 144 | vector from the benchmark suite will be used. 145 | shuffle (array): Optionbal shuffle vector. If None (default), the 146 | official permutation vector from the benchmark suite will be used. 147 | """ 148 | x = np.array(x) 149 | nx = x.shape[1] 150 | 151 | if rotation is None: 152 | rotation = transforms.rotations[nx][13] 153 | if shift is None: 154 | shift = transforms.shifts[13][:nx] 155 | if shuffle is None: 156 | shuffle = transforms.shuffles[nx][3] 157 | 158 | x_transformed = transforms.shift_rotate(x, shift, rotation) 159 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.2, 0.2, 0.2, 0.4]) 160 | 161 | y = basic.high_conditioned_elliptic(x_parts[0]) 162 | y += basic.ackley(x_parts[1]) 163 | y += basic.schaffers_f7(x_parts[2]) 164 | y += basic.rastrigin(x_parts[3]) 165 | return y + 1400.0 166 | 167 | 168 | def f15(x, rotation=None, shift=None, shuffle=None): 169 | """ 170 | Hybrid Function 5 (N=4) 171 | 172 | Args: 173 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 174 | rotation (matrix): Optional rotation matrix. If None (default), the 175 | official matrix from the benchmark suite will be used. 176 | shift (array): Optional shift vector. If None (default), the official 177 | vector from the benchmark suite will be used. 178 | shuffle (array): Optionbal shuffle vector. If None (default), the 179 | official permutation vector from the benchmark suite will be used. 180 | """ 181 | x = np.array(x) 182 | nx = x.shape[1] 183 | 184 | if rotation is None: 185 | rotation = transforms.rotations[nx][14] 186 | if shift is None: 187 | shift = transforms.shifts[14][:nx] 188 | if shuffle is None: 189 | shuffle = transforms.shuffles[nx][4] 190 | 191 | x_transformed = transforms.shift_rotate(x, shift, rotation) 192 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.2, 0.2, 0.3, 0.3]) 193 | 194 | y = basic.bent_cigar(x_parts[0]) 195 | y += basic.h_g_bat(x_parts[1]) 196 | y += basic.rastrigin(x_parts[2]) 197 | y += basic.rosenbrock(x_parts[3]) 198 | return y + 1500.0 199 | 200 | 201 | def f16(x, rotation=None, shift=None, shuffle=None): 202 | """ 203 | Hybrid Function 6 (N=4) 204 | 205 | Args: 206 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 207 | rotation (matrix): Optional rotation matrix. If None (default), the 208 | official matrix from the benchmark suite will be used. 209 | shift (array): Optional shift vector. If None (default), the official 210 | vector from the benchmark suite will be used. 211 | shuffle (array): Optionbal shuffle vector. If None (default), the 212 | official permutation vector from the benchmark suite will be used. 213 | """ 214 | x = np.array(x) 215 | nx = x.shape[1] 216 | 217 | if rotation is None: 218 | rotation = transforms.rotations[nx][15] 219 | if shift is None: 220 | shift = transforms.shifts[15][:nx] 221 | if shuffle is None: 222 | shuffle = transforms.shuffles[nx][5] 223 | 224 | x_transformed = transforms.shift_rotate(x, shift, rotation) 225 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.2, 0.2, 0.3, 0.3]) 226 | 227 | y = basic.expanded_schaffers_f6(x_parts[0]) 228 | y += basic.h_g_bat(x_parts[1]) 229 | y += basic.rosenbrock(x_parts[2]) 230 | y += basic.modified_schwefel(x_parts[3]) 231 | return y + 1600.0 232 | 233 | 234 | def f17(x, rotation=None, shift=None, shuffle=None): 235 | """ 236 | Hybrid Function 7 (N=5) 237 | 238 | Args: 239 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 240 | rotation (matrix): Optional rotation matrix. If None (default), the 241 | official matrix from the benchmark suite will be used. 242 | shift (array): Optional shift vector. If None (default), the official 243 | vector from the benchmark suite will be used. 244 | shuffle (array): Optionbal shuffle vector. If None (default), the 245 | official permutation vector from the benchmark suite will be used. 246 | """ 247 | x = np.array(x) 248 | nx = x.shape[1] 249 | 250 | if rotation is None: 251 | rotation = transforms.rotations[nx][16] 252 | if shift is None: 253 | shift = transforms.shifts[16][:nx] 254 | if shuffle is None: 255 | shuffle = transforms.shuffles[nx][6] 256 | 257 | x_transformed = transforms.shift_rotate(x, shift, rotation) 258 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.1, 0.2, 0.2, 0.2, 0.3]) 259 | 260 | y = basic.katsuura(x_parts[0]) 261 | y += basic.ackley(x_parts[1]) 262 | y += basic.expanded_griewanks_plus_rosenbrock(x_parts[2]) 263 | y += basic.modified_schwefel(x_parts[3]) 264 | y += basic.rastrigin(x_parts[4]) 265 | return y + 1700.0 266 | 267 | 268 | def f18(x, rotation=None, shift=None, shuffle=None): 269 | """ 270 | Hybrid Function 8 (N=5) 271 | 272 | Args: 273 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 274 | rotation (matrix): Optional rotation matrix. If None (default), the 275 | official matrix from the benchmark suite will be used. 276 | shift (array): Optional shift vector. If None (default), the official 277 | vector from the benchmark suite will be used. 278 | shuffle (array): Optionbal shuffle vector. If None (default), the 279 | official permutation vector from the benchmark suite will be used. 280 | """ 281 | x = np.array(x) 282 | nx = x.shape[1] 283 | 284 | if rotation is None: 285 | rotation = transforms.rotations[nx][17] 286 | if shift is None: 287 | shift = transforms.shifts[17][:nx] 288 | if shuffle is None: 289 | shuffle = transforms.shuffles[nx][7] 290 | 291 | x_transformed = transforms.shift_rotate(x, shift, rotation) 292 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.2, 0.2, 0.2, 0.2, 0.2]) 293 | 294 | y = basic.high_conditioned_elliptic(x_parts[0]) 295 | y += basic.ackley(x_parts[1]) 296 | y += basic.rastrigin(x_parts[2]) 297 | y += basic.h_g_bat(x_parts[3]) 298 | y += basic.discus(x_parts[4]) 299 | return y + 1800.0 300 | 301 | 302 | def f19(x, rotation=None, shift=None, shuffle=None): 303 | """ 304 | Hybrid Function 9 (N=5) 305 | 306 | Args: 307 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 308 | rotation (matrix): Optional rotation matrix. If None (default), the 309 | official matrix from the benchmark suite will be used. 310 | shift (array): Optional shift vector. If None (default), the official 311 | vector from the benchmark suite will be used. 312 | shuffle (array): Optionbal shuffle vector. If None (default), the 313 | official permutation vector from the benchmark suite will be used. 314 | """ 315 | x = np.array(x) 316 | nx = x.shape[1] 317 | 318 | if rotation is None: 319 | rotation = transforms.rotations[nx][18] 320 | if shift is None: 321 | shift = transforms.shifts[18][:nx] 322 | if shuffle is None: 323 | shuffle = transforms.shuffles[nx][8] 324 | 325 | x_transformed = transforms.shift_rotate(x, shift, rotation) 326 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.2, 0.2, 0.2, 0.2, 0.2]) 327 | 328 | y = basic.bent_cigar(x_parts[0]) 329 | y += basic.rastrigin(x_parts[1]) 330 | y += basic.expanded_griewanks_plus_rosenbrock(x_parts[2]) 331 | y += basic.weierstrass(x_parts[3]) 332 | y += basic.expanded_schaffers_f6(x_parts[4]) 333 | return y + 1900.0 334 | 335 | 336 | def f20(x, rotation=None, shift=None, shuffle=None): 337 | """ 338 | Hybrid Function 10 (N=6) 339 | 340 | Args: 341 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 342 | rotation (matrix): Optional rotation matrix. If None (default), the 343 | official matrix from the benchmark suite will be used. 344 | shift (array): Optional shift vector. If None (default), the official 345 | vector from the benchmark suite will be used. 346 | shuffle (array): Optionbal shuffle vector. If None (default), the 347 | official permutation vector from the benchmark suite will be used. 348 | """ 349 | x = np.array(x) 350 | nx = x.shape[1] 351 | 352 | if rotation is None: 353 | rotation = transforms.rotations[nx][19] 354 | if shift is None: 355 | shift = transforms.shifts[19][:nx] 356 | if shuffle is None: 357 | shuffle = transforms.shuffles[nx][9] 358 | 359 | x_transformed = transforms.shift_rotate(x, shift, rotation) 360 | x_parts = transforms.shuffle_and_partition(x_transformed, shuffle, [0.1, 0.1, 0.2, 0.2, 0.2, 0.2]) 361 | 362 | y = basic.happy_cat(x_parts[0]) 363 | y += basic.katsuura(x_parts[1]) 364 | y += basic.ackley(x_parts[2]) 365 | y += basic.rastrigin(x_parts[3]) 366 | y += basic.modified_schwefel(x_parts[4]) 367 | y += basic.schaffers_f7(x_parts[5]) 368 | return y + 2000.0 369 | 370 | 371 | all_functions = [ 372 | f11, 373 | f12, 374 | f13, 375 | f14, 376 | f15, 377 | f16, 378 | f17, 379 | f18, 380 | f19, 381 | f20 382 | ] 383 | -------------------------------------------------------------------------------- /cec2017/simple.py: -------------------------------------------------------------------------------- 1 | # cec2017.simple 2 | # Author: Duncan Tilley 3 | # Simple function definitions, f1 to f10 4 | 5 | from . import basic 6 | from . import transforms 7 | 8 | import numpy as np 9 | 10 | 11 | def f1(x, rotation=None, shift=None): 12 | """ 13 | Shifted and Rotated Bent Cigar Function 14 | 15 | Args: 16 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 17 | rotation (matrix): Optional rotation matrix. If None (default), the 18 | official matrix from the benchmark suite will be used. 19 | shift (array): Optional shift vector. If None (default), the official 20 | vector from the benchmark suite will be used. 21 | """ 22 | x = np.array(x) 23 | nx = x.shape[1] 24 | 25 | if rotation is None: 26 | rotation = transforms.rotations[nx][0] 27 | if shift is None: 28 | shift = transforms.shifts[0][:nx] 29 | 30 | x_transformed = transforms.shift_rotate(x, shift, rotation) 31 | return basic.bent_cigar(x_transformed) + 100.0 32 | 33 | 34 | def f2(x, rotation=None, shift=None): 35 | """ 36 | (Deprecated) Shifted and Rotated Sum of Different Power Function 37 | 38 | Args: 39 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 40 | rotation (matrix): Optional rotation matrix. If None (default), the 41 | official matrix from the benchmark suite will be used. 42 | shift (array): Optional shift vector. If None (default), the official 43 | vector from the benchmark suite will be used. 44 | """ 45 | if 'warned' not in f2.__dict__: 46 | f2.warned = True 47 | print('WARNING: f2 has been deprecated from the CEC 2017 benchmark suite') 48 | 49 | x = np.array(x) 50 | nx = x.shape[1] 51 | 52 | if rotation is None: 53 | rotation = transforms.rotations[nx][1] 54 | if shift is None: 55 | shift = transforms.shifts[1][:nx] 56 | x_transformed = transforms.shift_rotate(x, shift, rotation) 57 | return basic.sum_diff_pow(x_transformed) + 200.0 58 | 59 | 60 | def f3(x, rotation=None, shift=None): 61 | """ 62 | Shifted and Rotated Zakharov Function 63 | 64 | Args: 65 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 66 | rotation (matrix): Optional rotation matrix. If None (default), the 67 | official matrix from the benchmark suite will be used. 68 | shift (array): Optional shift vector. If None (default), the official 69 | vector from the benchmark suite will be used. 70 | """ 71 | x = np.array(x) 72 | nx = x.shape[1] 73 | 74 | if rotation is None: 75 | rotation = transforms.rotations[nx][2] 76 | if shift is None: 77 | shift = transforms.shifts[2][:nx] 78 | x_transformed = transforms.shift_rotate(x, shift, rotation) 79 | return basic.zakharov(x_transformed) + 300.0 80 | 81 | 82 | def f4(x, rotation=None, shift=None): 83 | """ 84 | Shifted and Rotated Rosenbrock's Function 85 | 86 | Args: 87 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 88 | rotation (matrix): Optional rotation matrix. If None (default), the 89 | official matrix from the benchmark suite will be used. 90 | shift (array): Optional shift vector. If None (default), the official 91 | vector from the benchmark suite will be used. 92 | """ 93 | x = np.array(x) 94 | nx = x.shape[1] 95 | 96 | if rotation is None: 97 | rotation = transforms.rotations[nx][3] 98 | if shift is None: 99 | shift = transforms.shifts[3][:nx] 100 | x_transformed = transforms.shift_rotate(x, shift, rotation) 101 | return basic.rosenbrock(x_transformed) + 400.0 102 | 103 | 104 | def f5(x, rotation=None, shift=None): 105 | """ 106 | Shifted and Rotated Rastrigin's Function 107 | 108 | Args: 109 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 110 | rotation (matrix): Optional rotation matrix. If None (default), the 111 | official matrix from the benchmark suite will be used. 112 | shift (array): Optional shift vector. If None (default), the official 113 | vector from the benchmark suite will be used. 114 | """ 115 | x = np.array(x) 116 | nx = x.shape[1] 117 | 118 | if rotation is None: 119 | rotation = transforms.rotations[nx][4] 120 | if shift is None: 121 | shift = transforms.shifts[4][:nx] 122 | x_transformed = transforms.shift_rotate(x, shift, rotation) 123 | return basic.rastrigin(x_transformed) + 500.0 124 | 125 | 126 | def f6(x, rotation=None, shift=None): 127 | """ 128 | Shifted and Rotated Schaffer's F7 Function 129 | 130 | Args: 131 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 132 | rotation (matrix): Optional rotation matrix. If None (default), the 133 | official matrix from the benchmark suite will be used. 134 | shift (array): Optional shift vector. If None (default), the official 135 | vector from the benchmark suite will be used. 136 | """ 137 | x = np.array(x) 138 | nx = x.shape[1] 139 | 140 | if rotation is None: 141 | rotation = transforms.rotations[nx][5] 142 | if shift is None: 143 | shift = transforms.shifts[5][:nx] 144 | x_transformed = transforms.shift_rotate(x, shift, rotation) 145 | return basic.schaffers_f7(x_transformed) + 600.0 146 | 147 | 148 | def f7(x, rotation=None, shift=None): 149 | """ 150 | Shifted and Rotated Lunacek Bi-Rastrigin's Function 151 | 152 | Args: 153 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 154 | rotation (matrix): Optional rotation matrix. If None (default), the 155 | official matrix from the benchmark suite will be used. 156 | shift (array): Optional shift vector. If None (default), the official 157 | vector from the benchmark suite will be used. 158 | """ 159 | x = np.array(x) 160 | nx = x.shape[1] 161 | 162 | if rotation is None: 163 | rotation = transforms.rotations[nx][6] 164 | if shift is None: 165 | shift = transforms.shifts[6][:nx] 166 | # pass the shift and rotation directly to the function 167 | return basic.lunacek_bi_rastrigin(x, shift, rotation) + 700.0 168 | 169 | 170 | def f8(x, rotation=None, shift=None): 171 | """ 172 | Shifted and Rotated Non-Continuous Rastrigin’s Function 173 | 174 | Args: 175 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 176 | rotation (matrix): Optional rotation matrix. If None (default), the 177 | official matrix from the benchmark suite will be used. 178 | shift (array): Optional shift vector. If None (default), the official 179 | vector from the benchmark suite will be used. 180 | """ 181 | x = np.array(x) 182 | nx = x.shape[1] 183 | 184 | if rotation is None: 185 | rotation = transforms.rotations[nx][7] 186 | if shift is None: 187 | shift = transforms.shifts[7][:nx] 188 | # pass the shift and rotation directly to the function 189 | return basic.non_cont_rastrigin(x, shift, rotation) + 800.0 190 | 191 | 192 | def f9(x, rotation=None, shift=None): 193 | """ 194 | Shifted and Rotated Levy Function 195 | 196 | Args: 197 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 198 | rotation (matrix): Optional rotation matrix. If None (default), the 199 | official matrix from the benchmark suite will be used. 200 | shift (array): Optional shift vector. If None (default), the official 201 | vector from the benchmark suite will be used. 202 | """ 203 | x = np.array(x) 204 | nx = x.shape[1] 205 | 206 | if rotation is None: 207 | rotation = transforms.rotations[nx][8] 208 | if shift is None: 209 | shift = transforms.shifts[8][:nx] 210 | x_transformed = transforms.shift_rotate(x, shift, rotation) 211 | return basic.levy(x_transformed) + 900.0 212 | 213 | 214 | def f10(x, rotation=None, shift=None): 215 | """ 216 | Shifted and Rotated Schwefel’s Function 217 | 218 | Args: 219 | x (array): Input vector of dimension 2, 10, 20, 30, 50 or 100. 220 | rotation (matrix): Optional rotation matrix. If None (default), the 221 | official matrix from the benchmark suite will be used. 222 | shift (array): Optional shift vector. If None (default), the official 223 | vector from the benchmark suite will be used. 224 | """ 225 | x = np.array(x) 226 | nx = x.shape[1] 227 | 228 | if rotation is None: 229 | rotation = transforms.rotations[nx][9] 230 | if shift is None: 231 | shift = transforms.shifts[9][:nx] 232 | x_transformed = transforms.shift_rotate(x, shift, rotation) 233 | return basic.modified_schwefel(x_transformed) + 1000.0 234 | 235 | 236 | all_functions = [ 237 | f1, 238 | f2, 239 | f3, 240 | f4, 241 | f5, 242 | f6, 243 | f7, 244 | f8, 245 | f9, 246 | f10 247 | ] 248 | -------------------------------------------------------------------------------- /cec2017/transforms.py: -------------------------------------------------------------------------------- 1 | # cec2017.transforms 2 | # Author: Duncan Tilley 3 | # Contains rotation, shift and shuffle data loaded from data.pkl. 4 | # Note that these correspond to the many .txt files provided along with the 5 | # original implementation and should be used for final benchmark results. 6 | 7 | import numpy as np 8 | import pickle 9 | import os 10 | 11 | with open(os.path.join(os.path.dirname(__file__), 'data.pkl'), 'rb') as _pkl_file: 12 | _pkl = pickle.load(_pkl_file) 13 | 14 | # Each has shape (20, N, N) containing an N-dimensional rotation matrix 15 | # for functions f1 to f20 16 | rotations = { 17 | 2: _pkl['M_D2'], 18 | 10: _pkl['M_D10'], 19 | 20: _pkl['M_D20'], 20 | 30: _pkl['M_D30'], 21 | 50: _pkl['M_D50'], 22 | 100: _pkl['M_D100'] 23 | } 24 | 25 | # Each has shape (10, 10, N, N) containing 10 N-dimensional rotation matrices 26 | # for functions f21 to f30 27 | rotations_cf = { 28 | 2: _pkl['M_cf_d2'], 29 | 10: _pkl['M_cf_D10'], 30 | 20: _pkl['M_cf_D20'], 31 | 30: _pkl['M_cf_D30'], 32 | 50: _pkl['M_cf_D50'], 33 | 100: _pkl['M_cf_D100'] 34 | } 35 | 36 | # Shape (20, 100) 37 | # Contains 100-dimension shift vectors for functions f1 to f20 38 | shifts = _pkl['shift'] 39 | 40 | # Shape (10, 10, 100) 41 | # Contains 10 100-dimension shift vectors for functions f21 to f30 42 | shifts_cf = _pkl['shift_cf'] 43 | 44 | # Each has shape (10, N) containing N-dimensional permutations for functions f11 45 | # to f20 (note: the original were 1-indexed, these are 0-indexed) 46 | shuffles = { 47 | 10: _pkl['shuffle_D10'], 48 | 30: _pkl['shuffle_D30'], 49 | 50: _pkl['shuffle_D50'], 50 | 100: _pkl['shuffle_D100'] 51 | } 52 | 53 | # Each has shape (2, 10, N) containing 10 N-dimensional permutations for 54 | # functions f29 and f30 (note: the original were 1-indexed, these are 0-indexed) 55 | shuffles_cf = { 56 | 10: _pkl['shuffle_cf_D10'], 57 | 30: _pkl['shuffle_cf_D30'], 58 | 50: _pkl['shuffle_cf_D50'], 59 | 100: _pkl['shuffle_cf_D100'] 60 | } 61 | 62 | 63 | def shift_rotate(x: np.ndarray, shift: np.ndarray, rotation: np.ndarray) -> np.ndarray: 64 | """ 65 | Apply the shift and rotation to vector x along its second axis. 66 | 67 | Args: 68 | x (np.ndarray): 69 | (M, N) array of M N-dimensional vectors. 70 | shift (np.ndarray): 71 | Array of size N providing the shift. 72 | rotation (np.ndarray): 73 | (N, N) array providing the rotation matrix. 74 | 75 | Returns: 76 | (M, N) array of M shifted and rotated N-dimensional vectors. 77 | """ 78 | shifted = np.expand_dims(x - np.expand_dims(shift, 0), -1) 79 | x_transformed = np.matmul(np.expand_dims(rotation, 0), shifted) 80 | return x_transformed[:, :, 0] 81 | 82 | 83 | def shuffle_and_partition(x, shuffle, partitions): 84 | """ 85 | First applies the given permutation, then splits x into partitions given 86 | the percentages. 87 | 88 | Args: 89 | x (array): Input vector. 90 | shuffle (array): Shuffle vector. 91 | partitions (list): List of percentages. Assumed to add up to 1.0. 92 | 93 | Returns: 94 | (list of arrays): The partitions of x after shuffling. 95 | """ 96 | nx = x.shape[1] 97 | 98 | # shuffle 99 | xs = np.zeros_like(x) 100 | for i in range(0, nx): 101 | xs[:, i] = x[:, shuffle[i]] 102 | # and partition 103 | parts = [] 104 | start, end = 0, 0 105 | for p in partitions[:-1]: 106 | end = start + int(np.ceil(p * nx)) 107 | parts.append(xs[:, start:end]) 108 | start = end 109 | parts.append(xs[:, end:]) 110 | return parts 111 | -------------------------------------------------------------------------------- /cec2017/utils.py: -------------------------------------------------------------------------------- 1 | # cec2017.utils 2 | # Author: Duncan Tilley 3 | # Additional functions for graphing and benchmarking 4 | 5 | def surface_plot(function, domain=(-100,100), points=30, dimension=2, ax=None): 6 | """ 7 | Creates a surface plot of a function. 8 | 9 | Args: 10 | function (function): The objective function to be called at each point. 11 | domain (num, num): The inclusive (min, max) domain for each dimension. 12 | points (int): The number of points to collect on each dimension. A total 13 | of points^2 function evaluations will be performed. 14 | dimension (int): The dimension to pass to the function. If this is more 15 | than 2, the elements after the first 2 will simply be zero, 16 | providing a slice at x_3 = 0, ..., x_n = 0. 17 | ax (matplotlib axes): Optional axes to use (must have projection='3d'). 18 | Note, if specified plt.show() will not be called. 19 | """ 20 | from mpl_toolkits import mplot3d 21 | import matplotlib.pyplot as plt 22 | import numpy as np 23 | 24 | # create points^2 tuples of (x,y) and populate z 25 | xys = np.linspace(domain[0], domain[1], points) 26 | xys = np.transpose([np.tile(xys, len(xys)), np.repeat(xys, len(xys))]) 27 | # zs = np.zeros(points*points) 28 | 29 | if dimension > 2: 30 | # concatenate remaining zeros 31 | tail = np.zeros((xys.shape[0], dimension - 2)) 32 | x = np.concatenate([xys, tail], axis=1) 33 | zs = function(x) 34 | # for i in range(0, xys.shape[0]): 35 | # zs[i] = function(np.concatenate([xys[i], tail])) 36 | else: 37 | zs = function(xys) 38 | # for i in range(0, xys.shape[0]): 39 | # zs[i] = function(xys[i]) 40 | 41 | # create the plot 42 | ax_in = ax 43 | if ax is None: 44 | ax = plt.axes(projection='3d') 45 | 46 | X = xys[:,0].reshape((points, points)) 47 | Y = xys[:,1].reshape((points, points)) 48 | Z = zs.reshape((points, points)) 49 | ax.plot_surface(X, Y, Z, cmap='gist_ncar', edgecolor='none') 50 | ax.set_title(function.__name__) 51 | ax.set_xlabel('x') 52 | ax.set_ylabel('y') 53 | ax.set_zlabel('z') 54 | 55 | if ax_in is None: 56 | plt.show() 57 | 58 | def time(function, domain=(-100,100), points=30): 59 | """ 60 | Returns the time in seconds to calculate points^2 evaluations of the 61 | given function. 62 | 63 | function 64 | The objective function to be called at each point. 65 | domain 66 | The inclusive (min, max) domain for each dimension. 67 | points 68 | The number of points to collect on each dimension. A total of points^2 69 | function evaluations will be performed. 70 | """ 71 | from time import time 72 | import numpy as np 73 | 74 | # create points^2 tuples of (x,y) and populate z 75 | xys = np.linspace(domain[0], domain[1], points) 76 | xys = np.transpose([np.tile(xys, len(xys)), np.repeat(xys, len(xys))]) 77 | zs = np.zeros(points*points) 78 | 79 | before = time() 80 | for i in range(0, xys.shape[0]): 81 | zs[i] = function(xys[i]) 82 | return time() - before 83 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import numpy as np 4 | 5 | # evaluate a specific function a few times with one sample 6 | import cec2017.functions as functions 7 | 8 | f = functions.f5 9 | dimension = 30 10 | for i in range(0, 10): 11 | x = np.random.uniform(low=-100, high=100, size=dimension) 12 | y = f([x])[0] 13 | print(f"f5({x[0]:.2f}, {x[1]:.2f}, ...) = {y:.2f}") 14 | 15 | # or with a population (i.e. multiple samples) 16 | f = functions.f3 17 | samples = 3 18 | dimension = 30 19 | for i in range(0, 10): 20 | x = np.random.uniform(low=-100, high=100, size=(samples, dimension)) 21 | y = f(x) 22 | for i in range(samples): 23 | print(f"f5({x[i, 0]:.2f}, {x[i, 1]:.2f}, ...) = {y[i]:.2f}") 24 | 25 | # or evaluate each function once 26 | samples = 3 27 | dimension = 50 28 | for f in functions.all_functions: 29 | x = np.random.uniform(-100, 100, size=(samples, dimension)) 30 | val = f(x) 31 | for i in range(samples): 32 | print(f"{f.__name__}({x[i, 0]:.2f}, {x[i, 1]:.2f}, ...) = {y[i]:.2f}") 33 | 34 | # or all hybrid functions (or basic, simple or composite functions...) 35 | import cec2017.simple as simple # cec2017.basic cec2017.hybrid cec2017.composite 36 | samples = 3 37 | dimension = 50 38 | for f in simple.all_functions: # f1 to f10 39 | x = np.random.uniform(low=-100, high=100, size=(samples, dimension)) 40 | y = f(x) 41 | for i in range(samples): 42 | print(f"{f.__name__}({x[i, 0]:.2f}, {x[i, 1]:.2f}, ...) = {y[i]:.2f}") 43 | 44 | # make a surface plot of f27 45 | import cec2017.utils as utils 46 | utils.surface_plot(functions.f27, points=120) 47 | # or of f14 (not defined for D=2, so specify D=10) 48 | utils.surface_plot(functions.f14, points=120, dimension=10) 49 | # or even a base function like Ackley 50 | import cec2017.basic as basic 51 | utils.surface_plot(basic.ackley, points=120) 52 | -------------------------------------------------------------------------------- /extra/plots.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tilleyd/cec2017-py/424a9fa2757914c3e4cfdd8f59a268b1aeb3197f/extra/plots.jpg -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="CEC2017", 5 | version="1.0", 6 | description="CEC 2017 single objective optimization suite", 7 | author="Duncan Tilley", 8 | url="https://github.com/tilleyd/cec2017-py", 9 | packages=["cec2017"], 10 | include_package_data=True, 11 | install_requires=["numpy"], 12 | ) 13 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tilleyd/cec2017-py/424a9fa2757914c3e4cfdd8f59a268b1aeb3197f/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_functions.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def test_simple_functions(): 5 | from cec2017.simple import all_functions 6 | 7 | assert len(all_functions) == 10 8 | 9 | M = 2 10 | dims = [2, 10, 20, 30, 50, 100] 11 | 12 | for i, f in enumerate(all_functions): 13 | assert f.__name__ == f"f{1 + i}" 14 | for d in dims: 15 | x = np.zeros((M, d)) 16 | y = f(x) 17 | assert y.shape == (M,) 18 | 19 | pass 20 | 21 | 22 | def test_hybrid_functions(): 23 | from cec2017.hybrid import all_functions 24 | 25 | assert len(all_functions) == 10 26 | 27 | M = 2 28 | dims = [10, 30, 50, 100] 29 | 30 | for i, f in enumerate(all_functions): 31 | assert f.__name__ == f"f{11 + i}" 32 | for d in dims: 33 | x = np.zeros((M, d)) 34 | y = f(x) 35 | assert y.shape == (M,) 36 | 37 | 38 | def test_composition_functions(): 39 | from cec2017.composition import all_functions 40 | 41 | assert len(all_functions) == 10 42 | 43 | M = 2 44 | dims = [10, 30, 50, 100] 45 | 46 | for i, f in enumerate(all_functions): 47 | assert f.__name__ == f"f{21 + i}" 48 | for d in dims: 49 | x = np.zeros((M, d)) 50 | y = f(x) 51 | assert y.shape == (M,) 52 | 53 | 54 | def test_all_functions(): 55 | from cec2017.functions import all_functions 56 | 57 | assert len(all_functions) == 30 58 | 59 | M = 2 60 | D = 10 61 | x = np.zeros((M, D)) 62 | 63 | for i, f in enumerate(all_functions): 64 | assert f.__name__ == f"f{1 + i}" 65 | y = f(x) 66 | assert y.shape == (M,) 67 | 68 | 69 | def test_list_inputs(): 70 | from cec2017.functions import all_functions 71 | 72 | M = 2 73 | D = 10 74 | x = [0.0] * D 75 | x = [x] * M 76 | # functions should allow 77 | for f in all_functions: 78 | y = f(x) 79 | assert y.shape == (M,) 80 | --------------------------------------------------------------------------------