Skip to content

Commit 3503c22

Browse files
Add shekel func
1 parent 30d34ef commit 3503c22

4 files changed

Lines changed: 180 additions & 0 deletions

File tree

src/surfaces/test_functions/algebraic/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
standard_functions_1d,
7070
standard_functions_2d,
7171
standard_functions_nd,
72+
ShekelFunction,
7273
)
7374

7475
__all__ = [
@@ -108,6 +109,7 @@
108109
"RosenbrockFunction",
109110
"SphereFunction",
110111
"StyblinskiTangFunction",
112+
"ShekelFunction",
111113
# Constrained
112114
"CantileverBeamFunction",
113115
"PressureVesselFunction",

src/surfaces/test_functions/algebraic/standard/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
RosenbrockFunction,
4343
SphereFunction,
4444
StyblinskiTangFunction,
45+
ShekelFunction,
4546
)
4647

4748
__all__ = [
@@ -76,6 +77,7 @@
7677
"RosenbrockFunction",
7778
"SphereFunction",
7879
"StyblinskiTangFunction",
80+
"ShekelFunction"
7981
]
8082

8183
standard_functions = [
@@ -110,6 +112,7 @@
110112
RosenbrockFunction,
111113
SphereFunction,
112114
StyblinskiTangFunction,
115+
ShekelFunction,
113116
]
114117

115118
standard_functions_1d = [
@@ -147,4 +150,5 @@
147150
RosenbrockFunction,
148151
SphereFunction,
149152
StyblinskiTangFunction,
153+
ShekelFunction,
150154
]

src/surfaces/test_functions/algebraic/standard/test_functions_nd/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
from .rosenbrock_function import RosenbrockFunction
99
from .sphere_function import SphereFunction
1010
from .styblinski_tang_function import StyblinskiTangFunction
11+
from .shekel_function import ShekelFunction
1112

