simopt.solvers.astrodf

ASTRO-DF Solver.

The ASTRO-DF solver progressively builds local models (quadratic with diagonal Hessian) using interpolation on a set of points on the coordinate bases of the best (incumbent) solution. Solving the local models within a trust region (closed ball around the incumbent solution) at each iteration suggests a candidate solution for the next iteration. If the candidate solution is worse than the best interpolation point, it is replaced with the latter (a.k.a. direct search). The solver then decides whether to accept the candidate solution and expand the trust-region or reject it and shrink the trust-region based on a success ratio test. The sample size at each visited point is determined adaptively and based on closeness to optimality. A detailed description of the solver can be found here.

This version does not require a delta_max, instead it estimates the maximum step size using get_random_solution(). Parameter tuning on delta_max is therefore not needed and removed from this version as well. - Delta_max is so longer a factor, instead the maximum step size is estimated using get_random_solution(). - Parameter tuning on delta_max is therefore not needed and removed from this version as well. - No upper bound on sample size may be better - testing - It seems for SAN we always use pattern search - why? because the problem is convex and model may be misleading at the beginning - Added sufficient reduction for the pattern search

Module Contents

class simopt.solvers.astrodf.ASTRODFConfig

Bases: simopt.base.SolverConfig

Configuration for ASTRO-DF solver.

eta_1: Annotated[float, Field(default=0.1, gt=0, description='threshold for a successful iteration')]
eta_2: Annotated[float, Field(default=0.8, description='threshold for a very successful iteration')]
gamma_1: Annotated[float, Field(default=2.5, gt=1, description='trust-region radius increase rate after successful iteration')]
gamma_2: Annotated[float, Field(default=0.5, gt=0, lt=1, description='trust-region radius decrease rate after failed iteration')]
lambda_min: Annotated[int, Field(default=5, gt=2, description='minimum sample size')]
easy_solve: Annotated[bool, Field(default=True, description='solve the subproblem approximately with Cauchy point')]
reuse_points: Annotated[bool, Field(default=True, description='reuse the previously visited points')]
ps_sufficient_reduction: Annotated[float, Field(default=0.1, ge=0, description='use pattern search if with sufficient reduction, 0 always allows it, large value never does')]
use_gradients: Annotated[bool, Field(default=True, description='if direct gradient observations are available, use them')]
class simopt.solvers.astrodf.ASTRODF(name: str = '', fixed_factors: dict | None = None)

Bases: simopt.base.Solver

The ASTRO-DF solver.

Initialize a solver object.

Parameters:
  • name (str, optional) – Name of the solver. Defaults to an empty string.

  • fixed_factors (dict | None, optional) – Dictionary of user-specified solver factors. Defaults to None.

name: str = 'ASTRODF'
config_class: ClassVar[type[simopt.base.SolverConfig]]

Configuration class for the solver.

class_name_abbr: ClassVar[str] = 'ASTRODF'

Short name of the solver class.

class_name: ClassVar[str] = 'ASTRO-DF'

Long name of the solver class.

objective_type: ClassVar[simopt.base.ObjectiveType]

Description of objective types.

constraint_type: ClassVar[simopt.base.ConstraintType]

Description of constraint types.

variable_type: ClassVar[simopt.base.VariableType]

Description of variable types.

gradient_needed: ClassVar[bool] = False

True if gradient of objective function is needed, otherwise False.

property iteration_count: int

Get the current iteration count.

property delta_k: float

Get the current delta_k value.

property delta_max: float

Get the current delta_max value.

property incumbent_x: tuple[float, Ellipsis]

Get the incumbent solution.

property incumbent_solution: simopt.base.Solution

Get the incumbent solution.

property h_k: numpy.ndarray

Get the Hessian approximation.

get_coordinate_vector(size: int, v_no: int) numpy.ndarray

Generate the coordinate vector corresponding to the variable number v_no.

get_rotated_basis(first_basis: numpy.ndarray, rotate_index: numpy.ndarray) numpy.ndarray

Generate the basis (rotated coordinate).

The first vector comes from the visited design points (origin basis)

evaluate_model(x_k: numpy.ndarray, q: numpy.ndarray) float

Evaluate a local quadratic model using linear interpolation and a diagonal Hessian.

Parameters:
  • x_k (np.ndarray) – The point at which to evaluate the model (decision variables).

  • q (np.ndarray) – Coefficient vector defining the local quadratic model.

Returns:

