Hide keyboard shortcuts

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

1# ------------------------------------------------------------------------------ 

2# Copyright (C) 2012-2017 Guillaume Sagnol 

3# Copyright (C) 2017-2019 Maximilian Stahlberg 

4# 

5# This file is part of PICOS. 

6# 

7# PICOS is free software: you can redistribute it and/or modify it under the 

8# terms of the GNU General Public License as published by the Free Software 

9# Foundation, either version 3 of the License, or (at your option) any later 

10# version. 

11# 

12# PICOS is distributed in the hope that it will be useful, but WITHOUT ANY 

13# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 

14# A PARTICULAR PURPOSE. See the GNU General Public License for more details. 

15# 

16# You should have received a copy of the GNU General Public License along with 

17# this program. If not, see <http://www.gnu.org/licenses/>. 

18# ------------------------------------------------------------------------------ 

19 

20"""Implementation of :class:`CVXOPTSolver`.""" 

21 

22import cvxopt 

23import numpy 

24 

25from ..apidoc import api_end, api_start 

26from ..constraints import (AffineConstraint, DummyConstraint, LMIConstraint, 

27 LogSumExpConstraint, RSOCConstraint, SOCConstraint) 

28from ..expressions import CONTINUOUS_VARTYPES, AffineExpression, LogSumExp 

29from ..modeling.footprint import Specification 

30from ..modeling.solution import (PS_FEASIBLE, PS_INFEASIBLE, PS_UNBOUNDED, 

31 PS_UNKNOWN, SS_INFEASIBLE, SS_OPTIMAL, 

32 SS_UNKNOWN, Solution) 

33from .solver import Solver 

34 

35_API_START = api_start(globals()) 

36# ------------------------------- 

37 

38 

39class CVXOPTSolver(Solver): 

40 """Interface to the CVXOPT solver. 

41 

42 Also used as an interface to the 

43 :class:`SMCP solver <picos.solvers.solver_smcp.SMCPSolver>`. 

44 """ 

45 

46 SUPPORTED = Specification( 

47 objectives=[ 

48 AffineExpression, 

49 LogSumExp], 

50 variables=CONTINUOUS_VARTYPES, 

51 constraints=[ 

52 DummyConstraint, 

53 AffineConstraint, 

54 SOCConstraint, 

55 RSOCConstraint, 

56 LMIConstraint, 

57 LogSumExpConstraint]) 

58 

59 @classmethod 

60 def supports(cls, footprint, explain=False): 

61 """Implement :meth:`~.solver.Solver.supports`.""" 

62 result = Solver.supports(footprint, explain) 

63 if not result or (explain and not result[0]): 

64 return result 

65 

66 if footprint not in cls.SUPPORTED: 

67 if explain: 

68 return False, cls.SUPPORTED.mismatch_reason(footprint) 

69 else: 

70 return False 

71 

72 return (True, None) if explain else True 

73 

74 @classmethod 

75 def default_penalty(cls): 

76 """Implement :meth:`~.solver.Solver.default_penalty`.""" 

77 return 1.0 # Stable free/open source solver. 

78 

79 @classmethod 

80 def test_availability(cls): 

81 """Implement :meth:`~.solver.Solver.test_availability`.""" 

82 # CVXOPT is a dependency of PICOS, so it is always available. 

83 pass 

84 

85 @classmethod 

86 def names(cls): 

87 """Implement :meth:`~.solver.Solver.names`.""" 

88 return "cvxopt", "CVXOPT", "Python Convex Optimization Solver" 

89 

90 @classmethod 

91 def is_free(cls): 

92 """Implement :meth:`~.solver.Solver.is_free`.""" 

93 return True 

94 

95 @property 

96 def is_smcp(self): 

97 """Whether to implement SMCP instead of CVXOPT.""" 

98 return False 

99 

100 def __init__(self, problem): 

101 """Initialize a CVXOPT solver interface. 

102 

103 :param ~picos.Problem problem: The problem to be solved. 

104 """ 

105 super(CVXOPTSolver, self).__init__(problem) 

106 

107 self._numVars = 0 

108 """Total number of scalar variables passed to CVXOPT.""" 

109 

110 self._cvxoptVarOffset = {} 

111 """Maps a PICOS variable to its offset in the constraint matrix.""" 

112 

113 # HACK: Setting this to false would result in variable bounds to be 

