24
24
from cvxpy .error import SolverError
25
25
import cvxpy .lin_ops .lin_utils as lu
26
26
import cvxpy .lin_ops as lo
27
- import cvxpy .lin_ops .tree_mat as tree_mat
27
+ import cvxpy .lin_ops .lin_to_matrix as op2mat
28
28
from cvxpy .constraints import (EqConstraint , LeqConstraint ,
29
- SOC , SOC_Elemwise , SDP , ExpCone )
29
+ SOC , SDP , ExpCone )
30
30
from cvxpy .problems .objective import Minimize , Maximize
31
31
from cvxpy .problems .kktsolver import get_kktsolver
32
- import cvxpy .problems .iterative as iterative
33
32
from cvxpy .problems .problem_data import ProblemData
34
33
35
34
from collections import OrderedDict
36
35
import warnings
37
- import itertools
38
- import numbers
39
36
import cvxopt
40
37
import cvxopt .solvers
41
38
import ecos
42
39
import numpy as np
43
- import scipy .sparse as sp
44
40
45
41
# Attempt to import SCS.
46
42
try :
@@ -69,16 +65,29 @@ class Problem(u.Canonical):
69
65
70
66
def __init__ (self , objective , constraints = None ):
71
67
if constraints is None :
72
- constraints = []
68
+ constraints = ()
73
69
# Check that objective is Minimize or Maximize.
74
70
if not isinstance (objective , (Minimize , Maximize )):
75
71
raise TypeError ("Problem objective must be Minimize or Maximize." )
76
- self .objective = objective
77
- self .constraints = constraints
72
+ # Constraints and objective are immutable.
73
+ self ._objective = objective
74
+ self ._constraints = tuple (constraints )
78
75
self ._value = None
79
76
self ._status = None
80
77
self ._cached_data = {}
81
78
79
+ @property
80
+ def objective (self ):
81
+ """Getter for objective.
82
+ """
83
+ return self ._objective
84
+
85
+ @property
86
+ def constraints (self ):
87
+ """Getter for constraints tuple.
88
+ """
89
+ return self ._constraints
90
+
82
91
@property
83
92
def value (self ):
84
93
"""The value from the last time the problem was solved.
@@ -104,7 +113,8 @@ def is_dcp(self):
104
113
"""
105
114
return all (exp .is_dcp () for exp in self .constraints + [self .objective ])
106
115
107
- def _filter_constraints (self , constraints ):
116
+ @staticmethod
117
+ def _filter_constraints (constraints ):
108
118
"""Separate the constraints by type.
109
119
110
120
Parameters
@@ -156,7 +166,8 @@ def canonicalize(self):
156
166
157
167
return (obj , constr_map )
158
168
159
- def _presolve (self , objective , constr_map ):
169
+ @staticmethod
170
+ def _presolve (objective , constr_map ):
160
171
"""Eliminates unnecessary constraints and short circuits the solver
161
172
if possible.
162
173
@@ -192,10 +203,11 @@ def _presolve(self, objective, constr_map):
192
203
coeff = op2mat .get_constant_coeff (constr .expr )
193
204
sign = intf .sign (coeff )
194
205
# For equality constraint, coeff must be zero.
195
- # For inequality (i.e. <= 0) constraint, coeff must be negative.
206
+ # For inequality (i.e. <= 0) constraint,
207
+ # coeff must be negative.
196
208
if key is s .EQ and not sign .is_zero () or \
197
- key is s .LEQ and not sign .is_negative ():
198
- return s .INFEASIBLE
209
+ key is s .LEQ and not sign .is_negative ():
210
+ return s .INFEASIBLE
199
211
else :
200
212
new_constraints .append (constr )
201
213
constr_map [key ] = new_constraints
@@ -355,22 +367,16 @@ def get_problem_data(self, solver):
355
367
# Raise an error if the solver cannot handle the problem.
356
368
self ._validate_solver (constr_map , solver )
357
369
dims = self ._format_for_solver (constr_map , solver )
370
+ # TODO factor out.
358
371
all_ineq = constr_map [s .EQ ] + constr_map [s .LEQ ]
372
+ # CVXOPT can have variables that only live in NonLinearConstraints.
373
+ nonlinear = constr_map [s .EXP ] if solver == s .CVXOPT else []
359
374
var_offsets , var_sizes , x_length = self ._get_var_offsets (objective ,
360
- all_ineq )
361
-
362
- if solver == s .ECOS and not (constr_map [s .SDP ] or constr_map [s .EXP ]):
363
- args , offset = self ._ecos_problem_data (objective , constr_map , dims ,
364
- var_offsets , x_length )
365
- elif solver == s .CVXOPT and not constr_map [s .EXP ]:
366
- args , offset = self ._cvxopt_problem_data (objective , constr_map , dims ,
367
- var_offsets , x_length )
368
- elif solver == s .SCS :
369
- args , offset = self ._scs_problem_data (objective , constr_map , dims ,
370
- var_offsets , x_length )
371
- else :
372
- raise SolverError ("Cannot return problem data for the solver %s." % solver )
373
- return args
375
+ all_ineq ,
376
+ nonlinear )
377
+ return SOLVERS [solver ].get_problem_data (self ._cached_data , objective ,
378
+ constr_map , dims ,
379
+ var_offsets , x_length )
374
380
375
381
def _solve (self , solver = None , ignore_dcp = False , verbose = False , ** kwargs ):
376
382
"""Solves a DCP compliant optimization problem.
@@ -425,7 +431,8 @@ def _solve(self, solver=None, ignore_dcp=False, verbose=False, **kwargs):
425
431
all_ineq ,
426
432
nonlinear )
427
433
if solver in s .SOLVERS :
428
- result = solve_methods [solver ](objective , constr_map , dims ,
434
+ result = SOLVERS [solver ].solve (self ._cached_data , objective ,
435
+ constr_map , dims ,
429
436
var_offsets , x_length ,
430
437
verbose , kwargs )
431
438
else :
@@ -453,7 +460,7 @@ def _ecos_problem_data(self, objective, constr_map, dims,
453
460
454
461
Parameters
455
462
----------
456
- objective: Expression
463
+ objective: LinOp
457
464
The canonicalized objective.
458
465
constr_map: dict
459
466
A dict of the canonicalized constraints.
@@ -520,7 +527,7 @@ def _ecos_solve(self, objective, constr_map, dims,
520
527
status = s .SOLVER_STATUS [s .ECOS ][results ['info' ]['exitFlag' ]]
521
528
if status in s .SOLUTION_PRESENT :
522
529
primal_val = results ['info' ]['pcost' ]
523
- value = self .objective ._primal_to_result (
530
+ value = self .objective .primal_to_result (
524
531
primal_val - obj_offset )
525
532
return (status , value ,
526
533
results ['x' ], results ['y' ], results ['z' ])
@@ -602,7 +609,7 @@ def _cvxopt_solve(self, objective, constr_map, dims,
602
609
"""
603
610
prob_data = self ._cvxopt_problem_data (objective , constr_map , dims ,
604
611
var_offsets , x_length )
605
- c , G , h , dims , A , b , F = prob_data [0 ]
612
+ c , G , h , dims , A , b = prob_data [0 ]
606
613
obj_offset = prob_data [1 ]
607
614
# Save original cvxopt solver options.
608
615
old_options = cvxopt .solvers .options
@@ -632,14 +639,14 @@ def _cvxopt_solve(self, objective, constr_map, dims,
632
639
kktsolver = kktsolver )
633
640
status = s .SOLVER_STATUS [s .CVXOPT ][results ['status' ]]
634
641
# Catch exceptions in CVXOPT and convert them to solver errors.
635
- except ValueError as e :
642
+ except ValueError :
636
643
status = s .SOLVER_ERROR
637
644
638
645
# Restore original cvxopt solver options.
639
646
cvxopt .solvers .options = old_options
640
647
if status in s .SOLUTION_PRESENT :
641
648
primal_val = results ['primal objective' ]
642
- value = self .objective ._primal_to_result (
649
+ value = self .objective .primal_to_result (
643
650
primal_val - obj_offset )
644
651
if constr_map [s .EXP ]:
645
652
ineq_dual = results ['zl' ]
@@ -746,11 +753,12 @@ def _handle_no_solution(self, status):
746
753
constr .save_value (None )
747
754
# Set the problem value.
748
755
if status in [s .INFEASIBLE , s .INFEASIBLE_INACCURATE ]:
749
- self ._value = self .objective ._primal_to_result (np .inf )
756
+ self ._value = self .objective .primal_to_result (np .inf )
750
757
elif status in [s .UNBOUNDED , s .UNBOUNDED_INACCURATE ]:
751
- self ._value = self .objective ._primal_to_result (- np .inf )
758
+ self ._value = self .objective .primal_to_result (- np .inf )
752
759
753
- def _get_var_offsets (self , objective , constraints , nonlinear = None ):
760
+ @staticmethod
761
+ def _get_var_offsets (objective , constraints , nonlinear = None ):
754
762
"""Maps each variable to a horizontal offset.
755
763
756
764
Parameters
0 commit comments