Example #1
0
    def facquisition_pref(xx, X, N, delta_E, dF, beta, rbf, epsil,
                          theta, sepvalue, ibest, acquisition_method, isUnknownFeasibilityConstrained, isUnknownSatisfactionConstrained,Feasibility_unkn,SatConst_unkn,delta_G,delta_S,iw_ibest,maxevals):
        # Acquisition function to minimize to get next sample

        epsilth = epsil * theta

        if acquisition_method == 1:
            v = rbf(X[0:N, :], xx, epsilth)
            fhat = v.ravel().dot(W.ravel())

            d = vecsum((X[0:N, ] - xx) ** 2, axis=-1)

            ii = where(d < 1e-12)
            if ii[0].size > 0:
                dhat = 0.0
                if isUnknownFeasibilityConstrained:
                    Ghat = Feasibility_unkn[ii]
                else:
                    Ghat = 1
                if isUnknownSatisfactionConstrained:
                    Shat = SatConst_unkn[ii]
                else:
                    Shat = 1
            else:
                w = vecexp(-d) / d
                sw = sum(w)
                if maxevals <= 30:
                    dhat = delta_E * atan(1.0 / sum(1.0 / d))  # for comparision, used in the original GLISp and when N_max <= 30 in C-GLISp
                else:
                    dhat = delta_E * ((1-N/maxevals)*atan((1/sum(1./d))/iw_ibest)+ N/maxevals *atan(1/sum(1./d)))  # used in C-GLISp

                if isUnknownFeasibilityConstrained:
                    Ghat = vecsum(Feasibility_unkn[0:N].T * w) / sw
                else:
                    Ghat = 1

                if isUnknownSatisfactionConstrained:
                    Shat = vecsum(SatConst_unkn[0:N].T * w) / sw
                else:
                    Shat = 1

            # f = fhat / dF - dhat  # for comparision, used in GLISp
            f = fhat / dF - dhat + delta_G * (1 - Ghat) + delta_S * (1 - Shat)  # used in C-GLISp

        elif acquisition_method == 2:

            v = rbf(X[0:N, :], xx, epsilth) - rbf(X[0:N, :], X[ibest, :], epsilth)
            PHIbeta = v.ravel().dot(beta.ravel())

            lm1 = max(PHIbeta + sepvalue, 0.0)
            l0 = max(0, PHIbeta - sepvalue, -PHIbeta - sepvalue)
            l1 = max(sepvalue - PHIbeta, 0.0)
            c0 = 1.0
            cm1 = 1.0
            c1 = 1.0
            em1 = exp(-cm1 * lm1)
            f = -em1 / (em1 + exp(-c0 * l0) + exp(-c1 * l1))

        return f
Example #2
0
    def get_delta_adpt(X,constraint_set,delta_const_default):
        ind = constraint_set.shape[0]
        sqr_error_feas = zeros((ind,1))
        for i in range(0,ind):
            xx = X[i,:]
            Xi = vstack((X[0:i, :], X[i + 1:ind, :]))
            constraint_set_i = vstack((constraint_set[0:i,],constraint_set[i+1:ind,]))
            Feas_xx = constraint_set[i]
            d = vecsum((Xi - xx) ** 2, axis=-1)
            w = vecexp(-d)/d
            sw = sum(w)
            ghat = vecsum(constraint_set_i.T * w) / sw
            sqr_error_feas[i] = (ghat-Feas_xx)**2

        std_feas = (sum(sqr_error_feas)/(ind-1))**(1/2)
        delta_adpt = (1-std_feas) *delta_const_default

        return delta_adpt
