def solve_sat(expression): if len(expression) == 0: return [] # Trivial case. Otherwise count vars. numvars = max([max([abs(v) for v in clause]) for clause in expression]) lp = yaposib.Problem( yaposib.available_solvers()[0]) # Construct an empty linear program. for i in range(2 * numvars): col = lp.cols.add(yaposib.vec( [])) # As many columns as there are literals. col.lowerbound = 0.0 # Literal must be between false and true. col.upperbound = 1.0 def lit2col(lit): # Function to compute column index. return [2 * (-lit) - 1, 2 * lit - 2][lit > 0] for i in xrange(1, numvars + 1): # Ensure "oppositeness" of literals. row = lp.rows.add(yaposib.vec([(lit2col(i), 1.0), (lit2col(-i), 1.0)])) row.lowerbound = row.upperbound = 1.0 # Must sum to exactly 1. for clause in expression: # Ensure "trueness" of each clause. row = lp.rows.add(yaposib.vec([(lit2col(lit), 1.0) for lit in clause])) row.lowerbound = 1.0 # At least one literal must be true. lp.solve() # Try to solve the relaxed problem. if lp.status != 'optimal': return None # If no relaxed solution, no exact sol. for col in lp.cols: col.integer = True lp.solveMIP() # Try to solve this integer problem. if lp.status != 'optimal': return None return [lp.cols[i].solution > 0.99 for i in range(0, len(lp.cols), 2)]
def __init__( self, mip=True, msg=True, timeLimit=None, epgap=None, solverName=None, **solverParams ): """ Initializes the yaposib solver. @param mip: if False the solver will solve a MIP as an LP @param msg: displays information from the solver to stdout @param timeLimit: not supported @param epgap: not supported @param solverParams: not supported """ LpSolver.__init__(self, mip, msg) if solverName: self.solverName = solverName else: self.solverName = yaposib.available_solvers()[0]
def halfint_correlation_clustering(similarity): """Wacky MIP-based half-int constrained correlation clustering.""" lp = yaposib.Problem(yaposib.available_solvers()[0]) # Define the linear program. items = len(similarity) # Get the number of items. lp.obj.maximize = True # Set as maximization. for i in range((items*(items-1))/2): lp.cols.add(yaposib.vec([])) # Each item pair has a var. for j in xrange(items): for i in xrange(j): index = pair2index(i, j) # Get the index for this pair. lp.cols[index].lowerbound = 0 # Each variable in range 0 to 1. lp.cols[index].upperbound = 2 # Each variable in range 0 to 1. lp.cols[index].integer = True # This should be integral. lp.obj[index]=similarity[j][i]/2 # If 1, this much added to obj. for k in xrange(items): # For all triples of items, we for j in xrange(k): # want to add in constraints to jk = pair2index(j,k) # enforce respect for the for i in xrange(j): # triangle inequality. ij, ik = pair2index(i,j), pair2index(i,k) # We want Xij >= Xik + Xjk - 1. # That is, we want 1 >= Xik + Xjk - Xij. # Add constraints to enforce this! lp.rows.add(yaposib.vec([(ij, 1), (ik, 1), (jk,-1)])) lp.rows.add(yaposib.vec([(ij, 1), (ik,-1), (jk, 1)])) lp.rows.add(yaposib.vec([(ij,-1), (ik, 1), (jk, 1)])) for row in lp.rows: # For each row. row.upperbound = 2 # Each row is <= 1. lp.solveMIP() # Find the basic solution. labels = [[lp.cols[pair2index(i,j)].solution/2 for i in xrange(j)] for j in xrange(items)] return lp.obj.value, labels
def halfint_correlation_clustering(similarity): """Wacky MIP-based half-int constrained correlation clustering.""" lp = yaposib.Problem( yaposib.available_solvers()[0]) # Define the linear program. items = len(similarity) # Get the number of items. lp.obj.maximize = True # Set as maximization. for i in range((items * (items - 1)) / 2): lp.cols.add(yaposib.vec([])) # Each item pair has a var. for j in xrange(items): for i in xrange(j): index = pair2index(i, j) # Get the index for this pair. lp.cols[index].lowerbound = 0 # Each variable in range 0 to 1. lp.cols[index].upperbound = 2 # Each variable in range 0 to 1. lp.cols[index].integer = True # This should be integral. lp.obj[ index] = similarity[j][i] / 2 # If 1, this much added to obj. for k in xrange(items): # For all triples of items, we for j in xrange(k): # want to add in constraints to jk = pair2index(j, k) # enforce respect for the for i in xrange(j): # triangle inequality. ij, ik = pair2index(i, j), pair2index(i, k) # We want Xij >= Xik + Xjk - 1. # That is, we want 1 >= Xik + Xjk - Xij. # Add constraints to enforce this! lp.rows.add(yaposib.vec([(ij, 1), (ik, 1), (jk, -1)])) lp.rows.add(yaposib.vec([(ij, 1), (ik, -1), (jk, 1)])) lp.rows.add(yaposib.vec([(ij, -1), (ik, 1), (jk, 1)])) for row in lp.rows: # For each row. row.upperbound = 2 # Each row is <= 1. lp.solveMIP() # Find the basic solution. labels = [[lp.cols[pair2index(i, j)].solution / 2 for i in xrange(j)] for j in xrange(items)] return lp.obj.value, labels
def test_duals_and_slacks(self): for solver in yaposib.available_solvers(): prob = duals_and_slacks(solver) yaposibTestCheck(prob, ["optimal"], sol=[4, -1, 6], reducedcosts=[0, 12, 0], duals=[0, 1, 8], #slacks=[2, 0, 0] )
def hamiltonian(edges): node2colnums = {} # Maps node to col indices of incident edges. for colnum, edge in enumerate(edges): n1, n2 = edge node2colnums.setdefault(n1, []).append(colnum) node2colnums.setdefault(n2, []).append(colnum) print node2colnums lp = yaposib.Problem(yaposib.available_solvers()[0]) # A new empty linear program for i in range(len(edges)): col = lp.cols.add(yaposib.vec([])) # A struct var for each edge col.integer = True # Make integer, not continuous col.lowerbound = 0 # Make binary, not continuous col.upperbound = 1 # Make binary, not continuous # For each node, select at least 1 and at most 2 incident edges. for edge_column_nums in node2colnums.values(): row = lp.rows.add(yaposib.vec([(cn, 1.0) for cn in edge_column_nums])) row.lowerbound = 1 row.upperbound = 2 # We should select exactly (number of nodes - 1) edges total row = lp.rows.add(yaposib.vec([(cn, 1.0) for cn in range(len(lp.cols))])) row.lowerbound = row.upperbound = len(node2colnums)-1 lp.solve() if lp.status != 'optimal': return None # If no relaxed sol., no exact sol. # Return the edges whose associated struct var has value 1. return [edge for edge, col in zip(edges, lp.cols) if col.solution > 0.99]
def test_duals_and_slacks(self): for solver in yaposib.available_solvers(): prob = duals_and_slacks(solver) yaposibTestCheck( prob, ["optimal"], sol=[4, -1, 6], reducedcosts=[0, 12, 0], duals=[0, 1, 8], #slacks=[2, 0, 0] )
def maxflow(capgraph, s, t): node2rnum = {} # Map non-source/sink nodes to row num. for nfrom, nto, cap in capgraph: if nfrom != s and nfrom != t: node2rnum.setdefault(nfrom, len(node2rnum)) if nto != s and nto != t: node2rnum.setdefault(nto, len(node2rnum)) lp = yaposib.Problem(yaposib.available_solvers()[0]) # Empty LP instance. for i in range(len(capgraph)): lp.cols.add(yaposib.vec([])) # As many columns cap-graph edges. mat = [] # Will hold constraint matrix entries. for colnum, (nfrom, nto, cap) in enumerate(capgraph): lp.cols[colnum].lowerbound = 0 # Flow along edge bounded by capacity. lp.cols[ colnum].upperbound = cap # Flow along edge bounded by capacity. if nfrom == s: lp.obj[colnum] = 1.0 # Flow from source increases flow value elif nto == s: lp.obj[colnum] = -1.0 # Flow to source decreases flow value if nfrom in node2rnum: # Flow from node decreases its net flow mat.append((node2rnum[nfrom], colnum, -1.0)) if nto in node2rnum: # Flow to node increases its net flow mat.append((node2rnum[nto], colnum, 1.0)) lp.obj.maximize = True # Want source s max flow maximized. for row_nr in range(len(node2rnum)): to_add = [(j, coef) for i, j, coef in mat if i == row_nr] row = lp.rows.add(yaposib.vec(to_add)) row.lowerbound = row.upperbound = 0 # Net flow for non-source/sink is 0. lp.solve() # This should work unless capgraph bad. return [ (nfrom, nto, col.solution) # Return edges with assigned flow. for col, (nfrom, nto, cap) in zip(lp.cols, capgraph) ]
def test_HotStart(self): """ Tests the hotstart feature. 1) solve, get a hotstart 2) wipe and resolve, knowing a hotstart now exists 3) wipe and unmark the hotstart, resolve again """ for solver in yaposib.available_solvers(): prob = continuous(solver) prob.solve() yaposibTestCheck(prob, ["optimal"], sol = [4.0, -1.0, 6.0, 0.0]) wipe_solution(prob) prob.markHotStart() #time = -clock() prob.solve() #time += clock() yaposibTestCheck(prob, ["optimal"], sol = [4.0, -1.0, 6.0, 0.0]) wipe_solution(prob) prob.unmarkHotStart() prob.solve() yaposibTestCheck(prob, ["optimal"], sol = [4.0, -1.0, 6.0, 0.0])
def test_HotStart(self): """ Tests the hotstart feature. 1) solve, get a hotstart 2) wipe and resolve, knowing a hotstart now exists 3) wipe and unmark the hotstart, resolve again """ for solver in yaposib.available_solvers(): prob = continuous(solver) prob.solve() yaposibTestCheck(prob, ["optimal"], sol=[4.0, -1.0, 6.0, 0.0]) wipe_solution(prob) prob.markHotStart() #time = -clock() prob.solve() #time += clock() yaposibTestCheck(prob, ["optimal"], sol=[4.0, -1.0, 6.0, 0.0]) wipe_solution(prob) prob.unmarkHotStart() prob.solve() yaposibTestCheck(prob, ["optimal"], sol=[4.0, -1.0, 6.0, 0.0])
def test_writeLp(self): lpfile = ["\Problem name: ", "", "Minimize", "OBJROW: x + 4 y + 9 z", "Subject To", "c1: x + y <= 5", "c2: x + z >= 10", "c3: - y + z = 7", "c4: w >= 0", "Bounds", " 0 <= x <= 4", " -1 <= y <= 1", "End",] for solver in yaposib.available_solvers(): prob = continuous(solver) prob.writeLp("debug") with open("debug.lp", "r") as f: for line, ref in zip(f, lpfile): if line.strip() != ref.strip(): error_msg = "\t%s != %s" % (line.strip(), ref.strip()) raise yaposib.YaposibError(error_msg)
def maxflow(capgraph, s, t): node2rnum = {} # Map non-source/sink nodes to row num. for nfrom, nto, cap in capgraph: if nfrom!=s and nfrom!=t: node2rnum.setdefault(nfrom, len(node2rnum)) if nto!=s and nto!=t: node2rnum.setdefault(nto, len(node2rnum)) lp = yaposib.Problem(yaposib.available_solvers()[0]) # Empty LP instance. for i in range(len(capgraph)): lp.cols.add(yaposib.vec([])) # As many columns cap-graph edges. mat = [] # Will hold constraint matrix entries. for colnum, (nfrom, nto, cap) in enumerate(capgraph): lp.cols[colnum].lowerbound = 0 # Flow along edge bounded by capacity. lp.cols[colnum].upperbound = cap # Flow along edge bounded by capacity. if nfrom == s: lp.obj[colnum] = 1.0 # Flow from source increases flow value elif nto == s: lp.obj[colnum] = -1.0 # Flow to source decreases flow value if nfrom in node2rnum: # Flow from node decreases its net flow mat.append((node2rnum[nfrom], colnum, -1.0)) if nto in node2rnum: # Flow to node increases its net flow mat.append((node2rnum[nto], colnum, 1.0)) lp.obj.maximize = True # Want source s max flow maximized. for row_nr in range(len(node2rnum)): to_add = [(j, coef) for i, j, coef in mat if i == row_nr] row = lp.rows.add(yaposib.vec(to_add)) row.lowerbound = row.upperbound = 0 # Net flow for non-source/sink is 0. lp.solve() # This should work unless capgraph bad. return [(nfrom, nto, col.solution) # Return edges with assigned flow. for col, (nfrom, nto, cap) in zip(lp.cols, capgraph)]
def tsp(edges): node2colnums = {} # Maps node to col indices of incident edges. for colnum, edge in enumerate(edges): n1, n2, cost = edge node2colnums.setdefault(n1, []).append(colnum) node2colnums.setdefault(n2, []).append(colnum) lp = yaposib.Problem(yaposib.available_solvers()[0]) # A new empty linear program for i in range(len(edges)): col = lp.cols.add(yaposib.vec([])) # A struct var for each edge col.integer = True # Make binary, not continuous col.lowerbound = 0 # Either edge selected (1) or not (0) col.upperbound = 1 # Either edge selected (1) or not (0) lp.rows.add(len(node2colnums)+1) # Constraint for each node lp.obj[:] = [e[-1] for e in edges] # Try to minimize the total costs. lp.obj.maximize = False # For each node, select two edges, i.e.., an arrival and a departure. for edge_column_nums in node2colnums.values(): row = lp.rows.add(yaposib.vec([(cn, 1.0) for cn in edge_column_nums])) row.lowerbound = row.upperbound = 2 # We should select exactly (number of nodes) edges total row = lp.rows.add(yaposib.vec([(cn, 1.0) for cn in range(len(lp.cols))])) row.lowerbound = row.upperbound = len(node2colnums) lp.solve() if lp.status != 'optimal': return None # If no relaxed sol., no exact sol. lp.solveMIP() if lp.status != 'optimal': return None # Count not find integer solution! # Return the edges whose associated struct var has value 1. return [edge for edge, col in zip(edges, lp.cols) if col.value > 0.99]
def test_writeLp(self): lpfile = [ "\Problem name: ", "", "Minimize", "OBJROW: x + 4 y + 9 z", "Subject To", "c1: x + y <= 5", "c2: x + z >= 10", "c3: - y + z = 7", "c4: w >= 0", "Bounds", " 0 <= x <= 4", " -1 <= y <= 1", "End", ] for solver in yaposib.available_solvers(): prob = continuous(solver) prob.writeLp("debug") with open("debug.lp", "r") as f: for line, ref in zip(f, lpfile): if line.strip() != ref.strip(): error_msg = "\t%s != %s" % (line.strip(), ref.strip()) raise yaposib.YaposibError(error_msg)
def solve_sat(expression): if len(expression)==0: return [] # Trivial case. Otherwise count vars. numvars = max([max([abs(v) for v in clause]) for clause in expression]) lp = yaposib.Problem(yaposib.available_solvers()[0]) # Construct an empty linear program. for i in range(2*numvars): col = lp.cols.add(yaposib.vec([])) # As many columns as there are literals. col.lowerbound = 0.0 # Literal must be between false and true. col.upperbound = 1.0 def lit2col(lit): # Function to compute column index. return [2*(-lit)-1,2*lit-2][lit>0] for i in xrange(1, numvars+1): # Ensure "oppositeness" of literals. row = lp.rows.add(yaposib.vec([(lit2col(i), 1.0), (lit2col(-i), 1.0)])) row.lowerbound = row.upperbound = 1.0 # Must sum to exactly 1. for clause in expression: # Ensure "trueness" of each clause. row = lp.rows.add(yaposib.vec([(lit2col(lit), 1.0) for lit in clause])) row.lowerbound = 1.0 # At least one literal must be true. lp.solve() # Try to solve the relaxed problem. if lp.status!='optimal': return None # If no relaxed solution, no exact sol. for col in lp.cols: col.integer = True lp.solveMIP() # Try to solve this integer problem. if lp.status != 'optimal': return None return [lp.cols[i].solution > 0.99 for i in range(0, len(lp.cols), 2) ]
def test_isProvenOptimal(self): for solver in yaposib.available_solvers(): prob = continuous(solver) yaposibTestCheck(prob, ["optimal"], sol = [4.0, -1.0, 6.0, 0.0]) if (not prob.isProvenOptimal): raise yaposib.YaposibError
def test_continuous_maximisation(self): for solver in yaposib.available_solvers(): prob = continuous_maximisation(solver) yaposibTestCheck(prob, ["optimal"], sol=[4.0, 1.0, 8.0, 0.0])
def test_isProvenDualInfeasible(self): for solver in yaposib.available_solvers(): prob = unbounded(solver) yaposibTestCheck(prob, ["infeasible"]) if (not prob.isProvenDualInfeasible): raise yaposib.YaposibError
def test_mip(self): for solver in yaposib.available_solvers(): prob = mip(solver) yaposibTestCheck(prob, ["optimal"], [3.0, -0.5, 7.0], solve_as_MIP = True)
def test_feasability_only(self): for solver in yaposib.available_solvers(): prob = feasability_only(solver) yaposibTestCheck(prob, ["optimal"])
def test_isDualObjectiveLimitReached(self): for solver in yaposib.available_solvers(): prob = infeasible(solver) yaposibTestCheck(prob, ["infeasible", "limitreached"]) if (not prob.isDualObjectiveLimitReached): raise yaposib.YaposibError
def test_continuous_maximisation(self): for solver in yaposib.available_solvers(): prob = continuous_maximisation(solver) yaposibTestCheck(prob, ["optimal"], sol = [4.0, 1.0, 8.0, 0.0])
def test_relaxed_mip(self): for solver in yaposib.available_solvers(): prob = mip(solver) prob.obj.name = "relaxed_mip" yaposibTestCheck(prob, ["optimal"], [3.5, -1, 6.5])
def test_isProvenOptimal(self): for solver in yaposib.available_solvers(): prob = continuous(solver) yaposibTestCheck(prob, ["optimal"], sol=[4.0, -1.0, 6.0, 0.0]) if (not prob.isProvenOptimal): raise yaposib.YaposibError
def test_isProvenPrimalInfeasible(self): for solver in yaposib.available_solvers(): prob = infeasible(solver) yaposibTestCheck(prob, ["infeasible", "limitreached"]) if (not prob.isProvenPrimalInfeasible): raise yaposib.YaposibError
def test_available_solvers(self): self.failIfEqual(yaposib.available_solvers(), [])
""" builds the following problem 0 <= x <= 4 -1 <= y <= 1 0 <= z 0 <= w minimize obj = x + 4*y + 9*z such that: c1: x+y <= 5 c2: x+z >= 10 c3: -y+z == 7 c4: w >= 0 """ prob = yaposib.Problem(yaposib.available_solvers()[0]) prob.obj.name = "MyProblem" prob.obj.maximize = False # names for i in range(4): prob.cols.add(yaposib.vec([])) prob.cols[0].name = "x" prob.cols[1].name = "y" prob.cols[2].name = "z" prob.cols[3].name = "w" # lowerbounds for col in prob.cols: col.lowerbound = 0 prob.cols[1].lowerbound = -1 # upperbounds
def test_isAbandoned(self): for solver in yaposib.available_solvers(): prob = continuous(solver) if (not prob.isAbandoned): raise yaposib.YaposibError
def test_continuous(self): for solver in yaposib.available_solvers(): prob = continuous(solver) yaposibTestCheck(prob, ["optimal"], sol=[4.0, -1.0, 6.0, 0.0])
def test_continuous(self): for solver in yaposib.available_solvers(): prob = continuous(solver) yaposibTestCheck(prob, ["optimal"], sol = [4.0, -1.0, 6.0, 0.0])
def test_unbounded(self): for solver in yaposib.available_solvers(): prob = unbounded(solver) yaposibTestCheck(prob, ["infeasible"])
def test_mip(self): for solver in yaposib.available_solvers(): prob = mip(solver) yaposibTestCheck(prob, ["optimal"], [3.0, -0.5, 7.0], solve_as_MIP=True)
def test_integer_infeasible(self): for solver in yaposib.available_solvers(): prob = integer_infeasible(solver) yaposibTestCheck(prob, ["infeasible", "limitreached"], solve_as_MIP = True)
def test_integer_infeasible(self): for solver in yaposib.available_solvers(): prob = integer_infeasible(solver) yaposibTestCheck(prob, ["infeasible", "limitreached"], solve_as_MIP=True)