114 # ignored, instead of added to the linear inequalities matrix. 

115 # This is used by a Problem method that prints the problem to a 

116 # file using a CVXOPTSolver instance. 

117 self.import_variable_bounds = True 

118 

119 # SMCP currently offers no option reset, so keep a backup. 

120 if self.is_smcp: 

121 import smcp 

122 self._smcp_default_options = smcp.solvers.options.copy() 

123 

124 def reset_problem(self): 

125 """Implement :meth:`~.solver.Solver.reset_problem`.""" 

126 self.int = None 

127 

128 self._numVars = 0 

129 self._cvxoptVarOffset.clear() 

130 

131 def _is_geometric_program(self): 

132 return isinstance(self.ext.no.function, LogSumExp) or \ 

133 any(isinstance(constraint, LogSumExpConstraint) 

134 for constraint in self.ext.constraints.values()) 

135 

136 def _affine_expression_to_G_and_h(self, expression): 

137 assert isinstance(expression, AffineExpression) 

138 

139 length = len(expression) 

140 

141 # Construct G. 

142 I, J, V = [], [], [] 

143 for variable, coefficients in expression._linear_coefs.items(): 

144 cvxoptVarOffset = self._cvxoptVarOffset[variable] 

145 

146 if not isinstance(coefficients, cvxopt.spmatrix): 

147 coefficients = cvxopt.sparse(coefficients) 

148 

149 I.extend(coefficients.I) 

150 J.extend([cvxoptVarOffset + j for j in coefficients.J]) 

151 V.extend(coefficients.V) 

152 G = cvxopt.spmatrix(V, I, J, (length, self._numVars), tc="d") 

153 

154 # Construct h. 

155 # TODO: Check if making a copy is necessary. 

156 h = cvxopt.matrix(expression._constant_coef, tc="d") 

157 

158 return G, h 

159 

160 _Gh = _affine_expression_to_G_and_h 

161 

162 def _import_variables_without_bounds(self): 

163 offset = 0 

164 for variable in self.ext.variables.values(): 

165 dim = variable.dim 

166 

167 # Register the variable. 

168 self._cvxoptVarOffset[variable] = offset 

169 offset += dim 

170 

171 assert offset == self._numVars 

172 

173 def _import_variable_bounds(self): 

174 for variable in self.ext.variables.values(): 

175 if self.import_variable_bounds: 

176 bounds = variable.bound_constraint 

177 if bounds: 

178 self._import_affine_constraint(bounds) 

179 

180 def _import_objective(self): 

181 direction, objective = self.ext.no 

182 

183 if self._is_geometric_program(): 

184 if isinstance(objective, LogSumExp): 

185 (F, g) = self._Gh(objective.x) 

186 else: 

187 assert isinstance(objective, AffineExpression) 

188 (F, g) = self._Gh(objective) 

189 

190 # NOTE: Needs to happen before LogSumExpConstraint are added. 

191 self.int["K"] = [F.size[0]] 

192 

193 if direction == "max": 

194 F, g = -F, -g 

195 

196 self.int["F"] = F 

197 self.int["g"] = g 

198 else: 

199 (c, _) = self._Gh(objective) 

200 

201 if direction == "max": 

202 c = -c 

203 

204 # Must be a dense column-vector. 

205 c = cvxopt.matrix(c).T 

206 

207 self.int["c"] = c 

208 

209 def _import_affine_constraint(self, constraint): 

210 assert isinstance(constraint, AffineConstraint) 

211 

212 (G_smaller, h_smaller) = self._Gh(constraint.smaller) 

213 (G_greater, h_greater) = self._Gh(constraint.greater) 

214 

215 G = G_smaller - G_greater 

216 h = h_greater - h_smaller 

217 

218 if constraint.is_equality(): 

219 self.int["A"] = cvxopt.sparse([self.int["A"], G]) 

220 self.int["b"] = cvxopt.matrix([self.int["b"], h]) 

221 else: 

222 self.int["Gl"] = cvxopt.sparse([self.int["Gl"], G]) 

223 self.int["hl"] = cvxopt.matrix([self.int["hl"], h]) 

224 

225 def _import_soc_constraint(self, constraint): 

226 assert isinstance(constraint, SOCConstraint) 

227 

228 (A, b) = self._Gh(constraint.ne) 

229 (c, d) = self._Gh(constraint.ub) 

