From 1bb035648c3cd91840cb455dfd41500869edffe8 Mon Sep 17 00:00:00 2001 From: Doresic <85789271+Doresic@users.noreply.github.com> Date: Fri, 8 May 2026 15:00:34 +0200 Subject: [PATCH 1/2] Profile: default to o0 and stabilize regression --- pypesto/profile/options.py | 14 +++++-- pypesto/profile/profile.py | 7 +++- pypesto/profile/profile_next_guess.py | 58 ++++++++++++++++++++------- test/profile/test_profile.py | 2 + 4 files changed, 62 insertions(+), 19 deletions(-) diff --git a/pypesto/profile/options.py b/pypesto/profile/options.py index 3003dfbe7..5c03a9a55 100644 --- a/pypesto/profile/options.py +++ b/pypesto/profile/options.py @@ -42,8 +42,12 @@ class ProfileOptions(dict): Number of profile points used for regression in regression based adaptive profile points proposal. reg_order: - Maximum degree of regression polynomial used in regression based - adaptive profile points proposal. + Polynomial degree for regression-based extrapolation. + Higher values may cause extrapolation instability. + correlation_threshold: + Minimum absolute correlation coefficient for extrapolating a parameter + in regression-based methods. Parameters with |correlation| below this + threshold will be kept at their current values instead of extrapolated. adaptive_target_scaling_factor: The scaling factor of the next_obj_target in next guess generation. Larger values result in larger next_guess step size (must be > 1). @@ -77,7 +81,8 @@ def __init__( delta_ratio_max: float = 0.1, ratio_min: float = 0.145, reg_points: int = 10, - reg_order: int = 4, + reg_order: int = 1, + correlation_threshold: float = 0.7, adaptive_target_scaling_factor: float = 1.5, whole_path: bool = False, profile_n_starts: int = 6, @@ -97,6 +102,7 @@ def __init__( self.delta_ratio_max = delta_ratio_max self.reg_points = reg_points self.reg_order = reg_order + self.correlation_threshold = correlation_threshold self.adaptive_target_scaling_factor = adaptive_target_scaling_factor self.whole_path = whole_path self.profile_n_starts = profile_n_starts @@ -165,6 +171,8 @@ def validate(self): if self.adaptive_target_scaling_factor < 1: raise ValueError("adaptive_target_scaling_factor must be > 1.") + if self.correlation_threshold < 0 or self.correlation_threshold > 1: + raise ValueError("correlation_threshold must be in [0, 1].") if self.profile_n_starts < 1: raise ValueError("profile_n_starts must be >= 1.") if self.profile_sampling_sigma <= 0: diff --git a/pypesto/profile/profile.py b/pypesto/profile/profile.py index 600fc7b8c..4ed4e7fb0 100644 --- a/pypesto/profile/profile.py +++ b/pypesto/profile/profile.py @@ -25,7 +25,7 @@ def parameter_profile( profile_index: Iterable[int] = None, profile_list: int = None, result_index: int = 0, - next_guess_method: Callable | str = "adaptive_step_order_1", + next_guess_method: Callable | str = "adaptive_step_order_0", profile_options: ProfileOptions = None, progress_bar: bool = None, filename: str | Callable | None = None, @@ -62,6 +62,11 @@ def parameter_profile( Method that creates the next starting point for optimization in profiling. One of the ``update_type`` options supported by :func:`pypesto.profile.profile_next_guess.next_guess`. + Default: ``"adaptive_step_order_0"`` (adaptive step size, no extrapolation + of other parameters). ``"adaptive_step_order_1"`` is a two-point + extrapolation based only on the last profile step and is not the same + as regression-based extrapolation. More robust than regression-based + methods for complex models. profile_options: Various options applied to the profile optimization. See :class:`pypesto.profile.options.ProfileOptions`. diff --git a/pypesto/profile/profile_next_guess.py b/pypesto/profile/profile_next_guess.py index 7db328289..0667ac3fe 100644 --- a/pypesto/profile/profile_next_guess.py +++ b/pypesto/profile/profile_next_guess.py @@ -51,9 +51,14 @@ def next_guess( Type of update for next profile point. Available options are: * ``fixed_step`` (see :func:`fixed_step`) - * ``adaptive_step_order_0`` (see :func:`adaptive_step`). - * ``adaptive_step_order_1`` (see :func:`adaptive_step`). - * ``adaptive_step_regression`` (see :func:`adaptive_step`). + * ``adaptive_step_order_0``: adaptive step size without extrapolating + the other parameters (see :func:`adaptive_step`). + * ``adaptive_step_order_1``: adaptive step size with two-point + extrapolation based only on the last profile step + (see :func:`adaptive_step`). + * ``adaptive_step_regression``: adaptive step size with regression-based + extrapolation using several recent profile points + (see :func:`adaptive_step`). current_profile: The profile which should be computed. problem: @@ -189,10 +194,12 @@ def adaptive_step( Specifies the precise algorithm for extrapolation. Available options are: - * ``0``: just one parameter is updated - * ``1``: the last two points are used to extrapolate all parameters - * ``np.nan``: indicates that a more complex regression should be used - as determined by :attr:`pypesto.profile.ProfileOptions.reg_order`. + * ``0``: just the profiled parameter is updated + * ``1``: the last two profile points are used to extrapolate all + parameters, i.e. only the last profile step direction is reused + * ``np.nan``: indicates that a regression over several recent profile + points should be used, as determined by + :attr:`pypesto.profile.ProfileOptions.reg_order`. min_step_increase_factor: Factor to increase the minimal step size bound. max_step_reduce_factor: @@ -469,28 +476,49 @@ def get_reg_polynomial( # set up matrix of regression parameters reg_par = [] + x_prof = current_profile.x_path[par_index, -reg_points:] + x_prof_std = np.std(x_prof) 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 a np.nan reg_par.append(np.nan) else: + y_par = current_profile.x_path[i_par, -reg_points:] + par_reg_order = reg_order + + # Only extrapolate a free parameter if it is sufficiently + # correlated with the profiled parameter over the recent + # profile history. Otherwise keep it at its current value. + if x_prof_std == 0 or np.std(y_par) == 0: + correlation = 0.0 + else: + correlation = np.corrcoef(x_prof, y_par)[0, 1] + if np.isnan(correlation): + correlation = 0.0 + + if abs(correlation) <= options.correlation_threshold: + reg_par.append( + np.array([0.0, current_profile.x_path[i_par, -1]]) + ) + continue + # Do polynomial interpolation of profile path # Determine rank of polynomial interpolation regression_tmp = np.polyfit( - current_profile.x_path[par_index, -reg_points:], - current_profile.x_path[i_par, -reg_points:], - reg_order, + x_prof, + y_par, + par_reg_order, full=True, ) # Decrease rank if interpolation problem is ill-conditioned - if regression_tmp[2] < reg_order: - reg_order = regression_tmp[2] + if regression_tmp[2] < par_reg_order: + par_reg_order = regression_tmp[2] regression_tmp = np.polyfit( - current_profile.x_path[par_index, -reg_points:], - current_profile.x_path[i_par, -reg_points:], - int(reg_order), + x_prof, + y_par, + int(par_reg_order), full=True, ) diff --git a/test/profile/test_profile.py b/test/profile/test_profile.py index 26f899064..b415050bf 100644 --- a/test/profile/test_profile.py +++ b/test/profile/test_profile.py @@ -466,6 +466,8 @@ def test_options_valid(): "default_step_size_relative": 0.03, "max_step_size_relative": 0.02, }, + {"correlation_threshold": -0.1}, + {"correlation_threshold": 1.1}, {"profile_n_starts": 0}, {"profile_sampling_sigma": 0}, {"step_size_precheck_mode": "invalid"}, From 39d14435a5e0ec685c6872cc771cd63efa138fd8 Mon Sep 17 00:00:00 2001 From: Doresic <85789271+Doresic@users.noreply.github.com> Date: Mon, 11 May 2026 14:48:18 +0200 Subject: [PATCH 2/2] Small docstring error fix --- pypesto/profile/options.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pypesto/profile/options.py b/pypesto/profile/options.py index 5c03a9a55..63af8d738 100644 --- a/pypesto/profile/options.py +++ b/pypesto/profile/options.py @@ -46,8 +46,9 @@ class ProfileOptions(dict): Higher values may cause extrapolation instability. correlation_threshold: Minimum absolute correlation coefficient for extrapolating a parameter - in regression-based methods. Parameters with |correlation| below this - threshold will be kept at their current values instead of extrapolated. + in regression-based methods. Parameters whose absolute correlation is + below this threshold will be kept at their current values instead of + extrapolated. adaptive_target_scaling_factor: The scaling factor of the next_obj_target in next guess generation. Larger values result in larger next_guess step size (must be > 1).