Source code for sambuca.scipy_objective

""" Objective function for parameter estimation using scipy minimisation.
"""


from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals)
from builtins import *

from collections import Callable

import sambuca_core as sbc

from .error import distance_f


[docs]class SciPyObjective(Callable): """ Configurable objective function for Sambuca parameter estimation, intended for use with the SciPy minimisation methods. Attributes: observed_rrs (array-like): The observed remotely-sensed reflectance. This attribute must be updated when you require the objective instance to use a different value. id (integer): The index of the substrate pair combination. This attribute must be updated when you require the objective instance to use a different substrate pair. """ def __init__( self, sensor_filter, fixed_parameters, error_function=distance_f, nedr=None): """ Initialise the ArrayWriter. Args: sensor_filter (array-like): The Sambuca sensor filter. fixed_parameters (sambuca.AllParameters): The fixed model parameters. error_function (Callable): The error function that will be applied to the modelled and observed rrs. nedr (array-like): Noise equivalent difference in reflectance. """ super().__init__() # check for being passed the (wavelengths, filter) tuple loaded by the # sambuca_core sensor_filter loading functions if isinstance(sensor_filter, tuple) and len(sensor_filter) == 2: self._sensor_filter = sensor_filter[1] else: self._sensor_filter = sensor_filter self._nedr = nedr self._fixed_parameters = fixed_parameters self._error_func = error_function self.observed_rrs = None self.id = None def __call__(self, parameters): """ Returns an objective score for the given parameter set. Args: parameters (ndarray): The parameter array in the order (chl, cdom, nap, depth, substrate_fraction) as defined in the FreeParameters tuple """ # TODO: do I need to implement this? Here or in a subclass? # To support algorithms without support for boundary values, we assign a high # score to out of range parameters. This may not be the best approach!!! # p_bounds is a tuple of (min, max) pairs for each parameter in p ''' if p_bounds is not None: for _p, lu in zip(p, p_bounds): l, u = lu if _p < l or _p > u: return 100000.0 ''' # Select the substrate pair from the list of substrates id1 = self._fixed_parameters.substrate_combinations[self.id][0] id2 = self._fixed_parameters.substrate_combinations[self.id][1] # Generate results from the given parameters model_results = sbc.forward_model( chl=parameters[0], cdom=parameters[1], nap=parameters[2], depth=parameters[3], substrate_fraction=parameters[4], substrate1=self._fixed_parameters.substrates[id1], wavelengths=self._fixed_parameters.wavelengths, a_water=self._fixed_parameters.a_water, a_ph_star=self._fixed_parameters.a_ph_star, num_bands=self._fixed_parameters.num_bands, substrate2=self._fixed_parameters.substrates[id2], a_cdom_slope=self._fixed_parameters.a_cdom_slope, a_nap_slope=self._fixed_parameters.a_nap_slope, bb_ph_slope=self._fixed_parameters.bb_ph_slope, bb_nap_slope=self._fixed_parameters.bb_nap_slope, lambda0cdom=self._fixed_parameters.lambda0cdom, lambda0nap=self._fixed_parameters.lambda0nap, lambda0x=self._fixed_parameters.lambda0x, x_ph_lambda0x=self._fixed_parameters.x_ph_lambda0x, x_nap_lambda0x=self._fixed_parameters.x_nap_lambda0x, a_cdom_lambda0cdom=self._fixed_parameters.a_cdom_lambda0cdom, a_nap_lambda0nap=self._fixed_parameters.a_nap_lambda0nap, bb_lambda_ref=self._fixed_parameters.bb_lambda_ref, water_refractive_index=self._fixed_parameters.water_refractive_index, theta_air=self._fixed_parameters.theta_air, off_nadir=self._fixed_parameters.off_nadir, q_factor=self._fixed_parameters.q_factor) closed_rrs = sbc.apply_sensor_filter( model_results.rrs, self._sensor_filter) return self._error_func(self.observed_rrs, closed_rrs, self._nedr)