@@ -45,7 +45,7 @@ def impf_step_generator(threshold: Number, paa: Number) -> ImpactFuncSet:
4545class Input :
4646 """Define the static input for a calibration task
4747
48- Parameters
48+ Attributes
4949 ----------
5050 hazard : climada.Hazard
5151 Hazard object to compute impacts from
@@ -108,7 +108,21 @@ def __post_init__(self, align):
108108
109109@dataclass
110110class Output :
111- """Define the output of a calibration task"""
111+ """Generic output of a calibration task
112+
113+ Attributes
114+ ----------
115+ params : Mapping (str, Number)
116+ The optimal parameters
117+ target : Number
118+ The target function value for the optimal parameters
119+ success : bool
120+ If the calibration succeeded. The definition depends on the actual optimization
121+ algorithm used.
122+ result
123+ A result object specific to the optimization algorithm used. See the optimizer
124+ documentation for details.
125+ """
112126
113127 params : Mapping [str , Number ]
114128 target : Number
@@ -118,19 +132,77 @@ class Output:
118132
119133@dataclass
120134class Optimizer (ABC ):
121- """Define the basic interface for an optimization"""
135+ """Abstract base class (interface) for an optimization
136+
137+ This defines the interface for optimizers in CLIMADA. New optimizers can be created
138+ by deriving from this class and overriding at least the :py:meth:`run` method.
139+
140+ Attributes
141+ ----------
142+ input : Input
143+ The input object for the optimization task. See :py:class:`Input`.
144+ """
122145
123146 input : Input
124147
125- def _target_func (self , impact : Impact , data : pd .DataFrame ):
148+ def _target_func (self , impact : Impact , data : pd .DataFrame ) -> Number :
149+ """Target function for the optimizer
150+
151+ The default version of this function simply returns the value of the cost
152+ function evaluated on the arguments.
153+
154+ Paramters
155+ ---------
156+ impact : climada.engine.Impact
157+ The impact object returned by the impact calculation.
158+ data : pandas.DataFrame
159+ The data used for calibration. See :py:attr:`Input.data`.
160+
161+ Returns
162+ -------
163+ The value of the target function for the optimizer.
164+ """
126165 return self .input .cost_func (impact , data )
127166
128167 def _kwargs_to_impact_func_gen (self , * _ , ** kwargs ) -> Dict [str , Any ]:
129- """Define how the parameters to 'opt_func' must be transformed"""
168+ """Define how the parameters to 'opt_func' must be transformed
169+
170+ Optimizers may implement different ways of representing the parameters (e.g.,
171+ key-value pairs, arrays, etc.). Depending on this representation, the parameters
172+ must be transformed to match the syntax of the impact function generator used,
173+ see :py:attr:`Input.impact_func_gen`.
174+
175+ In this default version, the method simply returns its keyword arguments as
176+ mapping. Override this method if the optimizer used *does not* represent
177+ parameters as key-value pairs.
178+
179+ Parameters
180+ ----------
181+ kwargs
182+ The parameters as key-value pairs.
183+
184+ Returns
185+ -------
186+ The parameters as key-value pairs.
187+ """
130188 return kwargs
131189
132- def _opt_func (self , * args , ** kwargs ):
133- """The optimization function that is iterated"""
190+ def _opt_func (self , * args , ** kwargs ) -> Number :
191+ """The optimization function iterated by the optimizer
192+
193+ This function takes arbitrary arguments from the optimizer, generates a new set
194+ of impact functions from it, computes the impact, and finally calculates the
195+ target function value and returns it.
196+
197+ Parameters
198+ ----------
199+ args, kwargs
200+ Arbitrary arguments from the optimizer, including parameters
201+
202+ Returns
203+ -------
204+ Target function value for the given arguments
205+ """
134206 params = self ._kwargs_to_impact_func_gen (* args , ** kwargs )
135207 impf_set = self .input .impact_func_gen (** params )
136208 impact = ImpactCalc (
@@ -143,7 +215,6 @@ def _opt_func(self, *args, **kwargs):
143215 @abstractmethod
144216 def run (self , ** opt_kwargs ) -> Output :
145217 """Execute the optimization"""
146- pass
147218
148219
149220@dataclass
@@ -155,14 +226,33 @@ def __post_init__(self):
155226 self ._param_names : List [str ] = list ()
156227
157228 def _kwargs_to_impact_func_gen (self , * args , ** _ ) -> Dict [str , Any ]:
229+ """Transform the array of parameters into key-value pairs"""
158230 return dict (zip (self ._param_names , args [0 ].flat ))
159231
160232 def _select_by_param_names (self , mapping : Mapping [str , Any ]) -> List [Any ]:
161233 """Return a list of entries from a map with matching keys or ``None``"""
162234 return [mapping .get (key ) for key in self ._param_names ]
163235
164- def run (self , ** opt_kwargs ):
165- """Execute the optimization"""
236+ def run (self , ** opt_kwargs ) -> Output :
237+ """Execute the optimization
238+
239+ Parameters
240+ ----------
241+ params_init : Mapping (str, Number)
242+ The initial guess for all parameters as key-value pairs.
243+ method : str, optional
244+ The minimization method applied. Defaults to ``"trust-constr"``.
245+ See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
246+ for details.
247+ kwargs
248+ Additional keyword arguments passed to ``scipy.optimize.minimize``.
249+
250+ Returns
251+ -------
252+ output : Output
253+ The output of the optimization. The :py:attr:`Output.result` attribute
254+ stores the associated ``scipy.optimize.OptimizeResult`` instance.
255+ """
166256 # Parse kwargs
167257 params_init = opt_kwargs .pop ("params_init" )
168258 method = opt_kwargs .pop ("method" , "trust-constr" )
@@ -213,9 +303,17 @@ def __post_init__(
213303 ** bayes_opt_kwds ,
214304 )
215305
216- def run (self , init_points : int = 100 , n_iter : int = 200 , ** opt_kwargs ):
306+ def run (self , ** opt_kwargs ):
217307 """Execute the optimization"""
308+ # Retrieve parameters
309+ num_params = len (self .input .bounds )
310+ init_points = opt_kwargs .pop ("init_points" , 10 ** num_params )
311+ n_iter = opt_kwargs .pop ("n_iter" , 10 ** num_params )
312+
313+ # Run optimizer
218314 self .optimizer .maximize (init_points = init_points , n_iter = n_iter , ** opt_kwargs )
315+
316+ # Return output
219317 opt = self .optimizer .max
220318 return Output (
221319 params = opt ["params" ],
0 commit comments