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 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. """ 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: 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 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. """ 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'] attr = { s.EXTRA_STATS: solution['model'], s.SOLVE_TIME: solution[s.SOLVE_TIME] } 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, attr) else: return failure_solution(status, attr)
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, 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 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'] + 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, 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): """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): """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): 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): status = self.STATUS_MAP[solution['status']] sln = solution['sln'] attr = {} if status in s.SOLUTION_PRESENT: opt_val = sln.rinfo[0] + inverse_data[s.OBJ_OFFSET] nr = inverse_data['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 sol = Solution(status, opt_val, primal_vars, dual_vars, attr) else: sol = failure_solution(status) return sol
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 bisect(problem, solver=None, low=None, high=None, eps=1e-6, verbose=False, max_iters=100, max_iters_interval_search=100): """Bisection on a one-parameter family of DCP problems. Bisects on a one-parameter family of DCP problems emitted by `Dqcp2Dcp`. Parameters ------ problem : Problem problem emitted by Dqcp2Dcp solver : Solver solver to use for bisection low : float lower bound for bisection (optional) high : float upper bound for bisection (optional) eps : float terminate bisection when width of interval is < eps verbose : bool whether to print verbose output related to the bisection max_iters : int the maximum number of iterations to run bisection Returns ------- A Solution object. """ if not hasattr(problem, '_bisection_data'): raise ValueError("`bisect` only accepts problems emitted by Dqcp2Dcp.") feas_problem, t, tighten_lower, tighten_higher = problem._bisection_data if verbose: print("\n******************************************************" "**************************\n" "Preparing to bisect problem\n\n%s\n" % _lower_problem(problem)) lowered_feas = _lower_problem(feas_problem) _solve(lowered_feas, solver) if _infeasible(lowered_feas): if verbose: print("Problem is infeasible.") return solution_module.failure_solution(s.INFEASIBLE) if low is None or high is None: if verbose: print("Finding interval for bisection ...") low, high = _find_bisection_interval(problem, t, solver, low, high, max_iters_interval_search) if verbose: print("initial lower bound: %0.6f" % low) print("initial upper bound: %0.6f\n" % high) soln, low, high = _bisect(problem, solver, t, low, high, tighten_lower, tighten_higher, eps, verbose, max_iters) soln.opt_val = (low + high) / 2.0 if verbose: print("Bisection completed, with lower bound %0.6f and upper bound " "%0.7f\n******************************************" "**************************************\n" % (low, high)) return soln