Esempio n. 1
0
    def test_get_rbf_matrix(self):
        """Test basic properties of the RBF matrix (e.g. symmetry, size).

        Verify that the RBF matrix is symmetric and it has the correct
        size for all types of RBF.
        """
        settings = RbfoptSettings()
        for i in range(50):
            dim = random.randint(1, 20)
            num_points = random.randint(10, 50)
            node_pos = np.array(
                [[random.uniform(-100, 100) for j in range(dim)]
                 for k in range(num_points)])
            # Possible shapes of the matrix
            for rbf_type in self.rbf_types:
                settings.rbf = rbf_type
                mat = ru.get_rbf_matrix(settings, dim, num_points, node_pos)
                self.assertIsInstance(mat, np.matrix)
                self.assertAlmostEqual(np.max(mat - mat.transpose()),
                                       0.0,
                                       msg='RBF matrix is not symmetric')
                size = num_points + 1
                if (ru.get_degree_polynomial(settings) > 0):
                    size += dim**ru.get_degree_polynomial(settings)
                self.assertEqual(mat.shape, (size, size))
        # Check that exception is raised for unknown RBF types
        settings.rbf = 'unknown'
        self.assertRaises(ValueError, ru.get_rbf_matrix, settings, dim,
                          num_points, node_pos)
Esempio n. 2
0
 def test_get_degree_polynomial(self):
     """Verify that the degree is always between 0 and 1."""
     settings = RbfoptSettings()
     for rbf_type in self.rbf_types:
         settings.rbf = rbf_type
         degree = ru.get_degree_polynomial(settings)
         self.assertTrue(degree == 0 or degree == 1)
Esempio n. 3
0
def initialize_h_k_aux_variables(settings, instance):
    """Initialize auxiliary variables for the h_k model.
    
    Initialize the rbfval and mu_k_inv variables of a problem
    instance, using the values for for x and u_pi already given. This
    helps the local search by starting at a feasible point.

    Parameters
    ----------
    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    instance : pyomo.ConcreteModel
        A concrete instance of mathematical optimization model.
    """
    assert(isinstance(settings, RbfSettings))

    instance.rbfval = math.fsum(instance.lambda_h[i] * instance.u_pi[i].value
                                for i in instance.Q)

    instance.mu_k_inv = ((-1)**ru.get_degree_polynomial(settings) *
                         math.fsum(instance.Ainv[i,j] * instance.u_pi[i].value
                                   * instance.u_pi[j].value
                                   for i in instance.Q for j in instance.Q) + 
                         instance.phi_0)
Esempio n. 4
0
def initialize_instance_variables(settings, instance):
    """Initialize the variables of a problem instance.

    Initialize the x variables of a problem instance, and set the
    corresponding values for the vectors :math:`(u,\pi)`. This helps
    the local search by starting at a feasible point.

    Parameters
    ----------
    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    instance : pyomo.ConcreteModel
        A concrete instance of mathematical optimization model.
    """
    assert(isinstance(settings, RbfSettings))
    
    # Obtain radial basis function
    rbf = ru.get_rbf_function(settings)

    # Initialize variables for local search
    for i in instance.N:
        instance.x[i] = random.uniform(instance.var_lower[i], 
                                       instance.var_upper[i])
    for j in instance.K:
        instance.u_pi[j] = rbf(math.sqrt(math.fsum((instance.x[i].value - 
                                                     instance.node[j, i])**2 
                                                    for i in instance.N)))
    if (ru.get_degree_polynomial(settings) == 1):
        for j in instance.N:
            instance.u_pi[instance.k + j] = instance.x[j].value
        instance.u_pi[instance.k + instance.n] = 1.0
    elif (ru.get_degree_polynomial(settings) == 0):
        # We use "+ 0" here to convert the symbolic parameter
        # instance.k into a number that can be used for indexing.
        instance.u_pi[instance.k + 0] = 1.0
Esempio n. 5
0
def create_max_h_k_model(settings, n, k, var_lower, var_upper, integer_vars,
                         node_pos, rbf_lambda, rbf_h, mat, target_val):
    """Create the concrete model to maximize h_k.

    Create the concrete model to maximize h_k, also known as the
    Global Search Step of the RBF method.

    Parameters
    ----------

    settings : :class:`rbfopt_settings.RbfoptSettings`
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    var_lower : 1D numpy.ndarray[float]
        Vector of variable lower bounds.

    var_upper : 1D numpy.ndarray[float]
        Vector of variable upper bounds.

    integer_vars : 1D numpy.ndarray[int]
        List of indices of integer variables.

    node_pos : 2D numpy.ndarray[float]
        List of coordinates of the nodes (one on each row).

    rbf_lambda : 1D numpy.ndarray[float]
        The lambda coefficients of the RBF interpolant, corresponding
        to the radial basis functions. List of dimension k.

    rbf_h : 1D numpy.ndarray[float]
        The h coefficients of the RBF interpolant, corresponding to
        the polynomial. List of dimension n+1.

    mat: numpy.matrix
        The matrix necessary for the computation. This is the inverse
        of the matrix [Phi P; P^T 0], see paper as cited above. Must
        be a numpy.matrix of dimension ((k+1) x (k+1))

    target_val : float
        Value f* that we want to find in the unknown objective
        function.

    Returns
    -------
    pyomo.ConcreteModel
        The concrete model describing the problem.
    """
    assert (isinstance(var_lower, np.ndarray))
    assert (isinstance(var_upper, np.ndarray))
    assert (isinstance(integer_vars, np.ndarray))
    assert (isinstance(node_pos, np.ndarray))
    assert (isinstance(rbf_lambda, np.ndarray))
    assert (isinstance(rbf_h, np.ndarray))
    assert (len(var_lower) == n)
    assert (len(var_upper) == n)
    assert (len(rbf_lambda) == k)
    assert (len(rbf_h) == (n + 1))
    assert (len(node_pos) == k)
    assert (isinstance(mat, np.matrix))
    assert (mat.shape == (n + k + 1, n + k + 1))
    assert (isinstance(settings, RbfoptSettings))
    assert (ru.get_degree_polynomial(settings) == 1)

    model = ConcreteModel()

    # Dimension of the space
    model.n = Param(initialize=n)
    model.N = RangeSet(0, model.n - 1)

    # Number of interpolation nodes
    model.k = Param(initialize=k)
    model.K = RangeSet(0, model.k - 1)

    # Dimension of the matrix
    model.q = Param(initialize=(n + k + 1))
    model.Q = RangeSet(0, model.q - 1)
    model.Qlast = RangeSet(model.q - 1, model.q - 1)

    # Coefficients of the RBF
    lambda_h_param = {}
    for i in range(k):
        lambda_h_param[i] = float(rbf_lambda[i])
    for i in range(n + 1):
        lambda_h_param[k + i] = float(rbf_h[i])
    model.lambda_h = Param(model.Q, initialize=lambda_h_param)

    # Coordinates of the nodes
    node_param = {}
    for i in range(k):
        for j in range(n):
            node_param[i, j] = float(node_pos[i][j])
    model.node = Param(model.K, model.N, initialize=node_param)

    # Variable bounds
    var_lower_param = {}
    var_upper_param = {}
    for i in range(n):
        var_lower_param[i] = float(var_lower[i])
        var_upper_param[i] = float(var_upper[i])
    model.var_lower = Param(model.N, initialize=var_lower_param)
    model.var_upper = Param(model.N, initialize=var_upper_param)

    # Inverse of the matrix [Phi P; P^T 0]. Because the matrix is
    # symmetric, we only save the upper right part, while doubling the
    # off-diagonal elements.
    Ainv_param = {}
    for i in range(n + k + 1):
        for j in range(i, n + k + 1):
            if (abs(mat[i, j]) != 0.0):
                if (i == j):
                    Ainv_param[i, j] = float(mat[i, j])
                else:
                    Ainv_param[i, j] = float(2 * mat[i, j])
    model.Ainv = Param(model.Q, model.Q, initialize=Ainv_param, default=0.0)

    # Target value
    model.fstar = Param(initialize=target_val)

    # Value of phi at zero, necessary for shift
    if (settings.rbf == 'cubic' or settings.rbf == 'thin_plate_spline'):
        model.phi_0 = Param(initialize=0.0)

    # Variable: the point in the space
    model.x = Var(model.N, domain=Reals, bounds=_x_bounds)

    # Auxiliary variables: the vectors (u, \pi),
    # see equations (6) and (7) in the paper by Costa and Nannicini
    model.u_pi = Var(model.Q, domain=Reals)

    # Auxiliary variable: value of the rbf at a given point
    model.rbfval = Var(domain=Reals)

    # Auxiliary variable: value of the inverse of \mu_k at a given point
    model.mu_k_inv = Var(domain=Reals)

    # Objective function.
    model.OBJ = Objective(rule=_max_h_k_obj_expression, sense=maximize)

    # Constraints. See definitions below.
    if (settings.rbf == 'cubic'):
        model.UdefConstraint = Constraint(model.K,
                                          rule=_udef_cubic_constraint_rule)
    elif (settings.rbf == 'thin_plate_spline'):
        model.UdefConstraint = Constraint(model.K,
                                          rule=_udef_thinplate_constraint_rule)
    model.PidefConstraint = Constraint(model.N, rule=_pidef_constraint_rule)
    model.NonhomoConstraint = Constraint(model.Qlast,
                                         rule=_nonhomo_constraint_rule)
    model.RbfdefConstraint = Constraint(rule=_rbfdef_constraint_rule)
    model.MukdefConstraint = Constraint(rule=_mukdef_constraint_rule)

    # Add integer variables if necessary
    if (len(integer_vars)):
        add_integrality_constraints(model, integer_vars)

    return model