def set(nvars):
    """
    Generate default problem structure for IDW-RBF Global Optimization.
    
    problem = idwgopt_default.set(n) generate a default problem structure for a
    an optimization with n variables.
     
    (C) 2019 A. Bemporad, July 6, 2019
    """

    from numpy import zeros, ones
    from numpy import sum as vecsum

    problem = {
        "f": "[set cost function here]",  # cost function to minimize
        "lb": -1 * ones(
            (nvars, 1)),  # lower bounds on the optimization variables
        "ub": 1 * ones(
            (nvars, 1)),  # upper bounds on the optimization variables
        "maxevals": 20,  # maximum number of function evaluations
        "alpha": 1,  # weight on function uncertainty variance measured by IDW
        "delta": 0.5,  # weight on distance from previous samples
        "nsamp": 2 * nvars,  # number of initial samples
        "useRBF": 1,  # 1 = use RBFs, 0 = use IDW interpolation
        "rbf": lambda x1, x2: 1 / (1 + 0.25 * vecsum(
            (x1 - x2)**2)),  # inverse quadratic 
        #                       RBF function (only used if useRBF=1)
        "scalevars": 1,  # scale problem variables
        "svdtol": 1e-6,  # tolerance used to discard small singular values
        "Aineq": zeros(
            (0, nvars)),  # matrix A defining linear inequality constraints 
        "bineq": zeros((0, 1)),  # right hand side of constraints A*x <= b
        "g":
        0,  # constraint function. Example: problem.g = lambda x: x[0]**2+x[1]**2-1
        "shrink_range":
        1,  # flag. If 0, disable shrinking lb and ub to bounding box of feasible set
        "constraint_penalty":
        1000,  # penalty term on violation of linear inequality
        #                             and nonlinear constraint
        "feasible_sampling":
        False,  # if True, initial samples are forced to be feasible
        "globoptsol": "direct",  # nonlinear solver used during acquisition.
        # interfaced solvers are:
        #   "direct" DIRECT from NLopt tool (nlopt.readthedocs.io)
        #   "pswarm" PySwarm solver (pythonhosted.org/pyswarm/)
        "display": 0,  # verbosity level (0=minimum)
        "PSOiters": 500,  # number of iterations in PSO solver
        "PSOswarmsize": 20,  # swarm size in PSO solver
        "epsDeltaF":
        1e-4  # minimum value used to scale the IDW distance function
    }

    return problem
    if use_nl_constraints and benchmark == "camelsixhumps":
        #problem["g"] = lambda x: array([(x[0]-1)**2+x[1]**2-.25,
        #       (x[0]-0.5)**2+(x[1]-0.5)**2-.25])
        problem["g"] = lambda x: array([x[0]**2 + (x[1] + 0.1)**2 - .5])

    problem["lb"] = lb
    problem["ub"] = ub
    problem["maxevals"] = maxevals
    problem["sepvalue"] = 1. / maxevals

    pref_fun = glisp_function(fun, comparetol)  # preference function object
    pref = lambda x, y: pref_fun.eval(x, y)
    problem["pref"] = pref

    epsil = 1.
    problem["rbf"] = lambda x1, x2, epsil: 1. / (1. + epsil**2 * vecsum(
        (x1 - x2)**2, axis=-1))  # inverse quadratic
    #problem["rbf"] = lambda x1,x2,epsil: exp(-(epsil**2*vecsum((x1-x2)**2,axis=-1)) # Gaussian RBF
    #problem["rbf"] = lambda x1,x2,epsil: sqrt((1.+epsil**2*vecsum((x1-x2)**2,axis=-1)) # multiquadric
    problem["epsil"] = epsil

    problem["RBFcalibrate"] = RBFcalibrate
    problem["thetas"] = logspace(-1, 1, 10, False)

    problem["delta"] = delta

    problem["nsamp"] = nsamp
    problem["svdtol"] = 1e-6
    #problem["globoptsol"] = "direct"
    problem["globoptsol"] = "pswarm"
    problem["display"] = 1
Example #5
0
 def fun_rbf(x1, x2):
     return 1.0 / (1.0 + epsil ** 2 * vecsum((x1 - x2) ** 2, axis=-1))
