Exemplo n.º 1
0
    def __init__(self,
                 bounds,
                 func_eval,
                 noise_eval,
                 truth_eval,
                 options,
                 A=None,
                 b=None):
        """
        Alpha DOGS is an efficient optimization algorithm for time-averaged statistics.
        :param bounds           :   The physical bounds of the input for the test problem,
                                    e.g. lower bound = [0, 0, 0] while upper bound = [1, 1, 1] then
                                    bnds = np.hstack((np.zeros((3,1)), np.ones((3,1))))

        :param func_eval        :   The objective function

        :param noise_eval       :   The function for noise evaluation

        :param truth_eval       :   The function to evaluate the truth values

        :param A                :   The linear constraints of parameter space

        :param b                :   The linear constraints of parameter space

        :param options          :   The options for alphaDOGS
        """
        # n: The dimension of input parameter space
        self.n = bounds.shape[0]
        # physical lb & ub: Normalize the physical bounds
        self.physical_lb = bounds[:, 0].reshape(-1, 1)
        self.physical_ub = bounds[:, 1].reshape(-1, 1)
        # lb & ub: The normalized search bounds
        self.lb = np.zeros((self.n, 1))
        self.ub = np.ones((self.n, 1))

        # ms: The mesh size for each iteration. This is the initial definition.
        self.initial_mesh_size = options.get_option('Initial mesh size')
        self.ms = 2**self.initial_mesh_size
        self.num_mesh_refine = options.get_option('Number of mesh refinement')
        self.max_mesh_size = 2**(self.initial_mesh_size + self.num_mesh_refine)

        self.iter = 0

        # Define the surrogate model
        if options.get_option('Constant surrogate') and options.get_option(
                'Adaptive surrogate'):
            raise ValueError(
                'Constant and Adaptive surrogate both activated. Set one to False then rerun code.'
            )
        elif not options.get_option(
                'Constant surrogate') and not options.get_option(
                    'Adaptive surrogate'):
            raise ValueError(
                'Constant and Adaptive surrogate both inactivated. Set one to True then rerun code.'
            )
        elif options.get_option('Constant surrogate'):
            self.surrogate_type = 'c'
        elif options.get_option('Adaptive surrogate'):
            self.surrogate_type = 'a'
        else:
            pass

        if self.surrogate_type == 'c':
            # Define the parameters for discrete and continuous constant search function
            self.L = options.get_option('Constant L')
            self.L0 = self.L
            self.K = options.get_option('Constant K')

        elif self.surrogate_type == 'a':
            self.y0 = options.get_option('Target value')

        # Define the linear constraints, Ax <= b. if A and b are None type, set them to be the box domain constraints.
        if (A and b) is None:
            self.Ain = np.concatenate(
                (np.identity(self.n), -np.identity(self.n)), axis=0)
            self.Bin = np.concatenate((np.ones(
                (self.n, 1)), np.zeros((self.n, 1))),
                                      axis=0)
        else:
            pass

        # Define the statistics for time length
        self.T0 = options.get_option('Initial time length')
        self.dt = options.get_option('Incremental time step')
        self.eval_times = options.get_option('Maximum evaluation times')
        self.Tmax = self.T0 + self.eval_times * self.dt
        self.Tmax_reached = None

        if options.get_option('Scipy solver') and options.get_option(
                'Snopt solver'):
            raise ValueError('More than one optimization solver specified!')
        elif not options.get_option('Scipy solver') and not options.get_option(
                'Snopt solver'):
            raise ValueError('No optimization solver specified!')
        elif options.get_option('Scipy solver'):
            self.solver_type = 'scipy'
        elif options.get_option('Snopt solver'):
            self.solver_type = 'snopy'
        else:
            pass

        # Initialize the function evaluation, noise sigma evaluation and truth function evaluation.
        # capsulate those three functions with physical bounds
        self.func_eval = partial(Utils.fun_eval, func_eval, self.physical_lb,
                                 self.physical_ub)
        self.sigma_eval = partial(Utils.fun_eval, noise_eval, self.physical_lb,
                                  self.physical_ub)
        self.truth_eval = partial(Utils.fun_eval, truth_eval, self.physical_lb,
                                  self.physical_ub)

        # Define the global optimum and its values
        if options.get_option('Global minimizer known'):
            self.xmin = Utils.normalize_bounds(
                options.get_option('Global minimizer'), self.physical_lb,
                self.physical_ub)
            self.y0 = options.get_option('Target value')
        else:
            self.xmin = None
            self.y0 = None

        # Define the iteration type for each sampling iteration.
        self.iter_type = None

        # Define the initial sites and their function evaluations
        if options.get_option('Initial sites known'):
            physical_initial_sites = options.get_option('Initial sites')
            # Normalize the bound
            self.xE = Utils.normalize_bounds(physical_initial_sites,
                                             self.physical_lb,
                                             self.physical_ub)
        else:
            self.xE = Utils.random_initial(self.n, 2 * self.n, self.ms,
                                           self.Ain, self.Bin, self.xU)

        if options.get_option('Initial function values') is not None:
            self.yE = options.get_option('Initial function values')
            self.T = self.T0 * np.ones(self.xE.shape[1], dtype=float)
            self.sigma = options.get_option('Initial funtion noise')
        else:
            # Compute the function values, time length, and noise level at initial sites
            self.yE = np.zeros(self.xE.shape[1])
            self.T = self.T0 * np.ones(self.xE.shape[1], dtype=float)
            self.sigma = np.zeros(self.xE.shape[1])

            for i in range(2 * self.n):
                self.yE[i] = self.func_eval(self.xE[:, i], self.T[i])
                self.sigma[i] = self.sigma_eval(self.xE[:, i], self.T0)

        self.iteration_summary_matrix = {}
        # Define the initial support points
        self.xU = Utils.bounds(self.lb, self.ub, self.n)
        self.xU = Utils.unique_support_points(self.xU, self.xE)
        self.yu = None

        self.K0 = np.ptp(self.yE, axis=0)
        # Define the interpolation
        self.inter_par = None
        self.yp = None

        # Define the discrete search function
        self.sd = None

        # Define the minimizer of continuous search function, parameter to be evaluated, xc & yc.
        self.xc = None
        self.yc = None

        # Define the minimizer of discrete search function
        self.xd = None
        self.yd = None
        self.index_min_yd = None

        # Although it is stochastic, just display the behavior instead of the actual data to show the trend.
        # Define the name of directory to store the figures
        self.algorithm_name = options.solverName

        #  ==== Plot section here ====
        self.plot = plot.PlotClass()

        # Define the parameter to save image or no, and what format to save for images
        self.save_fig = options.get_option('Plot saver')
        self.fig_format = options.get_option('Figure format')

        # Generate the folder path
        self.func_path = options.get_option(
            'Objective function name')  # Function folder, e.g. Lorenz
        self.current_path = None  # Directory path
        self.plot_folder = None  # Plot storage folder
        self.folder_path_generator()  # Determine the folder paths above
        self.func_name = options.get_option(
            'Objective function name'
        )  # Define the name of the function called.

        # All the prior function values should be provided by user, instead of solver calculating those values.
        # Generate the plot of test function
        self.func_initial_prior = True
        # The objective function values are stored in func_prior_yE
        # for the future iteration, just change the point of func_prior_sigma that is close to the evaluated point
        self.func_prior_xE_2DX = None
        self.func_prior_xE_2DY = None
        self.func_prior_xE_2DZ = None

        if options.get_option('Function prior file path') is not None:
            self.func_prior_file_name = options.get_option(
                'Function prior file path')
            data = io.loadmat(self.func_prior_file_name)
            # The prior data must have the following keyword
            self.func_prior_xE = data['x']
            self.func_prior_yE = data['y'][0]
            self.func_prior_sigma = data['sigma'][0] * 2
            if self.n == 1:
                # Define the range of plot in y-axis
                self.plot_ylow = np.min(self.func_prior_yE) * 2
                self.plot_yupp = np.max(self.func_prior_yE) * 2

        else:
            self.func_prior_file_name = None
            if options.get_option('Function evaluation cheap') and self.n < 3:
                if self.n == 1:
                    self.plot.initial_calc1D(self)
                elif self.n == 2:
                    self.plot.initial_calc2D(self)
                else:
                    pass
                if self.n == 1:
                    # Define the range of plot in y-axis
                    self.plot_ylow = np.min(self.func_prior_yE) * 2
                    self.plot_yupp = np.max(self.func_prior_yE) * 2

            else:
                self.func_initial_prior = False
                self.func_prior_xE = None
                self.func_prior_yE = None
                # Define the range of plot in y-axis
                self.plot_ylow = np.min(self.yE) * 2
                self.plot_yupp = np.max(self.yE) * 2

                print(
                    'Function evaluation is expensive and no file that contains prior function values information '
                    'is found.')
                if self.n >= 3:
                    print(
                        'Parameter space dimenion > 3, the plot for each iteration is unavailable.'
                    )

        if self.save_fig and self.func_initial_prior:
            if self.n == 1:
                self.initial_plot = self.plot.initial_plot1D
                self.iter_plot = self.plot.plot1D
            elif self.n == 2:
                self.initial_plot = self.plot.initial_plot2D
                self.iter_plot = self.plot.plot2D
            else:
                self.initial_plot = None
                print(
                    "Parameter space higher than 2, set 'Plot saver' to be False."
                )
            self.initial_plot(self)

        self.iter_summary = options.get_option('Iteration summary')
        self.optm_summary = options.get_option('Optimization summary')