Esempio n. 6
0
def create_min_rbf_model(settings, n, k, var_lower, var_upper, integer_vars,
                         node_pos, rbf_lambda, rbf_h):
    """Create the concrete model to minimize the RBF.

    Create the concrete model to minimize the RBF.

    Parameters
    ----------

    settings : :class:`rbfopt_settings.RbfoptSettings`
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    var_lower : 1D numpy.ndarray[float]
        Vector of variable lower bounds.

    var_upper : 1D numpy.ndarray[float]
        Vector of variable upper bounds.

    integer_vars : 1D numpy.ndarray[int]
        List of indices of integer variables.

    node_pos : 2D numpy.ndarray[float]
        List of coordinates of the nodes (one on each row).

    rbf_lambda : 1D numpy.ndarray[float]
        The lambda coefficients of the RBF interpolant, corresponding
        to the radial basis functions. List of dimension k.

    rbf_h : 1D numpy.ndarray[float]
        The h coefficients of the RBF interpolant, corresponding to
        the polynomial. List of dimension n+1.

    Returns
    -------
    pyomo.ConcreteModel
        The concrete model describing the problem.
    """
    assert (isinstance(var_lower, np.ndarray))
    assert (isinstance(var_upper, np.ndarray))
    assert (isinstance(integer_vars, np.ndarray))
    assert (isinstance(node_pos, np.ndarray))
    assert (isinstance(rbf_lambda, np.ndarray))
    assert (isinstance(rbf_h, np.ndarray))
    assert (len(var_lower) == n)
    assert (len(var_upper) == n)
    assert (len(rbf_lambda) == k)
    assert (len(rbf_h) == (n + 1))
    assert (len(node_pos) == k)
    assert (isinstance(settings, RbfoptSettings))
    assert (ru.get_degree_polynomial(settings) == 1)

    model = ConcreteModel()

    # Dimension of the space
    model.n = Param(initialize=n)
    model.N = RangeSet(0, model.n - 1)

    # Number of interpolation nodes
    model.k = Param(initialize=k)
    model.K = RangeSet(0, model.k - 1)

    # Dimension of u_pi
    model.q = Param(initialize=(n + k + 1))
    model.Q = RangeSet(0, model.q - 1)
    model.Qlast = RangeSet(model.q - 1, model.q - 1)

    # Coefficients of the RBF
    lambda_h_param = {}
    for i in range(k):
        lambda_h_param[i] = float(rbf_lambda[i])
    for i in range(n + 1):
        lambda_h_param[k + i] = float(rbf_h[i])
    model.lambda_h = Param(model.Q, initialize=lambda_h_param)

    # Coordinates of the nodes
    node_param = {}
    for i in range(k):
        for j in range(n):
            node_param[i, j] = float(node_pos[i][j])
    model.node = Param(model.K, model.N, initialize=node_param)

    # Variable bounds
    var_lower_param = {}
    var_upper_param = {}
    for i in range(n):
        var_lower_param[i] = float(var_lower[i])
        var_upper_param[i] = float(var_upper[i])
    model.var_lower = Param(model.N, initialize=var_lower_param)
    model.var_upper = Param(model.N, initialize=var_upper_param)

    # Variable: the point in the space
    model.x = Var(model.N, domain=Reals, bounds=_x_bounds)

    # Auxiliary variables: the vectors (u, \pi),
    # see equations (6) and (7) in the paper by Costa and Nannicini
    model.u_pi = Var(model.Q, domain=Reals)

    model.OBJ = Objective(rule=_min_rbf_obj_expression, sense=minimize)

    # Constraints. See definitions below.
    if (settings.rbf == 'cubic'):
        model.UdefConstraint = Constraint(model.K,
                                          rule=_udef_cubic_constraint_rule)
    elif (settings.rbf == 'thin_plate_spline'):
        model.UdefConstraint = Constraint(model.K,
                                          rule=_udef_thinplate_constraint_rule)
    model.PidefConstraint = Constraint(model.N, rule=_pidef_constraint_rule)
    model.NonhomoConstraint = Constraint(model.Qlast,
                                         rule=_nonhomo_constraint_rule)

    # Add integer variables if necessary
    if (len(integer_vars)):
        add_integrality_constraints(model, integer_vars)

    return model
Esempio n. 7
0
def create_max_one_over_mu_model(settings, n, k, var_lower, var_upper,
                                 integer_vars, node_pos, mat):
    """Create the concrete model to maximize 1/\mu.

    Create the concrete model to maximize :math: `1/\mu`, also known
    as the InfStep of the RBF method.

    See paper by Costa and Nannicini, equation (7) pag 4, and the
    references therein.

    Parameters
    ----------

    settings : :class:`rbfopt_settings.RbfoptSettings`
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    var_lower : 1D numpy.ndarray[float]
        Vector of variable lower bounds.

    var_upper : 1D numpy.ndarray[float]
        Vector of variable upper bounds.

    integer_vars : 1D numpy.ndarray[int]
        List of indices of integer variables.

    node_pos : 2D numpy.ndarray[float]
        List of coordinates of the nodes (one on each row).

    mat: numpy.matrix
        The matrix necessary for the computation. This is the inverse
        of the matrix [Phi P; P^T 0], see paper as cited above. Must
        be a numpy.matrix of dimension ((k+1) x (k+1))

    Returns
    -------
    pyomo.ConcreteModel
        The concrete model describing the problem.
    """
    assert (isinstance(var_lower, np.ndarray))
    assert (isinstance(var_upper, np.ndarray))
    assert (isinstance(integer_vars, np.ndarray))
    assert (isinstance(node_pos, np.ndarray))
    assert (len(var_lower) == n)
    assert (len(var_upper) == n)
    assert (len(node_pos) == k)
    assert (isinstance(mat, np.matrix))
    assert (mat.shape == (n + k + 1, n + k + 1))
    assert (isinstance(settings, RbfoptSettings))
    assert (ru.get_degree_polynomial(settings) == 1)

    model = ConcreteModel()

    # Dimension of the space
    model.n = Param(initialize=n)
    model.N = RangeSet(0, model.n - 1)

    # Number of interpolation nodes
    model.k = Param(initialize=k)
    model.K = RangeSet(0, model.k - 1)

    # Dimension of the matrix
    model.q = Param(initialize=(n + k + 1))
    model.Q = RangeSet(0, model.q - 1)
    model.Qlast = RangeSet(model.q - 1, model.q - 1)

    # Coordinates of the nodes
    node_param = {}
    for i in range(k):
        for j in range(n):
            node_param[i, j] = float(node_pos[i][j])
    model.node = Param(model.K, model.N, initialize=node_param)

    # Variable bounds
    var_lower_param = {}
    var_upper_param = {}
    for i in range(n):
        var_lower_param[i] = float(var_lower[i])
        var_upper_param[i] = float(var_upper[i])
    model.var_lower = Param(model.N, initialize=var_lower_param)
    model.var_upper = Param(model.N, initialize=var_upper_param)

    # Inverse of the matrix [Phi P; P^T 0]. Because the matrix is
    # symmetric, we only save the upper right part, while doubling the
    # off-diagonal elements.
    Ainv_param = {}
    for i in range(n + k + 1):
        for j in range(i, n + k + 1):
            if (abs(mat[i, j]) != 0.0):
                if (i == j):
                    Ainv_param[i, j] = float(mat[i, j])
                else:
                    Ainv_param[i, j] = float(2 * mat[i, j])
    model.Ainv = Param(model.Q, model.Q, initialize=Ainv_param, default=0.0)

    # Value of phi at zero, necessary for shift
    if (settings.rbf == 'cubic' or settings.rbf == 'thin_plate_spline'):
        model.phi_0 = Param(initialize=0.0)

    # Variable: the point in the space
    model.x = Var(model.N, domain=Reals, bounds=_x_bounds)

    # Auxiliary variables: the vectors (u, \pi),
    # see equations (6) and (7) in the paper by Costa and Nannicini
    model.u_pi = Var(model.Q, domain=Reals)

    # Objective function. Remember that there should be a constant
    # term \phi(0) at the beginning, but because this is zero in this
    # case we take it out.
    model.OBJ = Objective(rule=_max_one_over_mu_obj_expression, sense=maximize)

    # Constraints. See definitions below.
    if (settings.rbf == 'cubic'):
        model.UdefConstraint = Constraint(model.K,
                                          rule=_udef_cubic_constraint_rule)
    elif (settings.rbf == 'thin_plate_spline'):
        model.UdefConstraint = Constraint(model.K,
                                          rule=_udef_thinplate_constraint_rule)
    model.PidefConstraint = Constraint(model.N, rule=_pidef_constraint_rule)
    model.NonhomoConstraint = Constraint(model.Qlast,
                                         rule=_nonhomo_constraint_rule)

    # Add integer variables if necessary
    if (len(integer_vars)):
        add_integrality_constraints(model, integer_vars)

    return model
