def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = solution['status'] primal_vars = None dual_vars = None if status in s.SOLUTION_PRESENT: opt_val = solution['value'] + inverse_data[s.OFFSET] primal_vars = {inverse_data[GUROBI.VAR_ID]: solution['primal']} if "eq_dual" in solution and not inverse_data['is_mip']: eq_dual = utilities.get_dual_values( solution['eq_dual'], utilities.extract_dual_value, inverse_data[GUROBI.EQ_CONSTR]) leq_dual = utilities.get_dual_values( solution['ineq_dual'], utilities.extract_dual_value, inverse_data[GUROBI.NEQ_CONSTR]) eq_dual.update(leq_dual) dual_vars = eq_dual return Solution(status, opt_val, primal_vars, dual_vars, {}) else: return failure_solution(status) return Solution(status, opt_val, primal_vars, dual_vars, {})
def invert(self, solution, inverse_data): """Returns solution to original problem, given inverse_data. """ status = self.STATUS_MAP[solution['info']['exitFlag']] # Timing data attr = {} attr[s.SOLVE_TIME] = solution["info"]["timing"]["tsolve"] attr[s.SETUP_TIME] = solution["info"]["timing"]["tsetup"] attr[s.NUM_ITERS] = solution["info"]["iter"] if status in s.SOLUTION_PRESENT: primal_val = solution['info']['pcost'] opt_val = primal_val + inverse_data[s.OFFSET] primal_vars = { inverse_data[self.VAR_ID]: intf.DEFAULT_INTF.const_to_matrix(solution['x']) } dual_vars = utilities.get_dual_values( solution['z'], utilities.extract_dual_value, inverse_data[self.NEQ_CONSTR]) for con in inverse_data[self.NEQ_CONSTR]: if isinstance(con, ExpCone): cid = con.id n_cones = con.num_cones() perm = utilities.expcone_permutor(n_cones, ECOS.EXP_CONE_ORDER) dual_vars[cid] = dual_vars[cid][perm] eq_duals = utilities.get_dual_values(solution['y'], utilities.extract_dual_value, inverse_data[self.EQ_CONSTR]) dual_vars.update(eq_duals) return Solution(status, opt_val, primal_vars, dual_vars, attr) else: return failure_solution(status)
def invert(self, solution: Dict[str, Any], inverse_data: Dict[str, Any]) -> Solution: """Returns the solution to the original problem given the inverse_data.""" status = solution['status'] dual_vars = None if status in s.SOLUTION_PRESENT: opt_val = solution['value'] + inverse_data[s.OFFSET] primal_vars = {inverse_data[SCIP.VAR_ID]: solution['primal']} if "eq_dual" in solution and not inverse_data['is_mip']: eq_dual = utilities.get_dual_values( result_vec=solution['eq_dual'], parse_func=utilities.extract_dual_value, constraints=inverse_data[SCIP.EQ_CONSTR], ) leq_dual = utilities.get_dual_values( result_vec=solution['ineq_dual'], parse_func=utilities.extract_dual_value, constraints=inverse_data[SCIP.NEQ_CONSTR], ) eq_dual.update(leq_dual) dual_vars = eq_dual return Solution(status, opt_val, primal_vars, dual_vars, {}) else: return failure_solution(status)
def invert(self, solution, inverse_data): """Returns solution to original problem, given inverse_data. """ status = self.STATUS_MAP[solution['info']['exitFlag']] # Timing data attr = {} attr[s.SOLVE_TIME] = solution["info"]["timing"]["tsolve"] attr[s.SETUP_TIME] = solution["info"]["timing"]["tsetup"] attr[s.NUM_ITERS] = solution["info"]["iter"] if status in s.SOLUTION_PRESENT: primal_val = solution['info']['pcost'] opt_val = primal_val + inverse_data[s.OFFSET] primal_vars = { inverse_data[self.VAR_ID]: intf.DEFAULT_INTF.const_to_matrix(solution['x']) } eq_dual = utilities.get_dual_values( solution['y'], utilities.extract_dual_value, inverse_data[self.EQ_CONSTR]) leq_dual = utilities.get_dual_values( solution['z'], utilities.extract_dual_value, inverse_data[self.NEQ_CONSTR]) eq_dual.update(leq_dual) dual_vars = eq_dual return Solution(status, opt_val, primal_vars, dual_vars, attr) else: return failure_solution(status)
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = self.STATUS_MAP[solution['status']] primal_vars = None dual_vars = None if status in s.SOLUTION_PRESENT: primal_val = solution['fun'] opt_val = primal_val + inverse_data[s.OFFSET] primal_vars = {inverse_data[self.VAR_ID]: solution['x']} # SciPy linprog only returns duals for version >= 1.7.0 # and method is one of 'highs', 'highs-ds' or 'highs-ipm' if ('ineqlin' in solution.keys()): eq_dual = utilities.get_dual_values( -solution['eqlin']['marginals'], utilities.extract_dual_value, inverse_data[self.EQ_CONSTR]) leq_dual = utilities.get_dual_values( -solution['ineqlin']['marginals'], utilities.extract_dual_value, inverse_data[self.NEQ_CONSTR]) eq_dual.update(leq_dual) dual_vars = eq_dual return Solution(status, opt_val, primal_vars, dual_vars, {}) else: return failure_solution(status)
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = solution['status'] if status in s.SOLUTION_PRESENT: opt_val = solution['value'] primal_vars = {inverse_data[self.VAR_ID]: solution['primal']} eq_dual = utilities.get_dual_values(solution['eq_dual'], utilities.extract_dual_value, inverse_data[Solver.EQ_CONSTR]) leq_dual = utilities.get_dual_values( solution['ineq_dual'], utilities.extract_dual_value, inverse_data[Solver.NEQ_CONSTR]) eq_dual.update(leq_dual) dual_vars = eq_dual else: if status == s.INFEASIBLE: opt_val = np.inf elif status == s.UNBOUNDED: opt_val = -np.inf else: opt_val = None primal_vars = None dual_vars = None return Solution(status, opt_val, primal_vars, dual_vars, {})
def invert(self, results, inverse_data): model = results["model"] attr = {} if "cputime" in results: attr[s.SOLVE_TIME] = results["cputime"] attr[s.NUM_ITERS] = \ int(model.solution.progress.get_num_barrier_iterations()) \ if not inverse_data[CPLEX.IS_MIP] \ else 0 status = get_status(model) if status in s.SOLUTION_PRESENT: # Get objective value opt_val = model.solution.get_objective_value() + \ inverse_data[s.OFFSET] # Get solution x = np.array(model.solution.get_values()) primal_vars = { CPLEX.VAR_ID: intf.DEFAULT_INTF.const_to_matrix(np.array(x)) } # Only add duals if not a MIP. dual_vars = None if not inverse_data[CPLEX.IS_MIP]: y = -np.array(model.solution.get_dual_values()) dual_vars = {CPLEX.DUAL_VAR_ID: y} sol = Solution(status, opt_val, primal_vars, dual_vars, attr) else: sol = failure_solution(status, attr) return sol
def invert(self, solution, inverse_data): """Returns solution to original problem, given inverse_data. """ status = self.STATUS_MAP[solution['info']['exitFlag']] if status in s.SOLUTION_PRESENT: primal_val = solution['info']['pcost'] opt_val = primal_val + inverse_data[s.OFFSET] primal_vars = { inverse_data[self.VAR_ID]: intf.DEFAULT_INTF.const_to_matrix(solution['x']) } dual_vars = None if not inverse_data['is_mip']: eq_dual = utilities.get_dual_values( solution['y'], utilities.extract_dual_value, inverse_data[self.EQ_CONSTR]) leq_dual = utilities.get_dual_values( solution['z'], utilities.extract_dual_value, inverse_data[self.NEQ_CONSTR]) eq_dual.update(leq_dual) dual_vars = eq_dual return Solution(status, opt_val, primal_vars, dual_vars, {}) else: return failure_solution(status)
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = self.STATUS_MAP[solution["info"]["status"]] attr = {} attr[s.SOLVE_TIME] = solution["info"]["solveTime"] attr[s.SETUP_TIME] = solution["info"]["setupTime"] attr[s.NUM_ITERS] = solution["info"]["iter"] if status in s.SOLUTION_PRESENT: primal_val = solution["info"]["pobj"] opt_val = primal_val + inverse_data[s.OFFSET] # TODO expand primal and dual variables from lower triangular to full. # TODO but this makes map from solution to variables not a slice. primal_vars = { inverse_data[SCS.VAR_ID]: solution["x"] } eq_dual_vars = utilities.get_dual_values( solution["y"][:inverse_data[ConicSolver.DIMS].zero], self.extract_dual_value, inverse_data[SCS.EQ_CONSTR] ) ineq_dual_vars = utilities.get_dual_values( solution["y"][inverse_data[ConicSolver.DIMS].zero:], self.extract_dual_value, inverse_data[SCS.NEQ_CONSTR] ) dual_vars = {} dual_vars.update(eq_dual_vars) dual_vars.update(ineq_dual_vars) return Solution(status, opt_val, primal_vars, dual_vars, attr) else: return failure_solution(status)
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ model = solution["model"] attr = {} if s.SOLVE_TIME in solution: attr[s.SOLVE_TIME] = solution[s.SOLVE_TIME] status = get_status(model) primal_vars = None dual_vars = None if status in s.SOLUTION_PRESENT: opt_val = (model.solution.get_objective_value() + inverse_data[s.OFFSET]) x = np.array(model.solution.get_values()) primal_vars = {inverse_data[CPLEX.VAR_ID]: x} if not inverse_data['is_mip']: # The dual values are retrieved in the order that the # constraints were added in solve_via_data() below. We # must be careful to map them to inverse_data[EQ_CONSTR] # followed by inverse_data[NEQ_CONSTR] accordingly. y = -np.array(model.solution.get_dual_values()) dual_vars = utilities.get_dual_values( y, utilities.extract_dual_value, inverse_data[CPLEX.EQ_CONSTR] + inverse_data[CPLEX.NEQ_CONSTR]) return Solution(status, opt_val, primal_vars, dual_vars, attr) else: return failure_solution(status)
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = solution[s.STATUS] primal_vars = None dual_vars = None if status in s.SOLUTION_PRESENT: opt_val = solution[s.VALUE] + inverse_data[s.OFFSET] primal_vars = {inverse_data[XPRESS.VAR_ID]: solution['primal']} if not inverse_data['is_mip']: dual_vars = utilities.get_dual_values( solution[s.EQ_DUAL], utilities.extract_dual_value, inverse_data[s.EQ_CONSTR]) else: if status == s.INFEASIBLE: opt_val = np.inf elif status == s.UNBOUNDED: opt_val = -np.inf else: opt_val = None other = {} other[s.XPRESS_IIS] = solution[s.XPRESS_IIS] other[s.XPRESS_TROW] = solution[s.XPRESS_TROW] return Solution(status, opt_val, primal_vars, dual_vars, other)
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = solution[s.STATUS] if status in s.SOLUTION_PRESENT: opt_val = solution[s.VALUE] primal_vars = {inverse_data[self.VAR_ID]: solution[s.PRIMAL]} eq_dual = ConicSolver.get_dual_values(solution[s.EQ_DUAL], inverse_data[self.EQ_CONSTR]) leq_dual = ConicSolver.get_dual_values( solution[s.INEQ_DUAL], inverse_data[self.NEQ_CONSTR]) eq_dual.update(leq_dual) dual_vars = eq_dual else: if status == s.INFEASIBLE: opt_val = np.inf elif status == s.UNBOUNDED: opt_val = -np.inf else: opt_val = None primal_vars = None dual_vars = None return Solution(status, opt_val, primal_vars, dual_vars, {})
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = self.STATUS_MAP[solution["info"]["status"]] attr = {} attr[s.SOLVE_TIME] = solution["info"]["solveTime"] attr[s.SETUP_TIME] = solution["info"]["setupTime"] attr[s.NUM_ITERS] = solution["info"]["iter"] if status in s.SOLUTION_PRESENT: primal_val = solution["info"]["pobj"] opt_val = primal_val + inverse_data[s.OFFSET] primal_vars = { inverse_data[SCS.VAR_ID]: intf.DEFAULT_INTF.const_to_matrix(solution["x"]) } eq_dual_vars = utilities.get_dual_values( intf.DEFAULT_INTF.const_to_matrix( solution["y"][:inverse_data[ConicSolver.DIMS].zero]), self.extract_dual_value, inverse_data[SCS.EQ_CONSTR]) ineq_dual_vars = utilities.get_dual_values( intf.DEFAULT_INTF.const_to_matrix( solution["y"][inverse_data[ConicSolver.DIMS].zero:]), self.extract_dual_value, inverse_data[SCS.NEQ_CONSTR]) dual_vars = {} dual_vars.update(eq_dual_vars) dual_vars.update(ineq_dual_vars) return Solution(status, opt_val, primal_vars, dual_vars, attr) else: return failure_solution(status)
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = solution['status'] if status in s.SOLUTION_PRESENT: opt_val = solution['value'] + inverse_data[s.OFFSET] primal_vars = {inverse_data[self.VAR_ID]: solution['primal']} return Solution(status, opt_val, primal_vars, None, {}) else: return failure_solution(status)
def invert(self, solution, inverse_data): pvars = { vid: solution.primal_vars[vid] for vid in inverse_data.id_map if vid in solution.primal_vars } for vid in inverse_data.id_map: if vid not in pvars: # Variable was optimized out because it was unconstrainted. pvars[vid] = 0.0 return Solution(solution.status, solution.opt_val, pvars, {}, solution.attr)
def invert(self, solution, inverse_data): pvars = {} dvars = {} if solution.status in s.SOLUTION_PRESENT: for vid, var in inverse_data.id2var.items(): if var.is_real(): pvars[vid] = solution.primal_vars[vid] elif var.is_imag(): imag_id = inverse_data.real2imag[vid] pvars[vid] = 1j * solution.primal_vars[imag_id] elif var.is_complex() and var.is_hermitian(): imag_id = inverse_data.real2imag[vid] # Imaginary part may have been lost. if imag_id in solution.primal_vars: imag_val = solution.primal_vars[imag_id] pvars[vid] = solution.primal_vars[vid] + \ 1j*(imag_val - imag_val.T)/2 else: pvars[vid] = solution.primal_vars[vid] elif var.is_complex(): imag_id = inverse_data.real2imag[vid] # Imaginary part may have been lost. if imag_id in solution.primal_vars: pvars[vid] = solution.primal_vars[vid] + \ 1j*solution.primal_vars[imag_id] else: pvars[vid] = solution.primal_vars[vid] for cid, cons in inverse_data.id2cons.items(): if cons.is_real(): dvars[vid] = solution.dual_vars[cid] elif cons.is_imag(): imag_id = inverse_data.real2imag[cid] dvars[cid] = 1j * solution.dual_vars[imag_id] # For equality and inequality constraints. elif isinstance( cons, (Equality, Zero, NonPos)) and cons.is_complex(): imag_id = inverse_data.real2imag[cid] if imag_id in solution.dual_vars: dvars[cid] = solution.dual_vars[cid] + \ 1j*solution.dual_vars[imag_id] else: dvars[cid] = solution.dual_vars[cid] elif isinstance(cons, SOC) and cons.is_complex(): # TODO add dual variables for complex SOC. pass # For PSD constraints. elif isinstance(cons, PSD) and cons.is_complex(): n = cons.args[0].shape[0] dual = solution.dual_vars[cid] dvars[cid] = dual[:n, :n] + 1j * dual[n:, :n] else: raise Exception("Unknown constraint type.") return Solution(solution.status, solution.opt_val, pvars, dvars, solution.attr)
def solve_via_data(self, data, warm_start, verbose, solver_opts, solver_cache=None): import mosek if 'dualized' in data: if len(data[s.C]) == 0 and len(data['c_bar_data']) == 0: # primal problem was unconstrained minimization of a linear function. if np.linalg.norm(data[s.B]) > 0: sol = Solution(s.INFEASIBLE, -np.inf, None, None, dict()) return {'sol': sol} else: sol = Solution(s.OPTIMAL, 0.0, dict(), {s.EQ_DUAL: data[s.B]}, dict()) return {'sol': sol} else: env = mosek.Env() task = env.Task(0, 0) task = MOSEK._build_dualized_task(task, data) else: if len(data[s.C]) == 0: sol = Solution(s.OPTIMAL, 0.0, dict(), dict(), dict()) return {'sol': sol} else: env = mosek.Env() task = env.Task(0, 0) task = MOSEK._build_slack_task(task, data) # Set parameters, optimize the Mosek Task, and return the result. save_file = MOSEK.handle_options(env, task, verbose, solver_opts) if save_file: task.writedata(save_file) task.optimize() if verbose: task.solutionsummary(mosek.streamtype.msg) return {'env': env, 'task': task, 'solver_options': solver_opts}
def invert(self, solution, inverse_data): pvars = { vid: solution.primal_vars[vid] for vid in inverse_data.id_map if vid in solution.primal_vars } dvars = { orig_id: solution.dual_vars[vid] for orig_id, vid in inverse_data.cons_id_map.items() if vid in solution.dual_vars } return Solution(solution.status, solution.opt_val, pvars, dvars, solution.attr)
def invert(self, solution, inverse_data): status = self.STATUS_MAP[solution['status']] sln = solution['sln'] opt_val = None primal_vars = None dual_vars = None attr = {} if status in s.SOLUTION_PRESENT: opt_val = sln.rinfo[0] + inverse_data[s.OBJ_OFFSET] nr = inverse_data['nr'] x = [0.0] * nr x = sln.x[0:nr] primal_vars = {inverse_data[self.VAR_ID]: x} attr[s.SOLVE_TIME] = sln.stats[5] attr[s.NUM_ITERS] = sln.stats[0] # Recover dual variables dual_vars = dict() lin_dim = sum(ell for _, ell in inverse_data['lin_dim']) if lin_dim > 0: lin_dvars = np.zeros(lin_dim) idx = 0 for i in range(lin_dim): lin_dvars[i] = sln.u[idx+1] - sln.u[idx] idx += 2 idx = 0 for id, dim in inverse_data['lin_dim']: if dim == 1: dual_vars[id] = lin_dvars[idx] else: dual_vars[id] = np.array(lin_dvars[idx:(idx + dim)]) idx += dim soc_dim = sum(ell for _, ell in inverse_data['soc_dim']) if soc_dim > 0: idx = 0 for id, dim in inverse_data['soc_dim']: if dim == 1: dual_vars[id] = sln.uc[idx] else: dual_vars[id] = np.array(sln.uc[idx:(idx + dim)]) idx += dim else: if status == s.INFEASIBLE: opt_val = np.inf elif status == s.UNBOUNDED: opt_val = -np.inf else: opt_val = None primal_vars = None dual_vars = None return Solution(status, opt_val, primal_vars, dual_vars, attr)
def invert(self, results, inverse_data): model = results["model"] x_grb = model.getVars() n = len(x_grb) constraints_grb = model.getConstrs() m = len(constraints_grb) # Note: Gurobi does not always fill BarIterCount # and IterCount so better using try/except try: bar_iter_count = model.BarIterCount except AttributeError: bar_iter_count = 0 try: simplex_iter_count = model.IterCount except AttributeError: simplex_iter_count = 0 # Take the sum in case they both appear. One of them # will be 0 anyway iter_count = bar_iter_count + simplex_iter_count # Start populating attribute dictionary attr = { s.SOLVE_TIME: model.Runtime, s.NUM_ITERS: iter_count, s.EXTRA_STATS: model } # Map GUROBI statuses back to CVXPY statuses status = self.STATUS_MAP.get(model.Status, s.SOLVER_ERROR) if status == s.USER_LIMIT and not model.SolCount: status = s.INFEASIBLE_INACCURATE if (status in s.SOLUTION_PRESENT) or (model.solCount > 0): opt_val = model.objVal + inverse_data[s.OFFSET] x = np.array([x_grb[i].X for i in range(n)]) primal_vars = { GUROBI.VAR_ID: intf.DEFAULT_INTF.const_to_matrix(np.array(x)) } # Only add duals if not a MIP. dual_vars = None if not inverse_data[GUROBI.IS_MIP]: y = -np.array([constraints_grb[i].Pi for i in range(m)]) dual_vars = {GUROBI.DUAL_VAR_ID: y} sol = Solution(status, opt_val, primal_vars, dual_vars, attr) else: sol = failure_solution(status, attr) return sol
def invert(self, solution: Dict[str, Any], inverse_data: Dict[str, Any]) -> Solution: """Returns the solution to the original problem.""" status = solution["status"] if status in s.SOLUTION_PRESENT: primal_vars = {inverse_data[self.VAR_ID]: solution["primal"]} dual_vars = utilities.get_dual_values( result_vec=solution["dual"], parse_func=utilities.extract_dual_value, constraints=inverse_data["constraints"], ) return Solution(status, solution["value"], primal_vars, dual_vars, {}) else: return failure_solution(status)
def invert(self, solution, inverse_data): if not inverse_data: return solution id2new_var, id2old_var, cons_id_map = inverse_data pvars = {} for id, var in id2old_var.items(): new_var = id2new_var[id] if new_var.id in solution.primal_vars: pvars[id] = recover_value_for_variable( var, solution.primal_vars[new_var.id]) dvars = {orig_id: solution.dual_vars[vid] for orig_id, vid in cons_id_map.items() if vid in solution.dual_vars} return Solution(solution.status, solution.opt_val, pvars, dvars, solution.attr)
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ attr = {} if solution["solve_method"] == s.SCS: import scs if Version(scs.__version__) < Version('3.0.0'): status = scs_conif.SCS.STATUS_MAP[solution["info"]["statusVal"]] attr[s.SOLVE_TIME] = solution["info"]["solveTime"] attr[s.SETUP_TIME] = solution["info"]["setupTime"] else: status = scs_conif.SCS.STATUS_MAP[solution["info"]["status_val"]] attr[s.SOLVE_TIME] = solution["info"]["solve_time"] attr[s.SETUP_TIME] = solution["info"]["setup_time"] elif solution["solve_method"] == s.ECOS: status = self.STATUS_MAP[solution["info"]["status"]] attr[s.SOLVE_TIME] = solution["info"]["solveTime"] attr[s.SETUP_TIME] = solution["info"]["setupTime"] attr[s.NUM_ITERS] = solution["info"]["iter"] attr[s.EXTRA_STATS] = solution if status in s.SOLUTION_PRESENT: primal_val = solution["info"]["pobj"] opt_val = primal_val + inverse_data[s.OFFSET] # TODO expand primal and dual variables from lower triangular to full. # TODO but this makes map from solution to variables not a slice. primal_vars = { inverse_data[DIFFCP.VAR_ID]: solution["x"] } eq_dual_vars = utilities.get_dual_values( solution["y"][:inverse_data[ConicSolver.DIMS].zero], self.extract_dual_value, inverse_data[DIFFCP.EQ_CONSTR] ) ineq_dual_vars = utilities.get_dual_values( solution["y"][inverse_data[ConicSolver.DIMS].zero:], self.extract_dual_value, inverse_data[DIFFCP.NEQ_CONSTR] ) dual_vars = {} dual_vars.update(eq_dual_vars) dual_vars.update(ineq_dual_vars) return Solution(status, opt_val, primal_vars, dual_vars, attr) else: return failure_solution(status, attr)
def invert(self, solution, inverse_data): attr = {s.SOLVE_TIME: solution.info.run_time} attr[s.EXTRA_STATS] = solution # Map OSQP statuses back to CVXPY statuses status = self.STATUS_MAP.get(solution.info.status_val, s.SOLVER_ERROR) if status in s.SOLUTION_PRESENT: opt_val = solution.info.obj_val + inverse_data[s.OFFSET] primal_vars = { OSQP.VAR_ID: intf.DEFAULT_INTF.const_to_matrix(np.array(solution.x)) } dual_vars = {OSQP.DUAL_VAR_ID: solution.y} attr[s.NUM_ITERS] = solution.info.iter sol = Solution(status, opt_val, primal_vars, dual_vars, attr) else: sol = failure_solution(status, attr) return sol
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = solution['status'] if status in s.SOLUTION_PRESENT: opt_val = solution['value'] + inverse_data[s.OFFSET] primal_vars = {inverse_data[self.VAR_ID]: solution['primal']} eq_dual = utilities.get_dual_values(solution[s.EQ_DUAL], utilities.extract_dual_value, inverse_data[self.EQ_CONSTR]) leq_dual = utilities.get_dual_values(solution[s.INEQ_DUAL], utilities.extract_dual_value, inverse_data[self.NEQ_CONSTR]) eq_dual.update(leq_dual) dual_vars = eq_dual return Solution(status, opt_val, primal_vars, dual_vars, {}) else: return failure_solution(status)
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ status = solution['status'] if status in s.SOLUTION_PRESENT: opt_val = solution['value'] primal_vars = {inverse_data[self.VAR_ID]: solution['primal']} else: if status == s.INFEASIBLE: opt_val = np.inf elif status == s.UNBOUNDED: opt_val = -np.inf else: opt_val = None primal_vars = None dual_vars = None return Solution(status, opt_val, primal_vars, dual_vars, {})
def invert(self, results, inverse_data): # model = results["model"] attr = {} if s.SOLVE_TIME in results: attr[s.SOLVE_TIME] = results[s.SOLVE_TIME] attr[s.NUM_ITERS] = \ int(results['bariter']) \ if not inverse_data[XPRESS.IS_MIP] \ else 0 status_map_lp, status_map_mip = get_status_maps() if results['status'] == 'solver_error': status = 'solver_error' elif 'mip_' in results['getProbStatusString']: status = status_map_mip[results['status']] else: status = status_map_lp[results['status']] if status in s.SOLUTION_PRESENT: # Get objective value opt_val = results['getObjVal'] + inverse_data[s.OFFSET] # Get solution x = np.array(results['getSolution']) primal_vars = { XPRESS.VAR_ID: intf.DEFAULT_INTF.const_to_matrix(np.array(x)) } # Only add duals if not a MIP. dual_vars = None if not inverse_data[XPRESS.IS_MIP]: y = -np.array(results['getDual']) dual_vars = {XPRESS.DUAL_VAR_ID: y} sol = Solution(status, opt_val, primal_vars, dual_vars, attr) else: sol = failure_solution(status, attr) return sol
def invert(solution, inv_data): """ ``solution`` is a CVXPY Solution object, formatted where (D-Opt) max{ -b @ y : c = A.T @ y, y in K^* } + d is the primal problem from the solver's perspective. The purpose of this function is to map such a solution back to the format (P-Opt) min{ c.T @ x : A @ x + b in K } + d. This function handles mapping of primal and dual variables, and solver status codes. The variable "x" in (P-Opt) is trivially populated from the dual variables to the constraint "c = A.T @ y" in (D-Opt). Status codes also map back in a simple way. Details on required formatting of solution.primal_vars ------------------------------------------------------ We assume the dict solution.primal_vars is keyed by string-enums FREE ('fr'), NONNEG ('+'), SOC ('s'), PSD ('p'), and DUAL_EXP ('de'). The corresponding values are described below. solution.primal_vars[FREE] should be a single vector. It corresponds to the (possibly concatenated) components of "y" which are subject to no conic constraints. We map these variables back to dual variables for equality constraints in (P-Opt). solution.primal_vars[NONNEG] should also be a single vector, this time giving the possibly concatenated components of "y" which must be >= 0. We map these variables back to dual variables for inequality constraints in (P-Opt). solution.primal_vars[SOC] is a list of vectors specifying blocks of "y" which belong to the second-order-cone under the CVXPY standard ({ z : z[0] >= || z[1:] || }). We map these variables back to dual variables for SOC constraints in (P-Opt). solution.primal_vars[PSD] is a list of symmetric positive semidefinite matrices which result by lifting the vectorized PSD blocks of "y" back into matrix form. We assign these as dual variables to PSD constraints appearing in (P-Opt). solution.primal_vars[DUAL_EXP] is a vector of concatenated length-3 slices of y, where each constituent length-3 slice belongs to dual exponential cone as implied by the CVXPY standard of the primal exponential cone (see cvxpy/constraints/exponential.py:ExpCone). We map these back to dual variables for exponential cone constraints in (P-Opt). """ status = solution.status prob_attr = solution.attr primal_vars, dual_vars = None, None if status in s.SOLUTION_PRESENT: opt_val = solution.opt_val + inv_data[s.OBJ_OFFSET] primal_vars = {inv_data['x_id']: solution.dual_vars[s.EQ_DUAL]} dual_vars = dict() direct_prims = solution.primal_vars constr_map = inv_data['constr_map'] i = 0 for con in constr_map[Zero_obj]: dv = direct_prims[FREE][i:i + con.size] dual_vars[con.id] = dv if dv.size > 1 else dv.item() i += con.size i = 0 for con in constr_map[NonNeg_obj]: dv = direct_prims[NONNEG][i:i + con.size] dual_vars[con.id] = dv if dv.size > 1 else dv.item() i += con.size i = 0 for con in constr_map[SOC_obj]: block_len = con.shape[0] dv = np.concatenate(direct_prims[SOC][i:i + block_len]) dual_vars[con.id] = dv i += block_len for i, con in enumerate(constr_map[PSD_obj]): dv = direct_prims[PSD][i] dual_vars[con.id] = dv i = 0 for con in constr_map[ExpCone_obj]: dv = direct_prims[DUAL_EXP][i:i + con.size] dual_vars[con.id] = dv i += con.size for con in constr_map[PowCone_obj]: dv = direct_prims[DUAL_POW3D][i:i + con.size] dual_vars[con.id] = dv i += con.size elif status == s.INFEASIBLE: status = s.UNBOUNDED opt_val = -np.inf elif status == s.INFEASIBLE_INACCURATE: status = s.UNBOUNDED_INACCURATE opt_val = -np.inf elif status == s.UNBOUNDED: status = s.INFEASIBLE opt_val = np.inf elif status == s.UNBOUNDED_INACCURATE: status = s.INFEASIBLE_INACCURATE opt_val = np.inf else: status = s.SOLVER_ERROR opt_val = np.NaN sol = Solution(status, opt_val, primal_vars, dual_vars, prob_attr) return sol
def solve(self, problem, warm_start: bool, verbose: bool, solver_opts): if all(c.value() for c in problem.constraints): return Solution(s.OPTIMAL, problem.objective.value, {}, {}, {}) else: return Solution(s.INFEASIBLE, None, {}, {}, {})
def invert(self, results, inverse_data): """ Use information contained within "results" and "inverse_data" to properly define a cvxpy Solution object. :param results: a dictionary with three key-value pairs: results['env'] == the mosek Environment object generated during solve_via_data, results['task'] == the mosek Task object generated during solve_via_data, results['solver_options'] == the dictionary of parameters passed to solve_via_data. :param inverse_data: data recorded during "apply". :return: a cvxpy Solution object, instantiated with the following fields: (1) status - the closest cvxpy analog of mosek's status code. (2) opt_val - the optimal objective function value (after translation by a possible constant). (3) primal_vars - a dictionary with a single element: "z", represented as a list. (4) dual_vars - a dictionary with as many elements as constraints in the cvxpy standard form problem. The elements of the dictionary are either scalars, or numpy arrays. """ import mosek # Status map is taken from: # https://docs.mosek.com/8.1/pythonapi/constants.html?highlight=solsta#mosek.solsta STATUS_MAP = { mosek.solsta.optimal: s.OPTIMAL, mosek.solsta.integer_optimal: s.OPTIMAL, mosek.solsta.prim_feas: s.OPTIMAL_INACCURATE, # for integer problems mosek.solsta.prim_infeas_cer: s.INFEASIBLE, mosek.solsta.dual_infeas_cer: s.UNBOUNDED } # "Near" statuses only up to Mosek 8.1 if hasattr(mosek.solsta, 'near_optimal'): STATUS_MAP_INACCURATE = { mosek.solsta.near_optimal: s.OPTIMAL_INACCURATE, mosek.solsta.near_integer_optimal: s.OPTIMAL_INACCURATE, mosek.solsta.near_prim_infeas_cer: s.INFEASIBLE_INACCURATE, mosek.solsta.near_dual_infeas_cer: s.UNBOUNDED_INACCURATE } STATUS_MAP.update(STATUS_MAP_INACCURATE) STATUS_MAP = defaultdict(lambda: s.SOLVER_ERROR, STATUS_MAP) env = results['env'] task = results['task'] solver_opts = results['solver_options'] if inverse_data['integer_variables']: sol = mosek.soltype.itg elif 'bfs' in solver_opts and solver_opts['bfs'] and inverse_data[ 'is_LP']: sol = mosek.soltype.bas # the basic feasible solution else: sol = mosek.soltype.itr # the solution found via interior point method problem_status = task.getprosta(sol) solution_status = task.getsolsta(sol) status = STATUS_MAP[solution_status] # For integer problems, problem status determines infeasibility (no solution) if sol == mosek.soltype.itg and problem_status == mosek.prosta.prim_infeas: status = s.INFEASIBLE if status in s.SOLUTION_PRESENT: # get objective value opt_val = task.getprimalobj(sol) + inverse_data[s.OBJ_OFFSET] # recover the cvxpy standard-form primal variable z = [0.] * inverse_data['n0'] task.getxxslice(sol, 0, len(z), z) primal_vars = {inverse_data[self.VAR_ID]: z} # recover the cvxpy standard-form dual variables if sol == mosek.soltype.itg: dual_vars = None else: dual_vars = MOSEK.recover_dual_variables( task, sol, inverse_data) else: if status == s.INFEASIBLE: opt_val = np.inf elif status == s.UNBOUNDED: opt_val = -np.inf else: opt_val = None primal_vars = None dual_vars = None # Store computation time attr = {} attr[s.SOLVE_TIME] = task.getdouinf(mosek.dinfitem.optimizer_time) # Delete the mosek Task and Environment task.__exit__(None, None, None) env.__exit__(None, None, None) return Solution(status, opt_val, primal_vars, dual_vars, attr)