Exemplo n.º 2
0
def vertex_find(A, b, lb, ub):
    '''
    Find the vertices of a simplex;
    Ax<b: Linear constraints;
    lb<x<ub: actual constraints;
    Attension: Do not try to include lb&ub inside A&b matrix.
    Example:
    A = np.array([[-1, 1],[1, -1]])
    b = np.array([[0.5], [0.5]])
    lb = np.zeros((2, 1))
    ub = np.ones((2, 1))
    :param A:
    :param b:
    :param lb:
    :param ub:
    :return:
    '''
    if A.shape[0] == 0 and b.shape[0] == 0:
        if len(lb) != 0 and len(ub) != 0:
            Vertex = Utils.bounds(lb, ub, len(lb))
        else:
            Vertex = []
            raise ValueError('All inputs A, b, lb and ub have dimension 0.')
    else:
        if len(lb) != 0:
            Vertex = np.matrix([[], []])
            m = A.shape[0]
            n = A.shape[1]
            if m == 0:
                Vertex = Utils.bounds(lb, ub, len(lb))
            else:
                for r in range(min(n, m) + 1):
                    from itertools import combinations
                    nlist = np.arange(1, m + 1)
                    C = np.array([list(c) for c in combinations(nlist, r)])
                    nlist2 = np.arange(1, n + 1)
                    D = np.array([list(d) for d in combinations(nlist2, n - r)])
                    if r == 0:
                        F = Utils.bounds(lb, ub, n)
                        for kk in range(F.shape[1]):
                            x = np.copy(F[:, kk]).reshape(-1, 1)
                            if np.all(np.dot(A, x) - b < 0):
                                Vertex = np.column_stack((Vertex, x))
                    else:
                        for ii in range(len(C)):
                            index_A = np.copy(C[ii])
                            v1 = np.arange(1, m + 1)
                            index_A_C = np.setdiff1d(v1, index_A)
                            A1 = np.copy(A[index_A - 1, :])
                            b1 = np.copy(b[index_A - 1])
                            for jj in range(len(D)):
                                index_B = np.copy(D[jj])
                                v2 = np.arange(1, n + 1)
                                index_B_C = np.setdiff1d(v2, index_B)
                                if len(index_B) != 0 and len(index_B_C) != 0:
                                    F = Utils.bounds(lb[index_B - 1], ub[index_B - 1], n - r)
                                    A11 = np.copy(A1[:, index_B - 1])
                                    A12 = np.copy(A1[:, index_B_C - 1])
                                    for kk in range(F.shape[1]):
                                        A11 = np.copy(A1[:, index_B - 1])
                                        A12 = np.copy(A1[:, index_B_C - 1])
                                        xd = np.linalg.lstsq(A12, b1 - np.dot(A11, F[:, kk].reshape(-1, 1)), rcond=None)[0]
                                        x = np.zeros((n, 1))
                                        x[index_B - 1] = np.copy(F[:, kk])
                                        x[index_B_C - 1] = np.copy(xd)
                                        if r == m or (np.dot(A[index_A_C - 1, :], x) - b[index_A_C - 1]).min() < 0:
                                            if (x - ub).max() < 1e-6 and (x - lb).min() > -1e-6 and Utils.mindis(x, Vertex)[0] > 1e-6:
                                                Vertex = np.column_stack((Vertex, x))
        else:
            m = A.shape[0]
            n = A.shape[1]
            from itertools import combinations
            nlist = np.arange(1, m + 1)
            C = np.array([list(c) for c in combinations(nlist, n)])
            Vertex = np.empty(shape=[n, 0])
            for ii in range(len(C)):
                index_A = np.copy(C[ii])
                v1 = np.arange(1, m + 1)
                index_A_C = np.setdiff1d(v1, index_A)
                A1 = np.copy(A[index_A - 1, :])
                b1 = np.copy(b[index_A - 1])
                A2 = np.copy(A[index_A_C - 1])
                b2 = np.copy(b[index_A_C - 1])
                x = np.linalg.lstsq(A1, b1, rcond=None)[0]
                if (np.dot(A2, x) - b2).max() < 1e-6:
                    Vertex = np.column_stack((Vertex, x))
        # cant plot Vertex directly. must transform it into np.array.
    return np.array(Vertex)
