SimonBlanke/Gradient-Free-Optimizers

View on GitHub
gradient_free_optimizers/optimizers/global_opt/lipschitz_optimization.py

Summary

Maintainability
A
0 mins
Test Coverage
# Author: Simon Blanke
# Email: simon.blanke@yahoo.com
# License: MIT License

import numpy as np

from ..smb_opt.smbo import SMBO

from scipy.spatial.distance import cdist


class LipschitzFunction:
    def __init__(self, position_l):
        self.position_l = position_l

    def find_best_slope(self, X_sample, Y_sample):
        slopes = [
            abs(y_sample1 - y_sample2) / abs(x_sample1 - x_sample2)
            for x_sample1, y_sample1 in zip(X_sample, Y_sample)
            for x_sample2, y_sample2 in zip(X_sample, Y_sample)
            if y_sample1 is not y_sample2
            if np.prod((x_sample1 - x_sample2)) != 0
        ]

        if len(slopes) == 0:
            return 1
        return np.max(slopes)

    def calculate(self, X_sample, Y_sample, score_best):
        lip_c = self.find_best_slope(X_sample, Y_sample)

        positions_np = np.array(self.position_l)
        samples_np = np.array(X_sample)

        pos_dist = cdist(positions_np, samples_np) * lip_c

        upper_bound_l = pos_dist
        upper_bound_l += np.array(Y_sample)

        mx = np.ma.masked_array(upper_bound_l, mask=upper_bound_l == 0)
        upper_bound_l = mx.min(1).reshape(1, -1).T
        upper_bound_l[upper_bound_l <= score_best] = -np.inf

        return upper_bound_l


class LipschitzOptimizer(SMBO):
    name = "Lipschitz Optimizer"
    _name_ = "lipschitz_optimizer"
    __name__ = "LipschitzOptimizer"

    optimizer_type = "sequential"
    computationally_expensive = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def finish_initialization(self):
        self.all_pos_comb = self._all_possible_pos()
        return super().finish_initialization()

    @SMBO.track_new_pos
    @SMBO.track_X_sample
    def iterate(self):
        self.pos_comb = self._sampling(self.all_pos_comb)

        lip_func = LipschitzFunction(self.pos_comb)
        upper_bound_l = lip_func.calculate(
            self.X_sample, self.Y_sample, self.score_best
        )

        index_best = list(upper_bound_l.argsort()[::-1])
        all_pos_comb_sorted = self.pos_comb[index_best]
        pos_best = all_pos_comb_sorted[0]

        return pos_best