def generateCuts(self, si, cglTreeInfo): m = self.cyLPModel x = m.getVarByName('x') clpModel = si.clpModel clpModel.dual(startFinishOptions='x') solution = clpModel.primalVariableSolution bv = clpModel.basicVariables rhs = clpModel.rhs intInds = clpModel.integerInformation rhsIsInt = list(map(isInt, rhs)) cuts = [] for rowInd in range(s.nConstraints): basicVarInd = bv[rowInd] if basicVarInd < clpModel.nVariables and intInds[ basicVarInd] and not rhsIsInt[rowInd]: coef, b = gomoryCut(clpModel, rowInd) if b is not None: #print 'Adding cut: ', coef, '>=', b expr = CyLPArray(coef) * x >= b cuts.append(expr) return cuts
def generateQP(self): m = self.m n = self.n s = CyClpSimplex() iNonZero = set(random.randint(n, size=self.nnzPerCol)) iZero = [i for i in range(n) if i not in iNonZero] x_star = np.matrix(np.zeros((n, 1))) z_star = np.matrix(np.zeros((n, 1))) for i in iNonZero: x_star[i, 0] = 1 for i in iZero: z_star[i, 0] = 0 if random.randint(2) else random.random() G = getG(n) A = getA(m, n, self.nnzPerCol) y_star = np.matrix(random.random((m, 1))) c = -(G * x_star - A.T * y_star - z_star) obj = 0.5 * ((x_star.T * G) * x_star) + c.T * x_star print(obj) c = CyLPArray((c.T)[0]) b = CyLPArray(((A * x_star).T)[0]) b = np.squeeze(np.asarray(b)) x = s.addVariable('x', n) s += A * x == b s += x >= 0 c = CyLPArray(c) s.objective = c * x s.Hessian = G self.model = s return s
#print 'Adding cut: ', coef, '>=', b expr = CyLPArray(coef) * x >= b cuts.append(expr) return cuts if __name__ == '__main__': m = CyLPModel() firstExample = False if (firstExample): x = m.addVariable('x', 2, isInt=True) A = np.matrix([[7., -2.], [0., 1], [2., -2]]) b = CyLPArray([14, 3, 3]) m += A * x <= b m += x >= 0 c = CyLPArray([-4, 1]) m.objective = c * x s = CyClpSimplex(m) else: s = CyClpSimplex() #cylpDir = os.environ['CYLP_SOURCE_DIR'] inputFile = os.path.join('..', '..', 'input', 'p0033.mps') m = s.extractCyLPModel(inputFile) x = m.getVarByName('x') s.setInteger(x)
def solve(m, whichCuts = [], debug_print = False, epsilon = .01, max_iter = 100, max_cuts = 10, display = False): if not isinstance(m, MILPInstance): raise "Invalid first parameter: Must be of type MILPInstance" if not DISPLAY_ENABLED: display = False if m.lp.nCols > 2 or m.A is None: display = False m.lp.logLevel = 0 if display: disp_relaxation(m.A, m.b) for i in xrange(max_iter): print 'Iteration ', i m.lp.primal(startFinishOptions = 'x') print 'Current bound:', m.lp.objectiveValue #Binv = np.zeros(shape = (lp.nConstraints, lp.nConstraints)) #for i in range(lp.nVariables, lp.nVariables+lp.nConstraints): # lp.getBInvACol(i, Binv[i-lp.nVariables,:]) #rhs = lp.rhs if m.sense == '<=': rhs = np.dot(m.lp.basisInverse, m.lp.constraintsUpper) else: rhs = np.dot(m.lp.basisInverse, m.lp.constraintsLower) sol = m.lp.primalVariableSolution['x'] if debug_print: print 'Current basis inverse:' print m.lp.basisInverse print 'Condition number of basis inverse' print np.linalg.cond(m.lp.basisInverse) print "Current tableaux:" print m.lp.tableau print "Current right hand side:\n", rhs #print lp.rhs print 'Current solution: ', sol if isInt(sol[m.integerIndices], epsilon): print 'Integer solution found!' break cuts = [] disj = [] for (cg, args) in whichCuts: tmp_cuts, tmp_disj = cg(m.lp, m.integerIndices, m.sense, sol, **args) cuts += tmp_cuts disj += tmp_disj if cuts == []: print 'No cuts found!' break if display: disp_relaxation(m.A, m.b, cuts, sol, disj) for (coeff, r) in cuts[:max_cuts]: #TODO sort cuts by degree of violation if m.sense == '<=': print 'Adding cut: ', coeff, '<=', r m.lp += CyLPArray(coeff) * m.x <= r else: print 'Adding cut: ', coeff, '>=', r m.lp += CyLPArray(coeff) * m.x >= r if display: m.A.append(coeff.tolist()) m.b.append(r) if display: disp_relaxation(m.A, m.b)
def __init__(self, module_name=None, file_name=None, A=None, b=None, c=None, points=None, rays=None, sense=None, integerIndices=None, numVars=None): if file_name is not None: # We got a file name, so ignore everything else and read in the instance lp = CyClpSimplex() lp.extractCyLPModel(file_name) self.integerIndices = [ i for (i, j) in enumerate(lp.integerInformation) if j == True ] infinity = lp.getCoinInfinity() A = lp.coefMatrix b = CyLPArray([0 for _ in range(lp.nRows)]) for i in range(lp.nRows): if lp.constraintsLower[i] > -infinity: if lp.constraintsUpper[i] < infinity: raise Exception('Cannot handle ranged constraints') b[i] = -lp.constraintsLower[i] for j in range(lp.nCols): A[i, j] = -A[i, j] elif lp.constraintsUpper[i] < infinity: b[i] = lp.constraintsUpper[i] else: raise Exception('Constraint with no bounds detected') x = lp.addVariable('x', lp.nCols) lp += A * x <= b lp += x <= lp.variablesUpper lp += x >= lp.variablesLower lp.objective = lp.objective self.sense = '<=' numVars = lp.nCols else: min_or_max = None if module_name is not None: # We got a module name, read the data from there mip = ilib.import_module(module_name) self.A = mip.A if hasattr(mip, 'A') else None self.b = mip.b if hasattr(mip, 'b') else None points = mip.points if hasattr(mip, 'points') else None rays = mip.rays if hasattr(mip, 'rays') else None self.c = mip.c if hasattr(mip, 'c') else None self.sense = mip.sense[1] if hasattr(mip, 'sense') else None min_or_max = mip.sense[0] if hasattr(mip, 'sense') else None self.integerIndices = mip.integerIndices if hasattr( mip, 'integerIndices') else None x_u = CyLPArray(mip.x_u) if hasattr(mip, 'x_u') else None numVars = mip.numVars if hasattr(mip, 'numVars') else None self.x_sep = mip.x_sep if hasattr(mip, 'x_sep') else None if numVars is None and mip.A is not None: numVars = len(mip.A) if numVars is None: raise "Must specify number of variables when problem is not" else: self.A = A self.b = b self.c = c self.points = points self.rays = rays if sense is not None: self.sense = sense[1] min_or_max = sense[0] self.integerIndices = integerIndices x_u = None lp = CyClpSimplex() if self.A is not None: A = np.matrix(self.A) b = CyLPArray(self.b) elif numVars <= 2 and GRUMPY_INSTALLED: p = Polyhedron2D(points=points, rays=rays) A = np.matrix(p.hrep.A) b = np.matrix(p.hrep.b) else: raise "Must specify problem in inequality form with more than two variables\n" #Warning: At the moment, you must put bound constraints in explicitly for split cuts x_l = CyLPArray([0 for _ in range(numVars)]) x = lp.addVariable('x', numVars) lp += x >= x_l if x_u is not None: lp += x <= x_u lp += (A * x <= b if self.sense == '<=' else A * x >= b) c = CyLPArray(self.c) if min_or_max == 'Max': lp.objective = -c * x else: lp.objective = c * x self.lp = lp self.x = x
try: p = Polyhedron2D(A=LP.A, b=LP.b) except AttributeError: try: p = Polyhedron2D(points=LP.points, rays=LP.rays) except AttributeError: print 'Error: Must specify either A and b or points and rays' p = None if p is not None: if CYLP_INSTALLED: lp = CyClpSimplex() A = np.matrix(p.hrep.A) b = CyLPArray(p.hrep.b) print A print b if LP.numVars == 2: disp_polyhedron(A=A, b=b) x = lp.addVariable('x', LP.numVars) if LP.sense[0] == '>=': lp += A * x >= b else: lp += A * x <= b #lp += x >= 0
def solve(m, whichCuts=[], use_cglp=False, debug_print=False, eps=EPS, max_iter=100, max_cuts=10, display=False, filename=None): if not isinstance(m, MILPInstance): print("Invalid first parameter: Must be of type MILPInstance") exit if not DISPLAY_ENABLED: display = False else: f = Figure() if m.lp.nCols > 2 or m.A is None: display = False m.lp.logLevel = 0 #Include bounds explicitly in the constraint matrix for display and for #use in cut generators. infinity = m.lp.getCoinInfinity() if m.sense == '<=': b = m.lp.constraintsUpper.copy() mult = -1.0 else: b = m.lp.constraintsLower.copy() mult = 1.0 if type(m.A) == csc_matrixPlus: A = m.A.toarray() else: A = m.A.copy() for i in range(m.lp.nCols): e = np.zeros((1, m.lp.nCols)) if m.lp.variablesUpper[i] < infinity: b.resize(b.size + 1, refcheck=False) e[0, i] = -mult b[-1] = -mult * m.lp.variablesUpper[i] A = np.vstack((A, e)) if m.lp.variablesLower[i] > -infinity: b.resize(b.size + 1, refcheck=False) e[0, i] = mult b[-1] = mult * m.lp.variablesLower[i] A = np.vstack((A, e)) m.A = A m.b = b if display and filename is not None: disp_relaxation(f, m.A, m.b, filename=filename + '.png') elif display: disp_relaxation(f, m.A, m.b) disj = [] prev_sol = np.zeros((1, m.lp.nCols)) for i in range(max_iter): print('Iteration ', i) m.lp.primal(startFinishOptions='x') print('Current bound:', m.lp.objectiveValue) #Binv = np.zeros(shape = (lp.nConstraints, lp.nConstraints)) #for i in range(lp.nVariables, lp.nVariables+lp.nConstraints): # lp.getBInvACol(i, Binv[i-lp.nVariables,:]) #rhs = lp.rhs if m.sense == '<=': rhs = np.dot(m.lp.basisInverse, m.lp.constraintsUpper) else: rhs = np.dot(m.lp.basisInverse, m.lp.constraintsLower) sol = m.lp.primalVariableSolution['x'] if debug_print: print('Current basis inverse:') print(m.lp.basisInverse) print('Condition number of basis inverse', np.around(np.linalg.cond(m.lp.basisInverse))) print('Current tableaux:') print(m.lp.tableau) print('Current right hand side:\n', rhs) #print('Dual solution:', m.lp.dualConstraintSolution) #print lp.rhs print('Current solution: ', sol) if (sol - prev_sol).any(): prev_sol = sol else: print("Solution repeated, stalling detected") print("Exiting") break if isInt(sol[m.integerIndices], eps): print('Integer solution found!') break if np.around(np.linalg.cond(m.lp.basisInverse)) >= 10**32: print("Condition number of the basis matrix exceeds 10^32") print("Exiting") break cuts = [] if disj == []: for (cg, args) in whichCuts: tmp_cuts, tmp_disj = cg(m, **args, eps=eps) cuts += tmp_cuts disj += tmp_disj cur_num_cuts = len(cuts) if use_cglp: if len(disj) > 0: for d in disj: cuts += disjunctionToCut(m, d[0], d[1], eps=eps) if cuts == []: if disj == []: print('No cuts found and terminating!') break else: print('No cuts found but continuing!') if display and filename is not None: disp_relaxation(f, m.A, m.b, cuts, sol, disj, filename=filename + str(i) + '.png') elif display: disp_relaxation(f, m.A, m.b, cuts, sol, disj) if len(cuts) == cur_num_cuts: disj = [] for (coeff, r) in cuts[:max_cuts]: #TODO sort cuts by degree of violation if m.sense == '<=': coeff = np.floor(coeff * (10**eps)) / (10**eps) r = np.ceil(r * (10**eps)) / (10**eps) print('Adding cut: ', coeff, '<=', r) m.lp += CyLPArray(coeff) * m.x <= r else: coeff = np.ceil(coeff * (10**eps)) / (10**eps) r = np.floor(r * (10**eps)) / (10**eps) print('Adding cut: ', coeff, '>=', r) m.lp += CyLPArray(coeff) * m.x >= r m.A = np.vstack((m.A, np.array(coeff))) m.b.resize(m.b.size + 1, refcheck=False) m.b[-1] = r if display: disp_relaxation(f, m.A, m.b)
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 solve(m, whichCuts=[], use_cglp=False, debug_print=False, epsilon=.01, max_iter=100, max_cuts=10, display=False): if not isinstance(m, MILPInstance): print("Invalid first parameter: Must be of type MILPInstance") exit if not DISPLAY_ENABLED: display = False if m.lp.nCols > 2 or m.A is None: display = False m.lp.logLevel = 0 if display: disp_relaxation(m.A, m.b) disj = [] for i in range(max_iter): print('Iteration ', i) m.lp.primal(startFinishOptions='x') print('Current bound:', m.lp.objectiveValue) #Binv = np.zeros(shape = (lp.nConstraints, lp.nConstraints)) #for i in range(lp.nVariables, lp.nVariables+lp.nConstraints): # lp.getBInvACol(i, Binv[i-lp.nVariables,:]) #rhs = lp.rhs if m.sense == '<=': rhs = np.dot(m.lp.basisInverse, m.lp.constraintsUpper) else: rhs = np.dot(m.lp.basisInverse, m.lp.constraintsLower) sol = m.lp.primalVariableSolution['x'] if debug_print: print('Current basis inverse:') print(m.lp.basisInverse) print('Condition number of basis inverse') print(np.linalg.cond(m.lp.basisInverse)) print("Current tableaux:") print(m.lp.tableau) print("Current right hand side:\n", rhs) #print lp.rhs print('Current solution: ', sol) if isInt(sol[m.integerIndices], epsilon): print('Integer solution found!') break cuts = [] if disj == []: for (cg, args) in whichCuts: tmp_cuts, tmp_disj = cg(m.lp, m.integerIndices, m.sense, sol, **args) cuts += tmp_cuts disj += tmp_disj cur_num_cuts = len(cuts) if use_cglp and len(disj) > 0: for d in disj: cuts += disjunctionToCut(m.lp, d[0], d[1], sense=m.sense) if cuts == []: if disj == []: print('No cuts found and terminating!') break else: print('No cuts found but continuing!') if display: disp_relaxation(m.A, m.b, cuts, sol, disj) if len(cuts) == cur_num_cuts: disj = [] for (coeff, r) in cuts[:max_cuts]: #TODO sort cuts by degree of violation if m.sense == '<=': print('Adding cut: ', coeff, '<=', r) m.lp += CyLPArray(coeff) * m.x <= r else: print('Adding cut: ', coeff, '>=', r) m.lp += CyLPArray(coeff) * m.x >= r if display: m.A.append(coeff.tolist()) m.b.append(r) if display: disp_relaxation(m.A, m.b)
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 []