def test_cylp_packaging(self): # https://github.com/coin-or/CyLP#modeling-example s = CyClpSimplex() # Add variables x = s.addVariable('x', 3) y = s.addVariable('y', 2) # Create coefficients and bounds A = np.matrix([[1., 2., 0],[1., 0, 1.]]) B = np.matrix([[1., 0, 0], [0, 0, 1.]]) D = np.matrix([[1., 2.],[0, 1]]) a = CyLPArray([5, 2.5]) b = CyLPArray([4.2, 3]) x_u= CyLPArray([2., 3.5]) # Add constraints s += A * x <= a s += 2 <= B * x + D * y <= b s += y >= 0 s += 1.1 <= x[1:3] <= x_u # Set the objective function c = CyLPArray([1., -2., 3.]) s.objective = c * x + 2 * y.sum() # Solve using primal Simplex s.primal() print 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 test_write(self): self.model = CyLPModel() model = self.model x = model.addVariable('x', 3, isInt=True) y = model.addVariable('y', 2) A = np.matrix([[1., 2., 0], [1., 0, 1.]]) B = np.matrix([[1., 0, 0], [0, 0, 1.]]) D = np.matrix([[1., 2.], [0, 1]]) a = CyLPArray([5, 2.5]) b = CyLPArray([4.2, 3]) x_u = CyLPArray([2., 3.5]) model.addConstraint(A * x <= a) model.addConstraint(2 <= B * x + D * y <= b) model.addConstraint(y >= 0) model.addConstraint(1.1 <= x[1:3] <= x_u) c = CyLPArray([1., -2., 3.]) model.objective = c * x + 2 * y.sum() self.s = CyClpSimplex(model) s = self.s s.writeMps("cylptest.mps") os.remove("cylptest.mps") s.writeMps("cylptest.lp") os.remove("cylptest.lp")
def test_isInt(self): self.model = CyLPModel() model = self.model x = model.addVariable('x', 3, isInt=True) y = model.addVariable('y', 2) A = np.matrix([[1., 2., 0], [1., 0, 1.]]) B = np.matrix([[1., 0, 0], [0, 0, 1.]]) D = np.matrix([[1., 2.], [0, 1]]) a = CyLPArray([5, 2.5]) b = CyLPArray([4.2, 3]) x_u = CyLPArray([2., 3.5]) model.addConstraint(A * x <= a) model.addConstraint(2 <= B * x + D * y <= b) model.addConstraint(y >= 0) model.addConstraint(1.1 <= x[1:3] <= x_u) c = CyLPArray([1., -2., 3.]) model.objective = c * x + 2 * y.sum() self.s = CyClpSimplex(model) s = self.s cbcModel = s.getCbcModel() cbcModel.branchAndBound() sol_x = cbcModel.primalVariableSolution['x'] self.assertTrue((abs(sol_x - np.array([0, 2, 2])) <= 10**-6).all()) sol_y = cbcModel.primalVariableSolution['y'] self.assertTrue((abs(sol_y - np.array([0, 1])) <= 10**-6).all())
def setUp(self): 'Test remove constraint' model = CyLPModel() x = model.addVariable('x', 3) y = model.addVariable('y', 2) A = csc_matrixPlus(([1, 2, 1, 1], ([0, 0, 1, 1], [0, 1, 0, 2])), shape=(2, 3)) B = csc_matrixPlus(([1, 1], ([0, 1], [0, 2])), shape=(2, 3)) D = np.matrix([[1., 2.], [0, 1]]) a = CyLPArray([3, 2.5]) b = CyLPArray([4.2, 3]) x_u = CyLPArray([2., 3.5]) model.addConstraint(A * x <= a, 'res1') model.addConstraint(2 <= B * x + D * y <= b, 'res2') model.addConstraint(y >= 0) model.addConstraint(1.1 <= x[1:3] <= x_u) model.addConstraint(x[0] >= 0.1) c = CyLPArray([1., -2., 3.]) model.objective = c * x + 2 * y[0] + 2 * y[1] s = CyClpSimplex(model) self.s = s self.x = x self.y = y
def test_Sparse(self): model = CyLPModel() x = model.addVariable('x', 3) y = model.addVariable('y', 2) A = csc_matrixPlus(([1, 2, 1, 1], ([0, 0, 1, 1], [0, 1, 0, 2])), shape=(2, 3)) B = csc_matrixPlus(([1, 1], ([0, 1], [0, 2])), shape=(2, 3)) D = np.matrix([[1., 2.], [0, 1]]) a = CyLPArray([5, 2.5]) b = CyLPArray([4.2, 3]) x_u = CyLPArray([2., 3.5]) model.addConstraint(A * x <= a) model.addConstraint(2 <= B * x + D * y <= b) model.addConstraint(y >= 0) model.addConstraint(1.1 <= x[1:3] <= x_u) c = CyLPArray([1., -2., 3.]) model.objective = c * x + 2 * y[0] + 2 * y[1] s = CyClpSimplex(model) s.primal() sol = np.concatenate( (s.primalVariableSolution['x'], s.primalVariableSolution['y'])) self.assertTrue( (abs(sol - np.array([0.2, 2, 1.1, 0, 0.9])) <= 10**-6).all())
def f(x, z, y_pos, y_neg, x_0, z_0, x_b=1, z_b=1): dim_p = len(x) rhs_p = np.array([x_0, z_0, 1], dtype=np.double) dim_d = len(rhs_p) s_pos = CyClpSimplex() u_pos = s_pos.addVariable('u', dim_p) l_pos = s_pos.addVariable('l', dim_d) s_neg = CyClpSimplex() u_neg = s_neg.addVariable('u', dim_p) l_neg = s_neg.addVariable('l', dim_d) A_p_pos = np.vstack([y_pos, x, z, np.ones(dim_p)]) A_p_neg = np.vstack([y_neg, x, z, np.ones(dim_p)]) A_p_pos = np.matrix(A_p_pos) A_p_neg = np.matrix(A_p_neg) b_p = CyLPArray(np.hstack([0, rhs_p])) b_d_pos = CyLPArray(y_pos) b_d_neg = CyLPArray(y_neg) A_d = np.hstack( [x.reshape(-1, 1), z.reshape(-1, 1), np.ones(len(x)).reshape(-1, 1)]) A_d = np.matrix(A_d) # A_d1 = np.matrix(np.vstack([-rhs_p,np.zeros((3,3))])) t = np.array([x_b, z_b, 1], dtype=np.double) A_d1 = np.matrix(np.vstack([-t, np.zeros((3, 3))])) s_pos += A_p_pos * u_pos + A_d1 * l_pos == b_p s_neg += A_p_neg * u_neg + A_d1 * l_neg == b_p for i in range(dim_p): s_pos += u_pos[i] >= 0 s_neg += u_neg[i] >= 0 s_pos += A_d * l_pos <= b_d_pos s_neg += A_d * l_neg >= b_d_neg s_pos.objective = u_pos[0] s_neg.objective = u_neg[0] s_pos.primal() s_neg.primal() cond_pos = s_pos.primalVariableSolution['u'] yr_pos = np.dot(cond_pos, y_pos) cond_neg = s_neg.primalVariableSolution['u'] yr_neg = np.dot(cond_neg, y_neg) return yr_pos + yr_neg, x_0 * z_0, s_pos.getStatusString( ), s_neg.getStatusString( ), s_pos.primalVariableSolution['l'], s_neg.primalVariableSolution['l']
def main(): if len(sys.argv) != 2: print "Invalid number of arguments!\nUsage: \npython ", sys.argv[ 0], " <INPUT_FILE>\n" quit() infile = sys.argv[1] with open(infile) as f: m, n = [int(x) for x in next(f).split() ] # m = number of points in X, n = number of points in Y e = int(next(f)) # e = number of edges incidence_matrix = np.zeros((m, n)) edge_matrix = np.zeros((m + n, e)) weights = [] i = 1 for line in f: a, b, weight = [int(x) for x in line.split()] if a > m or b > n: print "Invalid vertex id. Aborting!" quit() incidence_matrix[a - 1][b - 1] = weight edge_matrix[a - 1][i - 1] = 1 edge_matrix[m + b - 1][i - 1] = 1 weights.append(weight) i += 1 print "Adjacency Matrix:" print incidence_matrix print "Edge Matrix:" print edge_matrix print "Weights:" print weights s = CyClpSimplex() # Add variables x = s.addVariable('x', e) # Create coefficients and bounds A = np.matrix( edge_matrix[0:m]) # vertices corresponding to the set X (Left) B = np.matrix( edge_matrix[m:]) # vertices corresponding to the set Y (Right) a = CyLPArray(np.ones(m)) b = CyLPArray(np.ones(n)) # Add constraints s += A * x <= a # all vertices in set X must be perfectly matched s += B * x <= b # matching should be valid s += 0 <= x <= 1 # Set the objective function s.objective = CyLPArray(np.array(weights) * -1) * x # Solve using primal Simplex s.primal() print s.primalVariableSolution['x'] for i in range(len(s.primalVariableSolution['x'])): if s.primalVariableSolution['x'][i] > 1e-3: print "Edge ", 1 + i, ": Weight ", weights[i]
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 _to_clp_constr(self, constraint): """ Converts a constraint to a CLP constraint """ if constraint.comparator == Comparator.GREATER_EQUALS: return self._to_clp_lin_expr(constraint.lhs) >= CyLPArray( constraint.rhs.array) elif constraint.comparator == Comparator.EQUALS: return self._to_clp_lin_expr(constraint.lhs) == CyLPArray( constraint.rhs.array) else: return self._to_clp_lin_expr(constraint.lhs) <= CyLPArray( constraint.rhs.array)
def test_SetInt_CopyIn(self): self.model = CyLPModel() model = self.model x = model.addVariable('x', 3) y = model.addVariable('y', 2) A = np.matrix([[1., 2., 0],[1., 0, 1.]]) B = np.matrix([[1., 0, 0], [0, 0, 1.]]) D = np.matrix([[1., 2.],[0, 1]]) a = CyLPArray([5, 2.5]) b = CyLPArray([4.2, 3]) x_u= CyLPArray([2., 3.5]) model.addConstraint(A*x <= a) model.addConstraint(2 <= B * x + D * y <= b) model.addConstraint(y >= 0) model.addConstraint(1.1 <= x[1:3] <= x_u) c = CyLPArray([1., -2., 3.]) model.objective = c * x + 2 * y.sum() self.s = CyClpSimplex(model) s = self.s s.setInteger(x[1:3]) cbcModel = s.getCbcModel() cbcModel.branchAndBound() sol_x = cbcModel.primalVariableSolution['x'] self.assertTrue((abs(sol_x - np.array([0.5, 2, 2]) ) <= 10**-6).all()) sol_y = cbcModel.primalVariableSolution['y'] self.assertTrue((abs(sol_y - np.array([0, 0.75]) ) <= 10**-6).all()) s.copyInIntegerInformation(np.array( [True, False, False, False, True], np.uint8)) cbcModel = s.getCbcModel() cbcModel.branchAndBound() sol_x = cbcModel.primalVariableSolution['x'] self.assertTrue((abs(sol_x - np.array([0, 2, 1.1]) ) <= 10**-6).all()) sol_y = cbcModel.primalVariableSolution['y'] self.assertTrue((abs(sol_y - np.array([0, 1]) ) <= 10**-6).all())
def test2(self): 'Same as test1, but use cylp indirectly.' s = CyClpSimplex() x = s.addVariable('x', 3) A = np.matrix([[1, 2, 3], [1, 1, 1]]) b = CyLPArray([5, 3]) s += A * x == b s += x >= 0 s.objective = 1 * x[0] + 1 * x[1] + 1.1 * x[2] # Solve it a first time s.primal() sol = s.primalVariableSolution['x'] self.assertTrue((abs(sol - np.array([1, 2, 0])) <= 10**-6).all()) # Add a cut s.addConstraint(x[0] >= 1.1) s.primal() sol = s.primalVariableSolution['x'] self.assertTrue((abs(sol - np.array([1.1, 1.8, 0.1])) <= 10**-6).all()) # Change the objective function c = csr_matrixPlus([[1, 10, 1.1]]).T s.objective = c.T * x s.primal() sol = s.primalVariableSolution['x'] self.assertTrue((abs(sol - np.array([2, 0, 1])) <= 10**-6).all())
def test(self): model = CyLPModel() x = model.addVariable('x', 3) A = np.matrix([[1, 2, 3], [1, 1, 1]]) b = CyLPArray([5, 3]) model.addConstraint(A * x == b) model.addConstraint(x >= 0) model.objective = 1 * x[0] + 1 * x[1] + 1.1 * x[2] # Solve it a first time s = CyClpSimplex(model) s.primal() sol = s.primalVariableSolution['x'] self.assertTrue((abs(sol - np.array([1, 2, 0])) <= 10**-6).all()) # Add a cut s.addConstraint(x[0] >= 1.1) s.primal() sol = s.primalVariableSolution['x'] self.assertTrue((abs(sol - np.array([1.1, 1.8, 0.1])) <= 10**-6).all()) # Change the objective function c = csr_matrixPlus([[1, 10, 1.1]]).T s.objective = c.T * x s.primal() sol = s.primalVariableSolution['x'] self.assertTrue((abs(sol - np.array([2, 0, 1])) <= 10**-6).all())
def _to_clp_lin_expr(self, lin_expr): """ Converts a HIPS linear expression to a CLP linear expression """ return sum( CyLPArray(lin_expr.coefficients[var].to_numpy()) * self.var_to_clp_var[var.id] for var in lin_expr.vars)
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 test_add_optimized_gomory_cuts(self): node = CuttingPlaneBoundNode(self.cut2_std.lp, self.cut2_std.integerIndices) # check function calls with patch.object(node, '_find_gomory_cuts') as fgb, \ patch.object(node, '_optimize_cut') as oc: fgb.return_value = [(CyLPArray([-3.3, -1.2]), -24.1), (CyLPArray([-1.2, -1.8]), -15.4)] oc.return_value = -50 node._add_optimized_gomory_cuts() self.assertTrue(fgb.called) self.assertTrue(oc.call_count == 2) self.assertTrue(len(node.lp.constraints) == 3) # check returns rtn = super(CuttingPlaneBoundNode, node).bound() self.assertTrue(isinstance(rtn, dict), 'should return dict')
def solve_cylp(self, verbose: bool = True, **kwargs): """ Inspired by the cvxpy cbc layer, ty :) """ from cylp.cy import CyClpSimplex from cylp.py.modeling.CyLPModel import CyLPModel, CyLPArray cons = self.combine_cons() n = int(self.next_var_idx - 1) # Number of variables. # Maximize c@x s.t. A@x <= b (variable bounds done by constraints) c = self.objective.rawdense().squeeze()[ 1:n + 1] # Objective coefficients, no constants. A = cons.raw()[:, 1:n + 1] # Constraint coefficients. b = cons[:, 0].rawdense().squeeze() * -1.0 # Constraint constants. model = CyLPModel() x = model.addVariable("x", n) # Variables model.objective = c # I hate this so much. Casting A to a matrix causes A.__mul__ to be # be called, which raises NotImplemented, so then x.__rmul__ is called # which gets the job done. Using np.matmul or np.dot doesn't # trigger x.__rmul__ # model.addConstraint(np.matrix(A) * x <= CyLPArray(b)) # Works model.addConstraint(x.__rmul__(A) <= CyLPArray(b)) model = CyClpSimplex(model) # Convert model model.logLevel = 0 if not verbose else model.logLevel is_integer_model = not np.isclose(self.int_vars.sum(), 0) if is_integer_model: model.setInteger(x[np.argwhere(self.int_vars)]) cbcModel = model.getCbcModel() cbcModel.solve() status = cbcModel.status solmodel = cbcModel else: status = model.initialSolve() solmodel = model sol_x = np.hstack( # Pad back to original shape ( [1.0], # Special constant 1.0 variable. solmodel.primalVariableSolution["x"], # Actual solution np.zeros(self.max_vars - n - 1), # Unused vars )) solution = { "status": status, "primal": sol_x, "value": solmodel.objectiveValue } return solution, solution["primal"]
def _find_split_inequality(self: G, idx: int, **kwargs: Any): assert idx in self._integer_indices, 'must lift and project on integer index' x_idx = self.solution[idx] assert self._is_fractional( x_idx), "must lift and project on index with fractional value" # build the CGLP model from ISE 418 Lecture 15 Slide 7 but for LP with >= constraints lp = CyClpSimplex() # declare variables pi = lp.addVariable('pi', self.lp.nVariables) pi0 = lp.addVariable('pi0', 1) u1 = lp.addVariable('u1', self.lp.nConstraints) u2 = lp.addVariable('u2', self.lp.nConstraints) w1 = lp.addVariable('w1', self.lp.nVariables) w2 = lp.addVariable('w2', self.lp.nVariables) # set bounds lp += u1 >= CyLPArray(np.zeros(self.lp.nConstraints)) lp += u2 >= CyLPArray(np.zeros(self.lp.nConstraints)) w_ub = CyLPArray(np.zeros(self.lp.nVariables)) w_ub[idx] = float('inf') lp += w_ub >= w1 >= CyLPArray(np.zeros(self.lp.nVariables)) lp += w_ub >= w2 >= CyLPArray(np.zeros(self.lp.nVariables)) # set constraints # (pi, pi0) must be valid for both parts of the disjunction lp += 0 >= -pi + self.lp.coefMatrix.T * u1 - w1 lp += 0 >= -pi + self.lp.coefMatrix.T * u2 + w2 lp += 0 <= -pi0 + CyLPArray( self.lp.constraintsLower) * u1 - floor(x_idx) * w1.sum() lp += 0 <= -pi0 + CyLPArray( self.lp.constraintsLower) * u2 + ceil(x_idx) * w2.sum() # normalize variables lp += u1.sum() + u2.sum() + w1.sum() + w2.sum() == 1 # set objective: find the deepest cut # since pi * x >= pi0 for all x in disjunction, we want min pi * x_star - pi0 lp.objective = CyLPArray(self.solution) * pi - pi0 # solve lp.primal(startFinishOptions='x') assert lp.getStatusCode() == 0, 'we should get optimal solution' assert lp.objectiveValue <= 0, 'pi * x >= pi -> pi * x - pi >= 0 -> ' \ 'negative objective at x^* since it gets cut off' # get solution return lp.primalVariableSolution['pi'], lp.primalVariableSolution[ 'pi0']
def generate_random_MILPInstance(numVars=40, numCons=20, density=0.2, maxObjCoeff=10, maxConsCoeff=10, tightness=2, rand_seed=2): cs, vs, objective, A, b = GenerateRandomMIP(numVars=numVars, numCons=numCons, density=density, maxObjCoeff=maxObjCoeff, maxConsCoeff=maxConsCoeff, tightness=tightness, rand_seed=rand_seed) A = np.asmatrix(pd.DataFrame.from_dict(A).to_numpy()) objective = CyLPArray(list(objective.values())) b = CyLPArray(b) l = CyLPArray([0] * len(vs)) u = CyLPArray([maxObjCoeff] * len(vs)) return MILPInstance(A=A, b=b, c=objective, l=l, u=u, sense=['Max', '<='], integerIndices=list(range(len(vs))), numVars=len(vs))
def create_problem(cobra_model, objective_sense="maximize", **kwargs): m = cobra_model.to_array_based_model() lp = Coin() v = lp.addVariable("v", len(m.reactions)) for i, rxn in enumerate(m.reactions): if rxn.variable_kind == "integer": lp.setInteger(v[i]) S = m.S v.lower = CyLPArray(m.lower_bounds) v.upper = CyLPArray(m.upper_bounds) inf = float("inf") cons = zip(m.b, m.constraint_sense) b_l = CyLPArray([-inf if s == "L" else b for b, s in cons]) b_u = CyLPArray([inf if s == "G" else b for b, s in cons]) lp.addConstraint(b_u >= S * v >= b_l, "b") lp.objectiveCoefficients = CyLPArray(m.objective_coefficients) set_parameter(lp, "objective_sense", objective_sense) set_parameter(lp, "tolerance_feasibility", 1e-9) lp.logLevel = 0 for key, value in kwargs.items(): set_parameter(lp, key, value) return lp
def plpd2d(xs, zs, ys, x_0, z_0): x, z, y = xs, zs, ys dim_p = len(x) rhs_p = np.array([x_0, z_0, 1], dtype=np.double) dim_d = len(rhs_p) s = CyClpSimplex() u = s.addVariable('u', dim_p) l = s.addVariable('l', dim_d) A_p = np.vstack([y, x, z, np.ones(dim_p)]) A_p = np.matrix(A_p) b_p = CyLPArray([0, x_0, z_0, 1]) A_d = np.hstack( [x.reshape(-1, 1), z.reshape(-1, 1), np.ones(len(x)).reshape(-1, 1)]) A_d = np.matrix(A_d) b_d = CyLPArray(y) A_d1 = np.matrix(np.vstack([-rhs_p, np.zeros((3, 3))])) s += A_p * u + A_d1 * l == b_p s += A_d * l <= b_d for i in range(dim_p): s += u[i] >= 0 s.optimizationDirection = 'max' s.objective = u[0] s.primal() cond = s.primalVariableSolution['u'] y_res = np.dot(cond, y) return y_res
def _find_gomory_cuts(self: G) -> List[Tuple[CyLPArray, float]]: """Find Gomory Mixed Integer Cuts (GMICs) for this node's solution. Defined in Lehigh University ISE 418 lecture 14 slide 18 and 5.31 in Conforti et al Integer Programming. Assumes Ax >= b and x >= 0. Warning: this method does not currently work. It creates some cuts that are invalid. Stuck on debugging because I can't spot the difference between this and what is referenced above. :return: cuts, a list of tuples (pi, pi0) that represent the cut pi*x >= pi0 """ cuts = [] for row_idx in self._row_indices: basic_idx = self.lp.basicVariables[row_idx] if basic_idx in self._integer_indices and \ self._is_fractional(self.solution[basic_idx]): f0 = self._get_fraction(self.solution[basic_idx]) # 0 for basic variables avoids getting small numbers that should be zero f = { i: 0 if i in self.lp.basicVariables else self._get_fraction(self.lp.tableau[row_idx, i]) for i in self._var_indices } a = { i: 0 if i in self.lp.basicVariables else self.lp.tableau[row_idx, i] for i in self._var_indices } # primary variable coefficients in GMI cut pi = CyLPArray([ f[j] / f0 if f[j] <= f0 and j in self._integer_indices else (1 - f[j]) / (1 - f0) if j in self._integer_indices else a[j] / f0 if a[j] > 0 else -a[j] / (1 - f0) for j in self._var_indices ]) # slack variable coefficients in GMI cut pi_slacks = np.array([ x / f0 if x > 0 else -x / (1 - f0) for x in self.lp.tableau[row_idx, self.lp.nVariables:] ]) # sub out slack variables for primary variables. Ax >= b =>'s # Ax - s = b => s = Ax - b. gomory is pi^T * x + pi_s^T * s >= 1, thus # pi^T * x + pi_s^T * (Ax - b) >= 1 => (pi + A^T * pi_s)^T * x >= 1 + pi_s^T * b pi += self.lp.coefMatrix.T * pi_slacks pi0 = 1 + np.dot(pi_slacks, self.lp.constraintsLower) # append pi >= pi0 cuts.append((pi, pi0)) return cuts
def test_multiDim(self): from cylp.cy import CyClpSimplex from cylp.py.modeling.CyLPModel import CyLPArray s = CyClpSimplex() x = s.addVariable('x', (5, 3, 6)) s += 2 * x[2, :, 3].sum() + 3 * x[0, 1, :].sum() >= 5 s += 0 <= x <= 1 c = CyLPArray(list(range(18))) s.objective = c * x[2, :, :] + c * x[0, :, :] s.primal() sol = s.primalVariableSolution['x'] self.assertTrue(abs(sol[0, 1, 0] - 1) <= 10**-6) self.assertTrue(abs(sol[2, 0, 3] - 1) <= 10**-6)
def test_init_fails_asserts(self): self.assertRaisesRegex(AssertionError, 'must have Ax >= b', CuttingPlaneBoundNode, cut1.lp, cut1.integerIndices) l = self.cut1_std.lp.variablesLower.copy() l[0] = -10 self.cut1_std.lp.variablesLower = l self.assertRaisesRegex(AssertionError, 'must have x >= 0 for all variables', CuttingPlaneBoundNode, self.cut1_std.lp, self.cut1_std.integerIndices) s = self.cut2_std.lp.addVariable('s', 1) self.cut2_std.lp += s >= CyLPArray([0]) self.assertRaisesRegex(AssertionError, 'x must be our only variable', CuttingPlaneBoundNode, self.cut2_std.lp, self.cut2_std.integerIndices)
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 test_ArrayIndexing(self): from cylp.cy import CyClpSimplex from cylp.py.modeling.CyLPModel import CyLPArray s = CyClpSimplex() x = s.addVariable('x', (5, 3, 6)) s += 2 * x[2, :, 3].sum() + 3 * x[0, 1, :].sum() >= 5 s += x[1, 2, [0, 3, 5]] - x[2, 1, np.array([1, 2, 4])] == 1 s += 0 <= x <= 1 c = CyLPArray(range(18)) s.objective = c * x[2, :, :] + c * x[0, :, :] s.primal() sol = s.primalVariableSolution['x'] self.assertTrue(abs(sol[1, 2, 0] - 1) <= 10**-6) self.assertTrue(abs(sol[1, 2, 3] - 1) <= 10**-6) self.assertTrue(abs(sol[1, 2, 5] - 1) <= 10**-6)
def test_onlyBounds2(self): s = CyClpSimplex() x = s.addVariable('x', 3) y = s.addVariable('y', 2) s += y >= 1 s += 2 <= x <= 4 c = CyLPArray([1., -2., 3.]) s.objective = c * x + 2 * y[0] + 2 * y[1] s.primal() sol = np.concatenate( (s.primalVariableSolution['x'], s.primalVariableSolution['y'])) self.assertTrue((abs(sol - np.array([2, 4, 2, 1, 1])) <= 10**-6).all())
def test_multiDim_Cbc_solve(self): from cylp.cy import CyClpSimplex from cylp.py.modeling.CyLPModel import CyLPArray s = CyClpSimplex() x = s.addVariable('x', (5, 3, 6)) s += 2 * x[2, :, 3].sum() + 3 * x[0, 1, :].sum() >= 5.5 s += 0 <= x <= 2.2 c = CyLPArray(list(range(18))) s.objective = c * x[2, :, :] + c * x[0, :, :] s.setInteger(x) cbcModel = s.getCbcModel() cbcModel.solve() sol_x = cbcModel.primalVariableSolution['x'] self.assertTrue(abs(sol_x[0, 1, 0] - 1) <= 10**-6) self.assertTrue(abs(sol_x[2, 0, 3] - 2) <= 10**-6)
def generate_random_value_functions(num_probs=40, num_evals=40): for prob_num in range(num_probs): num_vars = 4 num_constrs = 2 density = np.random.uniform(.2, .8) max_obj_coef = np.random.randint(10, 100) max_const_coef = np.random.randint(10, 100) tightness = 15 # get a random A and objective cs, vs, objective, A, b = GenerateRandomMIP( numVars=num_vars, numCons=num_constrs, density=density, maxObjCoeff=max_obj_coef, maxConsCoeff=max_const_coef, tightness=15) A = np.asmatrix(pd.DataFrame.from_dict(A).to_numpy()) objective = CyLPArray(list(objective.values())) l = CyLPArray([0] * len(vs)) u = CyLPArray([max_obj_coef] * len(vs)) # make a new directory to save all these instances fldr = f'example_value_functions/instance_{prob_num}' os.mkdir(fldr) for eval_num in range(num_evals): # make a random b for each evaluation of this instance b = CyLPArray([ np.random.randint( int(num_vars * density * max_const_coef / tightness), int(num_vars * density * max_const_coef / 1.5)) for i in range(num_constrs) ]) instance = MILPInstance(A=A, b=b, c=objective, l=l, u=u, sense=['Max', '<='], integerIndices=list(range(len(vs))), numVars=len(vs)) for i in instance.integerIndices: instance.lp.setInteger(i) file = f'evaluation_{eval_num}' instance.lp.writeMps(f'{os.path.join(fldr, file)}.mps')
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 CyLPModel, 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] data[s.BOOL_IDX] = solver_opts[s.BOOL_IDX] data[s.INT_IDX] = solver_opts[s.INT_IDX] n = c.shape[0] # Problem model = CyLPModel() # Variables x = model.addVariable('x', n) # Constraints # eq model += A[0:dims[s.EQ_DIM], :] * x == CyLPArray(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 <= CyLPArray(b[leq_start:leq_end]) # Objective model.objective = c # Convert model model = CyClpSimplex(model) # No boolean vars available in Cbc -> model as int + restrict to [0,1] if self.is_mip(data): # Mark integer- and binary-vars as "integer" model.setInteger(x[data[s.BOOL_IDX]]) model.setInteger(x[data[s.INT_IDX]]) # Restrict binary vars only idxs = data[s.BOOL_IDX] n_idxs = len(idxs) model.setColumnLowerSubset(np.arange(n_idxs, dtype=np.int32), np.array(idxs, np.int32), np.zeros(n_idxs)) model.setColumnUpperSubset(np.arange(n_idxs, dtype=np.int32), np.array(idxs, np.int32), np.ones(n_idxs)) # Verbosity Clp if not verbose: model.logLevel = 0 # Build model & solve status = None if self.is_mip(data): # Convert model cbcModel = model.getCbcModel() # Verbosity Cbc if not verbose: cbcModel.logLevel = 0 # cylp: /cylp/cy/CyCbcModel.pyx#L134 # Call CbcMain. Solve the problem using the same parameters used by # CbcSolver. Equivalent to solving the model from the command line # using cbc's binary. cbcModel.solve() status = cbcModel.status else: # cylp: /cylp/cy/CyClpSimplex.pyx # Run CLP's initialSolve. It does a presolve and uses primal or dual # Simplex to solve a problem. status = model.initialSolve() 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)