1- # copyright: hyperactive developers, MIT License (see LICENSE file)
2-
3- # todo: write an informative docstring for the file or module, remove the above
4-
5- __author__ = ["SimonBlanke" , "yashnator" ]
6-
71from ..base_optimizer import BaseOptimizer
82
93import numpy as np
@@ -57,15 +51,58 @@ class COBYLA(BaseOptimizer):
5751 ... )
5852 >>> opt.search(sphere_function, n_iter=10)
5953 """
54+
55+ name = "COBYLA"
56+ _name_ = "COBYLA"
57+ __name__ = "COBYLA"
6058
61- _tags = {
62- "authors" : ["SimonBlanke" , "yashnator" ],
63- }
59+ optimizer_type = "local"
60+ computationally_expensive = False
61+
62+ def __init__ (
63+ self ,
64+ search_space ,
65+ x_0 : np .array ,
66+ rho_beg : int ,
67+ rho_end : int ,
68+ initialize = {"grid" : 4 , "random" : 2 , "vertices" : 4 },
69+ constraints = [],
70+ random_state = None ,
71+ rand_rest_p = 0 ,
72+ nth_process = None ,
73+ alpha = 0.25 ,
74+ beta = 2.1 ,
75+ ):
76+ super ().__init__ (
77+ search_space = search_space ,
78+ initialize = initialize ,
79+ constraints = constraints ,
80+ random_state = random_state ,
81+ rand_rest_p = rand_rest_p ,
82+ nth_process = nth_process ,
83+ )
84+ self .dim = np .shape (x_0 )[0 ]
85+ self .simplex = self ._generate_initial_simplex (x_0 , rho_beg )
86+ self .rho_beg = rho_beg
87+ self .rho_end = rho_end
88+ self ._rho = rho_beg
89+
90+ self ._mu = 0
91+ self .state = 0
92+ self .FLAG = 0
93+
94+ if alpha <= 0 or alpha >= 1 :
95+ raise ValueError ("Parameter alpha must belong to the range (0, 1)" )
96+
97+ if beta <= 1 :
98+ raise ValueError ("Parameter beta must belong to the range (1, ∞)" )
99+
100+ self .alpha = alpha
101+ self .beta = beta
64102
65103 def _generate_initial_simplex (self , x_0_initial , rho_beg ):
66104 n = x_0_initial .shape [0 ]
67105 arr = np .ones ((n + 1 , 1 )) * x_0_initial + rho_beg * np .eye (n + 1 , n )
68- print (arr )
69106 return arr
70107
71108 def _vertices_to_oppsite_face_distances (self ):
@@ -76,9 +113,6 @@ def _vertices_to_oppsite_face_distances(self):
76113 For each vertex, the opposite hyperplane is obtained after removing the current
77114 vertex and then finding the projection on the subspace spanned by the hyperplane.
78115 The distance is then the L2 norm between projection and the current vertex.
79-
80- Args:
81- self: instance of current COBYLA class
82116
83117 Returns:
84118 distances: (n+1,) array of distances from each vertex to its opposite face.
@@ -96,66 +130,65 @@ def _vertices_to_oppsite_face_distances(self):
96130 return distances
97131
98132 def _is_simplex_acceptable (self ):
133+ """
134+ Determine whether the current simplex is acceptable according to COBYLA criteria.
135+
136+ In COBYLA, a simplex is acceptable if:
137+ 1. The distances between each vertex and the first vertex (η_j) do not exceed
138+ a threshold defined by `beta * rho`, controlling simplex edge lengths.
139+ 2. The distance from each vertex to its opposite (n-1)-dimensional face (σ_j)
140+ is not too small, ensuring the simplex is well-shaped. These distances must
141+ exceed a threshold given by `alpha * rho`.
142+
143+ This function enforces these two geometric conditions to ensure that the simplex
144+ maintains good numerical stability and approximation quality.
145+
146+ Returns:
147+ bool: True if both η and σ conditions are satisfied, False otherwise.
148+ """
99149 eta = [np .linalg .norm (x - self .simplex [0 ]) for x in self .simplex ]
100150 eta_constraint = self .beta * self ._rho
101151 for eta_j in eta :
102152 if eta_j > eta_constraint :
103153 return False
104154 sigma = self ._vertices_to_oppsite_face_distances ()
105155 sigma_constraint = self .alpha * self ._rho
106- print (sigma )
107156 for sigma_j in sigma :
108157 if sigma_j < sigma_constraint :
109158 return False
110159 return True
111160
112161 def _eval_constraints (self , pos ):
113- # TODO: evalute constraints in optimized way
114-
115- return None
162+ """
163+ Evaluates constraints for the given position
164+
165+ Returns:
166+ np.array: array containing the evaluated value of constraints
167+ """
168+ return [np .clip (f (pos ), 0 , np .inf ) for f in self .constraints ]
116169
117- def _merit_value (self , pos ):
118- # TODO: write the merit function using the _eval_constraints
119- return 0
170+ def _phi (self , pos ):
171+ """
172+ Compute the merit function Φ used in COBYLA to evaluate candidate points. Given by:
120173
121- def __init__ (
122- self ,
123- search_space ,
124- x_0 : np .array ,
125- rho_beg : int ,
126- rho_end : int ,
127- initialize = {"grid" : 4 , "random" : 2 , "vertices" : 4 },
128- constraints = [],
129- random_state = None ,
130- rand_rest_p = 0 ,
131- nth_process = None ,
132- alpha = 0.25 ,
133- beta = 2.1
134- ):
135- super ().__init__ (
136- search_space = search_space ,
137- initialize = initialize ,
138- constraints = constraints ,
139- random_state = random_state ,
140- rand_rest_p = rand_rest_p ,
141- nth_process = nth_process ,
142- )
143- self .dim = np .shape (x_0 )[0 ]
144- self .simplex = self ._generate_initial_simplex (x_0 , rho_beg )
145- self .rho_beg = rho_beg
146- self .rho_end = rho_end
147- self ._rho = rho_beg
148- self .state = 0
149- self .FLAG = 0
150-
151- if alpha <= 0 or alpha >= 1 :
152- raise ValueError ("Parameter alpha must belong to the range (0, 1)" )
153-
154- if beta <= 1 :
155- raise ValueError ("Parameter beta must belong to the range (1, ∞)" )
156-
157- self .alpha = alpha
158- self .beta = beta
174+ Φ(x) = f(x) + μ * max_i(max(c_i(x), 0))
175+
176+ Args:
177+ pos (np.array): The point in parameter space at which to evaluate the merit function.
178+
179+ Returns:
180+ float: The value of the merit function Φ at the given point.
181+ """
182+ c = self ._eval_constraints (pos )
183+ return self .objective_function (pos ) + self .mu * np .max (c )
184+
185+ def _rearrange_optimum_to_top (self ):
186+ """
187+ Rearrages simplex vertices such that first row is the optimal position
188+ """
189+ opt_idx = np .argmin ([self ._phi (vert ) for vert in self .simplex ])
190+ if opt_idx != 0 :
191+ self .simplex [[0 , opt_idx ]] = self .simplex [[opt_idx , 0 ]]
159192
160193 def iterate (self ):
161194 # TODO: Impl
@@ -167,7 +200,7 @@ def _score(self, pos):
167200
168201 def evaluate (self , pos ):
169202 # TODO: Impl
170- return self ._merit_value (pos )
203+ return self ._phi (pos )
171204
172205 def finish_search (self ):
173206 # TODO: Impl
0 commit comments