1213
__all__ = [
1314
"RastriginFunction",
1415
"RosenbrockFunction",
1516
"SphereFunction",
1617
"StyblinskiTangFunction",
1718
"GriewankFunction",
19+
"ShekelFunction",
1820
]
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Author: Zohaib Hassan
2+
3+
from typing import Any, Callable, Dict, List, Optional, Union
4+
import numpy as np
5+
from surfaces._array_utils import ArrayLike, get_array_namespace
6+
from surfaces.modifiers import BaseModifier
7+
8+
from ..._base_algebraic_function import AlgebraicFunction
9+
10+
11+
class ShekelFunction(AlgebraicFunction):
12+
"""Shekel 4-dimensional test function.
13+
14+
A multimodal, non-convex, continuous function. It is defined as the
15+
sum of m inverse quadratic functions.
16+
17+
The function is defined as:
18+
19+
.. math::
20+
21+
f(\\vec{x}) = - \\sum_{i=1}^{m} \\left( \\sum_{j=1}^{4} (x_j - a_{ij})^2 + c_i \\right)^{-1}
22+
23+
where :math:`m` is the number of maxima (typically 5, 7, or 10).
24+
25+
The global minimum is located at :math:`x \\approx (4, 4, 4, 4)` and
26+
the value depends on :math:`m`.
27+
28+
Parameters
29+
----------
30+
m : int, default=10
31+
Number of maxima. Standard values are 5, 7, or 10.
32+
metric : str, default="score"
33+
Either "loss" (minimize) or "score" (maximize).
34+
modifiers : list of BaseModifier, optional
35+
List of modifiers to apply to function evaluations.
36+
validate : bool, default=True
37+
Whether to validate parameters against the search space.
38+
39+
Attributes
40+
----------
41+
n_dim : int
42+
Number of dimensions (fixed at 4 for standard Shekel).
43+
default_bounds : tuple
44+
Default parameter bounds (0.0, 10.0).
45+
46+
Examples
47+
--------
48+
>>> from surfaces.test_functions import ShekelFunction
49+
>>> func = ShekelFunction(m=10)
50+
>>> result = func({"x0": 4.0, "x1": 4.0, "x2": 4.0, "x3": 4.0})
51+
>>> float(result) < -10.0
52+
True
53+
>>> len(func.search_space)
54+
4
55+
"""
56+
57+
name = "Shekel Function"
58+
_name_ = "shekel_function"
59+
__name__ = "ShekelFunction"
60+
61+
_spec = {
62+
"convex": False,
63+
"unimodal": False,
64+
"separable": False,
65+
"scalable": False,
66+
}
67+
68+
f_global = -10.536
69+
default_bounds = (0.0, 10.0)
70+
71+
latex_formula = r"f(\vec{x}) = - \sum_{i=1}^{m} \left( \sum_{j=1}^{4} (x_j - a_{ij})^2 + c_i \right)^{-1}"
72+
pgfmath_formula = None
73+
74+
# Function sheet attributes
75+
tagline = (
76+
"A multimodal function with m sharp peaks. Often called 'Foxholes', "
77+
"it tests an optimizer's ability to find a global minimum among many locals."
78+
)
79+
display_bounds = (0.0, 10.0)
80+
display_projection = {"fixed_values": {"x2": 4.0, "x3": 4.0}}
81+
reference = "Shekel, J. (1971). Test function for multivariate search problems."
82+
reference_url = "https://www.sfu.ca/~ssurjano/shekel.html"
83+
84+
def __init__(
85+
self,
86+
m: int = 10,
87+
objective: str = "minimize",
88+
modifiers: Optional[List[BaseModifier]] = None,
89+
memory: bool = False,
90+
collect_data: bool = True,
91+
callbacks: Optional[Union[Callable, List[Callable]]] = None,
92+
catch_errors: Optional[Dict[type, float]] = None,
93+
) -> None:
94+
super().__init__(objective, modifiers, memory, collect_data, callbacks, catch_errors)
95+
self.n_dim = 4
96+
self.m = m
97+
98+
self.A = np.array([
99+
[4.0, 4.0, 4.0, 4.0],
100+
[1.0, 1.0, 1.0, 1.0],
101+
[8.0, 8.0, 8.0, 8.0],
102+
[6.0, 6.0, 6.0, 6.0],
103+
[3.0, 7.0, 3.0, 7.0],
104+
[2.0, 9.0, 2.0, 9.0],
105+
[5.0, 5.0, 3.0, 3.0],
106+
[8.0, 1.0, 8.0, 1.0],
107+
[6.0, 2.0, 6.0, 2.0],
108+
[7.0, 3.6, 7.0, 3.6],
109+
])
110+
111+
self.c = np.array([0.1, 0.2, 0.2, 0.4, 0.4, 0.6, 0.3, 0.7, 0.5, 0.5])
112+
113+
if m < 10:
114+
self.A = self.A[:m]
115+
self.c = self.c[:m]
116+
117+
self.x_global = (4.0, 4.0, 4.0, 4.0)
118+
119+
def _create_objective_function(self) -> None:
120+
def shekel_function(params: Dict[str, Any]) -> float:
121+
x_input = np.array([params[f"x{i}"] for i in range(self.n_dim)])
122+
123+
result = 0.0
124+
for i in range(self.m):
125+
# (x - a_i)^T (x - a_i)
126+
diff = x_input - self.A[i]
127+
sq_sum = np.dot(diff, diff)
128+
129+
result -= 1.0 / (sq_sum + self.c[i])
130+
131+
return result
132+
133+
self.pure_objective_function = shekel_function
134+
135+
def _batch_objective(self, X: ArrayLike) -> ArrayLike:
136+
"""Vectorized batch evaluation.
137+
138+
Parameters
139+
----------
140+
X : ArrayLike
141+
Array of shape (n_points, n_dim).
142+
143+
Returns
144+
-------
145+
ArrayLike
146+
Array of shape (n_points,).
147+
"""
148+
xp = get_array_namespace(X)
149+
150+
A = xp.asarray(self.A)
151+
c = xp.asarray(self.c)
152+
153+
n_points = X.shape[0]
154+
result = xp.zeros(n_points)
155+
156+
for i in range(self.m):
157+
diff = X - A[i]
158+
159+
sq_sum = xp.sum(diff ** 2, axis=1)
160+
161+
result -= 1.0 / (sq_sum + c[i])
162+
163+
return result
164+
165+
def _search_space(
166+
self,
167+
min: float = 0.0,
168+
max: float = 10.0,
169+
size: int = 10000,
170+
value_types: str = "array",
171+
) -> Dict[str, Any]:
172+
return super()._create_n_dim_search_space(min, max, size=size, value_types=value_types)

0 commit comments

Comments
 (0)