230 

231 self.int["Gq"].append(cvxopt.sparse([-c, -A])) 

232 self.int["hq"].append(cvxopt.matrix([d, b])) 

233 

234 def _import_rsoc_constraint(self, constraint): 

235 assert isinstance(constraint, RSOCConstraint) 

236 

237 (A, b) = self._Gh(constraint.ne) 

238 (c1, d1) = self._Gh(constraint.ub1) 

239 (c2, d2) = self._Gh(constraint.ub2) 

240 

241 self.int["Gq"].append(cvxopt.sparse([-c1 - c2, -2 * A, c2 - c1])) 

242 self.int["hq"].append(cvxopt.matrix([d1 + d2, 2 * b, d1 - d2])) 

243 

244 def _import_lmi_constraint(self, constraint): 

245 assert isinstance(constraint, LMIConstraint) 

246 

247 (G_smaller, h_smaller) = self._Gh(constraint.smaller) 

248 (G_greater, h_greater) = self._Gh(constraint.greater) 

249 

250 self.int["Gs"].append(G_smaller - G_greater) 

251 self.int["hs"].append(h_greater - h_smaller) 

252 

253 def _import_lse_constraint(self, constraint): 

254 assert isinstance(constraint, LogSumExpConstraint) 

255 

256 (F, g) = self._Gh(constraint.le0.x) 

257 

258 self.int["F"] = cvxopt.sparse([self.int["F"], F]) 

259 self.int["g"] = cvxopt.matrix([self.int["g"], g]) 

260 self.int["K"].append(F.size[0]) 

261 

262 def _import_constraint(self, constraint): 

263 if isinstance(constraint, AffineConstraint): 

264 self._import_affine_constraint(constraint) 

265 elif isinstance(constraint, SOCConstraint): 

266 self._import_soc_constraint(constraint) 

267 elif isinstance(constraint, RSOCConstraint): 

268 self._import_rsoc_constraint(constraint) 

269 elif isinstance(constraint, LMIConstraint): 

270 self._import_lmi_constraint(constraint) 

271 elif isinstance(constraint, LogSumExpConstraint): 

272 self._import_lse_constraint(constraint) 

273 else: 

274 assert isinstance(constraint, DummyConstraint), \ 

275 "Unexpected constraint type: {}".format( 

276 constraint.__class__.__name__) 

277 

278 def _import_problem(self): 

279 self._numVars = sum(var.dim for var in self.ext.variables.values()) 

280 

281 # CVXOPT's internal problem representation is stateless; a number of 

282 # matrices are supplied to the appropriate solver function each time a 

283 # search is started. These matrices are thus stored in self.int. 

284 self.int = { 

285 # Objective function coefficients. 

286 "c": None, 

287 

288 # Linear equality left hand side. 

289 "A": cvxopt.spmatrix([], [], [], (0, self._numVars), tc="d"), 

290 

291 # Linear equality right hand side. 

292 "b": cvxopt.matrix([], (0, 1), tc="d"), 

293 

294 # Linear inequality left hand side. 

295 "Gl": cvxopt.spmatrix([], [], [], (0, self._numVars), tc="d"), 

296 

297 # Linear inequality right hand side. 

298 "hl": cvxopt.matrix([], (0, 1), tc="d"), 

299 

300 # Second order cone inequalities left hand sides. 

301 "Gq": [], 

302 

303 # Second order cone inequalities right hand sides. 

304 "hq": [], 

305 

306 # Semidefinite cone inequalities left hand sides. 

307 "Gs": [], 

308 

309 # Semidefinite cone inequalities right hand sides. 

310 "hs": [], 

311 

312 # Geometric program data. 

313 "F": None, 

314 "g": None, 

315 "K": None 

316 } 

317 

318 # Import variables without their bounds. 

319 self._import_variables_without_bounds() 

320 

321 # Set objective. 

322 # NOTE: This needs to happen before constraints are added as 

323 # self.int["K"][0] refers to an LSE objective while 

324 # self.int["K"][i] with i > 0 refers to LSE constraints. 

325 self._import_objective() 

326 

327 # Import constraints. 

328 for constraint in self.ext.constraints.values(): 

329 self._import_constraint(constraint) 

330 

331 # Import variable bounds as additional affine constraints. 

332 # NOTE: This needs to happen after constraints are added due to how the 

333 # dual values for constraints are extracted. 