Esempio n. 8
0
def get_noisy_rbf_coefficients(settings, n, k, Phimat, Pmat, node_val,
                               fast_node_index, fast_node_err_bounds,
                               init_rbf_lambda = None, init_rbf_h = None):
    """Obtain coefficients for the noisy RBF interpolant.

    Solve a quadratic problem to compute the coefficients of the RBF
    interpolant that minimizes bumpiness and lets all points with
    index in fast_node_index deviate by a specified percentage from
    their value.

    Parameters
    ----------

    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    Phimat : numpy.matrix
        Matrix Phi, i.e. top left part of the standard RBF matrix.

    Pmat : numpy.matrix
        Matrix P, i.e. top right part of the standard RBF matrix.

    node_val : List[float]
        List of values of the function at the nodes.
    
    fast_node_index : List[int]
        List of indices of nodes whose function value should be
        considered variable withing the allowed range.
    
    fast_node_err_bounds : List[(float, float)]
        Allowed deviation from node values for nodes affected by
        error. This is a list of pairs (lower, upper) of the same
        length as fast_node_index.

    init_rbf_lambda : List[float] or None
        Initial values that should be used for the lambda coefficients
        of the RBF. Can be None.

    init_rbf_h : List[float] or None
        Initial values that should be used for the h coefficients of
        the RBF. Can be None.

    Returns
    ---
    (List[float], List[float])
        Two vectors: lambda coefficients (for the radial basis
        functions), and h coefficients (for the polynomial). If
        initialization information was provided and was valid, then
        some values will always be returned. Otherwise, it will be
        None.

    Raises
    ------
    ValueError
        If the type of radial basis function is not supported.
    RuntimeError
        If the solver cannot be found.
    """    
    assert(isinstance(settings, RbfSettings))
    assert(len(node_val)==k)
    assert(isinstance(Phimat, np.matrix))
    assert(isinstance(Pmat, np.matrix))
    assert(len(fast_node_index)==len(fast_node_err_bounds))
    assert(init_rbf_lambda is None or len(init_rbf_lambda)==k)
    assert(init_rbf_h is None or len(init_rbf_h)==Pmat.shape[1])
    
    # Instantiate model
    if (ru.get_degree_polynomial(settings) == 1):
        model = rbfopt_degree1_models
    elif (ru.get_degree_polynomial(settings) == 0):
        model = rbfopt_degree0_models
    else:
        raise ValueError('RBF type ' + settings.rbf + ' not supported')

    instance = model.create_min_bump_model(settings, n, k, Phimat, Pmat,
                                           node_val, fast_node_index,
                                           fast_node_err_bounds)

    # Instantiate optimizer
    opt = pyomo.opt.SolverFactory(config.NLP_SOLVER_EXEC, solver_io='nl')
    if opt is None:
        raise RuntimeError('Solver ' + config.NLP_SOLVER_EXEC + ' not found')
    set_nlp_solver_options(opt)

    # Initialize instance variables with the solution provided (if
    # available). This should avoid any infeasibility.
    if (init_rbf_lambda is not None and init_rbf_h is not None):
        for i in range(len(init_rbf_lambda)):
            instance.rbf_lambda[i] = init_rbf_lambda[i]
            instance.slack[i] = 0.0
        for i in range(len(init_rbf_h)):
            instance.rbf_h[i] = init_rbf_h[i]

    # Solve and load results
    try:
        results = opt.solve(instance, keepfiles = False,
                            tee = settings.print_solver_output)
        if ((results.solver.status == pyomo.opt.SolverStatus.ok) and 
            (results.solver.termination_condition == 
             TerminationCondition.optimal)):
            # this is feasible and optimal
            instance.solutions.load_from(results)
            rbf_lambda = [instance.rbf_lambda[i].value for i in instance.K]
            rbf_h = [instance.rbf_h[i].value for i in instance.P]
        else:
            # If we have initialization information, return it. It is
            # a feasible solution. Otherwise, this will be None.
            rbf_lambda = init_rbf_lambda
            rbf_h = init_rbf_h
    except:
        # If we have initialization information, return it. It is
        # a feasible solution. Otherwise, this will be None.
        rbf_lambda = init_rbf_lambda
        rbf_h = init_rbf_h

    return (rbf_lambda, rbf_h)