The evaluated model value as a NumPy array.

Return type:

np.ndarray

get_stopping_time(pilot_run: int, sig2: float, delta: float, kappa: float) int

Compute the sample size using adaptive stopping based on the optimality gap.

Parameters:
  • pilot_run (int) – Number of initial samples used in the pilot run.

  • sig2 (float) – Estimated variance of the solution.

  • delta (float) – Optimality gap threshold.

  • kappa (float) – Constant in the stopping time denominator. If 0, it defaults to 1.

Returns:

The computed sample size, rounded up to the nearest integer.

Return type:

int

select_interpolation_points(delta_k: float, f_index: int) tuple[list, list]

Select interpolation points for the local model.

Parameters:
  • delta_k (float) – The current trust-region radius.

  • f_index (int) – The index of the farthest design point.

Returns:

A tuple containing:
  • var_y (list): The interpolation points.

  • var_z (list): The reused design point.

Return type:

tuple[list, list]

perform_adaptive_sampling(solution: simopt.base.Solution, pilot_run: int, delta_k: float, compute_kappa: bool = False) None

Perform adaptive sampling on a solution until the stopping condition is met.

Parameters:
  • solution (Solution) – The solution object being sampled.

  • pilot_run (int) – The number of initial pilot runs.

  • delta_k (float) – The current trust-region radius.

  • compute_kappa (bool) – Whether or not to compute kappa dynamically (needed in the first iteration).

construct_model() tuple[list[float], list, numpy.ndarray, numpy.ndarray, numpy.ndarray, list[simopt.base.Solution]]

Construct the local model for the current iteration.

Construct the “qualified” local model for each iteration k with the center point x_k reconstruct with new points in a shrunk trust-region if the model fails the criticality condition the criticality condition keeps the model gradient norm and the trust-region size in lock-step

get_model_coefficients(y_var: list, fval: list, problem: simopt.base.Problem) tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]

Compute model coefficients using 2d+1 design points and function values.

This method fits a quadratic model with a diagonal Hessian by evaluating 2 * dim + 1 points centered at the solution.

Parameters:
  • y_var (list) – List of sampled decision vectors (design points).

  • fval (list) – Corresponding function values for each design point.

  • problem (Problem) – Problem instance providing dimension and structure.

Returns:

A tuple containing:
  • q (np.ndarray): Coefficients of the fitted local quadratic model.

  • y_mean (np.ndarray): Mean of the y_var design points.

  • fval_mean (np.ndarray): Mean of the function values.

Return type:

tuple[np.ndarray, np.ndarray, np.ndarray]

get_coordinate_basis_interpolation_points(x_k: tuple[int | float, Ellipsis], delta: float, problem: simopt.base.Problem) list[list[list[int | float]]]

Compute the interpolation points (2d+1) using the coordinate basis.

get_rotated_basis_interpolation_points(x_k: numpy.ndarray, delta: float, problem: simopt.base.Problem, rotate_matrix: numpy.ndarray, reused_x: numpy.ndarray) list[list[numpy.ndarray]]

Compute the interpolation points (2d+1) using the rotated coordinate basis.

One design point is reused, which is the farthest point from the center point.

update_hessian(candidate_solution: simopt.base.Solution, grad: numpy.ndarray, s: numpy.ndarray) None

Performs Hessian update if gradients are enabled.

iterate() None

Run one iteration of the ASTRO-DF algorithm.

Build and solve a local model, update the current incumbent and trust-region radius, and save the data

solve(problem: simopt.base.Problem) None

Run a single macroreplication of a solver on a problem.

Parameters:

problem (Problem) – Simulation-optimization problem to solve.

Returns:

  • list [Solution]: List of solutions recommended throughout the budget.

  • list [int]: List of intermediate budgets when recommended solutions

    change.

Return type:

tuple

simopt.solvers.astrodf.clamp_with_epsilon(val: float, lower_bound: float, upper_bound: float, epsilon: float = 0.01) float

Clamp a value within bounds while avoiding exact boundary values.

Adds a small epsilon to the lower bound or subtracts it from the upper bound if val lies outside the specified range.

Parameters:
  • val (float) – The value to clamp.

  • lower_bound (float) – Minimum acceptable value.

  • upper_bound (float) – Maximum acceptable value.

  • epsilon (float, optional) – Small margin to avoid returning exact boundary values. Defaults to 0.01.

Returns:

The adjusted value, guaranteed to lie strictly within the bounds.

Return type:

float