@@ -253,69 +253,101 @@ def initialise_coordinate_directions(self, number_of_samples, num_directions, pa
253253
254254 at_lower_boundary = (self .model .sl > - 0.01 * self .delta ) # sl = xl - x0, should be -ve, actually < -rhobeg
255255 at_upper_boundary = (self .model .su < 0.01 * self .delta ) # su = xu - x0, should be +ve, actually > rhobeg
256-
257- xpts_added = np .zeros ((num_directions + 1 , self .n ()))
258- for k in range (1 , num_directions + 1 ):
259- # k = 0 --> base point (xpt = 0) [ not here]
260- # k = 1, ..., 2n --> coordinate directions [1,...,n and n+1,...,2n]
261- # k = 2n+1, ..., (n+1)(n+2)/2 --> off-diagonal directions
262- if 1 <= k < self .n () + 1 : # first step along coord directions
256+
257+ if params ("init.run_in_parallel" ) and num_directions <= self .n ():
258+ # Can do all the evaluation in parallel if <= n+1 interpolation points, but if larger
259+ # then the step depends on the function value at previous steps and does point swapping
260+ xpts_added = np .zeros ((num_directions + 1 , self .n ()))
261+ eval_obj_results = []
262+ for k in range (1 , num_directions + 1 ): # k = 1, ..., num_directions
263+ # always have k = 1, ..., n since num_directions <= n
263264 dirn = k - 1 # direction to move in (0,...,n-1)
264265 stepa = self .delta if not at_upper_boundary [dirn ] else - self .delta # take a +delta step if at lower, -delta if at upper
265266 stepb = None
266267 xpts_added [k , dirn ] = stepa # set new (relative) point to the step since we haven't done any moving, so relative point is all zeros.
268+
269+ # Evaluate objective at this new point
270+ x = self .model .as_absolute_coordinates (xpts_added [k , :])
271+ eval_obj_results .append (self .evaluate_objective (x , number_of_samples , params ))
272+
273+ # Evaluations done, now add to the model
274+ for k in range (1 , num_directions + 1 ):
275+ x = self .model .as_absolute_coordinates (xpts_added [k , :])
276+ rvec_list , obj_list , num_samples_run , exit_info = eval_obj_results [k - 1 ]
277+ # Handle exit conditions (f < min obj value or maxfun reached)
278+ if exit_info is not None :
279+ if num_samples_run > 0 :
280+ self .model .save_point (x , np .mean (rvec_list [:num_samples_run , :], axis = 0 ), num_samples_run , self .nx ,
281+ x_in_abs_coords = True )
282+ return exit_info # return & quit
267283
268- elif self .n () + 1 <= k < 2 * self .n () + 1 : # second step along coord directions
269- dirn = k - self .n () - 1 # direction to move in (0,...,n-1)
270- stepa = xpts_added [k - self .n (), dirn ] # previous step
271- stepb = - self .delta # new step
272- if at_lower_boundary [dirn ]:
273- # if at lower boundary, set the second step to be +ve
274- stepb = min (2.0 * self .delta , self .model .su [dirn ]) # su = xu - x0, should be +ve
275- if at_upper_boundary [dirn ]:
276- # if at upper boundary, set the second step to be -ve
277- stepb = max (- 2.0 * self .delta , self .model .sl [dirn ]) # sl = xl - x0, should be -ve
278- xpts_added [k , dirn ] = stepb
279-
280- else : # k = 2n+1, ..., (n+1)(n+2)/2
281- # p = (k - 1) % n + 1 # cycles through (1,...,n), starting at 2n+1 --> 1
282- # l = (k - 2 * n - 1) / n + 1 # (1,...,1, 2, ..., 2, etc.) where each number appears n times
283- # q = (p + l if p + l <= n else p + l - n)
284- stepa = None
285- stepb = None
286- itemp = (k - self .n () - 1 ) // self .n ()
287- q = k - itemp * self .n () - self .n ()
288- p = q + itemp
289- if p > self .n ():
290- p , q = q , p - self .n () # does swap correctly in Python
291-
292- xpts_added [k , p - 1 ] = xpts_added [p , p - 1 ]
293- xpts_added [k , q - 1 ] = xpts_added [q , q - 1 ]
284+ # Otherwise, add new results (increments model.npt_so_far)
285+ self .model .change_point (k , x - self .model .xbase , rvec_list [0 , :], self .nx ) # expect step, not absolute x
286+ for i in range (1 , num_samples_run ):
287+ self .model .add_new_sample (k , rvec_extra = rvec_list [i , :])
288+ else :
289+ xpts_added = np .zeros ((num_directions + 1 , self .n ()))
290+ for k in range (1 , num_directions + 1 ):
291+ # k = 0 --> base point (xpt = 0) [ not here]
292+ # k = 1, ..., 2n --> coordinate directions [1,...,n and n+1,...,2n]
293+ # k = 2n+1, ..., (n+1)(n+2)/2 --> off-diagonal directions
294+ if 1 <= k < self .n () + 1 : # first step along coord directions
295+ dirn = k - 1 # direction to move in (0,...,n-1)
296+ stepa = self .delta if not at_upper_boundary [dirn ] else - self .delta # take a +delta step if at lower, -delta if at upper
297+ stepb = None
298+ xpts_added [k , dirn ] = stepa # set new (relative) point to the step since we haven't done any moving, so relative point is all zeros.
299+
300+ elif self .n () + 1 <= k < 2 * self .n () + 1 : # second step along coord directions
301+ dirn = k - self .n () - 1 # direction to move in (0,...,n-1)
302+ stepa = xpts_added [k - self .n (), dirn ] # previous step
303+ stepb = - self .delta # new step
304+ if at_lower_boundary [dirn ]:
305+ # if at lower boundary, set the second step to be +ve
306+ stepb = min (2.0 * self .delta , self .model .su [dirn ]) # su = xu - x0, should be +ve
307+ if at_upper_boundary [dirn ]:
308+ # if at upper boundary, set the second step to be -ve
309+ stepb = max (- 2.0 * self .delta , self .model .sl [dirn ]) # sl = xl - x0, should be -ve
310+ xpts_added [k , dirn ] = stepb
311+
312+ else : # k = 2n+1, ..., (n+1)(n+2)/2
313+ # p = (k - 1) % n + 1 # cycles through (1,...,n), starting at 2n+1 --> 1
314+ # l = (k - 2 * n - 1) / n + 1 # (1,...,1, 2, ..., 2, etc.) where each number appears n times
315+ # q = (p + l if p + l <= n else p + l - n)
316+ stepa = None
317+ stepb = None
318+ itemp = (k - self .n () - 1 ) // self .n ()
319+ q = k - itemp * self .n () - self .n ()
320+ p = q + itemp
321+ if p > self .n ():
322+ p , q = q , p - self .n () # does swap correctly in Python
323+
324+ xpts_added [k , p - 1 ] = xpts_added [p , p - 1 ]
325+ xpts_added [k , q - 1 ] = xpts_added [q , q - 1 ]
294326
295- # Evaluate objective at this new point
296- x = self .model .as_absolute_coordinates (xpts_added [k , :])
297- rvec_list , obj_list , num_samples_run , exit_info = self .evaluate_objective (x , number_of_samples , params )
327+ # Evaluate objective at this new point
328+ x = self .model .as_absolute_coordinates (xpts_added [k , :])
329+ rvec_list , obj_list , num_samples_run , exit_info = self .evaluate_objective (x , number_of_samples , params )
298330
299- # Handle exit conditions (f < min obj value or maxfun reached)
300- if exit_info is not None :
301- if num_samples_run > 0 :
302- self .model .save_point (x , np .mean (rvec_list [:num_samples_run , :], axis = 0 ), num_samples_run , self .nx ,
303- x_in_abs_coords = True )
304- return exit_info # return & quit
331+ # Handle exit conditions (f < min obj value or maxfun reached)
332+ if exit_info is not None :
333+ if num_samples_run > 0 :
334+ self .model .save_point (x , np .mean (rvec_list [:num_samples_run , :], axis = 0 ), num_samples_run , self .nx ,
335+ x_in_abs_coords = True )
336+ return exit_info # return & quit
305337
306- # Otherwise, add new results (increments model.npt_so_far)
307- self .model .change_point (k , x - self .model .xbase , rvec_list [0 , :], self .nx ) # expect step, not absolute x
308- for i in range (1 , num_samples_run ):
309- self .model .add_new_sample (k , rvec_extra = rvec_list [i , :])
310-
311- # If k exceeds N+1, then the positions of the k-th and (k-N)-th interpolation
312- # points may be switched, in order that the function value at the first of them
313- # contributes to the off-diagonal second derivative terms of the initial quadratic model.
314- # Note: this works because the steps for (k) and (k-n) points were in the same coordinate direction
315- if self .n () + 1 <= k < 2 * self .n () + 1 :
316- # Only swap if steps were in different directions AND new pt has lower objective
317- if stepa * stepb < 0.0 and self .model .objval [k ] < self .model .objval [k - self .n ()]:
318- xpts_added [[k , k - self .n ()]] = xpts_added [[k - self .n (), k ]]
338+ # Otherwise, add new results (increments model.npt_so_far)
339+ self .model .change_point (k , x - self .model .xbase , rvec_list [0 , :], self .nx ) # expect step, not absolute x
340+ for i in range (1 , num_samples_run ):
341+ self .model .add_new_sample (k , rvec_extra = rvec_list [i , :])
342+
343+ # If k exceeds N+1, then the positions of the k-th and (k-N)-th interpolation
344+ # points may be switched, in order that the function value at the first of them
345+ # contributes to the off-diagonal second derivative terms of the initial quadratic model.
346+ # Note: this works because the steps for (k) and (k-n) points were in the same coordinate direction
347+ if self .n () + 1 <= k < 2 * self .n () + 1 :
348+ # Only swap if steps were in different directions AND new pt has lower objective
349+ if stepa * stepb < 0.0 and self .model .objval [k ] < self .model .objval [k - self .n ()]:
350+ xpts_added [[k , k - self .n ()]] = xpts_added [[k - self .n (), k ]]
319351
320352 return None # return & continue
321353
0 commit comments