def get_model_quality_estimate_clp(settings, n, k, node_pos, node_val,
                                   num_iterations):
    """Compute an estimate of model quality using LPs with Clp.

    Computes an estimate of model quality, performing
    cross-validation. This version does not use all interpolation
    nodes, but rather only the best num_iterations ones, and it uses a
    sequence of LPs instead of a brute-force calculation. It should be
    much faster than the brute-force calculation, as each LP in the
    sequence requires only two pivots. The solver is COIN-OR Clp.

    Parameters
    ----------
    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    n : int
        Dimension of the problem, i.e. the space where the point lives.

    k : int
        Number of nodes, i.e. interpolation points.

    node_pos : List[List[float]]
        Location of current interpolation nodes.

    node_val : List[float]
        List of values of the function at the nodes.

    num_iterations : int
        Number of nodes on which quality should be tested.
    
    Returns
    -------
    float
        An estimate of the leave-one-out cross-validation error, which
        can be interpreted as a measure of model quality.

    Raises
    ------
    RuntimeError
        If the LPs cannot be solved.

    ValueError
        If some settings are not supported.
    """
    assert(isinstance(settings, RbfSettings))
    assert(len(node_val)==k)
    assert(len(node_pos)==k)
    assert(num_iterations <= k)
    assert(cpx_available)
    # We cannot find a leave-one-out interpolant if the following
    # condition is not met.
    assert(k > n + 1)

    # Get size of polynomial part of the matrix (p) and sign of obj
    # function (sign)
    if (ru.get_degree_polynomial(settings) == 1):
        p = n + 1
        sign = 1
    elif (ru.get_degree_polynomial(settings) == 0):
        p = 1
        sign = -1
    else:
        raise ValueError('RBF type ' + settings.rbf + ' not supported')

    # Sort interpolation nodes by increasing objective function value
    sorted_list = sorted([(node_val[i], node_pos[i]) for i in range(k)])

    # Initialize the arrays used for the cross-validation
    cv_node_pos = [sorted_list[i][1] for i in range(k)]
    cv_node_val = [sorted_list[i][0] for i in range(k)]

    # Compute the RBF matrix, and the two submatrices of interest
    Amat = ru.get_rbf_matrix(settings, n, k, cv_node_pos)
    Phimat = Amat[:k, :k]
    Pmat = Amat[:k, k:]
    identity = np.matrix(np.identity(k))

    # Instantiate model
    model = CyLPModel()

    # Add variables: lambda, h, slacks
    rbf_lambda = model.addVariable('rbf_lambda', k)
    rbf_h = model.addVariable('rbf_h', p)
    slack = model.addVariable('slack', k)

    # Add constraints: interpolation conditions, unisolvence
    F = CyLPArray(cv_node_val)
    zeros = CyLPArray([0] * p)
    ones = CyLPArray([1] * p)
    if (p > 1):
        # We can use matrix form
        model += (Phimat * rbf_lambda + Pmat * rbf_h + identity * slack == F)
        model += (Pmat.transpose() * rbf_lambda == zeros)
    else:
        # Workaround for one dimensional variables: one row at a time
        for i in range(k):
            model += Phimat[i, :] * rbf_lambda + rbf_h + slack[i] == F[i]
        model += (Pmat.transpose() * rbf_lambda == zeros)

    # Add objective
    obj = CyLPArray([sign*val for val in node_val])
    model.objective = obj * rbf_lambda
                           
    # Create LP, suppress output, and set bounds
    lp = clp.CyClpSimplex(model)
    lp.logLevel = 0
    infinity = lp.getCoinInfinity()
    for i in range(k + p):
        lp.setColumnLower(i, -infinity)
        lp.setColumnUpper(i, infinity)
    for i in range(k):
        lp.setColumnLower(k + p + i, 0.0)
        lp.setColumnUpper(k + p + i, 0.0)

    # Estimate of the model error
    loo_error = 0.0

    for i in range(num_iterations):
        # Fix lambda[i] to zero
        lp.setColumnLower(i, 0.0)
        lp.setColumnUpper(i, 0.0)
        # Set slack[i] to unconstrained
        lp.setColumnLower(k + p + i, -infinity)
        lp.setColumnUpper(k + p + i, infinity)

        lp.dual(startFinishOptions = 'sx')

        if (not lp.getStatusCode() == 0):
            raise RuntimeError('Could not solve LP with Clp, status ' +
                               lp.getStatusString())

        lambda_sol = lp.primalVariableSolution['rbf_lambda'].tolist()
        h_sol = lp.primalVariableSolution['rbf_h'].tolist()

        # Compute value of the interpolant at the removed node
        predicted_val = ru.evaluate_rbf(settings, cv_node_pos[i], n, k,
                                        cv_node_pos, lambda_sol, h_sol)

        # Update leave-one-out error
        loo_error += abs(predicted_val - cv_node_val[i])

        # Fix lambda[i] to zero
        lp.setColumnLower(i, -infinity)
        lp.setColumnUpper(i, infinity)
        # Set slack[i] to unconstrained
        lp.setColumnLower(k + p + i, 0.0)
        lp.setColumnUpper(k + p + i, 0.0)

    return loo_error/num_iterations
Esempio n. 10
0
def maximize_one_over_mu(settings, n, k, var_lower, var_upper, node_pos,
                         mat, integer_vars):
    """Compute the maximum of :math: `1/\mu`.

    Construct a PyOmo model to maximize :math: `1/\mu`

    See paper by Costa and Nannicini, equation (7) pag 4, and the
    references therein.

    Parameters
    ----------
 
    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    var_lower : List[float]
        Vector of variable lower bounds.
    
    var_upper : List[float]
        Vector of variable upper bounds.

    node_pos : List[List[float]]
        List of coordinates of the nodes

    mat : numpy.matrix
        The matrix necessary for the computation. This is the inverse
        of the matrix [Phi P; P^T 0], see paper as cited above. Must
        be a square numpy.matrix of appropriate dimension.

    integer_vars : List[int] or None
        A list containing the indices of the integrality constrained
        variables. If None or empty list, all variables are assumed to
        be continuous.

    Returns
    -------
    float
        A maximizer. It is difficult to do global optimization so
        typically this method returns a local maximum.

    Raises
    ------
    ValueError
        If the type of radial basis function is not supported.
    RuntimeError
        If the solver cannot be found.
    """
    assert(len(var_lower)==n)
    assert(len(var_upper)==n)
    assert(len(node_pos)==k)
    assert(isinstance(mat, np.matrix))
    assert(isinstance(settings, RbfSettings))

    # Determine the size of the P matrix
    p = ru.get_size_P_matrix(settings, n)
    assert(mat.shape==(k + p, k + p))

    # Instantiate model
    if (ru.get_degree_polynomial(settings) == 1):
        model = rbfopt_degree1_models
    elif (ru.get_degree_polynomial(settings) == 0):
        model = rbfopt_degree0_models
    else:
        raise ValueError('RBF type ' + settings.rbf + ' not supported')

    instance = model.create_max_one_over_mu_model(settings, n, k, var_lower, 
                                                  var_upper, node_pos, mat,
                                                  integer_vars)

    # Initialize variables for local search
    initialize_instance_variables(settings, instance)

    # Instantiate optimizer
    opt = pyomo.opt.SolverFactory(config.MINLP_SOLVER_EXEC, solver_io='nl')
    if opt is None:
        raise RuntimeError('Solver ' + config.MINLP_SOLVER_EXEC + ' not found')
    set_minlp_solver_options(opt)

    # Solve and load results
    try:
        results = opt.solve(instance, keepfiles = False, 
                            tee = settings.print_solver_output)
        if ((results.solver.status == pyomo.opt.SolverStatus.ok) and 
            (results.solver.termination_condition == 
             TerminationCondition.optimal)):
            # this is feasible and optimal
            instance.solutions.load_from(results)
            point = [instance.x[i].value for i in instance.N]
            ru.round_integer_vars(point, integer_vars)
        else:
            point = None
    except:
        point = None

    return point
Esempio n. 11
0
def minimize_rbf(settings, n, k, var_lower, var_upper, node_pos,
                 rbf_lambda, rbf_h, integer_vars):
    """Compute the minimum of the RBF interpolant.

    Compute the minimum of the RBF interpolant with a PyOmo model.

    Parameters
    ----------

    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    var_lower : List[float]
        Vector of variable lower bounds.

    var_upper : List[float]
        Vector of variable upper bounds.

    node_pos : List[List[float]]
        List of coordinates of the nodes.

    rbf_lambda : List[float]
        The lambda coefficients of the RBF interpolant, corresponding
        to the radial basis functions. List of dimension k.

    rbf_h : List[float]
        The h coefficients of the RBF interpolant, corresponding to
        the polynomial. List of dimension n+1.

    integer_vars: List[int] or None
        A list containing the indices of the integrality constrained
        variables. If None or empty list, all variables are assumed to
        be continuous.

    Returns
    -------
    float
        A minimizer. It is difficult to do global optimization so
        typically this method returns a local minimum.

    Raises
    ------
    ValueError
        If the type of radial basis function is not supported.
    RuntimeError
        If the solver cannot be found.
    """    

    assert(len(var_lower)==n)
    assert(len(var_upper)==n)
    assert(len(rbf_lambda)==k)
    assert(len(node_pos)==k)
    assert(isinstance(settings, RbfSettings))

    # Determine the size of the P matrix
    p = ru.get_size_P_matrix(settings, n)
    assert(len(rbf_h)==(p))

    # Instantiate model
    if (ru.get_degree_polynomial(settings) == 1):
        model = rbfopt_degree1_models
    elif (ru.get_degree_polynomial(settings) == 0):
        model = rbfopt_degree0_models
    else:
        raise ValueError('RBF type ' + settings.rbf + ' not supported')

    instance = model.create_min_rbf_model(settings, n, k, var_lower, 
                                          var_upper, node_pos, rbf_lambda,
                                          rbf_h, integer_vars)

    # Initialize variables for local search
    initialize_instance_variables(settings, instance)

    # Instantiate optimizer
    opt = pyomo.opt.SolverFactory(config.MINLP_SOLVER_EXEC, solver_io='nl')
    if opt is None:
        raise RuntimeError('Solver ' + config.MINLP_SOLVER_EXEC + ' not found')
    set_minlp_solver_options(opt)

    # Solve and load results
    try:
        results = opt.solve(instance, keepfiles = False,
                            tee = settings.print_solver_output)
        if ((results.solver.status == pyomo.opt.SolverStatus.ok) and 
            (results.solver.termination_condition == 
             TerminationCondition.optimal)):
            # this is feasible and optimal
            instance.solutions.load_from(results)
            point = [instance.x[i].value for i in instance.N]
            ru.round_integer_vars(point, integer_vars)
        else:
            point = None
    except:
        point = None

    return point