334 self._import_variable_bounds() 

335 

336 def _update_problem(self): 

337 raise NotImplementedError 

338 

339 def _solve(self): 

340 if self.is_smcp: 

341 import smcp 

342 

343 p = self.int 

344 isGP = self._is_geometric_program() 

345 

346 # Clear all options set previously. This is necessary because CVXOPT 

347 # options are global, and might be changed even by another problem. 

348 cvxopt.solvers.options.clear() 

349 

350 # verbosity 

351 cvxopt.solvers.options["show_progress"] = (self.verbosity() >= 1) 

352 

353 # rel_prim_fsb_tol, rel_dual_fsb_tol 

354 feasibilityTols = [tol for tol in (self.ext.options.rel_prim_fsb_tol, 

355 self.ext.options.rel_dual_fsb_tol) if tol is not None] 

356 if feasibilityTols: 

357 cvxopt.solvers.options["feastol"] = min(feasibilityTols) 

358 

359 # abs_ipm_opt_tol 

360 if self.ext.options.abs_ipm_opt_tol is not None: 

361 cvxopt.solvers.options["abstol"] = self.ext.options.abs_ipm_opt_tol 

362 

363 # rel_ipm_opt_tol 

364 if self.ext.options.rel_ipm_opt_tol is not None: 

365 cvxopt.solvers.options["reltol"] = self.ext.options.rel_ipm_opt_tol 

366 

367 # max_iterations 

368 if self.ext.options.max_iterations is not None: 

369 cvxopt.solvers.options["maxiters"] = self.ext.options.max_iterations 

370 else: 

371 cvxopt.solvers.options["maxiters"] = int(1e6) 

372 

373 # Allow solving GPs that are not strictly feasible and work around 

374 # constraint matrix rank requirements. 

375 if self.ext.options.cvxopt_kktreg is not None: 

376 cvxopt.solvers.options["kktreg"] = self.ext.options.cvxopt_kktreg 

377 

378 # Handle unsupported options. 

379 self._handle_unsupported_options( 

380 "lp_root_method", "lp_node_method", "timelimit", "treememory", 

381 "max_fsb_nodes", "hotstart") 

382 

383 # TODO: Add CVXOPT-sepcific options. Candidates are: 

384 # - refinement 

385 

386 # Set options for SMCP. 

387 if self.is_smcp: 

388 # Restore default options. 

389 smcp.solvers.options = self._smcp_default_options.copy() 

390 

391 # Copy options also used by CVXOPT. 

392 smcp.solvers.options.update(cvxopt.solvers.options) 

393 

394 # Further handle "verbose" option. 

395 smcp.solvers.options["debug"] = (self.verbosity() >= 2) 

396 

397 # TODO: Add SMCP-sepcific options. 

398 

399 if self._debug(): 

400 from pprint import pformat 

401 self._debug("Setting options:\n{}\n".format(pformat( 

402 smcp.solvers.options if self.is_smcp 

403 else cvxopt.solvers.options))) 

404 

405 # Print a header. 

406 if self.is_smcp: 

407 subsolverText = None 

408 else: 

409 if isGP: 

410 subsolverText = "internal GP solver" 

411 else: 

412 subsolverText = "internal CONELP solver" 

413 

414 # Further prepare the problem for the CVXOPT/SMCP CONELP solvers. 

415 # TODO: This should be done during import. 

416 if not isGP: 

417 # Retrieve the structure of the cone, which is a cartesian product 

418 # of the non-negative orthant of dimension l, a number of second 

419 # order cones with dimensions in q and a number of positive 

420 # semidefinite cones with dimensions in s. 

421 dims = { 

422 "l": p["Gl"].size[0], 

423 "q": [Gqi.size[0] for Gqi in p["Gq"]], 

424 "s": [int(numpy.sqrt(Gsi.size[0])) for Gsi in p["Gs"]] 

425 } 

426 

427 # Construct G and h to contain all conic inequalities, starting with 

428 # those with respect to the non-negative orthant. 

429 G = p["Gl"] 

430 h = p["hl"] 

431 

432 # SMCP's ConeLP solver does not handle (linear) equalities, so cast 

433 # them as inequalities. 

434 if self.is_smcp: 

435 smcp_eps = min(feasibilityTols) 

436 if p["A"].size[0] > 0: 

437 G = cvxopt.sparse([G, p["A"]]) 