Exemplo n.º 3
0
    def continuous_search_plot1D(self, adogs):
        '''
        Plot the continuous search function.
        :return:
        '''
        xU = Utils.bounds(adogs.lb, adogs.ub, adogs.n)
        if adogs.iter_type == 'scmin' and Utils.mindis(adogs.xc, xU)[0] > 1e-6:
            xi = np.hstack((adogs.xE[:, :-1], adogs.xU))
        else:
            xi = np.hstack((adogs.xE, adogs.xU))

        sx = sorted(range(xi.shape[1]), key=lambda x: xi[:, x])
        tri = np.zeros((xi.shape[1] - 1, 2))
        tri[:, 0] = sx[:xi.shape[1] - 1]
        tri[:, 1] = sx[1:]
        tri = tri.astype(np.int32)

        num_plot_points = 2000
        xe_plot = np.zeros((tri.shape[0], num_plot_points))
        e_plot = np.zeros((tri.shape[0], num_plot_points))
        sc_plot = np.zeros((tri.shape[0], num_plot_points))

        for ii in range(len(tri)):
            simplex_range = np.copy(xi[:, tri[ii, :]])

            # Discretized mesh grid on x direction in one simplex
            x = np.linspace(simplex_range[0, 0], simplex_range[0, 1],
                            num_plot_points)

            # Circumradius and circumcenter for current simplex
            R2, xc = Utils.circhyp(xi[:, tri[ii, :]], adogs.n)

            for jj in range(len(x)):
                # Interpolation p(x)
                p = adogs.inter_par.inter_val(x[jj])

                # Uncertainty function e(x)
                e_plot[ii, jj] = (R2 - np.linalg.norm(x[jj] - xc)**2)

                # Continuous search function s(x)
                sc_plot[ii, jj] = p - adogs.K * adogs.K0 * e_plot[ii, jj]

            xe_plot[ii, :] = np.copy(x)

        for i in range(len(tri)):
            # Plot the uncertainty function e(x)
            # plt.plot(xe_plot[i, :], e_plot[i, :] - 5.5, c='g', label=r'$e(x)$')
            # Plot the continuous search function sc(x)
            plt.plot(xe_plot[i, :],
                     sc_plot[i, :],
                     'r--',
                     zorder=20,
                     label=r'$S_c(x)$')

        yc_min = sc_plot.flat[np.abs(xe_plot - adogs.xc).argmin()]
        plt.scatter(adogs.xc,
                    yc_min,
                    c=(1, 0.769, 0.122),
                    marker='D',
                    zorder=15,
                    label=r'min $S_c(x)$')
