def update(self, sample: GrainSizeSample, distribution: BaseDistribution, func_args: typing.Iterable[float], fraction: float): self.__func_args = func_args if np.any(np.isnan(func_args)): self.__fraction = np.nan self.__distribution = np.full_like(sample.distribution, fill_value=np.nan) self.__geometric_moments = INVALID_STATISTIC self.__logarithmic_moments = INVALID_STATISTIC self.__is_valid = False else: self.__fraction = fraction self.__distribution = distribution.single_function( sample.classes_φ, *func_args) self.__geometric_moments = geometric(sample.classes_μm, self.__distribution) self.__logarithmic_moments = logarithmic(sample.classes_φ, self.__distribution) values_to_check = [self.__fraction] values_to_check.extend(self.__distribution) keys = ["mean", "std", "skewness", "kurtosis"] for key in keys: values_to_check.append(self.__geometric_moments[key]) values_to_check.append(self.__logarithmic_moments[key]) values_to_check = np.array(values_to_check) # if any value is nan of inf, this result is invalid if np.any(np.isnan(values_to_check) | np.isinf(values_to_check)): self.__is_valid = False else: self.__is_valid = True
def get_distance(self, distance: str): distance_func = distance_func_numpy(distance) distribution = BaseDistribution.get_distribution( self.__distribution_type, self.__n_components) values = distribution.mixed_function(self.__sample.classes_φ, *self.__mixed_func_args) targets = self.__sample.distribution distance = distance_func(values, targets) return distance
def on_try_clicked(self): if self.last_task is None: return new_task = copy.copy(self.last_task) reference, fractions = self.expected initial_guess = BaseDistribution.get_initial_guess( self.last_task.distribution_type, reference, fractions=fractions) new_task.initial_guess = initial_guess self.async_worker.execute_task(new_task)
def setup_task(self, task: SSUTask): self.last_task = task self.try_button.setEnabled(True) if self.n_components != task.n_components: self.change_n_components(task.n_components) reference, fractions = self.expected initial_guess = BaseDistribution.get_initial_guess( task.distribution_type, reference, fractions=fractions) result = SSUResult(task, initial_guess) self.chart.show_model(result.view_model, quick=False)
def get_distance_series(self, distance: str): distance_func = distance_func_numpy(distance) distribution = BaseDistribution.get_distribution( self.__distribution_type, self.__n_components) distance_series = [] for func_args in self.__history: values = distribution.mixed_function(self.__sample.classes_φ, *func_args) targets = self.__sample.distribution distance = distance_func(values, targets) distance_series.append(distance) return distance_series
def update_chart(self): if self.last_task is None: return reference, fractions = self.expected for comp_ref in reference: if comp_ref["std"] == 0.0: return # print(reference) initial_guess = BaseDistribution.get_initial_guess( self.last_task.distribution_type, reference, fractions=fractions) result = SSUResult(self.last_task, initial_guess) self.chart.show_model(result.view_model, quick=True)
def update(self, mixed_func_args: typing.Iterable[float]): distribution = BaseDistribution.get_distribution( self.__distribution_type, self.__n_components) if np.any(np.isnan(mixed_func_args)): self.__distribution = np.full_like(self.__sample.distribution, fill_value=np.nan) self.__is_valid = False else: self.__distribution = distribution.mixed_function( self.__sample.classes_φ, *mixed_func_args) unpacked_args = distribution.unpack_parameters(mixed_func_args) if len(self.__components) == 0: for func_args, fraction in unpacked_args: component_result = ComponentResult(self.__sample, distribution, func_args, fraction) self.__components.append(component_result) else: for component, (func_args, fraction) in zip(self.__components, unpacked_args): component.update(self.__sample, distribution, func_args, fraction) # sort by mean φ values # reverse is necessary self.__components.sort( key=lambda component: component.logarithmic_moments["mean"], reverse=True) self.__is_valid = True if np.any( np.isnan(self.__distribution) | np.isinf(self.__distribution)): self.__is_valid = False for component in self.__components: if not component.is_valid: self.__is_valid = False break
def try_fit(self, task: SSUTask) -> typing.Tuple[FittingState, object]: assert task.resolver == "classic" history = [] distribution = BaseDistribution.get_distribution( task.distribution_type, task.n_components) if task.resolver_setting is None: setting = ClassicResolverSetting() else: assert isinstance(task.resolver_setting, ClassicResolverSetting) setting = task.resolver_setting distance = distance_func_numpy(setting.distance) start_time = time.time() self.on_fitting_started() use_weights = False if use_weights: weights = self.get_weights(task.sample.classes_φ, task.sample.distribution) def closure(params): params[-task.n_components:] = np.abs( params[-task.n_components:]) current_values = distribution.mixed_function( task.sample.classes_φ, *params) return distance(current_values * weights, task.sample.distribution * weights) else: def closure(params): params[-task.n_components:] = np.abs( params[-task.n_components:]) current_values = distribution.mixed_function( task.sample.classes_φ, *params) return distance(current_values, task.sample.distribution) def local_callback(mixed_func_args, *addtional): history.append(mixed_func_args) self.local_iteration_callback(mixed_func_args) initial_guess = task.initial_guess if task.initial_guess is None: initial_guess = np.array(distribution.defaults) if task.reference is not None: assert len(task.reference) == task.n_components initial_guess = BaseDistribution.get_initial_guess( task.distribution_type, task.reference) if setting.minimizer == "trust-constr": GO_options = { "maxiter": setting.GO_minimizer_max_niter, # "ftol": setting.GO_minimizer_ftol, "disp": False } FLO_options = { "maxiter": setting.FLO_max_niter, # "ftol": setting.FLO_ftol, "disp": False } else: GO_options = { "maxiter": setting.GO_minimizer_max_niter, "ftol": setting.GO_minimizer_ftol, "disp": False } FLO_options = { "maxiter": setting.FLO_max_niter, "ftol": setting.FLO_ftol, "disp": False } if setting.try_GO: global_optimization_minimizer_kwargs = \ dict(method=setting.minimizer, tol=setting.GO_minimizer_tol, bounds=distribution.bounds, constraints=distribution.constrains, callback=local_callback, options=GO_options) GO_result = \ basinhopping(closure, x0=initial_guess, minimizer_kwargs=global_optimization_minimizer_kwargs, callback=self.global_iteration_callback, niter_success=setting.GO_success_niter, niter=setting.GO_max_niter, stepsize=setting.GO_step) if GO_result.lowest_optimization_result.success or \ GO_result.lowest_optimization_result.status == 9: self.on_global_fitting_succeeded(GO_result) initial_guess = GO_result.x else: self.on_global_fitting_failed(GO_result) self.on_fitting_finished() return FittingState.Failed, GO_result FLO_result = \ minimize(closure, method=setting.minimizer, x0=initial_guess, tol=setting.FLO_tol, bounds=distribution.bounds, constraints=distribution.constrains, callback=local_callback, options=FLO_options) # judge if the final fitting succeed # see https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_slsqp.html # When the minimizer is "Nelder-Mead", it will return failed result if it has reached the max niter if FLO_result.success or FLO_result.status == 9 or setting.minimizer == "Nelder-Mead" or setting.minimizer == "trust-constr": finish_time = time.time() self.on_fitting_finished() time_spent = finish_time - start_time fitting_result = SSUResult(task, FLO_result.x, history=history, time_spent=time_spent) self.on_fitting_succeeded(FLO_result, fitting_result) return FittingState.Succeeded, fitting_result else: self.on_final_fitting_failed(FLO_result) self.on_fitting_finished() return FittingState.Failed, FLO_result
def get_initial_guess(distribution_type: DistributionType, reference): return BaseDistribution.get_initial_guess(distribution_type, reference)