Esempio n. 12
0
def create_min_bump_model(settings, n, k, Phimat, Pmat, node_val, 
                          fast_node_index, fast_node_err_bounds):
    """Create a model to find RBF coefficients with min bumpiness.

    Create a quadratic problem to compute the coefficients of the RBF
    interpolant that minimizes bumpiness and lets all points with
    index in fast_node_index deviate by a specified percentage from
    their value.

    Parameters
    ----------
    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    Phimat : numpy.matrix
        Matrix Phi, i.e. top left part of the standard RBF matrix.

    Pmat : numpy.matrix
        Matrix P, i.e. top right part of the standard RBF matrix.

    node_val : List[float]
        List of values of the function at the nodes.
    
    fast_node_index : List[int]
        List of indices of nodes whose function value should be
        considered variable withing the allowed range.
    
    fast_node_err_bounds : List[(float, float)]
        Allowed deviation from node values for nodes affected by
        error. This is a list of pairs (lower, upper) of the same
        length as fast_node_index.

    Returns
    -------
    pyomo.ConcreteModel
        The concrete model describing the problem.
    """
    assert(isinstance(settings, RbfSettings))
    assert(len(node_val)==k)
    assert(isinstance(Phimat, np.matrix))
    assert(isinstance(Pmat, np.matrix))
    assert(Phimat.shape==(k,k))
    assert(Pmat.shape==(k,n+1))
    assert(len(fast_node_index)==len(fast_node_err_bounds))
    assert(ru.get_degree_polynomial(settings)==1)

    model = ConcreteModel()

    # Dimension of the space
    model.n = Param(initialize=n)
    model.N = RangeSet(0, model.n - 1)

    # Dimension of P matrix
    model.p = Param(initialize=n+1)
    model.P = RangeSet(0, model.n)
        
    # Number of interpolation nodes
    model.k = Param(initialize=k)
    model.K = RangeSet(0, model.k - 1)

    # Node values, i.e. right hand sides of the first set of equations
    # in the constraints
    node_val_param = {}
    for i in range(k):
        node_val_param[i] = float(node_val[i])
    model.node_val = Param(model.K, initialize=node_val_param)

    # Slack variable bounds
    slack_lower_param = {}
    slack_upper_param = {}
    for (pos, var_index) in enumerate(fast_node_index):
        slack_lower_param[var_index] = float(fast_node_err_bounds[pos][0])
        slack_upper_param[var_index] = float(fast_node_err_bounds[pos][1])
    model.slack_lower = Param(model.K, initialize=slack_lower_param,
                              default=0.0)
    model.slack_upper = Param(model.K, initialize=slack_upper_param,
                              default=0.0)

    # Phi matrix.
    Phi_param = {}
    for i in range(k):
        for j in range(k):
            if (abs(Phimat[i, j]) != 0.0):
                Phi_param[i, j] = float(Phimat[i, j])
    model.Phi = Param(model.K, model.K, initialize=Phi_param,
                      default=0.0)

    # P matrix.
    Pm_param = {}
    for i in range(k):
        for j in range(n+1):
            if (abs(Pmat[i, j]) != 0.0):
                Pm_param[i, j] = float(Pmat[i, j])
    model.Pm = Param(model.K, model.P, initialize=Pm_param,
                     default=0.0)

    # Variable: the lambda coefficients of the RBF
    model.rbf_lambda = Var(model.K, domain=Reals)

    # Variable: the h coefficients of the RBF
    model.rbf_h = Var(model.P, domain=Reals)

    # Variable: the slacks for the equality constraints
    model.slack = Var(model.K, domain=Reals, bounds=_slack_bounds)

    # Objective function.
    model.OBJ = Objective(rule=_min_bump_obj_expression, sense=minimize)

    # Constraints. See definitions below.
    model.IntrConstraint = Constraint(model.K, rule=_intr_constraint_rule)
    model.UnisConstraint = Constraint(model.P, rule=_unis_constraint_rule)    

    return model
Esempio n. 13
0
def create_max_h_k_model(settings, n, k, var_lower, var_upper,
                         node_pos, rbf_lambda, rbf_h, mat, target_val,
                         integer_vars = None):
    """Create the abstract model to maximize h_k.

    Create the abstract model to maximize h_k, also known as the
    Global Search Step of the RBF method.

    Parameters
    ----------

    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    var_lower : List[float]
        Vector of variable lower bounds.

    var_upper : List[float]
        Vector of variable upper bounds.

    node_pos : List[List[float]]
        List of coordinates of the nodes.

    rbf_lambda : List[float]
        The lambda coefficients of the RBF interpolant, corresponding
        to the radial basis functions. List of dimension k.

    rbf_h : List[float]
        The h coefficients of the RBF interpolant, corresponding to
        the polynomial. List of dimension n+1.

    mat: numpy.matrix
        The matrix necessary for the computation. This is the inverse
        of the matrix [Phi P; P^T 0], see paper as cited above. Must
        be a numpy.matrix of dimension ((k+1) x (k+1))

    target_val : float
        Value f* that we want to find in the unknown objective
        function.

    integer_vars : List[int]
        List of indices of integer variables.

    Returns
    -------
    pyomo.ConcreteModel
        The concrete model describing the problem.
    """
    assert(len(var_lower)==n)
    assert(len(var_upper)==n)
    assert(len(rbf_lambda)==k)
    assert(len(rbf_h)==(n+1))
    assert(len(node_pos)==k)
    assert(isinstance(mat, np.matrix))
    assert(mat.shape==(n+k+1,n+k+1))
    assert(isinstance(settings, RbfSettings))
    assert(ru.get_degree_polynomial(settings)==1)

    model = ConcreteModel()

    # Dimension of the space
    model.n = Param(initialize=n)
    model.N = RangeSet(0, model.n - 1)
        
    # Number of interpolation nodes
    model.k = Param(initialize=k)
    model.K = RangeSet(0, model.k - 1)

    # Dimension of the matrix
    model.q = Param(initialize=(n+k+1))
    model.Q = RangeSet(0, model.q - 1)
    model.Qlast = RangeSet(model.q - 1, model.q - 1)

    # Coefficients of the RBF
    lambda_h_param = {}
    for i in range(k):
        lambda_h_param[i] = float(rbf_lambda[i])
    for i in range(n+1):
        lambda_h_param[k+i] = float(rbf_h[i])
    model.lambda_h = Param(model.Q, initialize=lambda_h_param)

    # Coordinates of the nodes
    node_param = {}
    for i in range(k):
        for j in range(n):
            node_param[i, j] = float(node_pos[i][j])
    model.node = Param(model.K, model.N, initialize=node_param)

    # Variable bounds
    var_lower_param = {}
    var_upper_param = {}
    for i in range(n):
        var_lower_param[i] = float(var_lower[i])
        var_upper_param[i] = float(var_upper[i])
    model.var_lower = Param(model.N, initialize=var_lower_param)
    model.var_upper = Param(model.N, initialize=var_upper_param)

    # Inverse of the matrix [Phi P; P^T 0]. Because the matrix is
    # symmetric, we only save the upper right part, while doubling the
    # off-diagonal elements.
    Ainv_param = {}
    for i in range(n+k+1):
        for j in range(i, n+k+1):
            if (abs(mat[i, j]) != 0.0):
                if (i == j):
                    Ainv_param[i, j] = float(mat[i, j])
                else:
                    Ainv_param[i, j] = float(2*mat[i, j])
    model.Ainv = Param(model.Q, model.Q, initialize=Ainv_param,
                       default=0.0)

    # Target value
    model.fstar = Param(initialize=target_val)

    # Value of phi at zero, necessary for shift
    if (settings.rbf == 'cubic' or settings.rbf == 'thin_plate_spline'):
        model.phi_0 = Param(initialize=0.0)

    # Variable: the point in the space
    model.x = Var(model.N, domain=Reals, bounds=_x_bounds)

    # Auxiliary variables: the vectors (u, \pi),
    # see equations (6) and (7) in the paper by Costa and Nannicini
    model.u_pi = Var(model.Q, domain=Reals)

    # Auxiliary variable: value of the rbf at a given point
    model.rbfval = Var(domain=Reals)

    # Auxiliary variable: value of the inverse of \mu_k at a given point
    model.mu_k_inv = Var(domain=Reals)

    # Objective function.
    model.OBJ = Objective(rule=_max_h_k_obj_expression, sense=maximize)

    # Constraints. See definitions below.
    if (settings.rbf == 'cubic'):        
        model.UdefConstraint = Constraint(model.K, 
                                          rule=_udef_cubic_constraint_rule)
    elif (settings.rbf == 'thin_plate_spline'):
        model.UdefConstraint = Constraint(model.K, 
                                          rule=_udef_thinplate_constraint_rule)
    model.PidefConstraint = Constraint(model.N, rule=_pidef_constraint_rule)
    model.NonhomoConstraint = Constraint(model.Qlast, 
                                         rule=_nonhomo_constraint_rule)
    model.RbfdefConstraint = Constraint(rule=_rbfdef_constraint_rule)
    model.MukdefConstraint = Constraint(rule=_mukdef_constraint_rule)

    # Add integer variables if necessary
    if (integer_vars is not None and len(integer_vars) > 0):
        add_integrality_constraints(model, integer_vars)

    return model
