diff --git a/pypesto/profile/profile_next_guess.py b/pypesto/profile/profile_next_guess.py index 32da44b79..7a2cf5364 100644 --- a/pypesto/profile/profile_next_guess.py +++ b/pypesto/profile/profile_next_guess.py @@ -62,14 +62,15 @@ def next_guess( return fixed_step( x, par_index, par_direction, profile_options, problem ) - elif update_type == 'adaptive_step_order_0': + + if update_type == 'adaptive_step_order_0': order = 0 elif update_type == 'adaptive_step_order_1': order = 1 elif update_type == 'adaptive_step_regression': order = np.nan else: - raise Exception( + raise ValueError( f'Unsupported `update_type` {update_type} for `next_guess`.' ) @@ -207,14 +208,12 @@ def clip_to_bounds(step_proposal): # check whether we must make a minimum step anyway, since we're close to # the next bound min_delta_x = x[par_index] + par_direction * options.min_step_size - if par_direction == -1: - if min_delta_x < problem.lb_full[par_index]: - step_length = problem.lb_full[par_index] - x[par_index] - return x + step_length * delta_x_dir - else: - if min_delta_x > problem.ub_full[par_index]: - step_length = problem.ub_full[par_index] - x[par_index] - return x + step_length * delta_x_dir + if par_direction == -1 and (min_delta_x < problem.lb_full[par_index]): + step_length = problem.lb_full[par_index] - x[par_index] + return x + step_length * delta_x_dir + elif par_direction == 1 and (min_delta_x > problem.ub_full[par_index]): + step_length = problem.ub_full[par_index] - x[par_index] + return x + step_length * delta_x_dir # parameter extrapolation function def par_extrapol(step_length): @@ -263,37 +262,19 @@ def par_extrapol(step_length): next_obj = problem.objective(problem.get_reduced_vector(next_x)) # iterate until good step size is found - if next_obj_target < next_obj: - # The step is rather too long - return do_line_search( - next_x, - step_size_guess, - 'decrease', - par_extrapol, - next_obj, - next_obj_target, - clip_to_minmax, - clip_to_bounds, - par_index, - problem, - options, - ) - - else: - # The step is rather too short - return do_line_search( - next_x, - step_size_guess, - 'increase', - par_extrapol, - next_obj, - next_obj_target, - clip_to_minmax, - clip_to_bounds, - par_index, - problem, - options, - ) + return do_line_search( + next_x, + step_size_guess, + "decrease" if next_obj_target < next_obj else "increase", + par_extrapol, + next_obj, + next_obj_target, + clip_to_minmax, + clip_to_bounds, + par_index, + problem, + options, + ) def handle_profile_history( @@ -336,10 +317,6 @@ def handle_profile_history( last_delta_x = ( current_profile.x_path[:, -1] - current_profile.x_path[:, -2] ) - step_size_guess = np.abs( - current_profile.x_path[par_index, -1] - - current_profile.x_path[par_index, -2] - ) delta_x_dir = last_delta_x / step_size_guess elif np.isnan(order): # compute the regression polynomial for parameter extrapolation @@ -372,7 +349,7 @@ def get_reg_polynomial( for i_par in range(problem.dim_full): if i_par in problem.x_fixed_indices: # if we meet the current profiling parameter or a fixed parameter, - # there is nothing to do, so pass an np.nan + # there is nothing to do, so pass a np.nan reg_par.append(np.nan) else: # Do polynomial interpolation of profile path @@ -403,7 +380,7 @@ def get_reg_polynomial( def do_line_search( next_x: np.ndarray, step_size_guess: float, - direction: str, + direction: Literal['increase', 'decrease'], par_extrapol: Callable, next_obj: float, next_obj_target: float, @@ -443,21 +420,19 @@ def do_line_search( if hit_bounds: return next_x - else: - # compute new objective value - problem.fix_parameters(par_index, next_x[par_index]) - last_obj = copy.copy(next_obj) - next_obj = problem.objective(problem.get_reduced_vector(next_x)) - - # check for root crossing and compute correct step size in case - if direction == 'decrease' and next_obj_target >= next_obj: - return next_x_interpolate( - next_obj, last_obj, next_x, last_x, next_obj_target - ) - elif direction == 'increase' and next_obj_target <= next_obj: - return next_x_interpolate( - next_obj, last_obj, next_x, last_x, next_obj_target - ) + + # compute new objective value + problem.fix_parameters(par_index, next_x[par_index]) + last_obj = copy.copy(next_obj) + next_obj = problem.objective(problem.get_reduced_vector(next_x)) + + # check for root crossing and compute correct step size in case + if (direction == 'decrease' and next_obj_target >= next_obj) or ( + direction == 'increase' and next_obj_target <= next_obj + ): + return next_x_interpolate( + next_obj, last_obj, next_x, last_x, next_obj_target + ) def next_x_interpolate( @@ -480,12 +455,15 @@ def clip( lower: Union[float, np.ndarray], upper: Union[float, np.ndarray], ) -> Union[float, np.ndarray]: - """Restrict a scalar or a vector to given bounds.""" + """Restrict a scalar or a vector to given bounds. + + ``vector_guess`` is modified in-place if it is an array. + """ if isinstance(vector_guess, float): - vector_guess = np.max([np.min([vector_guess, upper]), lower]) - else: - for i_par, i_guess in enumerate(vector_guess): - vector_guess[i_par] = np.max( - [np.min([i_guess, upper[i_par]]), lower[i_par]] - ) + return np.max([np.min([vector_guess, upper]), lower]) + + for i_par, i_guess in enumerate(vector_guess): + vector_guess[i_par] = np.max( + [np.min([i_guess, upper[i_par]]), lower[i_par]] + ) return vector_guess