438 G = cvxopt.sparse([G, -p["A"]]) 

439 h = cvxopt.matrix([h, p["b"]+smcp_eps]) 

440 h = cvxopt.matrix([h, smcp_eps-p["b"]]) 

441 dims["l"] += (2 * p["A"].size[0]) 

442 

443 # Remove the lines in G and h corresponding to 0==0 or 0<=0 

444 JP = list(set(G.I)) 

445 IP = range(len(JP)) 

446 VP = [1] * len(JP) 

447 

448 if len(JP) != dims["l"]: 

449 # is there a constraint of the form 0<=a, (a<0) ? 

450 if any([b < -smcp_eps for (i, b) in enumerate(h) 

451 if i not in JP]): 

452 raise Exception( 

453 'infeasible constraint of the form ' 

454 '0 <= a, with a<0') 

455 

456 # left-multiply with PPP-matrix to remove 0-constraints 

457 PPP = cvxopt.spmatrix(VP, IP, JP, (len(IP), G.size[0])) 

458 dims["l"] = len(JP) 

459 G = PPP * G 

460 h = PPP * h 

461 

462 # Add second-order cone inequalities. 

463 for i in range(len(dims["q"])): 

464 G = cvxopt.sparse([G, p["Gq"][i]]) 

465 h = cvxopt.matrix([h, p["hq"][i]]) 

466 

467 # Add semidefinite cone inequalities. 

468 for i in range(len(dims["s"])): 

469 G = cvxopt.sparse([G, p["Gs"][i]]) 

470 h = cvxopt.matrix([h, p["hs"][i]]) 

471 

472 # Remove zero lines from linear equality constraint matrix, as 

473 # CVXOPT expects this matrix to have full row rank. 

474 JP = list(set(p["A"].I)) 

475 IP = range(len(JP)) 

476 VP = [1] * len(JP) 

477 

478 # Skip solution on an infeasible constraint. 

479 if any([b for (i, b) in enumerate(p["b"]) if i not in JP]): 

480 return Solution( 

481 primals=None, duals=None, problem=self.ext, solver="PICOS", 

482 primalStatus=SS_INFEASIBLE, dualStatus=SS_UNKNOWN, 

483 problemStatus=PS_INFEASIBLE, vectorizedPrimals=True) 

484 

485 P = cvxopt.spmatrix(VP, IP, JP, (len(IP), p["A"].size[0])) 

486 A = P * p["A"] 

487 b = P * p["b"] 

488 

489 # Attempt to solve the problem. 

490 with self._header(subsolverText), self._stopwatch(): 

491 if self.is_smcp: 

492 if self._debug(): 

493 self._debug("Calling smcp.solvers.conelp(c, G, h, dims) " 

494 "with\nc:\n{}\nG:\n{}\nh:\n{}\ndims:\n{}\n" 

495 .format(p["c"], G, h, dims)) 

496 try: 

497 result = smcp.solvers.conelp(p["c"], G, h, dims) 

498 except TypeError: 

499 # HACK: Work around "'NoneType' object is not subscriptable" 

500 # exception with infeasible/unbounded problems. 

501 result = None 

502 else: 

503 kwargs = {} 

504 if self.ext.options.cvxopt_kktsolver is not None: 

505 kwargs["kktsolver"] = self.ext.options.cvxopt_kktsolver 

506 

507 if isGP: 

508 result = cvxopt.solvers.gp(p["K"], p["F"], p["g"], p["Gl"], 

509 p["hl"], p["A"], p["b"], **kwargs) 

510 else: 

511 result = cvxopt.solvers.conelp( 

512 p["c"], G, h, dims, A, b, **kwargs) 

513 

514 # Retrieve primals. 

515 primals = {} 

516 if self.ext.options.primals is not False and result is not None \ 

517 and result["x"] is not None: 

518 for variable in self.ext.variables.values(): 

519 offset = self._cvxoptVarOffset[variable] 

520 value = list(result["x"][offset:offset + variable.dim]) 

521 primals[variable] = value 

522 

523 # Retrieve duals. 

524 duals = {} 

525 if self.ext.options.duals is not False and result is not None: 

526 (indy, indzl, indzq, indznl, indzs) = (0, 0, 0, 0, 0) 

527 

528 if isGP: 

529 zkey = "zl" 

530 zqkey = "zq" 

