Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
r""" CVXOPT SDP Backend
AUTHORS:
- Ingolfur Edvardsson (2014-05) : initial implementation
- Dima Pasechnik (2015-12) : minor fixes
""" #***************************************************************************** # Copyright (C) 2014 Ingolfur Edvardsson <ingolfured@gmail.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function
from .generic_sdp_backend cimport GenericSDPBackend
cdef class CVXOPTSDPBackend(GenericSDPBackend): cdef list objective_function #c_matrix cdef list coeffs_matrix cdef bint is_maximize
cdef list row_name_var cdef list col_name_var cdef dict answer cdef dict param cdef str name
def __cinit__(self, maximization = True): """ Cython constructor
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT")
"""
"maxiters":100, "abstol":1e-7, "reltol":1e-6, "feastol":1e-7, "refinement":1 } else: self.set_sense(-1)
def get_matrix(self): """ Get a block of a matrix coefficient
EXAMPLES::
sage: p = SemidefiniteProgram(solver="cvxopt") sage: x = p.new_variable() sage: a1 = matrix([[1, 2.], [2., 3.]]) sage: a2 = matrix([[3, 4.], [4., 5.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] <= a1) sage: b = p.get_backend() sage: b.get_matrix()[0][0] ( [-1.0 -2.0] -1, [-2.0 -3.0] )
"""
cpdef int add_variable(self, obj=0.0, name=None) except -1: """ Add a variable.
This amounts to adding a new column of matrices to the matrix. By default, the variable is both positive and real.
INPUT:
- ``obj`` - (optional) coefficient of this variable in the objective function (default: 0.0)
- ``name`` - an optional name for the newly added variable (default: ``None``).
OUTPUT: The index of the newly created variable
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.ncols() 0 sage: p.add_variable() 0 sage: p.ncols() 1 sage: p.add_variable() 1 sage: p.add_variable(name='x',obj=1.0) 2 sage: p.col_name(2) 'x' sage: p.objective_coefficient(2) 1.00000000000000 """ if self.matrices_dim.get(i) is not None: row.append( Matrix.zero(self.matrices_dim[i], self.matrices_dim[i]) ) else: row.append(0) i+=1
cpdef int add_variables(self, int n, names=None) except -1: """ Add ``n`` variables.
This amounts to adding new columns to the matrix. By default, the variables are both positive and real.
INPUT:
- ``n`` - the number of new variables (must be > 0)
- ``names`` - optional list of names (default: ``None``)
OUTPUT: The index of the variable created last.
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.ncols() 0 sage: p.add_variables(5) 4 sage: p.ncols() 5 sage: p.add_variables(2, names=['a','b']) 6 """
cpdef set_sense(self, int sense): """ Set the direction (maximization/minimization).
INPUT:
- ``sense`` (integer) :
* +1 => Maximization * -1 => Minimization
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.is_maximization() True sage: p.set_sense(-1) sage: p.is_maximization() False """ else:
cpdef objective_coefficient(self, int variable, coeff=None): """ Set or get the coefficient of a variable in the objective function
INPUT:
- ``variable`` (integer) -- the variable's id
- ``coeff`` (double) -- its coefficient
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.add_variable() 0 sage: p.objective_coefficient(0) 0.0 sage: p.objective_coefficient(0,2) sage: p.objective_coefficient(0) 2.0 """ else:
cpdef set_objective(self, list coeff, d=0.0): """ Set the objective function.
INPUT:
- ``coeff`` -- a list of real values, whose ith element is the coefficient of the ith variable in the objective function.
- ``d`` (double) -- the constant term in the linear function (set to `0` by default)
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.add_variables(5) 4 sage: p.set_objective([1, 1, 2, 1, 3]) sage: [p.objective_coefficient(x) for x in range(5)] [1, 1, 2, 1, 3] """
cpdef add_linear_constraint(self, coefficients, name=None): """ Add a linear constraint.
INPUT:
- ``coefficients`` an iterable with ``(c,v)`` pairs where ``c`` is a variable index (integer) and ``v`` is a value (matrix). The pairs come sorted by indices. If c is -1 it represents the constant coefficient.
- ``name`` - an optional name for this row (default: ``None``)
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.add_variables(2) 1 sage: p.add_linear_constraint( [(0, matrix([[33., -9.], [-9., 26.]])) , (1, matrix([[-7., -11.] ,[ -11., 3.]]) )]) sage: p.row(0) ([0, 1], [ [ 33.0000000000000 -9.00000000000000] [-9.00000000000000 26.0000000000000], <BLANKLINE> [-7.00000000000000 -11.0000000000000] [-11.0000000000000 3.00000000000000] ]) sage: p.add_linear_constraint( [(0, matrix([[33., -9.], [-9., 26.]])) , (1, matrix([[-7., -11.] ,[ -11., 3.]]) )],name="fun") sage: p.row_name(-1) 'fun' """ raise ValueError("The coefficients must be matrices") raise ValueError("The matrix has to be a square") raise ValueError("the matrices have to be of the same dimension")
cpdef add_linear_constraints(self, int number, names=None): """ Add constraints.
INPUT:
- ``number`` (integer) -- the number of constraints to add.
- ``names`` - an optional list of names (default: ``None``)
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.add_variables(5) 4 sage: p.add_linear_constraints(5) sage: p.row(4) ([], []) """
cpdef int solve(self) except -1: """ Solve the problem.
.. NOTE::
This method raises ``SDPSolverException`` exceptions when the solution can not be computed for any reason (none exists, or the LP solver was not able to find it, etc...)
EXAMPLES::
sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) sage: x = p.new_variable() sage: p.set_objective(x[0] - x[1] + x[2]) sage: a1 = matrix([[-7., -11.], [-11., 3.]]) sage: a2 = matrix([[7., -18.], [-18., 8.]]) sage: a3 = matrix([[-2., -8.], [-8., 1.]]) sage: a4 = matrix([[33., -9.], [-9., 26.]]) sage: b1 = matrix([[-21., -11., 0.], [-11., 10., 8.], [0., 8., 5.]]) sage: b2 = matrix([[0., 10., 16.], [10., -10., -10.], [16., -10., 3.]]) sage: b3 = matrix([[-5., 2., -17.], [2., -6., 8.], [-17., 8., 6.]]) sage: b4 = matrix([[14., 9., 40.], [9., 91., 10.], [40., 10., 15.]]) sage: p.add_constraint(a1*x[0] + a3*x[2] <= a4) sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) sage: round(p.solve(), 3) -3.225 sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) sage: x = p.new_variable() sage: p.set_objective(x[0] - x[1] + x[2]) sage: a1 = matrix([[-7., -11.], [-11., 3.]]) sage: a2 = matrix([[7., -18.], [-18., 8.]]) sage: a3 = matrix([[-2., -8.], [-8., 1.]]) sage: a4 = matrix([[33., -9.], [-9., 26.]]) sage: b1 = matrix([[-21., -11., 0.], [-11., 10., 8.], [0., 8., 5.]]) sage: b2 = matrix([[0., 10., 16.], [10., -10., -10.], [16., -10., 3.]]) sage: b3 = matrix([[-5., 2., -17.], [2., -6., 8.], [-17., 8., 6.]]) sage: b4 = matrix([[14., 9., 40.], [9., 91., 10.], [40., 10., 15.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] + a3*x[2] <= a4) sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) sage: round(p.solve(), 3) -3.154
"""
#cvxopt minimizes on default else:
else: #raise Exception("G_matrix " + str(debug_g) + "\nh_matrix: " + str(debug_h) + "\nc_matrix: " + str(debug_c))
#solvers comes from the cvxopt library
#possible outcomes pass raise SDPSolverException("CVXOPT: dual infeasible") raise SDPSolverException("CVXOPT: Terminated early due to numerical difficulties or because the maximum number of iterations was reached.")
cpdef get_objective_value(self): """ Return the value of the objective function.
.. NOTE::
Behaviour is undefined unless ``solve`` has been called before.
EXAMPLES::
sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) sage: x = p.new_variable() sage: p.set_objective(x[0] - x[1] + x[2]) sage: a1 = matrix([[-7., -11.], [-11., 3.]]) sage: a2 = matrix([[7., -18.], [-18., 8.]]) sage: a3 = matrix([[-2., -8.], [-8., 1.]]) sage: a4 = matrix([[33., -9.], [-9., 26.]]) sage: b1 = matrix([[-21., -11., 0.], [-11., 10., 8.], [0., 8., 5.]]) sage: b2 = matrix([[0., 10., 16.], [10., -10., -10.], [16., -10., 3.]]) sage: b3 = matrix([[-5., 2., -17.], [2., -6., 8.], [-17., 8., 6.]]) sage: b4 = matrix([[14., 9., 40.], [9., 91., 10.], [40., 10., 15.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] + a3*x[2] <= a4) sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) sage: round(p.solve(),3) -3.154 sage: round(p.get_backend().get_objective_value(),3) -3.154 """
cpdef _get_answer(self): """ return the complete output dict of the solver
Mainly for testing purposes
TESTS::
sage: p = SemidefiniteProgram(maximization = False, solver='cvxopt') sage: x = p.new_variable() sage: p.set_objective(x[0] - x[1]) sage: a1 = matrix([[1, 2.], [2., 3.]]) sage: a2 = matrix([[3, 4.], [4., 5.]]) sage: a3 = matrix([[5, 6.], [6., 7.]]) sage: b1 = matrix([[1, 1.], [1., 1.]]) sage: b2 = matrix([[2, 2.], [2., 2.]]) sage: b3 = matrix([[3, 3.], [3., 3.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) sage: p.solve(); # tol 1e-08 -3.0 sage: p.get_backend()._get_answer() {...} """
cpdef get_variable_value(self, int variable): """ Return the value of a variable given by the solver.
.. NOTE::
Behaviour is undefined unless ``solve`` has been called before.
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "cvxopt") sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) sage: x = p.new_variable() sage: p.set_objective(x[0] - x[1] + x[2]) sage: a1 = matrix([[-7., -11.], [-11., 3.]]) sage: a2 = matrix([[7., -18.], [-18., 8.]]) sage: a3 = matrix([[-2., -8.], [-8., 1.]]) sage: a4 = matrix([[33., -9.], [-9., 26.]]) sage: b1 = matrix([[-21., -11., 0.], [-11., 10., 8.], [0., 8., 5.]]) sage: b2 = matrix([[0., 10., 16.], [10., -10., -10.], [16., -10., 3.]]) sage: b3 = matrix([[-5., 2., -17.], [2., -6., 8.], [-17., 8., 6.]]) sage: b4 = matrix([[14., 9., 40.], [9., 91., 10.], [40., 10., 15.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] + a3*x[2] <= a4) sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) sage: round(p.solve(),3) -3.154 sage: round(p.get_backend().get_variable_value(0),3) -0.368 sage: round(p.get_backend().get_variable_value(1),3) 1.898 sage: round(p.get_backend().get_variable_value(2),3) -0.888
"""
cpdef int ncols(self): """ Return the number of columns/variables.
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.ncols() 0 sage: p.add_variables(2) 1 sage: p.ncols() 2 """
cpdef int nrows(self): """ Return the number of rows/constraints.
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.nrows() 0 sage: p.add_variables(5) 4 sage: p.add_linear_constraints(2) sage: p.nrows() 2 """
cpdef bint is_maximization(self): """ Test whether the problem is a maximization
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.is_maximization() True sage: p.set_sense(-1) sage: p.is_maximization() False """ else:
cpdef problem_name(self, char * name = NULL): """ Return or define the problem's name
INPUT:
- ``name`` (``char *``) -- the problem's name. When set to ``NULL`` (default), the method returns the problem's name.
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.problem_name("There once was a french fry") sage: print(p.problem_name()) There once was a french fry """
cpdef row(self, int i): """ Return a row
INPUT:
- ``index`` (integer) -- the constraint's id.
OUTPUT:
A pair ``(indices, coeffs)`` where ``indices`` lists the entries whose coefficient is nonzero, and to which ``coeffs`` associates their coefficient on the model of the ``add_linear_constraint`` method.
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.add_variables(5) 4 sage: p.add_linear_constraint( [(0, matrix([[33., -9.], [-9., 26.]])) , (1, matrix([[-7., -11.] ,[ -11., 3.]]) )]) sage: p.row(0) ([0, 1], [ [ 33.0000000000000 -9.00000000000000] [-9.00000000000000 26.0000000000000], <BLANKLINE> [-7.00000000000000 -11.0000000000000] [-11.0000000000000 3.00000000000000] ]) """
cpdef dual_variable(self, int i, sparse=False): """ The `i`-th dual variable
Available after self.solve() is called, otherwise the result is undefined
- ``index`` (integer) -- the constraint's id.
OUTPUT:
The matrix of the `i`-th dual variable
EXAMPLES::
sage: p = SemidefiniteProgram(maximization = False, solver='cvxopt') sage: x = p.new_variable() sage: p.set_objective(x[0] - x[1]) sage: a1 = matrix([[1, 2.], [2., 3.]]) sage: a2 = matrix([[3, 4.], [4., 5.]]) sage: a3 = matrix([[5, 6.], [6., 7.]]) sage: b1 = matrix([[1, 1.], [1., 1.]]) sage: b2 = matrix([[2, 2.], [2., 2.]]) sage: b3 = matrix([[3, 3.], [3., 3.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) sage: p.solve() # tol 1e-08 -3.0 sage: B=p.get_backend() sage: x=p.get_values(x).values() sage: -(a3*B.dual_variable(0)).trace()-(b3*B.dual_variable(1)).trace() # tol 1e-07 -3.0 sage: g = sum((B.slack(j)*B.dual_variable(j)).trace() for j in range(2)); g # tol 1.5e-08 0.0
TESTS::
sage: B.dual_variable(7) ... Traceback (most recent call last): ... IndexError: list index out of range sage: abs(g - B._get_answer()['gap']) # tol 1e-22 0.0
""" cdef int n
cpdef slack(self, int i, sparse=False): """ Slack of the `i`-th constraint
Available after self.solve() is called, otherwise the result is undefined
- ``index`` (integer) -- the constraint's id.
OUTPUT:
The matrix of the slack of the `i`-th constraint
EXAMPLES::
sage: p = SemidefiniteProgram(maximization = False, solver='cvxopt') sage: x = p.new_variable() sage: p.set_objective(x[0] - x[1]) sage: a1 = matrix([[1, 2.], [2., 3.]]) sage: a2 = matrix([[3, 4.], [4., 5.]]) sage: a3 = matrix([[5, 6.], [6., 7.]]) sage: b1 = matrix([[1, 1.], [1., 1.]]) sage: b2 = matrix([[2, 2.], [2., 2.]]) sage: b3 = matrix([[3, 3.], [3., 3.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) sage: p.solve() # tol 1e-08 -3.0 sage: B = p.get_backend() sage: B1 = B.slack(1); B1 # tol 1e-08 [0.0 0.0] [0.0 0.0] sage: B1.is_positive_definite() True sage: x = p.get_values(x).values() sage: x[0]*b1 + x[1]*b2 - b3 + B1 # tol 1e-09 [0.0 0.0] [0.0 0.0]
TESTS::
sage: B.slack(7) ... Traceback (most recent call last): ... IndexError: list index out of range
""" cdef int n
cpdef row_name(self, int index): """ Return the ``index`` th row name
INPUT:
- ``index`` (integer) -- the row's id
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.add_linear_constraints(1, names="A") sage: p.row_name(0) 'A'
"""
cpdef col_name(self, int index): """ Return the ``index`` th col name
INPUT:
- ``index`` (integer) -- the col's id
- ``name`` (``char *``) -- its name. When set to ``NULL`` (default), the method returns the current name.
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.add_variable(name="I am a variable") 0 sage: p.col_name(0) 'I am a variable' """
cpdef solver_parameter(self, name, value = None): """ Return or define a solver parameter
INPUT:
- ``name`` (string) -- the parameter
- ``value`` -- the parameter's value if it is to be defined, or ``None`` (default) to obtain its current value.
.. NOTE::
The list of available parameters is available at :meth:`~sage.numerical.sdp.SemidefiniteProgram.solver_parameter`.
EXAMPLES::
sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "CVXOPT") sage: p.solver_parameter("show_progress") False sage: p.solver_parameter("show_progress", True) sage: p.solver_parameter("show_progress") True """ else:
|