Exemplo n.º 4
0
sc = "AdaptiveK"  # The type of continuous search function
alg_name = 'DDOGS/'

# Calculate the Initial trinagulation points
num_iter = 0  # Represents how many iteration the algorithm goes
Nm = 8  # Initial mesh grid size
L_refine = 0  # Initial refinement sign

# Truth function
fun, lb, ub, y0, xmin, fname = Utils.test_fun(fun_arg, n)
func_eval = partial(Utils.fun_eval, fun, lb, ub)
# safe constraints
safe_fun, lb, ub, safe_name, L_safe = Utils.test_safe_fun(safe_fun_arg, n)
safe_eval = partial(Utils.fun_eval, safe_fun, lb, ub)

xU = Utils.bounds(np.zeros([n, 1]), np.ones([n, 1]), n)
Ain = np.concatenate((np.identity(n), -np.identity(n)), axis=0)
Bin = np.concatenate((np.ones((n, 1)), np.zeros((n, 1))), axis=0)

regret = np.zeros((nff, iter_max))
estimate = np.zeros((nff, iter_max))
datalength = np.zeros((nff, iter_max))
mesh = np.zeros((nff, iter_max))

for ff in range(nff):
    xE = np.array([[0.2]])
    num_ini = xE.shape[1]
    yE = np.zeros(xE.shape[1])  # yE stores the objective function value
    y_safe = np.zeros(
        xE.shape[1])  # y_safe stores the value of safe constraints.