Esempio n. 14
0
def create_min_rbf_model(settings, n, k, var_lower, var_upper, node_pos, 
                         rbf_lambda, rbf_h, integer_vars = None):
    """Create the concrete model to minimize the RBF.

    Create the concrete model to minimize the RBF.

    Parameters
    ----------

    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    var_lower : List[float]
        Vector of variable lower bounds.

    var_upper : List[float]
        Vector of variable upper bounds.

    node_pos : List[List[float]]
        List of coordinates of the nodes.

    rbf_lambda : List[float]
        The lambda coefficients of the RBF interpolant, corresponding
        to the radial basis functions. List of dimension k.

    rbf_h : List[float]
        The h coefficients of the RBF interpolant, corresponding to
        the polynomial. List of dimension n+1.

    integer_vars : List[int]
        List of indices of integer variables.

    Returns
    -------
    pyomo.ConcreteModel
        The concrete model describing the problem.
    """    
    assert(len(var_lower)==n)
    assert(len(var_upper)==n)
    assert(len(rbf_lambda)==k)
    assert(len(rbf_h)==(n+1))
    assert(len(node_pos)==k)
    assert(isinstance(settings, RbfSettings))
    assert(ru.get_degree_polynomial(settings)==1)

    model = ConcreteModel()
    
    # Dimension of the space
    model.n = Param(initialize=n)
    model.N = RangeSet(0, model.n - 1)
        
    # Number of interpolation nodes
    model.k = Param(initialize=k)
    model.K = RangeSet(0, model.k - 1)

    # Dimension of u_pi
    model.q = Param(initialize=(n+k+1))
    model.Q = RangeSet(0, model.q - 1)
    model.Qlast = RangeSet(model.q-1, model.q-1)

    # Coefficients of the RBF
    lambda_h_param = {}
    for i in range(k):
        lambda_h_param[i] = float(rbf_lambda[i])
    for i in range(n+1):
        lambda_h_param[k+i] = float(rbf_h[i])
    model.lambda_h = Param(model.Q, initialize=lambda_h_param)

    # Coordinates of the nodes
    node_param = {}
    for i in range(k):
        for j in range(n):
            node_param[i, j] = float(node_pos[i][j])
    model.node = Param(model.K, model.N, initialize=node_param)

    # Variable bounds
    var_lower_param = {}
    var_upper_param = {}
    for i in range(n):
        var_lower_param[i] = float(var_lower[i])
        var_upper_param[i] = float(var_upper[i])
    model.var_lower = Param(model.N, initialize=var_lower_param)
    model.var_upper = Param(model.N, initialize=var_upper_param)

    # Variable: the point in the space
    model.x = Var(model.N, domain=Reals, bounds=_x_bounds)

    # Auxiliary variables: the vectors (u, \pi),
    # see equations (6) and (7) in the paper by Costa and Nannicini
    model.u_pi = Var(model.Q, domain=Reals)

    model.OBJ = Objective(rule=_min_rbf_obj_expression, sense=minimize)

    # Constraints. See definitions below.
    if (settings.rbf == 'cubic'):        
        model.UdefConstraint = Constraint(model.K, 
                                          rule=_udef_cubic_constraint_rule)
    elif (settings.rbf == 'thin_plate_spline'):
        model.UdefConstraint = Constraint(model.K, 
                                          rule=_udef_thinplate_constraint_rule)
    model.PidefConstraint = Constraint(model.N, rule=_pidef_constraint_rule)
    model.NonhomoConstraint = Constraint(model.Qlast, 
                                         rule=_nonhomo_constraint_rule)

    # Add integer variables if necessary
    if (integer_vars is not None and len(integer_vars) > 0):
        add_integrality_constraints(model, integer_vars)

    return model
Esempio n. 15
0
def create_max_one_over_mu_model(settings, n, k, var_lower, var_upper, 
                                 node_pos, mat, integer_vars = None):

    """Create the concrete model to maximize 1/\mu.

    Create the concrete model to maximize :math: `1/\mu`, also known
    as the InfStep of the RBF method.

    See paper by Costa and Nannicini, equation (7) pag 4, and the
    references therein.

    Parameters
    ----------

    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    var_lower : List[float]
        Vector of variable lower bounds.

    var_upper : List[float]
        Vector of variable upper bounds.

    node_pos : List[List[float]]
        List of coordinates of the nodes.

    mat: numpy.matrix
        The matrix necessary for the computation. This is the inverse
        of the matrix [Phi P; P^T 0], see paper as cited above. Must
        be a numpy.matrix of dimension ((k+1) x (k+1))

    integer_vars : List[int]
        List of indices of integer variables.

    Returns
    -------
    pyomo.ConcreteModel
        The concrete model describing the problem.
    """
    assert(len(var_lower)==n)
    assert(len(var_upper)==n)
    assert(len(node_pos)==k)
    assert(isinstance(mat, np.matrix))
    assert(mat.shape==(n+k+1,n+k+1))
    assert(isinstance(settings, RbfSettings))
    assert(ru.get_degree_polynomial(settings)==1)

    model = ConcreteModel()

    # Dimension of the space
    model.n = Param(initialize=n)
    model.N = RangeSet(0, model.n - 1)
        
    # Number of interpolation nodes
    model.k = Param(initialize=k)
    model.K = RangeSet(0, model.k - 1)

    # Dimension of the matrix
    model.q = Param(initialize=(n+k+1))
    model.Q = RangeSet(0, model.q - 1)
    model.Qlast = RangeSet(model.q - 1, model.q - 1)

    # Coordinates of the nodes
    node_param = {}
    for i in range(k):
        for j in range(n):
            node_param[i, j] = float(node_pos[i][j])
    model.node = Param(model.K, model.N, initialize=node_param)

    # Variable bounds
    var_lower_param = {}
    var_upper_param = {}
    for i in range(n):
        var_lower_param[i] = float(var_lower[i])
        var_upper_param[i] = float(var_upper[i])
    model.var_lower = Param(model.N, initialize=var_lower_param)
    model.var_upper = Param(model.N, initialize=var_upper_param)

    # Inverse of the matrix [Phi P; P^T 0]. Because the matrix is
    # symmetric, we only save the upper right part, while doubling the
    # off-diagonal elements.
    Ainv_param = {}
    for i in range(n+k+1):
        for j in range(i, n+k+1):
            if (abs(mat[i, j]) != 0.0):
                if (i == j):
                    Ainv_param[i, j] = float(mat[i, j])
                else:
                    Ainv_param[i, j] = float(2*mat[i, j])
    model.Ainv = Param(model.Q, model.Q, initialize=Ainv_param,
                       default=0.0)

    # Value of phi at zero, necessary for shift
    if (settings.rbf == 'cubic' or settings.rbf == 'thin_plate_spline'):
        model.phi_0 = Param(initialize=0.0)

    # Variable: the point in the space
    model.x = Var(model.N, domain=Reals, bounds=_x_bounds)

    # Auxiliary variables: the vectors (u, \pi),
    # see equations (6) and (7) in the paper by Costa and Nannicini
    model.u_pi = Var(model.Q, domain=Reals)

    # Objective function. Remember that there should be a constant
    # term \phi(0) at the beginning, but because this is zero in this
    # case we take it out.
    model.OBJ = Objective(rule=_max_one_over_mu_obj_expression,
                          sense=maximize)

    # Constraints. See definitions below.
    if (settings.rbf == 'cubic'):        
        model.UdefConstraint = Constraint(model.K, 
                                          rule=_udef_cubic_constraint_rule)
    elif (settings.rbf == 'thin_plate_spline'):
        model.UdefConstraint = Constraint(model.K, 
                                          rule=u_def_thinplate_constraint_rule)
    model.PidefConstraint = Constraint(model.N, rule=_pidef_constraint_rule)
    model.NonhomoConstraint = Constraint(model.Qlast, 
                                         rule=_nonhomo_constraint_rule)

    # Add integer variables if necessary
    if (integer_vars is not None and len(integer_vars) > 0):
        add_integrality_constraints(model, integer_vars)

    return model
