Source code for optimagic.parameters.scaling

from dataclasses import dataclass
from typing import Literal, TypedDict

from typing_extensions import NotRequired

from optimagic.exceptions import InvalidScalingError


[docs]@dataclass(frozen=True) class ScalingOptions: """Scaling options in optimization problems. Attributes: method: The method used for scaling. Can be "start_values" or "bounds". Default is "start_values". clipping_value: The minimum value to which elements are clipped to avoid division by zero. Must be a positive number. Default is 0.1. magnitude: A factor by which the scaled parameters are multiplied to adjust their magnitude. Must be a positive number. Default is 1.0. Raises: InvalidScalingError: If scaling options cannot be processed, e.g. because they do not have the correct type. """ method: Literal["start_values", "bounds"] = "start_values" clipping_value: float = 0.1 magnitude: float = 1.0 def __post_init__(self) -> None: _validate_attribute_types_and_values(self)
class ScalingOptionsDict(TypedDict): method: NotRequired[Literal["start_values", "bounds"]] clipping_value: NotRequired[float] magnitude: NotRequired[float] def pre_process_scaling( scaling: bool | ScalingOptions | ScalingOptionsDict | None, ) -> ScalingOptions | None: """Convert all valid types of scaling options to optimagic.ScalingOptions. This just harmonizes multiple ways of specifying scaling options into a single format. It performs runtime type checks, but it does not check whether scaling options are consistent with other option choices. Args: scaling: The user provided scaling options. Returns: The scaling options in the optimagic format. Raises: InvalidScalingError: If scaling options cannot be processed, e.g. because they do not have the correct type. """ if isinstance(scaling, bool): scaling = ScalingOptions() if scaling else None elif isinstance(scaling, ScalingOptions) or scaling is None: pass else: try: scaling = ScalingOptions(**scaling) except (KeyboardInterrupt, SystemExit): raise except Exception as e: if isinstance(e, InvalidScalingError): raise e raise InvalidScalingError( f"Invalid scaling options of type: {type(scaling)}. Scaling options " "must be of type optimagic.ScalingOptions, a dictionary with a subset " "of the keys {'method', 'clipping_value', 'magnitude'}, None, or a " "boolean." ) from e return scaling def _validate_attribute_types_and_values(options: ScalingOptions) -> None: if options.method not in ("start_values", "bounds"): raise InvalidScalingError( f"Invalid scaling method: {options.method}. Valid methods are " "'start_values' and 'bounds'." ) if ( not isinstance(options.clipping_value, int | float) or options.clipping_value <= 0 ): raise InvalidScalingError( f"Invalid clipping value: {options.clipping_value}. Clipping value " "must be a positive number." ) if not isinstance(options.magnitude, int | float) or options.magnitude <= 0: raise InvalidScalingError( f"Invalid scaling magnitude: {options.magnitude}. Scaling magnitude " "must be a positive number." )