Skip to content

Commit 5b482c4

Browse files
committed
add engineering based test functions
1 parent 70dab4d commit 5b482c4

File tree

8 files changed

+1247
-1
lines changed

8 files changed

+1247
-1
lines changed

src/surfaces/test_functions/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@
88
from .machine_learning import machine_learning_functions
99
from .machine_learning import * # noqa: F401,F403
1010

11+
from .engineering import * # noqa: F401,F403
12+
13+
# Engineering function list
14+
engineering_functions = [
15+
"ThreeBarTrussFunction",
16+
"WeldedBeamFunction",
17+
"PressureVesselFunction",
18+
"TensionCompressionSpringFunction",
19+
"CantileverBeamFunction",
20+
]
1121

1222
# Backwards compatibility alias
1323
mathematical_functions = algebraic_functions
1424

15-
test_functions: list = algebraic_functions + machine_learning_functions
25+
test_functions: list = algebraic_functions + machine_learning_functions + engineering_functions
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Author: Simon Blanke
2+
# Email: simon.blanke@yahoo.com
3+
# License: MIT License
4+
5+
"""Engineering design optimization benchmark functions.
6+
7+
This module provides a collection of classic engineering design optimization
8+
problems. These are real-world inspired benchmarks with physical meaning,
9+
commonly used to evaluate constrained optimization algorithms.
10+
11+
All problems are formulated as minimization with constraints handled via
12+
penalty methods. Each function provides:
13+
14+
- `raw_objective(params)`: The pure engineering objective (cost, weight, etc.)
15+
- `constraints(params)`: Constraint function values (g <= 0 is feasible)
16+
- `constraint_violations(params)`: Positive violation amounts
17+
- `is_feasible(params)`: Boolean feasibility check
18+
- `penalty(params)`: Total penalty for constraint violations
19+
20+
The default `__call__` method returns the penalized objective:
21+
f(x) + penalty_coefficient * sum(max(0, g_i(x))^2)
22+
23+
Available Functions
24+
-------------------
25+
Structural Engineering:
26+
ThreeBarTrussFunction : 2D, 3 constraints
27+
Minimize weight of a symmetric truss structure.
28+
CantileverBeamFunction : 5D, 1 constraint
29+
Minimize weight of a stepped cantilever beam.
30+
31+
Manufacturing/Mechanical:
32+
WeldedBeamFunction : 4D, 5 constraints
33+
Minimize fabrication cost of a welded beam joint.
34+
PressureVesselFunction : 4D, 3 constraints
35+
Minimize cost of a cylindrical pressure vessel.
36+
TensionCompressionSpringFunction : 3D, 4 constraints
37+
Minimize weight of a helical compression spring.
38+
39+
Examples
40+
--------
41+
>>> from surfaces.test_functions.engineering import WeldedBeamFunction
42+
>>> func = WeldedBeamFunction()
43+
>>> # Evaluate with penalty
44+
>>> cost = func({"h": 0.2, "l": 3.5, "t": 9.0, "b": 0.2})
45+
>>> # Check raw objective without penalty
46+
>>> raw_cost = func.raw_objective({"h": 0.2, "l": 3.5, "t": 9.0, "b": 0.2})
47+
>>> # Check if solution is feasible
48+
>>> func.is_feasible({"h": 0.2, "l": 3.5, "t": 9.0, "b": 0.2})
49+
50+
References
51+
----------
52+
Most problems originate from:
53+
54+
.. [1] Coello, C.A.C. (2000). "Use of a self-adaptive penalty approach
55+
for engineering optimization problems."
56+
.. [2] Deb, K. (2000). "An efficient constraint handling method for
57+
genetic algorithms."
58+
"""
59+
60+
from ._base_engineering_function import EngineeringFunction
61+
from .three_bar_truss import ThreeBarTrussFunction
62+
from .welded_beam import WeldedBeamFunction
63+
from .pressure_vessel import PressureVesselFunction
64+
from .tension_compression_spring import TensionCompressionSpringFunction
65+
from .cantilever_beam import CantileverBeamFunction
66+
67+
__all__ = [
68+
"EngineeringFunction",
69+
"ThreeBarTrussFunction",
70+
"WeldedBeamFunction",
71+
"PressureVesselFunction",
72+
"TensionCompressionSpringFunction",
73+
"CantileverBeamFunction",
74+
]
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Author: Simon Blanke
2+
# Email: simon.blanke@yahoo.com
3+
# License: MIT License
4+
5+
"""Base class for engineering design optimization test functions."""
6+
7+
import numpy as np
8+
from typing import Dict, Any, List, Tuple, Union
9+
10+
from .._base_test_function import BaseTestFunction
11+
12+
13+
class EngineeringFunction(BaseTestFunction):
14+
"""Base class for real-world engineering design optimization problems.
15+
16+
Engineering functions represent practical design optimization problems
17+
from domains like structural mechanics, manufacturing, and mechanical
18+
engineering. Unlike purely mathematical test functions, these problems
19+
have physical meaning and constraints derived from engineering principles.
20+
21+
Most engineering problems are inherently constrained. This base class
22+
provides infrastructure for handling constraints via penalty methods,
23+
converting constrained problems into unconstrained ones suitable for
24+
general-purpose optimizers.
25+
26+
Parameters
27+
----------
28+
objective : str, default="minimize"
29+
Either "minimize" or "maximize".
30+
sleep : float, default=0
31+
Artificial delay in seconds added to each evaluation.
32+
penalty_coefficient : float, default=1e6
33+
Coefficient for constraint violation penalties. Higher values
34+
enforce constraints more strictly but may create steep gradients.
35+
36+
Attributes
37+
----------
38+
n_dim : int
39+
Number of design variables.
40+
variable_names : list of str
41+
Names of design variables (e.g., ["thickness", "radius"]).
42+
variable_bounds : list of tuple
43+
Bounds for each variable as (min, max) pairs.
44+
45+
Notes
46+
-----
47+
Constraint handling uses the exterior penalty method:
48+
49+
.. math::
50+
51+
F(x) = f(x) + r \\sum_{i} \\max(0, g_i(x))^2
52+
53+
where f(x) is the objective, g_i(x) are inequality constraints
54+
(g_i(x) <= 0 is feasible), and r is the penalty coefficient.
55+
"""
56+
57+
_spec = {
58+
"default_bounds": None, # Engineering functions have variable-specific bounds
59+
"continuous": True,
60+
"differentiable": True,
61+
"constrained": True,
62+
}
63+
64+
default_size: int = 10000
65+
66+
# Subclasses should define these
67+
variable_names: List[str] = []
68+
variable_bounds: List[Tuple[float, float]] = []
69+
70+
def __init__(
71+
self,
72+
objective: str = "minimize",
73+
sleep: float = 0,
74+
penalty_coefficient: float = 1e6
75+
):
76+
self.penalty_coefficient = penalty_coefficient
77+
super().__init__(objective, sleep)
78+
79+
@property
80+
def n_dim(self) -> int:
81+
"""Number of design variables."""
82+
return len(self.variable_names)
83+
84+
@property
85+
def search_space(self) -> Dict[str, Any]:
86+
"""Search space based on variable bounds."""
87+
search_space_ = {}
88+
total_size = self.default_size
89+
dim_size = int(total_size ** (1 / self.n_dim))
90+
91+
for i, (name, (lb, ub)) in enumerate(
92+
zip(self.variable_names, self.variable_bounds)
93+
):
94+
step_size = (ub - lb) / dim_size
95+
values = np.arange(lb, ub, step_size)
96+
search_space_[name] = values
97+
98+
return search_space_
99+
100+
def _get_values(self, params: Dict[str, Any]) -> np.ndarray:
101+
"""Extract variable values from params dict in order."""
102+
return np.array([params[name] for name in self.variable_names])
103+
104+
def constraints(self, params: Dict[str, Any]) -> List[float]:
105+
"""Evaluate constraint functions.
106+
107+
Override in subclasses to define problem-specific constraints.
108+
Each constraint g_i should be formulated such that g_i <= 0 is feasible.
109+
110+
Parameters
111+
----------
112+
params : dict
113+
Design variable values.
114+
115+
Returns
116+
-------
117+
list of float
118+
Constraint function values. Negative or zero means feasible.
119+
"""
120+
return []
121+
122+
def constraint_violations(self, params: Dict[str, Any]) -> List[float]:
123+
"""Calculate constraint violations (positive values only).
124+
125+
Parameters
126+
----------
127+
params : dict
128+
Design variable values.
129+
130+
Returns
131+
-------
132+
list of float
133+
Violation amounts. Zero means constraint is satisfied.
134+
"""
135+
return [max(0, g) for g in self.constraints(params)]
136+
137+
def is_feasible(self, params: Dict[str, Any]) -> bool:
138+
"""Check if a solution satisfies all constraints.
139+
140+
Parameters
141+
----------
142+
params : dict
143+
Design variable values.
144+
145+
Returns
146+
-------
147+
bool
148+
True if all constraints are satisfied.
149+
"""
150+
return all(g <= 0 for g in self.constraints(params))
151+
152+
def penalty(self, params: Dict[str, Any]) -> float:
153+
"""Calculate total penalty for constraint violations.
154+
155+
Parameters
156+
----------
157+
params : dict
158+
Design variable values.
159+
160+
Returns
161+
-------
162+
float
163+
Penalty value (sum of squared violations times coefficient).
164+
"""
165+
violations = self.constraint_violations(params)
166+
return self.penalty_coefficient * sum(v ** 2 for v in violations)
167+
168+
def raw_objective(self, params: Dict[str, Any]) -> float:
169+
"""Evaluate the raw objective function without penalties.
170+
171+
Override in subclasses to define the engineering objective.
172+
173+
Parameters
174+
----------
175+
params : dict
176+
Design variable values.
177+
178+
Returns
179+
-------
180+
float
181+
Raw objective function value.
182+
"""
183+
raise NotImplementedError("Subclasses must implement raw_objective")
184+
185+
def _create_objective_function(self):
186+
"""Create objective function with penalty for constraint violations."""
187+
def penalized_objective(params):
188+
return self.raw_objective(params) + self.penalty(params)
189+
190+
self.pure_objective_function = penalized_objective

0 commit comments

Comments
 (0)