Esempio n. 16
0
def create_min_bump_model(settings, n, k, Phimat, Pmat, node_val,
                          fast_node_index, fast_node_err_bounds):
    """Create a model to find RBF coefficients with min bumpiness.

    Create a quadratic problem to compute the coefficients of the RBF
    interpolant that minimizes bumpiness and lets all points with
    index in fast_node_index deviate by a specified percentage from
    their value.

    Parameters
    ----------
    settings : :class:`rbfopt_settings.RbfoptSettings`
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    Phimat : numpy.matrix
        Matrix Phi, i.e. top left part of the standard RBF matrix.

    Pmat : numpy.matrix
        Matrix P, i.e. top right part of the standard RBF matrix.

    node_val : 1D numpy.ndarray[float]
        List of values of the function at the nodes.
    
    fast_node_index : 1D numpy.ndarray[int]
        List of indices of nodes whose function value should be
        considered variable withing the allowed range.
    
    fast_node_err_bounds : List[(float, float)]
        Allowed deviation from node values for nodes affected by
        error. This is a list of pairs (lower, upper) of the same
        length as fast_node_index.

    Returns
    -------
    pyomo.ConcreteModel
        The concrete model describing the problem.
    """
    assert (isinstance(node_val, np.ndarray))
    assert (isinstance(fast_node_index, np.ndarray))
    assert (isinstance(settings, RbfoptSettings))
    assert (len(node_val) == k)
    assert (isinstance(Phimat, np.matrix))
    assert (isinstance(Pmat, np.matrix))
    assert (Phimat.shape == (k, k))
    assert (Pmat.shape == (k, n + 1))
    assert (len(fast_node_index) == len(fast_node_err_bounds))
    assert (ru.get_degree_polynomial(settings) == 1)

    model = ConcreteModel()

    # Dimension of the space
    model.n = Param(initialize=n)
    model.N = RangeSet(0, model.n - 1)

    # Dimension of P matrix
    model.p = Param(initialize=n + 1)
    model.P = RangeSet(0, model.n)

    # Number of interpolation nodes
    model.k = Param(initialize=k)
    model.K = RangeSet(0, model.k - 1)

    # Node values, i.e. right hand sides of the first set of equations
    # in the constraints
    node_val_param = {}
    for i in range(k):
        node_val_param[i] = float(node_val[i])
    model.node_val = Param(model.K, initialize=node_val_param)

    # Slack variable bounds
    slack_lower_param = {}
    slack_upper_param = {}
    for (pos, var_index) in enumerate(fast_node_index):
        slack_lower_param[var_index] = float(fast_node_err_bounds[pos][0])
        slack_upper_param[var_index] = float(fast_node_err_bounds[pos][1])
    model.slack_lower = Param(model.K,
                              initialize=slack_lower_param,
                              default=0.0)
    model.slack_upper = Param(model.K,
                              initialize=slack_upper_param,
                              default=0.0)

    # Phi matrix.
    Phi_param = {}
    for i in range(k):
        for j in range(k):
            if (abs(Phimat[i, j]) != 0.0):
                Phi_param[i, j] = float(Phimat[i, j])
    model.Phi = Param(model.K, model.K, initialize=Phi_param, default=0.0)

    # P matrix.
    Pm_param = {}
    for i in range(k):
        for j in range(n + 1):
            if (abs(Pmat[i, j]) != 0.0):
                Pm_param[i, j] = float(Pmat[i, j])
    model.Pm = Param(model.K, model.P, initialize=Pm_param, default=0.0)

    # Variable: the lambda coefficients of the RBF
    model.rbf_lambda = Var(model.K, domain=Reals)

    # Variable: the h coefficients of the RBF
    model.rbf_h = Var(model.P, domain=Reals)

    # Variable: the slacks for the equality constraints
    model.slack = Var(model.K, domain=Reals, bounds=_slack_bounds)

    # Objective function.
    model.OBJ = Objective(rule=_min_bump_obj_expression, sense=minimize)

    # Constraints. See definitions below.
    model.IntrConstraint = Constraint(model.K, rule=_intr_constraint_rule)
    model.UnisConstraint = Constraint(model.P, rule=_unis_constraint_rule)

    return model