531 zskey = "zs" 

532 else: 

533 zkey = "z" 

534 zqkey = "z" 

535 zskey = "z" 

536 indzq = dims["l"] 

537 indzs = dims["l"] + sum(dims["q"]) 

538 

539 if self.is_smcp: 

540 # Equality constraints were cast as two inequalities. 

541 ieq = p["Gl"].size[0] 

542 neq = (dims["l"] - ieq) // 2 

543 soleq = result["z"][ieq:ieq + neq] 

544 soleq -= result["z"][ieq + neq:ieq + 2 * neq] 

545 else: 

546 soleq = result["y"] 

547 

548 for constraint in self.ext.constraints.values(): 

549 if isinstance(constraint, DummyConstraint): 

550 duals[constraint] = cvxopt.spmatrix( 

551 [], [], [], constraint.size) 

552 continue 

553 

554 dual = None 

555 consSz = len(constraint) 

556 

557 if isinstance(constraint, AffineConstraint): 

558 if constraint.is_equality(): 

559 if soleq is not None: 

560 dual = -(P.T * soleq)[indy:indy + consSz] 

561 indy += consSz 

562 else: 

563 if result[zkey] is not None: 

564 dual = result[zkey][indzl:indzl + consSz] 

565 indzl += consSz 

566 elif isinstance(constraint, SOCConstraint) \ 

567 or isinstance(constraint, RSOCConstraint): 

568 if result[zqkey] is not None: 

569 if isGP: 

570 dual = result[zqkey][indzq] 

571 dual[1:] = -dual[1:] 

572 indzq += 1 

573 else: 

574 dual = result[zqkey][indzq:indzq + consSz] 

575 if isinstance(constraint, RSOCConstraint): 

576 # RScone were cast as a SOcone on import, so 

577 # transform the dual to a proper RScone dual. 

578 alpha = dual[0] + dual[-1] 

579 beta = dual[0] - dual[-1] 

580 z = 2.0 * dual[1:-1] 

581 dual = cvxopt.matrix([alpha, beta, z]) 

582 indzq += consSz 

583 elif isinstance(constraint, LMIConstraint): 

584 if result[zskey] is not None: 

585 matSz = constraint.size[0] 

586 if isGP: 

587 dual = cvxopt.matrix( 

588 result[zskey][indzs], (matSz, matSz)) 

589 indzs += 1 

590 else: 

591 dual = cvxopt.matrix( 

592 result[zskey][indzs:indzs + consSz], 

593 (matSz, matSz)) 

594 indzs += consSz 

595 elif isinstance(constraint, LogSumExpConstraint): 

596 # TODO: Retrieve LSE duals. 

597 indznl += 1 

598 

599 duals[constraint] = dual 

600 

601 # Retrieve objective value. 

602 if result is None: 

603 value = None 

604 elif isGP: 

605 value = None 

606 else: 

607 p = result['primal objective'] 

608 d = result['dual objective'] 

609 

610 if p is not None and d is not None: 

611 value = 0.5 * (p + d) 

612 elif p is not None: 

613 value = p 

614 elif d is not None: 

615 value = d 

616 else: 

617 value = None 

618 

619 if value is not None and self.ext.no.direction == "max": 

620 value = -value 

621 

622 if self.is_smcp: 

623 value = -value 

624 

625 # Retrieve solution status. 

626 status = result["status"] if result else "unknown" 

627 if status == "optimal": 

628 primalStatus = SS_OPTIMAL 

629 dualStatus = SS_OPTIMAL 

630 problemStatus = PS_FEASIBLE 

631 elif status == "primal infeasible": 

632 primalStatus = SS_INFEASIBLE 

633 dualStatus = SS_UNKNOWN 

634 problemStatus = PS_INFEASIBLE 

635 elif status == "dual infeasible": 

636 primalStatus = SS_UNKNOWN 

637 dualStatus = SS_INFEASIBLE 

638 problemStatus = PS_UNBOUNDED 

639 else: 

640 primalStatus = SS_UNKNOWN 

641 dualStatus = SS_UNKNOWN 

642 problemStatus = PS_UNKNOWN 

643 

644 return self._make_solution(value, primals, duals, primalStatus, 

645 dualStatus, problemStatus, {"cvxopt_sol": result}) 

646 

647 

648# -------------------------------------- 

649__all__ = api_end(_API_START, globals())