Example #6
0
def solve(prob):
    """
    Active preference learning to solve global optimization
    problem using radial basis functions (RBFs) to fit a surrogate of the
    latent function to minimize by preference queries. The acquisition
    used to generate new samples is based either on inverse distance
    weighting (IDW) or probability of improvement.

    (C) 2019 A. Bemporad

    sol = glisp.solve(prob) solves the active preference learning
    problem

    find x such that pref(x,y) <= 0 for all x,y in X,
    X = {x: lb <= x <=ub, A*x <=b, g(x)<=0}

    where pref(x,y) = -1 if x "better than" y
                       0 if x "as good as" y
                       1 if x "worse than" y

    A special case is to solve the global optimization problem

    min  f(x)
    s.t. lb <= x <=ub, A*x <=b, g(x)<=0

    based only on comparisons between function values

    pref(x,y) = -1 if f(x1) <= f(x2) - tol
              =  0 if |f(x1)-f(x2)| <= tol
              =  1 if f(x1) >= f(x2) + tol

    where tol is the threshold deciding the outcome of the comparison,
    i.e., comparison is "even" if |f(x1)-f(x2)| <= tol

    The algorithm is described in [1] and builds upon the global optimization
    algorithm described in [2] and is particularly useful when f(x) is not
    defined, but rather only comparisons between samples is available.

    The default problem structure is

    prob = glisp.default(nvars)

    where nvars = dimension of optimization vector x.

    See function glis_default for a description of all other available
    options.

    The following options differ from glis:
        prob["pref"] is the preference function pref(x1,x2)

        prob["sepvalue"] is the value used in constructing the surrogate function fhat:
            fhat(x1)<=fhat(x2)-sepvalue if pref(x1,x2) = -1
            |fhat(x1)-fhat(x2)|<=sepvalue if pref(x1,x2) = 0.

        prob["epsil"] epsil parameter used in defining RBF

        prob["rbf"] the RBF function also includes epsil as a parameter:
            rbf = fun(x1,x2,epsil)

        prob["RBFcalibrate"] if true, recalibrate scaling parameter theta of RBF
            during iterations, i.e., fun(x1,x2,epsil*theta) is
            used as RBF.

        prob["thetas"] array of theta values to test during calibration.
             At least one element in thetas must be 1.

        prob["RBFcalibrationSteps"] iterations at which RBF is recalibrated.
             Values smaller than prob["nsamp"] are ignored.

        prob["comparetol"] threshold to decide outcome of comparison during
            recalibration in cross validation, comparison is "even" if
            |fhat(x1)-fhat(x2)|<=comparetol, fhat=surrogate function.

        prob["acquisition_method"] acquisition method
            1 = scaled surrogate - delta * IDW
            2 = probability of improvement

        prog["f"]         not used
        prob["svdtol"]    not used
        prob["alpha"]     not used
        prob["epsDeltaF"] redundant if epsDeltaF<=1
        prob["useRBF"]    forced to true.

    The output argument 'out' is a structure reporting the following information:

    out["X"]:     trace of all samples x generated by the algorithm
    out["I"]:     results of comparisons: out.X(out.I(j,1)) "better than" out.X(out.I(j,2))
    out["Ieq"]:   results of comparisons: out.X(out.Ieq(j,1)) "as good as" out.X(out.Ieq(j,2))
    out["W"]:     final set of weights
    out["M"]:     final RBF matrix
    out["xopt"]:  best sample found during search
    out["theta"]: scaling parameter multiplied by epsil in final RBF matrix

    out["time_iter"], out["time_opt_acquisition"], out["time_fit_surrogate"], out["time_f_eval"]
    store timing recorded during the execution of the algorithm.

    Required Python packages:
        pyDOE:     https://pythonhosted.org/pyDOE/
        nlopt:     https://nlopt.readthedocs.io (required only if DIRECT solver is used)
        pyswarm:   https://pythonhosted.org/pyswarm/ (required only if PSO solver is used)
        qpsolvers: https://github.com/stephane-caron/qpsolvers
        cvxopt:    https://cvxopt.org

    [1] A. Bemporad, "Global optimization via inverse weighting and radial basis functions,"
        Computational Optimization and Applications, vol. 77, pp. 571–595.

    [2] A. Bemporad and D. Piga, “Active preference learning based on radial basis functions,”
        Machine Learning, vol. 110, no. 2, pp. 417–448, 2021,
        Available on arXiv at http://arxiv.org/abs/1909.13049.

%%%%%%%%%%%%%%%%%%%%%%
    % (C-GLISp)
    % Note: Add features to handle unknown constraints (M. Zhu, June, 07, 2021)
    %       Known constraints will be handled via penalty functions
    %
    % Following are the new parameters introduced in C-GLISp
    % opts["isUnknownFeasibilityConstrained"]: if true, unknown feasibility constraints are involved
    % opts["isUnknownSatisfactionConstrained"]: if true, unknown satisfaction constraints are involed
    % delta_E: delta for te pure IDW exploration term, \delta_E in the paper
    % delta_G_default: delta for feasibility constraints, \delta_{G,default} in the paper
    % delta_S_default: delta for satisfaction constraints, \delta_{S,default} in the paper
    % Feasibility_unkn: feasibility labels for unknown feasibility constraints
    % SatConst_unkn: satisfaction labels for unknown satisfactory constraints
    """

    import glis.glis_init as glis_init

    from pyswarm import pso # https://pythonhosted.org/pyswarm/
    from qpsolvers import solve_qp
    from numpy import zeros, ones, diag, isin
    from numpy import where, maximum, dot, array, vstack, empty
    from numpy import sum as vecsum
    from numpy import exp as vecexp
    from math import atan, pi, exp
    import contextlib
    import io
    import time

    def get_weights(X, I, Ieq, M, n, ibest, sepvalue):
        # Fit RBF satisfying comparison constraints at sampled points
        #
        # optimization vector x=[beta;epsil] where:
        #    beta  = rbf coefficients
        #    epsil = vector of slack vars, one per constraint

        normalize = 0

        m = I.shape[0]
        meq = Ieq.shape[0]
        A = zeros([m + 2 * meq, n + m + meq])
        b = zeros([m + 2 * meq, 1])
        for k in range(0, m):
            i = I[k][0]
            j = I[k][1]
            # f(x(i))<f(x(j))
            # sum_h(beta(h)*phi(x(i,:),x(h,:))<=sum_h(beta(h)*phi(x(j,:),x(h,:))+eps_k-sepvalue
            A[k, 0:n] = M[i, 0:n] - M[j, 0:n]
            A[k, n + k] = -1.0
            b[k] = -sepvalue

        # |f(x(i))-f(x(j))|<=comparetol
        # --> f(x(i))<=f(x(j))+comparetol+epsil
        # --> f(x(j))<=f(x(i))+comparetol+epsil
        # sum_h(beta(h)*phi(x(i,:),x(h,:))<=sum_h(beta(h)*phi(x(j,:),x(h,:))+sepvalue+epsil
        # sum_h(beta(h)*phi(x(j,:),x(h,:))<=sum_h(beta(h)*phi(x(i,:),x(h,:))+sepvalue+epsil
        for k in range(0, meq):
            i = Ieq[k][0]
            j = Ieq[k][1]
            A[m + 2 * k, 0:n] = M[i, 0:n] - M[j, 0:n]
            A[m + 2 * k, n + m + k] = -1.0
            b[m + 2 * k] = sepvalue
            A[m + 2 * k + 1, 0:n] = M[j, 0:n] - M[i, 0:n]
            A[m + 2 * k + 1, n + m + k] = -1.0
            b[m + 2 * k + 1] = sepvalue

        if normalize:
            # Add constraints to avoid trivial solution surrogate=flat:
            #    sum_h(beta.*phi(x(ibest,:),x(h,:))) = 0
            #    sum_h(beta.*phi(x(ii,:),x(h,:))) = 1

            # Look for sample where function is worse,i.e., f(ii) is largest
            ii = I[0][1]
            for k in range(0, m):
                if I[k][0] == ii:
                    ii = I[k][1]
            Aeq = zeros([2, n + m + meq])
            beq = zeros([2, 1])
            Aeq[0, 0:n] = M[ibest, 0:n]
            Aeq[1, 0:n] = M[ii, 0:n]
            beq[0] = 0.0
            beq[1] = 1.0
        else:
            Aeq = zeros([0, n + m + meq])
            beq = zeros([0, 1])

        c = zeros([n + m + meq, 1])
        # penalize more violations involving zbest
        for i in range(0, m):
            if (I[i][0] == ibest or I[i][1] == ibest):
                c[n + i] = 10.0
            else:
                c[n + i] = 1.0

        for i in range(0, meq):
            if (Ieq[i][0] == ibest or Ieq[i][1] == ibest):
                c[n + m + i] = 10.0
            else:
                c[n + m + i] = 1.0

        # Solve QP problem
        q = zeros(n + m + meq)
        q[0:n] = 1.0e-6
        Q = diag(q)
        if beq.size == 0:
            # x_sol = solve_qp(sparse.csc_matrix(Q), c, sparse.csc_matrix(A), b, solver="osqp")
            x_sol = solve_qp(Q, c, A, b, solver="cvxopt")
        else:
            # x_sol = solve_qp(sparse.csc_matrix(Q), c, sparse.csc_matrix(A), b,
            #    sparse.csc_matrix(Aeq), beq, solver="osqp")
            x_sol = solve_qp(Q, c, A, b, Aeq, beq, solver="cvxopt")

        try:
            beta = x_sol[0:n].reshape(n, 1)
        except:
            # try with better conditioned problem
            q[0:n] = 1.0e-3
            Q = diag(q)
            x_sol = solve_qp(Q, c, A, b, Aeq, beq, solver="cvxopt")
            beta = x_sol[0:n].reshape(n, 1)

        return beta

    def get_delta_adpt(X,constraint_set,delta_const_default):
        ind = constraint_set.shape[0]
        sqr_error_feas = zeros((ind,1))
        for i in range(0,ind):
            xx = X[i,:]
            Xi = vstack((X[0:i, :], X[i + 1:ind, :]))
            constraint_set_i = vstack((constraint_set[0:i,],constraint_set[i+1:ind,]))
            Feas_xx = constraint_set[i]
            d = vecsum((Xi - xx) ** 2, axis=-1)
            w = vecexp(-d)/d
            sw = sum(w)
            ghat = vecsum(constraint_set_i.T * w) / sw
            sqr_error_feas[i] = (ghat-Feas_xx)**2

        std_feas = (sum(sqr_error_feas)/(ind-1))**(1/2)
        delta_adpt = (1-std_feas) *delta_const_default

        return delta_adpt

    def facquisition_pref(xx, X, N, delta_E, dF, beta, rbf, epsil,
                          theta, sepvalue, ibest, acquisition_method, isUnknownFeasibilityConstrained, isUnknownSatisfactionConstrained,Feasibility_unkn,SatConst_unkn,delta_G,delta_S,iw_ibest,maxevals):
        # Acquisition function to minimize to get next sample

        epsilth = epsil * theta

        if acquisition_method == 1:
            v = rbf(X[0:N, :], xx, epsilth)
            fhat = v.ravel().dot(W.ravel())

            d = vecsum((X[0:N, ] - xx) ** 2, axis=-1)

            ii = where(d < 1e-12)
            if ii[0].size > 0:
                dhat = 0.0
                if isUnknownFeasibilityConstrained:
                    Ghat = Feasibility_unkn[ii]
                else:
                    Ghat = 1
                if isUnknownSatisfactionConstrained:
                    Shat = SatConst_unkn[ii]
                else:
                    Shat = 1
            else:
                w = vecexp(-d) / d
                sw = sum(w)
                if maxevals <= 30:
                    dhat = delta_E * atan(1.0 / sum(1.0 / d))  # for comparision, used in the original GLISp and when N_max <= 30 in C-GLISp
                else:
                    dhat = delta_E * ((1-N/maxevals)*atan((1/sum(1./d))/iw_ibest)+ N/maxevals *atan(1/sum(1./d)))  # used in C-GLISp

                if isUnknownFeasibilityConstrained:
                    Ghat = vecsum(Feasibility_unkn[0:N].T * w) / sw
                else:
                    Ghat = 1

                if isUnknownSatisfactionConstrained:
                    Shat = vecsum(SatConst_unkn[0:N].T * w) / sw
                else:
                    Shat = 1

            # f = fhat / dF - dhat  # for comparision, used in GLISp
            f = fhat / dF - dhat + delta_G * (1 - Ghat) + delta_S * (1 - Shat)  # used in C-GLISp

        elif acquisition_method == 2:

            v = rbf(X[0:N, :], xx, epsilth) - rbf(X[0:N, :], X[ibest, :], epsilth)
            PHIbeta = v.ravel().dot(beta.ravel())

            lm1 = max(PHIbeta + sepvalue, 0.0)
            l0 = max(0, PHIbeta - sepvalue, -PHIbeta - sepvalue)
            l1 = max(sepvalue - PHIbeta, 0.0)
            c0 = 1.0
            cm1 = 1.0
            c1 = 1.0
            em1 = exp(-cm1 * lm1)
            f = -em1 / (em1 + exp(-c0 * l0) + exp(-c1 * l1))

        return f

    def results_display(N, z, display, nvar, scalevars, dd, d0, epsil, RBFcalibrate):
        # Display intermediate results
        if display > 0:

            string = ("N = %4d: x = [" % N)
            for j in range(nvar):
                aux = z[j]
                if scalevars:
                    aux = aux * dd[j] + d0[j]

                string = string + ('%7.4f' % aux)
                if j < nvar - 1:
                    string = string + ", "

            string = string + "], "

            if RBFcalibrate:
                string = string + "\u03B5(rbf) = " + ('%5.4f' % epsil)

            print(string)

        return

    def rbf_calibrate(X, I, Ieq, M, N, ibest, sepvalue, rbf, epsil, comparetol, display, thetas, MM, iM, itheta):
        # calibrate scaling of epsil parameter in RBF by cross-validation

        if display > 0:
            print("Recalibrating RBF: ", end='')

        # MM is a maxevals-by-maxevals-by-numel(thetas) matrix
        # used to save previously computed RBF values
        nth = thetas.size
        imax = 0
        successmax = -1

        for k in range(nth):

            epsilth = epsil * thetas[k]

            # Update matrix containing RBF values for all thetas
            if (k == itheta):
                MM[itheta, 0:N][:, 0:N] = M.copy()[0:N, 0:N]  # values already computed for current theta
            else:
                for j in range(iM + 1, N):
                    for h in range(N):
                        MM[k, j, h] = rbf(X[j, :], X[h, :], epsilth)
                        MM[k, h, j] = MM.copy()[k, j, h]

            Ncomparisons = 0
            success = 0

            for i in range(N):
                if not (i == ibest):
                    Xi = vstack((X[0:i, :], X[i + 1:N, :]))
                    if ibest > i:
                        newibest = ibest - 1
                    else:
                        newibest = ibest

                    Ii = empty((0, 2)).astype('int')
                    ni = 0
                    for j in range(I.shape[0]):
                        if not (I[j, 0] == i) and not (I[j, 1] == i):
                            Ii = vstack((Ii, I[j, :]))
                            if I[j, 0] > i:
                                Ii[ni, 0] = Ii[ni, 0] - 1
                            if I[j, 1] > i:
                                Ii[ni, 1] = Ii[ni, 1] - 1
                            ni = ni + 1

                    Ieqi = empty((0, 2)).astype('int')
                    ni = 0
                    for j in range(Ieq.shape[0]):
                        if not (Ieq[j, 0] == i) and not (Ieq[j, 1] == i):
                            Ieqi = vstack((Ieqi, Ieq[j, :]))
                            if Ieq[j, 0] > i:
                                Ieqi[ni, 0] = Ieqi[ni, 0] - 1
                            if Ieq[j, 1] > i:
                                Ieqi[ni, 1] = Ieqi[ni, 1] - 1
                            ni = ni + 1

                    ind = list(range(0, i)) + list(range(i + 1, N))
                    Mi = MM[k, ind][:, ind]  # no need to copy, as Mi is not changed

                    Wi = get_weights(Xi, Ii, Ieqi, Mi, N - 1, newibest, sepvalue)
                    # Compute RBF @X[i,:]
                    FH = zeros((N, 1))
                    FH[ind] = dot(Mi, Wi)  # rbf at samples

                    v = rbf(Xi[0:N - 1, :], X[i, :], epsilth)
                    FH[i] = v.ravel().dot(Wi.ravel())

                    # Cross validation
                    for j in range(I.shape[0]):
                        if (I[j, 0] == i) or (I[j, 1] == i):
                            Ncomparisons = Ncomparisons + 1
                            i1 = I[j, 0]
                            i2 = I[j, 1]
                            if FH[i1] <= FH[i2] - comparetol:
                                success = success + 1

                    for j in range(Ieq.shape[0]):
                        if (Ieq[j, 0] == i) or (Ieq[j, 1] == i):
                            Ncomparisons = Ncomparisons + 1
                            i1 = Ieq[j, 0]
                            i2 = Ieq[j, 1]
                            if FH[i1] <= FH[i2] - comparetol:
                                success = success + 1
            if (display > 0):
                print(".", end='')

            success = success / Ncomparisons * 100.0
            # NOTE: normalization is only for visualization purposes

            # Find theta such that success is max, and closest to 1 among maximizers
            if (success > successmax) or (success == successmax and
                                          (thetas[k] - 1) ** 2 < (thetas[imax] - 1) ** 2):
                imax = k
                successmax = success

        itheta = imax
        theta = thetas[itheta]
        iM = N - 1

        # Update matrix M
        M[0:N, 0:N] = MM.copy()[itheta, 0:N][:, 0:N]

        print(" done.")

        return theta, itheta, M, MM, iM

    #############################
    # Start of main function

    prob["useRBF"] = True
    prob["alpha"] = 0.0  # dummy
    epsil = prob["epsil"]
    prob["f"] = lambda x: 0.0  # dummy
    rbf = prob["rbf"]
    prob["rbf"] = lambda x1, x2: rbf(x1, x2, epsil)  # used to create matrix M

    (_, lb, ub, nvar, Aineq, bineq, g, isLinConstrained, isNLConstrained,
     X, _, z, nsamp, maxevals, epsDeltaF, alpha, delta, rhoC, display, _,
     dd, d0, _, _, M, scalevars, globoptsol, DIRECTopt,
     PSOiters, PSOswarmsize) = glis_init.init(prob)

    prob["rbf"] = rbf
    isUnknownFeasibilityConstrained = prob["isUnknownFeasibilityConstrained"]
    isUnknownSatisfactionConstrained = prob['isUnknownSatisfactionConstrained']
    delta_E = delta
    delta_G_default = delta
    delta_S_default = delta/2

    if scalevars:
        # Rescale problem variables in [-1,1]
        pref = lambda x, y: prob["pref"](x * dd + d0, y * dd + d0)
    else:
        pref = prob["pref"]

    time_iter = []
    time_f_eval = []
    time_opt_acquisition = []
    time_fit_surrogate = []

    sepvalue = prob["sepvalue"]
    RBFcalibrate = prob["RBFcalibrate"]
    if RBFcalibrate:
        found = False
        thetas = prob["thetas"]
        for i in range(0, thetas.size):
            if abs(thetas[i] - 1.0) <= 1.0e-14:
                found = True
                thetas[i] = 1.0
                itheta = i
                break

        if not (found):
            raise NameError('At least one element in thetas must be equal to 1')

        MM = zeros((thetas.size, maxevals, maxevals))
        iM = -1  # index denoting the portion of MM already computed

    RBFcalibrationSteps = prob["RBFcalibrationSteps"]
    if RBFcalibrationSteps.size == 0:
        RBFcalibrationSteps = array([nsamp, nsamp + round((maxevals - nsamp) / 4),
                                     nsamp + round((maxevals - nsamp) / 2),
                                     nsamp + round(3 * (maxevals - nsamp) / 4)])
    comparetol = prob["comparetol"]
    acquisition_method = prob["acquisition_method"]

    # Fills in initial preference vectors and find best initial guess
    zbest = X[0,].flatten("c")
    ibest = 0
    I = []  # I[i,1:2]=[h k] if F(h)<F(k)-comparetol
    Ieq = []  # Ieq[i,1:2]=[h k] if |F(h)-F(k)|<=comparetol
    Feasibility_unkn = [] # feasibility labels
    SatConst_unkn = [] # satisfactory constraint label
    isfeas_seq = ones((maxevals,1)).astype(int) # keep track the feasibility of the decision variables(including both known and unknown constraints)
    ibestseq = ones((maxevals,1)).astype(int) # keep track of the ibest throughout

    # Initial sampling phase
    for i in range(1, nsamp):
        if i == 1:
            time_fun_eval_start = time.perf_counter()
            if isUnknownSatisfactionConstrained and isUnknownFeasibilityConstrained: # when has both unknown feasibility and satisfactory constraints
                (prefi, fesi, fesbest,satconsti,satconstbest) = pref(X[i,].flatten("c"), zbest)
                SatConst_unkn.append([satconstbest])
                Feasibility_unkn.append([fesbest])
            elif ~isUnknownSatisfactionConstrained and isUnknownFeasibilityConstrained: # when only has unknown feasibility constraints
                (prefi, fesi, fesbest) = pref(X[i,].flatten("c"), zbest)
                Feasibility_unkn.append([fesbest])
            elif isUnknownSatisfactionConstrained and ~isUnknownFeasibilityConstrained:  # when only has unknown satisfactory constraints
                (prefi, satconsti,satconstbest) = pref(X[i,].flatten("c"), zbest)
                SatConst_unkn.append([satconstbest])
            else: # when there is no unknown constraints
                prefi = pref(X[i,].flatten("c"), zbest)
            time_fun_eval_i = time.perf_counter() - time_fun_eval_start
            time_iter.append(time_fun_eval_i)
            time_f_eval.append(time_fun_eval_i)
            time_opt_acquisition.append(0.0)
            time_fit_surrogate.append(0.0)
        else:
            time_fun_eval_start = time.perf_counter()
            if isUnknownSatisfactionConstrained and isUnknownFeasibilityConstrained:
                (prefi, fesi,_,satconsti,_) = pref(X[i,].flatten("c"), zbest)
            elif ~isUnknownSatisfactionConstrained and isUnknownFeasibilityConstrained: # when only has unknown feasibility constraints
                (prefi, fesi, _) = pref(X[i,].flatten("c"), zbest)
            elif isUnknownSatisfactionConstrained and ~isUnknownFeasibilityConstrained:  # when only has unknown satisfactory constraints
                (prefi, satconsti,_) = pref(X[i,].flatten("c"), zbest)
            else: # when there is no unknown constraints
                prefi = pref(X[i,].flatten("c"), zbest)
            time_fun_eval_i = time.perf_counter() - time_fun_eval_start
            time_iter.append(time_fun_eval_i)
            time_f_eval.append(time_fun_eval_i)
            time_opt_acquisition.append(0.0)
            time_fit_surrogate.append(0.0)

        if isUnknownFeasibilityConstrained:
            Feasibility_unkn.append([fesi])
        if isUnknownSatisfactionConstrained:
            SatConst_unkn.append([satconsti])

        isfeas = True
        # for known constraints(Note: no query is required for known constraints)
        if isLinConstrained:
            isfeas = isfeas and all(Aineq.dot(X[i,].T) <= bineq.flatten("c"))
        if isNLConstrained:
            isfeas = isfeas and all(g(X[i,]) <= 0)
        if isUnknownFeasibilityConstrained:
            isfeas = isfeas and fesi> 0
        if isUnknownSatisfactionConstrained:
            isfeas = isfeas and satconsti > 0

        if prefi == -1:
            I.append([i, ibest])
            zbest = X[i,].flatten("c")
            ibest = i
        elif prefi == 1:
            I.append([ibest, i])
        else:
            Ieq.append([i, ibest])
        ibestseq[i] = ibest
        isfeas_seq[i] = isfeas

    I = array(I).astype(int)
    Ieq = array(Ieq).astype(int)

    Feasibility_unkn = array(Feasibility_unkn).astype(int)
    SatConst_unkn = array(SatConst_unkn).astype((int))

    if I.size == 0:
        I = empty((0, 2), int)
    if Ieq.size == 0:
        Ieq = empty((0, 2), int)

    N = nsamp

    if RBFcalibrate:
        theta, itheta, M, MM, iM = rbf_calibrate(X, I, Ieq, M, N, ibest, sepvalue, rbf, epsil, comparetol, display,
                                                 thetas, MM, iM, itheta)
    else:
        theta = 1.0

    W = get_weights(X, I, Ieq, M, N, ibest, sepvalue)

    delta_G = get_delta_adpt(X,Feasibility_unkn,delta_G_default)
    if isUnknownSatisfactionConstrained:
        delta_S = get_delta_adpt(X,SatConst_unkn,delta_S_default)
    else:
        delta_S =0

    results_display(N, zbest, display, nvar, scalevars, dd, d0, theta * epsil, RBFcalibrate)

    # Active learning phase
    while N < maxevals:

        time_iter_start = time.perf_counter()

        # Compute range of current surrogate function
        FH = dot(M[0:N, 0:N], W)  # surrogate at current samples
        dF = max(max(FH) - min(FH), epsDeltaF)

        if isLinConstrained or isNLConstrained:
            # penalty = rhoC * dF # for GLIS
            penalty = rhoC  # for GLISp
        if isLinConstrained and isNLConstrained:
            constrpenalty = lambda x: (penalty * (sum(maximum((Aineq.dot(x) - bineq).flatten("c"), 0.0) ** 2)
                                                  + sum(maximum(g(x), 0) ** 2)))
        elif isLinConstrained and not isNLConstrained:
            constrpenalty = lambda x: penalty * (sum(maximum((Aineq.dot(x) - bineq).flatten("c"), 0.0) ** 2))
        elif not isLinConstrained and isNLConstrained:
            constrpenalty = lambda x: penalty * sum(maximum(g(x), 0.0) ** 2)
        else:
            constrpenalty = lambda x: 0.0

        # Compute the IDW function of current best x (used in the acquisition term)
        d_ibest = vecsum((vstack((X[0:ibest,:],X[ibest+1:,:]))-X[ibest,:])**2,axis=-1)
        ii = where(d_ibest<1e-12)
        if ii[0].size > 0:
            iw_ibest = 0
        else:
            iw_ibest = 1.0/sum(1.0/d_ibest)


        acquisition = lambda x: (facquisition_pref(x, X, N, delta_E, dF, W, rbf, epsil,
                                                   theta, sepvalue, ibest, acquisition_method, isUnknownFeasibilityConstrained,isUnknownSatisfactionConstrained,Feasibility_unkn,SatConst_unkn,delta_G,delta_S,iw_ibest,maxevals) + constrpenalty(x))

        time_opt_acq_start = time.perf_counter()

        if globoptsol == "pswarm":
            # pso(func, lb, ub, ieqcons=[], f_ieqcons=None, args=(), kwargs={},
            #    swarmsize=100, omega=0.5, phip=0.5, phig=0.5, maxiter=100, minstep=1e-8,
            #    minfunc=1e-8, debug=False)
            with contextlib.redirect_stdout(io.StringIO()):
                z, cost = pso(acquisition, lb, ub, swarmsize=PSOswarmsize,
                              minfunc=dF * 1.0e-8, maxiter=PSOiters)

        elif globoptsol == "direct":
            DIRECTopt.set_min_objective(lambda x, grad: acquisition(x)[0])
            z = DIRECTopt.optimize(z.flatten("c"))

        time_opt_acquisition.append(time.perf_counter() - time_opt_acq_start)
        time_fun_eval_start = time.perf_counter()
        time_f_eval.append(time.perf_counter() - time_fun_eval_start)

        N = N + 1

        X[N - 1,] = z.T

        time_fit_surrogate_start = time.perf_counter()

        # Just update last row and column of M
        epsilth = epsil * theta
        for h in range(N):
            mij = rbf(X[h,], X[N - 1,], epsilth)
            M[h, N - 1] = mij
            M[N - 1, h] = mij
        time_fit_surrogate.append(time.perf_counter() - time_fit_surrogate_start)

        #
        # Assessment of comparison
        #

        if isUnknownSatisfactionConstrained and isUnknownFeasibilityConstrained:
            (prefN,fesN,_,satconstN,_) = pref(z,zbest) # preference query
        elif ~isUnknownSatisfactionConstrained and isUnknownFeasibilityConstrained:
            (prefN, fesN, _) = pref(z, zbest)
        elif isUnknownSatisfactionConstrained and ~isUnknownFeasibilityConstrained:
            (prefN, satconstN, _) = pref(z, zbest)
        else:
            prefN = pref(z, zbest)

        if isUnknownFeasibilityConstrained:
            Feasibility_unkn = vstack((Feasibility_unkn,fesN))
            delta_G = get_delta_adpt(X, Feasibility_unkn, delta_G_default)
        else:
            delta_G = 0

        if isUnknownSatisfactionConstrained:
            SatConst_unkn = vstack((SatConst_unkn, satconstN))
            delta_S = get_delta_adpt(X, SatConst_unkn, delta_S_default)
        else:
            delta_S = 0

        isfeas = True
        if isLinConstrained:
            isfeas = isfeas and all(Aineq.dot(z) <= bineq.flatten("c"))
        if isNLConstrained:
            isfeas = isfeas and all(g(z) <= 0)
        if isUnknownFeasibilityConstrained:
            isfeas = isfeas and Feasibility_unkn[N-1] > 0
        if isUnknownSatisfactionConstrained:
            isfeas = isfeas and SatConst_unkn[N-1] > 0

        if prefN == -1:
            I = vstack((I, [N - 1, ibest]))
            zbest = z.copy()
            ibest = N - 1
        elif prefN == 1:
            I = vstack((I, [ibest, N - 1]))
        else:
            Ieq = vstack((Ieq, [ibest, N - 1]))

        ibestseq[N-1] = ibest
        isfeas_seq[N-1] = ibest

        if RBFcalibrate and isin(N, RBFcalibrationSteps):
            theta, itheta, M, MM, iM = rbf_calibrate(X, I, Ieq, M, N, ibest, sepvalue, rbf, epsil, comparetol, display,
                                                     thetas, MM, iM, itheta)

        W = get_weights(X, I, Ieq, M, N, ibest, comparetol)

        results_display(N, z, display, nvar, scalevars, dd, d0, epsil * theta, RBFcalibrate)

        time_iter.append(time.perf_counter() - time_iter_start)
    # end while

    xopt = zbest.copy()
    if ~isUnknownFeasibilityConstrained:
        Feasibility_unkn = ones((maxevals, 1))

    if ~isUnknownSatisfactionConstrained and ~isUnknownFeasibilityConstrained:
        SatConst_unkn = ones((maxevals, 1))
    elif ~isUnknownSatisfactionConstrained and isUnknownFeasibilityConstrained:
        SatConst_unkn = Feasibility_unkn

    fes_opt_unkn = Feasibility_unkn[ibest]
    satConst_opt_unkn = SatConst_unkn[ibest]
    feas_opt_comb = isfeas_seq[ibest]

    if scalevars:
        # Scale variables back
        xopt = xopt * dd + d0
        X = X * (ones((N, 1)) * dd) + ones((N, 1)) * d0

    out = {"xopt": xopt,
           "X": X,
           "W": W,
           "M": M,
           "I": I,
           "Ieq": Ieq,
           "theta": theta,
           "Feasibility_unkn": Feasibility_unkn,
           "fes_opt_unkn": fes_opt_unkn,
           "SatConst_unkn": SatConst_unkn,
           "satConst_opt_unkn": satConst_opt_unkn,
           "isfeas_seq": isfeas_seq,
           "feas_opt_comb": feas_opt_comb,
           "ibest": ibest,
           "ibestseq": ibestseq,
           "time_iter": array(time_iter),
           "time_opt_acquisition": array(time_opt_acquisition),
           "time_fit_surrogate": array(time_fit_surrogate),
           "time_f_eval": array(time_f_eval)
           }

    return out
    problem["nsamp"] = nsamp
    problem["svdtol"] = 1e-6
    # problem["globoptsol"] = "direct"
    problem["globoptsol"] = "pswarm"
    problem["display"] = 1

    problem["scalevars"] = 1
    problem["compare_tol"] = 1e-6

    problem["constraint_penalty"] = 1e5
    problem["feasible_sampling"] = False

    if runGLISp:
        problem["rbf"] = lambda x1, x2, epsil: 1 / (
                    1 + epsil ** 2 * vecsum((x1 - x2) ** 2, axis=-1))  # inverse quadratic
        # problem["rbf"] = lambda x1,x2,epsil: exp(-(epsil**2*vecsum((x1-x2)**2,axis=-1)) # Gaussian RBF
        # problem["rbf"] = lambda x1,x2,epsil: sqrt((1+epsil**2*vecsum((x1-x2)**2,axis=-1)) # multiquadric
        print("Running GLISp optimization:\n")
    if runGLIS:
        problem["useRBF"] = 1
        problem["alpha"] = delta/5
        problem["f"] = fun
        if problem["useRBF"]:
            epsil = .5
            def fun_rbf(x1, x2):
                return 1 / (1 + epsil ** 2 * vecsum((x1 - x2) ** 2, axis=-1))
            problem["rbf"] = fun_rbf
        print("Running GLIS optimization:\n")

    viridis = plt.cm.get_cmap('viridis', Ntests)