Esempio n. 17
0
def create_min_msrsm_model(settings, n, k, var_lower, var_upper, integer_vars,
                           node_pos, rbf_lambda, rbf_h, dist_weight, dist_min,
                           dist_max, fmin, fmax):
    """Create the concrete model to optimize the MSRSM objective.

    Create the concreate model to minimize a weighted combination of
    the value of the RBF interpolant and the (negative of the)
    distance from the closes interpolation node. This is the Global
    Search Step of the MSRSM method.

    Parameters
    ----------

    settings : :class:`rbfopt_settings.RbfoptSettings`
        Global and algorithmic settings.

    n : int
        The dimension of the problem, i.e. size of the space.

    k : int
        Number of nodes, i.e. interpolation points.

    var_lower : 1D numpy.ndarray[float]
        Vector of variable lower bounds.

    var_upper : 1D numpy.ndarray[float]
        Vector of variable upper bounds.

    integer_vars : 1D numpy.ndarray[int]
        List of indices of integer variables.

    node_pos : 2D numpy.ndarray[float]
        List of coordinates of the nodes (one on each row).

    rbf_lambda : 1D numpy.ndarray[float]
        The lambda coefficients of the RBF interpolant, corresponding
        to the radial basis functions. List of dimension k.

    rbf_h : 1D numpy.ndarray[float]
        The h coefficients of the RBF interpolant, corresponding to
        the polynomial. List of dimension n+1.

    dist_weight : float
        The weight paramater for distance and RBF interpolant
        value. Must be between 0 and 1. A weight of 1.0 corresponds to
        using solely distance, 0.0 to objective function.

    dist_min : float
        The minimum distance between two interpolation nodes.

    dist_max : float
        The maximum distance between two interpolation nodes.

    fmin : float
        The minimum value of an interpolation node.

    fmax : float
        The maximum value of an interpolation node.

    Returns
    -------
    pyomo.ConcreteModel
        The concrete model describing the problem.

    """
    assert (isinstance(var_lower, np.ndarray))
    assert (isinstance(var_upper, np.ndarray))
    assert (isinstance(integer_vars, np.ndarray))
    assert (isinstance(node_pos, np.ndarray))
    assert (isinstance(rbf_lambda, np.ndarray))
    assert (isinstance(rbf_h, np.ndarray))
    assert (len(var_lower) == n)
    assert (len(var_upper) == n)
    assert (len(rbf_lambda) == k)
    assert (len(rbf_h) == (n + 1))
    assert (len(node_pos) == k)
    assert (isinstance(settings, RbfoptSettings))
    assert (ru.get_degree_polynomial(settings) == 1)
    assert (0 <= dist_weight <= 1)
    assert (dist_max >= dist_min >= 0)

    model = ConcreteModel()

    # Dimension of the space
    model.n = Param(initialize=n)
    model.N = RangeSet(0, model.n - 1)

    # Number of interpolation nodes
    model.k = Param(initialize=k)
    model.K = RangeSet(0, model.k - 1)

    # Dimension of u_pi
    model.q = Param(initialize=(n + k + 1))
    model.Q = RangeSet(0, model.q - 1)
    model.Qlast = RangeSet(model.q - 1, model.q - 1)

    # Coefficients of the RBF
    lambda_h_param = {}
    for i in range(k):
        lambda_h_param[i] = float(rbf_lambda[i])
    for i in range(n + 1):
        lambda_h_param[k + i] = float(rbf_h[i])
    model.lambda_h = Param(model.Q, initialize=lambda_h_param)

    # Coordinates of the nodes
    node_param = {}
    for i in range(k):
        for j in range(n):
            node_param[i, j] = float(node_pos[i][j])
    model.node = Param(model.K, model.N, initialize=node_param)

    # Variable bounds
    var_lower_param = {}
    var_upper_param = {}
    for i in range(n):
        var_lower_param[i] = float(var_lower[i])
        var_upper_param[i] = float(var_upper[i])
    model.var_lower = Param(model.N, initialize=var_lower_param)
    model.var_upper = Param(model.N, initialize=var_upper_param)

    # Adjust parameters to avoid zero denominators in expressions
    if (fmax <= fmin + settings.eps_zero):
        fmax = fmin + 1
    if (dist_max <= dist_min + settings.eps_zero):
        dist_min = dist_max - 1
    # Minimum and maximum distance
    model.dist_min = Param(initialize=dist_min)
    model.dist_max = Param(initialize=dist_max)
    # Minimum and maximum of function values interpolation nodes
    model.fmin = Param(initialize=fmin)
    model.fmax = Param(initialize=fmax)
    # Weight of the distance and objective criteria
    model.dist_weight = Param(initialize=dist_weight)
    model.obj_weight = Param(
        initialize=(1.0 if settings.modified_msrsm_score else 1 - dist_weight))

    # Value of phi at zero, necessary for shift
    if (settings.rbf == 'cubic' or settings.rbf == 'thin_plate_spline'):
        model.phi_0 = Param(initialize=0.0)

    # Variable: the point in the space
    model.x = Var(model.N, domain=Reals, bounds=_x_bounds)

    # Auxiliary variables: the vectors (u, \pi),
    # see equations (6) and (7) in the paper by Costa and Nannicini
    model.u_pi = Var(model.Q, domain=Reals)

    # Auxiliary variable: value of the rbf at a given point
    model.rbfval = Var(domain=Reals)

    # Auxiliary variable: value of the inverse of \mu_k at a given point
    model.mindistsq = Var(domain=NonNegativeReals)

    # Objective function.
    model.OBJ = Objective(rule=_min_msrsm_obj_expression, sense=minimize)

    # Constraints. See definitions below.
    if (settings.rbf == 'cubic'):
        model.UdefConstraint = Constraint(model.K,
                                          rule=_udef_cubic_constraint_rule)
    elif (settings.rbf == 'thin_plate_spline'):
        model.UdefConstraint = Constraint(model.K,
                                          rule=_udef_thinplate_constraint_rule)
    model.PidefConstraint = Constraint(model.N, rule=_pidef_constraint_rule)
    model.NonhomoConstraint = Constraint(model.Qlast,
                                         rule=_nonhomo_constraint_rule)
    model.RbfdefConstraint = Constraint(rule=_rbfdef_constraint_rule)
    model.MdistdefConstraint = Constraint(model.K,
                                          rule=_mdistdef_constraint_rule)

    # Add integer variables if necessary
    if (len(integer_vars)):
        add_integrality_constraints(model, integer_vars)

    return model
Esempio n. 18
0
def get_model_quality_estimate_cpx(settings, n, k, node_pos, node_val,
                                   num_iterations):
    """Compute an estimate of model quality using LPs with Cplex.

    Computes an estimate of model quality, performing
    cross-validation. This version does not use all interpolation
    nodes, but rather only the best num_iterations ones, and it uses a
    sequence of LPs instead of a brute-force calculation. It should be
    much faster than the brute-force calculation, as each LP in the
    sequence requires only two pivots. The solver is IBM-ILOG Cplex.

    Parameters
    ----------
    settings : rbfopt_settings.RbfSettings
        Global and algorithmic settings.

    n : int
        Dimension of the problem, i.e. the space where the point lives.

    k : int
        Number of nodes, i.e. interpolation points.

    node_pos : List[List[float]]
        Location of current interpolation nodes.

    node_val : List[float]
        List of values of the function at the nodes.

    num_iterations : int
        Number of nodes on which quality should be tested.
    
    Returns
    -------
    float
        An estimate of the leave-one-out cross-validation error, which
        can be interpreted as a measure of model quality.

    Raises
    ------
    RuntimeError
        If the LPs cannot be solved.

    ValueError
        If some settings are not supported.
    """
    assert(isinstance(settings, RbfSettings))
    assert(len(node_val)==k)
    assert(len(node_pos)==k)
    assert(num_iterations <= k)
    assert(cpx_available)
    # We cannot find a leave-one-out interpolant if the following
    # condition is not met.
    assert(k > n + 1)

    # Get size of polynomial part of the matrix (p) and sign of obj
    # function (sign)
    if (ru.get_degree_polynomial(settings) == 1):
        p = n + 1
        sign = 1
    elif (ru.get_degree_polynomial(settings) == 0):
        p = 1
        sign = -1
    else:
        raise ValueError('RBF type ' + settings.rbf + ' not supported')

    # Sort interpolation nodes by increasing objective function value
    sorted_list = sorted([(node_val[i], node_pos[i]) for i in range(k)])

    # Initialize the arrays used for the cross-validation
    cv_node_pos = [sorted_list[i][1] for i in range(k)]
    cv_node_val = [sorted_list[i][0] for i in range(k)]

    # Compute the RBF matrix
    Amat = ru.get_rbf_matrix(settings, n, k, cv_node_pos)

    # Instantiate model and suppress output
    lp = cpx.Cplex()
    lp.set_log_stream(None)
    lp.set_error_stream(None)
    lp.set_warning_stream(None)
    lp.set_results_stream(None)
    lp.cleanup(settings.eps_zero)
    lp.parameters.threads.set(1)
    # lp.parameters.simplex.display.set(2)

    # Add variables: lambda, h, slacks
    lp.variables.add(obj = [sign*val for val in node_val],
                     lb = [-cpx.infinity] * k,
                     ub = [cpx.infinity] * k)
    lp.variables.add(lb = [-cpx.infinity] * p,
                     ub = [cpx.infinity] * p)
    lp.variables.add(lb = [0] * k,
                     ub = [0] * k)

    # Add constraints: interpolation conditions, unisolvence
    expr = [cpx.SparsePair(ind = [j for j in range(k + p)] + [k + p + i],
                           val = [Amat[i, j] for j in range(k + p)] + [1.0])
            for i in range(k)]
    lp.linear_constraints.add(lin_expr = expr, senses = ['E'] * k,
                              rhs = cv_node_val)
    expr = [cpx.SparsePair(ind = [j for j in range(k + p)],
                           val = [Amat[i, j] for j in range(k + p)])
            for i in range(k, k + p)]
    lp.linear_constraints.add(lin_expr = expr, senses = ['E'] * p,
                              rhs = [0] * p)

    # Estimate of the model error
    loo_error = 0.0

    for i in range(num_iterations):
        # Fix lambda[i] to zero
        lp.variables.set_lower_bounds(i, 0.0)
        lp.variables.set_upper_bounds(i, 0.0)
        # Set slack[i] to unconstrained
        lp.variables.set_lower_bounds(k + p + i, -cpx.infinity)
        lp.variables.set_upper_bounds(k + p + i, cpx.infinity)

        lp.solve()

        if (not lp.solution.is_primal_feasible()):
            raise RuntimeError('Could not solve LP with Cplex')

        rbf_l = lp.solution.get_values(0, k - 1)
        rbf_h = lp.solution.get_values(k, k + p - 1)

        # Compute value of the interpolant at the removed node
        predicted_val = ru.evaluate_rbf(settings, cv_node_pos[i], n, k,
                                        cv_node_pos, rbf_l, rbf_h)

        # Update leave-one-out error
        loo_error += abs(predicted_val - cv_node_val[i])

        # Fix lambda[i] to zero
        lp.variables.set_lower_bounds(i, -cpx.infinity)
        lp.variables.set_upper_bounds(i, cpx.infinity)
        # Set slack[i] to unconstrained
        lp.variables.set_lower_bounds(k + p + i, 0.0)
        lp.variables.set_upper_bounds(k + p + i, 0.0)

    return loo_error/num_iterations