def solve(A, rhs, ct=None, logLevel=0): s = CyClpSimplex() s.logLevel = logLevel lp_dim = A.shape[1] x = s.addVariable('x', lp_dim) A = np.matrix(A) rhs = CyLPArray(rhs) s += A * x >= rhs s += x[lp_dim - 1] >= 0 s.objective = x[lp_dim - 1] nnz = np.count_nonzero(A) logging.debug(f"TASK SIZE XCOUNT: {lp_dim} GXCOUNT: {len(rhs)}") s.primal() k = list(s.primalConstraintSolution.keys())[0] k2 = list(s.dualConstraintSolution.keys())[0] q = s.dualConstraintSolution[k2] logging.debug(f"{s.getStatusString()} objective: {s.objectiveValue}") logging.debug( f"nonzeros rhs: {np.count_nonzero(s.primalConstraintSolution[k])}") logging.debug( f"nonzeros dual: {np.count_nonzero(s.dualConstraintSolution[k2])}") basis = s.getBasisStatus() print("Basis[0]") print(basis[0]) print("basis[1]") print(basis[1]) np.savetxt("out", basis[1]) print("#" * 100) s = CyClpSimplex() s.logLevel = logLevel lp_dim = A.shape[1] x = s.addVariable('x', lp_dim) A = np.matrix(A) rhs = CyLPArray(rhs) s += A * x >= rhs s += x[lp_dim - 1] >= 0 s.objective = x[lp_dim - 1] nnz = np.count_nonzero(A) logging.debug(f"TASK SIZE XCOUNT: {lp_dim} GXCOUNT: {len(rhs)}") s.setBasisStatus(*basis) s.primal() return s.primalVariableSolution['x']
def solve(A, rhs, ct=None, logLevel=0, extnd=False, basis=False, mps_file=None): s = CyClpSimplex() s.logLevel = logLevel lp_dim = A.shape[1] x = s.addVariable('x', lp_dim) A = np.matrix(A) rhs = CyLPArray(rhs) s += A * x >= rhs s += x[lp_dim - 1] >= 0 s.objective = x[lp_dim - 1] nnz = np.count_nonzero(A) if not mps_file is None: s.writeMps(mps_file) return None logging.debug(f"TASK SIZE XCOUNT: {lp_dim} GXCOUNT: {len(rhs)}") s.primal() k = list(s.primalConstraintSolution.keys())[0] k2 =list(s.dualConstraintSolution.keys())[0] q = s.dualConstraintSolution[k2] logging.debug(f"{s.getStatusString()} objective: {s.objectiveValue}") logging.debug(f"nonzeros rhs: {np.count_nonzero(s.primalConstraintSolution[k])}") logging.debug(f"nonzeros dual: {np.count_nonzero(s.dualConstraintSolution[k2])}") if extnd and not basis: return s.primalVariableSolution['x'],s.primalConstraintSolution[k],s.dualConstraintSolution[k2] elif not extnd and basis: basis = s.getBasisStatus() return s.primalVariableSolution['x'],*basis else: return s.primalVariableSolution['x']
def classical_exactcover_solver(A, w=None, num_threads=4): nrows, ncolumns = np.shape(A) if w is None: w = np.ones(ncolumns) assert(len(w) == ncolumns) assert(sum(w >= 0)) model = CyLPModel() # Decision variables, one for each cover x = model.addVariable('x', ncolumns, isInt=True) # Adding the box contraints model += 0 <= x <= 1 # Adding the cover constraints # Sum_j x_ij == 1 for i in range(nrows): model += CyLPArray(A[i,:]) * x == 1 # Adding the objective function model.objective = CyLPArray(w) * x lp = CyClpSimplex(model) lp.logLevel = 0 lp.optimizationDirection = 'min' mip = lp.getCbcModel() mip.logLevel = 0 # Setting number of threads mip.numberThreads = num_threads mip.solve() return mip.objectiveValue, [int(i) for i in mip.primalVariableSolution['x']]
def __init__(self: T, lp: CyClpSimplex, integerIndices: List[int], lower_bound: Union[float, int] = -float('inf'), b_idx: int = None, b_dir: str = None, b_val: float = None, depth=0): """ :param lp: model object simplex is run against :param integerIndices: indices of variables we aim to find integer solutions :param lower_bound: starting lower bound on optimal objective value for the minimization problem in this node :param b_idx: index of the branching variable :param b_dir: direction of branching :param b_val: initial value of the branching variable :param depth: how deep in the tree this node is """ assert isinstance(lp, CyClpSimplex), 'lp must be CyClpSimplex instance' assert all(0 <= idx < lp.nVariables and isinstance(idx, int) for idx in integerIndices), 'indices must match variables' assert len(set(integerIndices)) == len(integerIndices), \ 'indices must be distinct' assert isinstance(lower_bound, float) or isinstance(lower_bound, int), \ 'lower bound must be a float or an int' assert (b_dir is None) == (b_idx is None) == (b_val is None), \ 'none are none or all are none' assert b_idx in integerIndices or b_idx is None, \ 'branch index corresponds to integer variable if it exists' assert b_dir in ['up', 'down'] or b_dir is None, \ 'we can only round a variable up or down when branching' if b_val is not None: good_down = 0 < b_val - lp.variablesUpper[b_idx] < 1 good_up = 0 < lp.variablesLower[b_idx] - b_val < 1 assert (b_dir == 'down' and good_down) or \ (b_dir == 'up' and good_up), 'branch val should be within 1 of both bounds' assert isinstance(depth, int) and depth >= 0, 'depth is a positive integer' lp.logLevel = 0 self._lp = lp self._integerIndices = integerIndices self.lower_bound = lower_bound self.objective_value = None self.solution = None self.lp_feasible = None self.unbounded = None self.mip_feasible = None self._epsilon = .0001 self._b_dir = b_dir self._b_idx = b_idx self._b_val = b_val self.depth = depth self.search_method = 'best first' self.branch_method = 'most fractional'
def branch_and_bound(G, num_threads=4): N = len(G) model = CyLPModel() # Decision variables, one for each node x = model.addVariable('x', N, isInt=True) # Adjacency matrix (possibly weighted) W = nx.to_numpy_matrix(G) z_ind = dict() ind = 0 w = [] for i in range(N): j_range = range(N) if (not nx.is_directed(G)): # Reduced range for undirected graphs j_range = range(i, N) for j in j_range: if (W[i,j] == 0): continue if (i not in z_ind): z_ind[i] = dict() z_ind[i][j] = ind w.append(W[i,j]) ind += 1 # Aux variables, one for each edge z = model.addVariable('z', len(w), isInt=True) # Adding the box contraints model += 0 <= x <= 1 model += 0 <= z <= 1 # Adding the cutting constraints # If x_i == x_j then z_ij = 0 # If x_i != x_j then z_ij = 1 for i in z_ind: for j in z_ind[i]: model += z[z_ind[i][j]] - x[i] - x[j] <= 0 model += z[z_ind[i][j]] + x[i] + x[j] <= 2 # Adding the objective function model.objective = CyLPArray(w) * z lp = CyClpSimplex(model) lp.logLevel = 0 lp.optimizationDirection = 'max' mip = lp.getCbcModel() mip.logLevel = 0 # Setting number of threads mip.numberThreads = num_threads mip.solve() return mip.objectiveValue, [int(i) for i in mip.primalVariableSolution['x']]
def computePriceSweep(thisSlice,chunkNum, qpid, qresult): myLength = thisSlice.shape[1] myPID = multiprocessing.current_process().pid # Add our process id to the list of process ids while qpid.empty(): time.sleep(0.01) pidList = qpid.get() pidList.append(myPID) qpid.put(pidList) storagePriceSet = np.arange(1.0,20.1,0.25) storagePriceSet = np.arange(1.0,3.1,1) eff_round = 0.9 # Round-trip efficiency E_min = 0 E_max = 1 # Endogenous parameters; calculated automatically (eff_in, eff_out) = [np.sqrt(eff_round)] *2 # Properly account for the round trip efficiency of storage P_max = E_max/eff_in # Max discharge power at grid intertie, e.g max limit for C_i P_min = -1*E_max/eff_out # Max charge power at grid intertie, e.g. min D_i # Create a set of zero-filled dataframes for storing results resultIndex = pd.MultiIndex.from_product([thisSlice.index,['size','kwhPassed','profit']]) results = pd.DataFrame(index = resultIndex, columns=storagePriceSet) # Create clean problem matrices - needs efficiency and length! (A, b, A_eq, b_eq) = createABMatrices(myLength, delta_T, eff_in, eff_out, P_min, P_max, E_min, E_max) # Define model: coolStart = CyClpSimplex() coolStart.logLevel = 0 x_var = coolStart.addVariable('x',myLength*3+2) # Add constraints to model: coolStart += A * x_var <= b.toarray() coolStart += A_eq * x_var == b_eq.toarray() newIDs = [] # this holds the names of nodes which we've recently processed def dumpResults(): results.dropna(axis=0,how='all').to_csv('Data/priceSweepResults_pid'+str(myPID)+'temp.csv' ) # Make sure that the results queue hasn't been checked out by somebody else while qresult.empty(): time.sleep(0.01) resultList = qresult.get() resultList[chunkNum] = resultList[chunkNum] + newIDs qresult.put(resultList) for i in range(thisSlice.shape[0]): ## Set up prices myNodeName = thisSlice.index[i] energyPrice = thisSlice.loc[myNodeName,:] / 1000.0 # Price $/kWh as array # if (np.random.random(1)[0] < 0.05): # sys.exit(1) # Randomly kill the process c = np.concatenate([[0.0],[0.0]*(myLength+1),energyPrice,energyPrice],axis=0) #placeholder; No cost for storage state; charged for what we consume, get paid for what we discharge #[[storagePricePlaceholder],[0]*(myLength+1),myPrice,myPrice],axis=1) c_clp = CyLPArray(c) sweepStartTime = time.time() for myStoragePrice in storagePriceSet: c_clp[0] = myStoragePrice * simulationYears coolStart.objective = c_clp * x_var # Run the model coolStart.primal() # Results- Rows are Nodes, indexed by name. Columns are Storage price, indexed by price x_out = coolStart.primalVariableSolution['x'] results.loc[(myNodeName,'size'), myStoragePrice] = x_out[0] results.loc[(myNodeName,'profit'), myStoragePrice] = np.dot(-c, x_out) results.loc[(myNodeName,'kwhPassed'),myStoragePrice] = sum(x_out[2+myLength : 2+myLength*2]) * eff_in # Note: this is the net power pulled from the grid, not the number of cycles when the system is unconstrained storagePriceSet = storagePriceSet[::-1] # Reverse the price set so that we start from the same price for the next node to make warm-start more effective newIDs.append(myNodeName) if (i+1)%5==0: dumpResults() # Write the results to file, and then also update the process Queue with the newIDs = [] # The results should now be all done! dumpResults()
def solve(self, objective, constraints, cached_data, warm_start, verbose, solver_opts): """Returns the result of the call to the solver. Parameters ---------- objective : LinOp The canonicalized objective. constraints : list The list of canonicalized cosntraints. cached_data : dict A map of solver name to cached problem data. warm_start : bool Not used. verbose : bool Should the solver print output? solver_opts : dict Additional arguments for the solver. Returns ------- tuple (status, optimal value, primal, equality dual, inequality dual) """ # Import basic modelling tools of cylp from cylp.cy import CyClpSimplex from cylp.py.modeling.CyLPModel import CyLPArray # Get problem data data = self.get_problem_data(objective, constraints, cached_data) c = data[s.C] b = data[s.B] A = data[s.A] dims = data[s.DIMS] n = c.shape[0] solver_cache = cached_data[self.name()] # Problem model = CyClpSimplex() # Variables x = model.addVariable('x', n) if self.is_mip(data): for i in data[s.BOOL_IDX]: model.setInteger(x[i]) for i in data[s.INT_IDX]: model.setInteger(x[i]) # Constraints # eq model += A[0:dims[s.EQ_DIM], :] * x == b[0:dims[s.EQ_DIM]] # leq leq_start = dims[s.EQ_DIM] leq_end = dims[s.EQ_DIM] + dims[s.LEQ_DIM] model += A[leq_start:leq_end, :] * x <= b[leq_start:leq_end] # no boolean vars available in cbc -> model as int + restrict to [0,1] if self.is_mip(data): for i in data[s.BOOL_IDX]: model += 0 <= x[i] <= 1 # Objective model.objective = c # Build model & solve status = None if self.is_mip(data): cbcModel = model.getCbcModel() # need to convert model if not verbose: cbcModel.logLevel = 0 # Add cut-generators (optional) for cut_name, cut_func in six.iteritems(self.SUPPORTED_CUT_GENERATORS): if cut_name in solver_opts and solver_opts[cut_name]: module = importlib.import_module("cylp.cy.CyCgl") funcToCall = getattr(module, cut_func) cut_gen = funcToCall() cbcModel.addCutGenerator(cut_gen, name=cut_name) # solve status = cbcModel.branchAndBound() else: if not verbose: model.logLevel = 0 status = model.primal() # solve results_dict = {} results_dict["status"] = status if self.is_mip(data): results_dict["x"] = cbcModel.primalVariableSolution['x'] results_dict["obj_value"] = cbcModel.objectiveValue else: results_dict["x"] = model.primalVariableSolution['x'] results_dict["obj_value"] = model.objectiveValue return self.format_results(results_dict, data, cached_data)
x = lp.addVariable('x', LP.numVars) if LP.sense[0] == '>=': lp += A * x >= b else: lp += A * x <= b #lp += x >= 0 c = CyLPArray(LP.c) # We are maximizing, so negate objective if LP.sense[1] == 'Min': lp.objective = c * x else: lp.objective = -c * x lp.logLevel = 0 lp.primal(startFinishOptions = 'x') np.set_printoptions(precision = 2, linewidth = 200) print('Basic variables: ', lp.basicVariables) print("Current tableaux and reduced costs:") print(lp.reducedCosts) print(np.around(lp.tableau, decimals = 3)) print('Right-hand side of optimal tableaux:') #There is a bug in CyLP and this is wrong #print lp.rhs if LP.sense[0] == '<=': print(np.dot(lp.basisInverse, lp.constraintsUpper)) else: print(np.dot(lp.basisInverse, lp.constraintsLower)) print('Inverse of optimal basis:') print(np.around(lp.basisInverse, 3))
def disjunctionToCut(lp, pi, pi0, integerIndices = None, sense = '>=', sol = None, debug_print = False, use_cylp = True): me = "cglp_cuts: " if sol is None: sol = lp.primalVariableSolution['x'] infinity = lp.getCoinInfinity() if debug_print: print(me, "constraints sense = ", sense) print(me, "con lower bounds = ", lp.constraintsLower) print(me, "con upper bounds = ", lp.constraintsUpper) print(me, "con matrix = ", lp.coefMatrix.toarray()) print(me, "vars lower bounds = ", lp.variablesLower) print(me, "vars upper bounds = ", lp.variablesUpper) print(me, "Assuming objective is to minimize") print(me, "objective = ", lp.objective) print(me, "infinity = ", infinity) print(me, "current point = ", sol) print(me, "pi = ", pi) print(me, "pi0 = ", pi0) A = lp.coefMatrix.toarray() #c = lp.objective ## Convert to >= if the problem is in <= form. if sense == '<=': b = deepcopy(lp.constraintsUpper) b = -1.0*b A = -1.0*A else: b = deepcopy(lp.constraintsLower) #Add bounds on variables as explicit constraints for i in range(lp.nCols): e = np.zeros((1, lp.nCols)) if lp.variablesUpper[i] < infinity: b.resize(b.size+1, refcheck = False) e[0, i] = -1.0 b[-1] = -1.0*lp.variablesUpper[i] A = np.vstack((A, e)) if lp.variablesLower[i] > -infinity: b.resize(b.size+1, refcheck = False) e[0, i] = 1.0 b[-1] = lp.variablesLower[i] A = np.vstack((A, e)) A = csc_matrixPlus(A) ############################################################################ ## There are two given LPs: ## s.t. Ax >= b s.t. Ax >= b ## -pi.x >= -pi_0 pi.x >= pi_0+1 ## A, b, c, pi, pi_0 are given ## ## CGLP: alpha.x >= beta should be valid for both LPs above ## ## min alpha.x* - beta ## uA - u0.pi = alpha ## vA + v0.pi = alpha ## ub - u0.pi_0 >= beta ## vb + v0.(pi_0 + 1) >= beta ## u0 + v0 = 1 ## u, v, u0, v0 >= 0 ## if min value comes out < 0, then (alpha.x >= beta) is a cut. ############################################################################ b = CyLPArray(b) pi = CyLPArray(pi) Atran = A.transpose() if use_cylp: sp = CyLPModel() u = sp.addVariable('u', A.shape[0], isInt = False) v = sp.addVariable('v', A.shape[0], isInt = False) u0 = sp.addVariable('u0', 1, isInt = False) v0 = sp.addVariable('v0', 1, isInt = False) alpha = sp.addVariable('alpha', lp.nVariables, isInt = False) beta = sp.addVariable('beta', 1, isInt = False) for i in range(A.shape[1]): sp += alpha[i] - sum(Atran[i,j]*u[j] for j in range(A.shape[0])) + pi[i]*u0 == 0 for i in range(A.shape[1]): sp += alpha[i] - sum(Atran[i,j]*v[j] for j in range(A.shape[0])) - pi[i]*v0 == 0 sp += beta - b*u + pi0*u0 <= 0 sp += beta - b*v - (pi0 + 1)*v0 <= 0 sp += u0 + v0 == 1 if sense == '<=': sp += u >= 0 sp += v >= 0 sp += u0 >= 0 sp += v0 >= 0 else: #TODO this direction is not debugged # Is this all we need? sp += u <= 0 sp += v <= 0 sp += u0 <= 0 sp += v0 <= 0 sp.objective = sum(sol[i]*alpha[i] for i in range(A.shape[1])) - beta cbcModel = CyClpSimplex(sp).getCbcModel() cbcModel.logLevel = 0 #cbcModel.maximumSeconds = 5 cbcModel.solve() beta = cbcModel.primalVariableSolution['beta'][0] alpha = cbcModel.primalVariableSolution['alpha'] u = cbcModel.primalVariableSolution['u'] v = cbcModel.primalVariableSolution['v'] u0 = cbcModel.primalVariableSolution['u0'][0] v0 = cbcModel.primalVariableSolution['v0'][0] if debug_print: print('Objective Value: ', cbcModel.objectiveValue) print('alpha: ', alpha, 'alpha*sol: ', np.dot(alpha, sol)) print('beta: ', beta) print('Violation of cut: ', np.dot(alpha, sol) - beta) else: CG = AbstractModel() CG.u = Var(list(range(A.shape[0])), domain=NonNegativeReals, bounds = (0.0, None)) CG.v = Var(list(range(A.shape[0])), domain=NonNegativeReals, bounds = (0.0, None)) CG.u0 = Var(domain=NonNegativeReals, bounds = (0.0, None)) CG.v0 = Var(domain=NonNegativeReals, bounds = (0.0, None)) CG.alpha = Var(list(range(A.shape[0])), domain=Reals, bounds = (None, None)) CG.beta = Var(domain=Reals, bounds = (None, None)) ## Constraints def pi_rule_left(CG, i): x = float(pi[i]) return(sum(Atran[i, j]*CG.u[j] for j in range(A.shape[0])) - x*CG.u0 - CG.alpha[i] == 0.0) CG.pi_rule_left = Constraint(list(range(A.shape[1])), rule=pi_rule_left) def pi_rule_right(CG, i): x = float(pi[i]) return(sum(Atran[i, j]*CG.v[j] for j in range(A.shape[0])) + x*CG.v0 - CG.alpha[i] == 0.0) CG.pi_rule_right = Constraint(list(range(A.shape[1])), rule=pi_rule_right) def pi0_rule_left(CG): return(sum(b[j]*CG.u[j] for j in range(A.shape[0])) - pi0*CG.u0 - CG.beta >= 0.0) CG.pi0_rule_left = Constraint(rule=pi0_rule_left) def pi0_rule_right(CG): return(sum(b[j]*CG.v[j] for j in range(A.shape[0])) + (pi0 + 1)*CG.v0 - CG.beta >= 0.0) CG.pi0_rule_right = Constraint(rule=pi0_rule_right) def normalization_rule(CG): return(CG.u0 + CG.v0 == 1.0) CG.normalization_rule = Constraint(rule=normalization_rule) def objective_rule(CG): return(sum(sol[i]*CG.alpha[i] for i in range(A.shape[1])) - CG.beta) CG.objective = Objective(sense=minimize, rule=objective_rule) opt = SolverFactory("cbc") instance = CG.create_instance() #instance.pprint() #instance.write("foo.nl", format = "nl") #opt.options['bonmin.bb_log_level'] = 5 #opt.options['bonmin.bb_log_interval'] = 1 results = opt.solve(instance, tee=False) #results = opt.solve(instance) instance.solutions.load_from(results) beta = instance.beta.value alpha = np.array([instance.alpha[i].value for i in range(lp.nVariables)]) violation = beta - np.dot(alpha, sol) if debug_print: print(me, 'Beta: ', beta) print(me, 'alpha: ', alpha) print(me, 'Violation of cut: ', violation) if violation > 0.001: if (sense == ">="): return [(alpha, beta)] else: return [(-alpha, -beta)] return []
x = lp.addVariable('x', LP.numVars) if LP.sense[0] == '>=': lp += A * x >= b else: lp += A * x <= b #lp += x >= 0 c = CyLPArray(LP.c) # We are maximizing, so negate objective if LP.sense[1] == 'Min': lp.objective = c * x else: lp.objective = -c * x lp.logLevel = 0 lp.primal(startFinishOptions='x') np.set_printoptions(precision=2, linewidth=200) print 'Basic variables: ', lp.basicVariables print "Current tableaux and reduced costs:" print lp.reducedCosts print np.around(lp.tableau, decimals=3) print 'Right-hand side of optimal tableaux:' #There is a bug in CyLP and this is wrong #print lp.rhs if LP.sense[0] == '<=': print np.dot(lp.basisInverse, lp.constraintsUpper) else: print np.dot(lp.basisInverse, lp.constraintsLower) print 'Inverse of optimal basis:' print np.around(lp.basisInverse, 3)
def maxViolationSplitCuts(lp, integerIndices = None, sense = '>=', sol = None, max_coeff = 1): #Warning: At the moment, you must put bound constraints in explicitly for split cuts A = lp.coefMatrix if sense == '<=': b = CyLPArray(lp.constraintsUpper) else: b = CyLPArray(lp.constraintsLower) if integerIndices is None: integerIndices = range(lp.nVariables) if sol is None: sol = lp.primalVariableSolution['x'] s = A*sol - b best = lp.getCoinInfinity() best_theta = None for theta in [0.01, 0.02, 0.03, 0.04, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.46, 0.47, 0.48, 0.49, 0.5]: sp = CyLPModel() u = sp.addVariable('u', lp.nConstraints, isInt = False) v = sp.addVariable('v', lp.nConstraints, isInt = False) pi = sp.addVariable('pi', lp.nVariables, isInt = True) pi0 = sp.addVariable('pi0', 1, isInt = True) sp += pi + A.transpose()*u - A.transpose()*v == 0 sp += pi0 + b*u - b*v == theta - 1 if sense == '<=': sp += u >= 0 sp += v >= 0 else: #TODO this direction is not debugged # Is this all we need? sp += u <= 0 sp += v <= 0 sp.objective = (theta-1)*s*u - theta*s*v for i in xrange(lp.nVariables): if i in integerIndices: sp += -max_coeff <= pi[i] <= max_coeff else: sp[i] += pi[i] == 0 cbcModel = CyClpSimplex(sp).getCbcModel() cbcModel.logLevel = 0 #cbcModel.maximumSeconds = 5 cbcModel.solve() if debug_print: #print 'Theta: ', theta, #print 'Objective Value: ', cbcModel.objectiveValue - theta*(1-theta) #print 'pi: ', cbcModel.primalVariableSolution['pi'] #print 'pi0: ', cbcModel.primalVariableSolution['pi0'] multu = cbcModel.primalVariableSolution['u'] disjunction = cbcModel.primalVariableSolution['pi'] rhs = cbcModel.primalVariableSolution['pi0'] alpha = A.transpose()*multu + theta*disjunction beta = np.dot(b, multu) + theta*rhs #print 'alpha: ', alpha, 'alpha*sol: ', np.dot(alpha, sol) #print 'beta: ', beta #print 'Violation of cut: ', np.dot(alpha, sol) - beta if cbcModel.objectiveValue - theta*(1-theta) < best: best = cbcModel.objectiveValue - theta*(1-theta) best_multu = cbcModel.primalVariableSolution['u'] best_multv = cbcModel.primalVariableSolution['v'] best_disjunction = cbcModel.primalVariableSolution['pi'] best_rhs = cbcModel.primalVariableSolution['pi0'] best_theta = theta if best_theta is not None: alpha = A.transpose()*best_multu + best_theta*best_disjunction beta = np.dot(b, best_multu) + best_theta*best_rhs if debug_print: print 'Violation of cut: ', np.dot(alpha, sol) - beta print 'pi: ', best_disjunction print 'pi0: ', best_rhs print 'theta: ', best_theta if (abs(alpha) > 1e-6).any(): return [(alpha, beta)] return []
def classical_maxkcut_solver(G, num_partitions, num_threads=4): # G: NetworkX graph # num_partitions: the number partitions or groups in which we should # subdivide the nodes (i.e., the value of K) N = len(G) model = CyLPModel() # Decision variables, one for each node x = model.addVariable('x', num_partitions * N, isInt=True) # Adjacency matrix (possibly weighted) W = nx.to_numpy_matrix(G) z_ind = dict() ind = 0 w = [] for i in range(N): j_range = range(N) if (not nx.is_directed(G)): # Reduced range for undirected graphs j_range = range(i, N) for j in j_range: if (W[i, j] == 0): continue if (i not in z_ind): z_ind[i] = dict() z_ind[i][j] = ind w.append(W[i, j]) ind += 1 # Aux variables, one for each edge z = model.addVariable('z', len(w), isInt=True) # Adding the box contraints model += 0 <= x <= 1 model += 0 <= z <= 1 # Adding the selection constraints for i in range(N): indices = [i + k * N for k in range(num_partitions)] model += x[indices].sum() == 1 # Adding the cutting constraints for i in z_ind: for j in z_ind[i]: for k in range(num_partitions): shift = k * N model += z[z_ind[i][j]] + x[i + shift] + x[j + shift] <= 2 model += z[z_ind[i][j]] + x[i + shift] - x[j + shift] >= 0 model += z[z_ind[i][j]] - x[i + shift] + x[j + shift] >= 0 # Adding the objective function model.objective = CyLPArray(w) * z lp = CyClpSimplex(model) lp.logLevel = 0 lp.optimizationDirection = 'max' mip = lp.getCbcModel() mip.logLevel = 0 # Setting number of threads mip.numberThreads = num_threads mip.solve() sol = [int(i) for i in mip.primalVariableSolution['x']] sol_formatted = [] for i in range(N): indices = [i + k * N for k in range(num_partitions)] for j in range(num_partitions): if (sol[indices[j]] == 1): sol_formatted.append(j) break assert (len(sol_formatted) == N) return mip.objectiveValue, sol_formatted
def computePriceSweep(thisSlice): ###### BEGINNING OF MULTIPROCESSING FUNCTION ########### myLength = thisSlice.shape[1] # Simulation parameters - set these! storagePriceSet = np.arange(1.0,20.1,0.25) # storagePriceSet = np.arange(1.0,3.1,1) eff_round = 0.9 # Round-trip efficiency E_min = 0 E_max = 1 # Endogenous parameters; calculated automatically (eff_in, eff_out) = [np.sqrt(eff_round)] *2 # Properly account for the round trip efficiency of storage P_max = E_max/eff_in # Max discharge power at grid intertie, e.g max limit for C_i P_min = -1*E_max/eff_out # Max charge power at grid intertie, e.g. min D_i # Create a set of zero-filled dataframes for storing results resultIndex = pd.MultiIndex.from_product([thisSlice.index,['size','kwhPassed','profit']]) results = pd.DataFrame(index = resultIndex, columns=storagePriceSet) # Create clean problem matrices - needs efficiency and length! (A, b, A_eq, b_eq) = createABMatrices(myLength, delta_T, eff_in, eff_out, P_min, P_max, E_min, E_max) # Define model: coolStart = CyClpSimplex() coolStart.logLevel = 0 x_var = coolStart.addVariable('x',myLength*3+2) # Add constraints to model: coolStart += A * x_var <= b.toarray() coolStart += A_eq * x_var == b_eq.toarray() # print("Finished with problem setup") # sys.stdout.flush() # everythingStarts = time.time() # startTime = time.time() for myNodeName in thisSlice.index: ## Set up prices # myNodeName = thisSlice.index[i] energyPrice = thisSlice.loc[myNodeName,:] / 1000.0 # Price $/kWh as array c = np.concatenate([[0.0],[0.0]*(myLength+1),energyPrice,energyPrice],axis=0) #placeholder; No cost for storage state; charged for what we consume, get paid for what we discharge #[[storagePricePlaceholder],[0]*(myLength+1),myPrice,myPrice],axis=1) c_clp = CyLPArray(c) sweepStartTime = time.time() for myStoragePrice in storagePriceSet: c_clp[0] = myStoragePrice * simulationYears coolStart.objective = c_clp * x_var # Run the model coolStart.primal() # Results- Rows are Nodes, indexed by name. Columns are Storage price, indexed by price x_out = coolStart.primalVariableSolution['x'] results.loc[(myNodeName,'size'), myStoragePrice] = x_out[0] results.loc[(myNodeName,'profit'), myStoragePrice] = np.dot(-c, x_out) results.loc[(myNodeName,'kwhPassed'),myStoragePrice] = sum(x_out[2+myLength : 2+myLength*2]) * eff_in # Note: this is the net power pulled from the grid, not the number of cycles when the system is unconstrained storagePriceSet = storagePriceSet[::-1] # Reverse the price set so that we start from the same price for the next node to make warm-start more effective # if ((i+1) % reportFrequency == 0): # Save our progress along the way # elapsedTime = time.time()-startTime # print("Finished node %s; \t%s computations in \t%.4f s \t(%.4f s/solve)" # % (i, scenariosPerReport,elapsedTime,elapsedTime/scenariosPerReport)) # sys.stdout.flush() # sizeDf.to_csv('Data/VaryingPrices_StorageSizing_v2.csv') # profitDf.to_csv('Data/VaryingPrices_StorageProfits_v2.csv') # cycleDf.to_csv('Data/VaryingPrices_StorageCycles_v2.csv') # print("Done in %.3f s"%(time.time()-everythingStarts)) return results
def solve(self, objective, constraints, cached_data, warm_start, verbose, solver_opts): """Returns the result of the call to the solver. Parameters ---------- objective : LinOp The canonicalized objective. constraints : list The list of canonicalized cosntraints. cached_data : dict A map of solver name to cached problem data. warm_start : bool Not used. verbose : bool Should the solver print output? solver_opts : dict Additional arguments for the solver. Returns ------- tuple (status, optimal value, primal, equality dual, inequality dual) """ # Import basic modelling tools of cylp from cylp.cy import CyClpSimplex # Get problem data data = self.get_problem_data(objective, constraints, cached_data) c = data[s.C] b = data[s.B] A = data[s.A] dims = data[s.DIMS] n = c.shape[0] # Problem model = CyClpSimplex() # Variables x = model.addVariable('x', n) if self.is_mip(data): for i in data[s.BOOL_IDX]: model.setInteger(x[i]) for i in data[s.INT_IDX]: model.setInteger(x[i]) # Constraints # eq model += A[0:dims[s.EQ_DIM], :] * x == b[0:dims[s.EQ_DIM]] # leq leq_start = dims[s.EQ_DIM] leq_end = dims[s.EQ_DIM] + dims[s.LEQ_DIM] model += A[leq_start:leq_end, :] * x <= b[leq_start:leq_end] # no boolean vars available in cbc -> model as int + restrict to [0,1] if self.is_mip(data): for i in data[s.BOOL_IDX]: model += 0 <= x[i] <= 1 # Objective model.objective = c # Build model & solve status = None if self.is_mip(data): cbcModel = model.getCbcModel() # need to convert model if not verbose: cbcModel.logLevel = 0 # Add cut-generators (optional) for cut_name, cut_func in six.iteritems( self.SUPPORTED_CUT_GENERATORS): if cut_name in solver_opts and solver_opts[cut_name]: module = importlib.import_module("cylp.cy.CyCgl") funcToCall = getattr(module, cut_func) cut_gen = funcToCall() cbcModel.addCutGenerator(cut_gen, name=cut_name) # solve status = cbcModel.branchAndBound() else: if not verbose: model.logLevel = 0 status = model.primal() # solve results_dict = {} results_dict["status"] = status if self.is_mip(data): results_dict["x"] = cbcModel.primalVariableSolution['x'] results_dict["obj_value"] = cbcModel.objectiveValue else: results_dict["x"] = model.primalVariableSolution['x'] results_dict["obj_value"] = model.objectiveValue return self.format_results(results_dict, data, cached_data)
def disjunctionToCut(m, pi, pi0, debug_print=False, use_cylp=True, eps=EPS): '''Generate the most violated valid inequality from a given disjunction''' me = "cglp_cuts: " lp = m.lp sol = lp.primalVariableSolution['x'] if debug_print: print(me, "constraints sense = ", m.sense) print(me, "matrix = ") print(m.A) print(me, "rhs = ", m.b) print(me, "vars lower bounds = ", lp.variablesLower) print(me, "vars upper bounds = ", lp.variablesUpper) print(me, "objective = ", lp.objective) print(me, "current solution = ", sol) print(me, "pi = ", pi) print(me, "pi0 = ", pi0) ############################################################################ ## There are two given LPs: ## s.t. Ax >= b s.t. Ax >= b ## -pi.x >= -pi_0 pi.x >= pi_0+1 ## A, b, c, pi, pi_0 are given ## ## CGLP: alpha.x >= beta should be valid for both LPs above ## ## min alpha.x* - beta ## uA - u0.pi = alpha ## vA + v0.pi = alpha ## ub - u0.pi_0 >= beta ## vb + v0.(pi_0 + 1) >= beta ## u0 + v0 = 1 ## u, v, u0, v0 >= 0 ## if min value comes out < 0, then (alpha.x >= beta) is a cut. ############################################################################ pi = CyLPArray(pi) Atran = m.A.transpose() b = CyLPArray(m.b) numRows, numCols = m.A.shape if use_cylp: sp = CyLPModel() u = sp.addVariable('u', numRows, isInt=False) v = sp.addVariable('v', numRows, isInt=False) u0 = sp.addVariable('u0', 1, isInt=False) v0 = sp.addVariable('v0', 1, isInt=False) alpha = sp.addVariable('alpha', lp.nVariables, isInt=False) beta = sp.addVariable('beta', 1, isInt=False) #This should be as simple as this, but it doesn't work. #Maybe a bug in CyLP? #sp += alpha - Atran*u - pi*u0 == 0 #sp += alpha - Atran*v + pi*v0 == 0 for i in range(numCols): sp += alpha[i] - sum(Atran[i, j] * u[j] for j in range(numRows)) - pi[i] * u0 == 0 for i in range(numCols): sp += alpha[i] - sum(Atran[i, j] * v[j] for j in range(numRows)) + pi[i] * v0 == 0 if m.sense == '<=': sp += beta - b * u - pi0 * u0 >= 0 sp += beta - b * v + (pi0 + 1) * v0 >= 0 else: sp += beta - b * u - pi0 * u0 <= 0 sp += beta - b * v + (pi0 + 1) * v0 <= 0 sp += u0 + v0 == 1 sp += u >= 0 sp += v >= 0 sp += u0 >= 0 sp += v0 >= 0 if m.sense == '<=': sp.objective = sum(-sol[i] * alpha[i] for i in range(numCols)) + beta else: #This direction is not debugged sp.objective = sum(sol[i] * alpha[i] for i in range(numCols)) - beta cglp = CyClpSimplex(sp) # If we want to solve it as an MILP # cglp = CyClpSimplex(sp).getCbcModel() #cglp.writeLp('lp.lp') cglp.logLevel = 0 cglp.primal(startFinishOptions='x') # Solve as MILP # cglp.solve() beta = cglp.primalVariableSolution['beta'][0] alpha = cglp.primalVariableSolution['alpha'] u = cglp.primalVariableSolution['u'] v = cglp.primalVariableSolution['v'] u0 = cglp.primalVariableSolution['u0'][0] v0 = cglp.primalVariableSolution['v0'][0] if debug_print: print(me, 'Objective Value: ', cglp.objectiveValue) if debug_print: print(me, 'u: ', u) print(me, 'v: ', v) print(me, 'u0: ', u0) print(me, 'v0: ', v0) else: CG = AbstractModel() CG.u = Var(list(range(numRows)), domain=NonNegativeReals, bounds=(0.0, None)) CG.v = Var(list(range(numRows)), domain=NonNegativeReals, bounds=(0.0, None)) CG.u0 = Var(domain=NonNegativeReals, bounds=(0.0, None)) CG.v0 = Var(domain=NonNegativeReals, bounds=(0.0, None)) CG.alpha = Var(list(range(numRows)), domain=Reals, bounds=(None, None)) CG.beta = Var(domain=Reals, bounds=(None, None)) ## Constraints def pi_rule_left(CG, i): x = float(pi[i]) return (sum(Atran[i, j] * CG.u[j] for j in range(numRows)) - x * CG.u0 - CG.alpha[i] == 0.0) CG.pi_rule_left = Constraint(list(range(numCols)), rule=pi_rule_left) def pi_rule_right(CG, i): x = float(pi[i]) return (sum(Atran[i, j] * CG.v[j] for j in range(numRows)) + x * CG.v0 - CG.alpha[i] == 0.0) CG.pi_rule_right = Constraint(list(range(numCols)), rule=pi_rule_right) if m.sense == '<=': def pi0_rule_left(CG): return (sum(b[j] * CG.u[j] for j in range(numRows)) - pi0 * CG.u0 - CG.beta <= 0.0) CG.pi0_rule_left = Constraint(rule=pi0_rule_left) def pi0_rule_right(CG): return (sum(b[j] * CG.v[j] for j in range(numRows)) + (pi0 + 1) * CG.v0 - CG.beta <= 0.0) CG.pi0_rule_right = Constraint(rule=pi0_rule_right) else: def pi0_rule_left(CG): return (sum(b[j] * CG.u[j] for j in range(numRows)) - pi0 * CG.u0 - CG.beta >= 0.0) CG.pi0_rule_left = Constraint(rule=pi0_rule_left) def pi0_rule_right(CG): return (sum(b[j] * CG.v[j] for j in range(numRows)) + (pi0 + 1) * CG.v0 - CG.beta >= 0.0) CG.pi0_rule_right = Constraint(rule=pi0_rule_right) def normalization_rule(CG): return (CG.u0 + CG.v0 == 1.0) CG.normalization_rule = Constraint(rule=normalization_rule) def objective_rule(CG): return (sum(sol[i] * CG.alpha[i] for i in range(numCols)) - CG.beta) if m.sense == '<=': CG.objective = Objective(sense=maximize, rule=objective_rule) else: CG.objective = Objective(sense=minimize, rule=objective_rule) opt = SolverFactory("cbc") instance = CG.create_instance() #instance.pprint() #instance.write("foo.nl", format = "nl") #opt.options['bonmin.bb_log_level'] = 5 #opt.options['bonmin.bb_log_interval'] = 1 results = opt.solve(instance, tee=False) #results = opt.solve(instance) instance.solutions.load_from(results) beta = instance.beta.value alpha = np.array( [instance.alpha[i].value for i in range(lp.nVariables)]) violation = beta - np.dot(alpha, sol) if debug_print: print(me, 'Beta: ', beta) print(me, 'alpha: ', alpha) print(me, 'Violation of cut: ', violation) if np.abs(violation) > 10**(-eps): return [(alpha, beta)] print('No violated cuts found solving CGLP', violation) return []
def splitCuts(lp, integerIndices = None, sense = '>=', sol = None, max_coeff = 1): A = lp.coefMatrix b = CyLPArray(lp.constraintsUpper) if integerIndices is None: integerIndices = range(lp.nVariables) if sol is None: sol = lp.primalVariableSolution['x'] s = A*sol - b best = lp.getCoinInfinity() best_theta = None for theta in [0.1, 0.2, 0.3, 0.4, 0.5]: sp = CyLPModel() u = sp.addVariable('u', lp.nConstraints, isInt = False) v = sp.addVariable('v', lp.nConstraints, isInt = False) pi = sp.addVariable('pi', lp.nVariables, isInt = True) pi0 = sp.addVariable('pi0', 1, isInt = True) sp += pi + A.transpose()*u - A.transpose()*v == 0 sp += pi0 + b*u - b*v == theta - 1 if sense == '<=': sp += u >= 0 sp += v >= 0 else: #TODO this direction is not debugged # Is this all we need? sp += u <= 0 sp += v <= 0 sp.objective = (theta-1)*s*u - theta*s*v for i in xrange(lp.nVariables): if i in integerIndices: sp += -max_coeff <= pi[i] <= max_coeff else: sp[i] += pi[i] == 0 cbcModel = CyClpSimplex(sp).getCbcModel() cbcModel.logLevel = 0 #cbcModel.maximumSeconds = 5 cbcModel.solve() if debug_print: print theta, cbcModel.objectiveValue print cbcModel.primalVariableSolution['pi'], print cbcModel.primalVariableSolution['pi0'] if cbcModel.objectiveValue < best: best = cbcModel.objectiveValue multu = cbcModel.primalVariableSolution['u'] disjunction = cbcModel.primalVariableSolution['pi'] rhs = cbcModel.primalVariableSolution['pi0'] best_theta = theta if best_theta is not None: alpha = A.transpose()*multu + best_theta*disjunction if sense == '<=': beta = np.dot(lp.constraintsUpper, multu) + best_theta*rhs else: beta = np.dot(lp.constraintsLower, multu) + best_theta*rhs if (abs(alpha) > 1e-6).any(): return [(alpha, beta)] return []
def disjunctionToCut(lp, pi, pi0, integerIndices=None, sense='>=', sol=None, debug_print=False, use_cylp=True): me = "cglp_cuts: " if sol is None: sol = lp.primalVariableSolution['x'] infinity = lp.getCoinInfinity() if debug_print: print(me, "constraints sense = ", sense) print(me, "con lower bounds = ", lp.constraintsLower) print(me, "con upper bounds = ", lp.constraintsUpper) print(me, "con matrix = ", lp.coefMatrix.toarray()) print(me, "vars lower bounds = ", lp.variablesLower) print(me, "vars upper bounds = ", lp.variablesUpper) print(me, "Assuming objective is to minimize") print(me, "objective = ", lp.objective) print(me, "infinity = ", infinity) print(me, "current point = ", sol) print(me, "pi = ", pi) print(me, "pi0 = ", pi0) A = lp.coefMatrix.toarray() #c = lp.objective ## Convert to >= if the problem is in <= form. if sense == '<=': b = deepcopy(lp.constraintsUpper) b = -1.0 * b A = -1.0 * A else: b = deepcopy(lp.constraintsLower) #Add bounds on variables as explicit constraints for i in range(lp.nCols): e = np.zeros((1, lp.nCols)) if lp.variablesUpper[i] < infinity: b.resize(b.size + 1, refcheck=False) e[0, i] = -1.0 b[-1] = -1.0 * lp.variablesUpper[i] A = np.vstack((A, e)) if lp.variablesLower[i] > -infinity: b.resize(b.size + 1, refcheck=False) e[0, i] = 1.0 b[-1] = lp.variablesLower[i] A = np.vstack((A, e)) A = csc_matrixPlus(A) ############################################################################ ## There are two given LPs: ## s.t. Ax >= b s.t. Ax >= b ## -pi.x >= -pi_0 pi.x >= pi_0+1 ## A, b, c, pi, pi_0 are given ## ## CGLP: alpha.x >= beta should be valid for both LPs above ## ## min alpha.x* - beta ## uA - u0.pi = alpha ## vA + v0.pi = alpha ## ub - u0.pi_0 >= beta ## vb + v0.(pi_0 + 1) >= beta ## u0 + v0 = 1 ## u, v, u0, v0 >= 0 ## if min value comes out < 0, then (alpha.x >= beta) is a cut. ############################################################################ b = CyLPArray(b) pi = CyLPArray(pi) Atran = A.transpose() if use_cylp: sp = CyLPModel() u = sp.addVariable('u', A.shape[0], isInt=False) v = sp.addVariable('v', A.shape[0], isInt=False) u0 = sp.addVariable('u0', 1, isInt=False) v0 = sp.addVariable('v0', 1, isInt=False) alpha = sp.addVariable('alpha', lp.nVariables, isInt=False) beta = sp.addVariable('beta', 1, isInt=False) for i in range(A.shape[1]): sp += alpha[i] - sum(Atran[i, j] * u[j] for j in range(A.shape[0])) + pi[i] * u0 == 0 for i in range(A.shape[1]): sp += alpha[i] - sum(Atran[i, j] * v[j] for j in range(A.shape[0])) - pi[i] * v0 == 0 sp += beta - b * u + pi0 * u0 <= 0 sp += beta - b * v - (pi0 + 1) * v0 <= 0 sp += u0 + v0 == 1 if sense == '<=': sp += u >= 0 sp += v >= 0 sp += u0 >= 0 sp += v0 >= 0 else: #TODO this direction is not debugged # Is this all we need? sp += u <= 0 sp += v <= 0 sp += u0 <= 0 sp += v0 <= 0 sp.objective = sum(sol[i] * alpha[i] for i in range(A.shape[1])) - beta cbcModel = CyClpSimplex(sp).getCbcModel() cbcModel.logLevel = 0 #cbcModel.maximumSeconds = 5 cbcModel.solve() beta = cbcModel.primalVariableSolution['beta'][0] alpha = cbcModel.primalVariableSolution['alpha'] u = cbcModel.primalVariableSolution['u'] v = cbcModel.primalVariableSolution['v'] u0 = cbcModel.primalVariableSolution['u0'][0] v0 = cbcModel.primalVariableSolution['v0'][0] if debug_print: print('Objective Value: ', cbcModel.objectiveValue) print('alpha: ', alpha, 'alpha*sol: ', np.dot(alpha, sol)) print('beta: ', beta) print('Violation of cut: ', np.dot(alpha, sol) - beta) else: CG = AbstractModel() CG.u = Var(list(range(A.shape[0])), domain=NonNegativeReals, bounds=(0.0, None)) CG.v = Var(list(range(A.shape[0])), domain=NonNegativeReals, bounds=(0.0, None)) CG.u0 = Var(domain=NonNegativeReals, bounds=(0.0, None)) CG.v0 = Var(domain=NonNegativeReals, bounds=(0.0, None)) CG.alpha = Var(list(range(A.shape[0])), domain=Reals, bounds=(None, None)) CG.beta = Var(domain=Reals, bounds=(None, None)) ## Constraints def pi_rule_left(CG, i): x = float(pi[i]) return (sum(Atran[i, j] * CG.u[j] for j in range(A.shape[0])) - x * CG.u0 - CG.alpha[i] == 0.0) CG.pi_rule_left = Constraint(list(range(A.shape[1])), rule=pi_rule_left) def pi_rule_right(CG, i): x = float(pi[i]) return (sum(Atran[i, j] * CG.v[j] for j in range(A.shape[0])) + x * CG.v0 - CG.alpha[i] == 0.0) CG.pi_rule_right = Constraint(list(range(A.shape[1])), rule=pi_rule_right) def pi0_rule_left(CG): return (sum(b[j] * CG.u[j] for j in range(A.shape[0])) - pi0 * CG.u0 - CG.beta >= 0.0) CG.pi0_rule_left = Constraint(rule=pi0_rule_left) def pi0_rule_right(CG): return (sum(b[j] * CG.v[j] for j in range(A.shape[0])) + (pi0 + 1) * CG.v0 - CG.beta >= 0.0) CG.pi0_rule_right = Constraint(rule=pi0_rule_right) def normalization_rule(CG): return (CG.u0 + CG.v0 == 1.0) CG.normalization_rule = Constraint(rule=normalization_rule) def objective_rule(CG): return (sum(sol[i] * CG.alpha[i] for i in range(A.shape[1])) - CG.beta) CG.objective = Objective(sense=minimize, rule=objective_rule) opt = SolverFactory("cbc") instance = CG.create_instance() #instance.pprint() #instance.write("foo.nl", format = "nl") #opt.options['bonmin.bb_log_level'] = 5 #opt.options['bonmin.bb_log_interval'] = 1 results = opt.solve(instance, tee=False) #results = opt.solve(instance) instance.solutions.load_from(results) beta = instance.beta.value alpha = np.array( [instance.alpha[i].value for i in range(lp.nVariables)]) violation = beta - np.dot(alpha, sol) if debug_print: print(me, 'Beta: ', beta) print(me, 'alpha: ', alpha) print(me, 'Violation of cut: ', violation) if violation > 0.001: if (sense == ">="): return [(alpha, beta)] else: return [(-alpha, -beta)] return []