def getqconstrmultipliers(cplex, tol): qpi = dict() x = dict(zip(cplex.variables.get_names(), cplex.solution.get_values())) # Helper function to map a variable index to a variable name v2name = lambda x: cplex.variables.get_names(x) for q in cplex.quadratic_constraints.get_names(): # 'dense' is the dense slack vector dense = dict(zip(cplex.variables.get_names(), [0] * cplex.variables.get_num())) grad = dict(zip(cplex.variables.get_names(), [0] * cplex.variables.get_num())) qdslack = cplex.solution.get_quadratic_dualslack(q) for (var, val) in zip(map(v2name, qdslack.ind), qdslack.val): dense[var] = val # Compute value of derivative at optimal solution. # The derivative of a quadratic constraint x^TQx + a^Tx + b <= 0 # is Q^Tx + Qx + a. conetop = True quad = cplex.quadratic_constraints.get_quadratic_components(q) for (row, col, val) in zip(map(v2name, quad.ind1), map(v2name, quad.ind2), quad.val): if fabs(x[row]) > tol or fabs(x[col]) > tol: conetop = False grad[row] += val * x[col] grad[col] += val * x[row] l = cplex.quadratic_constraints.get_linear_components(q) for (var, val) in zip(map(v2name, l.ind), l.val): grad[var] += val if fabs(x[var]) > tol: conetop = False if conetop: raise Exception("Cannot compute dual multiplier at cone top!") # Compute qpi[q] as slack/gradient. # We may have several indices to choose from and use the one # with largest absolute value in the denominator. ok = False maxabs = -1.0 for v in cplex.variables.get_names(): if fabs(grad[v]) > tol: if fabs(grad[v]) > maxabs: qpi[q] = dense[v] / grad[v] maxabs = fabs(grad[v]) ok = True if not ok: qpi[q] = 0 return qpi
def getqconstrmultipliers(cplex, tol): qpi = dict() x = dict(zip(cplex.variables.get_names(), cplex.solution.get_values())) # Helper function to map a variable index to a variable name def v2name(x): return cplex.variables.get_names(x) for q in cplex.quadratic_constraints.get_names(): # 'dense' is the dense slack vector dense = dict( zip(cplex.variables.get_names(), [0] * cplex.variables.get_num())) grad = dict( zip(cplex.variables.get_names(), [0] * cplex.variables.get_num())) qdslack = cplex.solution.get_quadratic_dualslack(q) for (var, val) in zip(map(v2name, qdslack.ind), qdslack.val): dense[var] = val # Compute value of derivative at optimal solution. # The derivative of a quadratic constraint x^TQx + a^Tx + b <= 0 # is Q^Tx + Qx + a. conetop = True quad = cplex.quadratic_constraints.get_quadratic_components(q) for (row, col, val) in zip(map(v2name, quad.ind1), map(v2name, quad.ind2), quad.val): if fabs(x[row]) > tol or fabs(x[col]) > tol: conetop = False grad[row] += val * x[col] grad[col] += val * x[row] l = cplex.quadratic_constraints.get_linear_components(q) for (var, val) in zip(map(v2name, l.ind), l.val): grad[var] += val if fabs(x[var]) > tol: conetop = False if conetop: raise Exception("Cannot compute dual multiplier at cone top!") # Compute qpi[q] as slack/gradient. # We may have several indices to choose from and use the one # with largest absolute value in the denominator. ok = False maxabs = -1.0 for v in cplex.variables.get_names(): if fabs(grad[v]) > tol: if fabs(grad[v]) > maxabs: qpi[q] = dense[v] / grad[v] maxabs = fabs(grad[v]) ok = True if not ok: qpi[q] = 0 return qpi
def qcpdual(): # ###################################################################### # # # # S E T U P P R O B L E M # # # # ###################################################################### # c = cplex.Cplex() c.variables.add(obj=Data.obj, lb=Data.lb, ub=Data.ub, names=Data.cname) c.linear_constraints.add(lin_expr=Data.rows, senses=Data.sense, rhs=Data.rhs, names=Data.rname) for q in range(0, len(Data.qname)): c.quadratic_constraints.add(lin_expr=Data.qlin[q], quad_expr=Data.quad[q], sense=Data.qsense[q], rhs=Data.qrhs[q], name=Data.qname[q]) # ###################################################################### # # # # O P T I M I Z E P R O B L E M # # # # ###################################################################### # c.parameters.barrier.qcpconvergetol.set(1e-10) c.solve() if not c.solution.get_status() == c.solution.status.optimal: raise Exception("No optimal solution found") # ###################################################################### # # # # Q U E R Y S O L U T I O N # # # # ###################################################################### # # We store all results in a dictionary so that we can easily access # them by name. # Optimal solution and slacks for linear and quadratic constraints. x = dict(zip(c.variables.get_names(), c.solution.get_values())) slack = dict( zip(c.linear_constraints.get_names(), c.solution.get_linear_slacks())) qslack = dict( zip(c.quadratic_constraints.get_names(), c.solution.get_quadratic_slacks())) # Dual multipliers for constraints. cpi = dict(zip(c.variables.get_names(), c.solution.get_reduced_costs())) rpi = dict( zip(c.linear_constraints.get_names(), c.solution.get_dual_values())) qpi = getqconstrmultipliers(c, ZEROTOL) # Some CPLEX functions return results by index instead of by name. # Define a function that translates from index to name. v2name = lambda x: c.variables.get_names(x) # ###################################################################### # # # # C H E C K K K T C O N D I T I O N S # # # # Here we verify that the optimal solution computed by CPLEX (and # # the qpi[] values computed above) satisfy the KKT conditions. # # # # ###################################################################### # # Primal feasibility: This example is about duals so we skip this test. # # Dual feasibility: We must verify # - for <= constraints (linear or quadratic) the dual # multiplier is non-positive. # - for >= constraints (linear or quadratic) the dual # multiplier is non-negative. for r in c.linear_constraints.get_names(): if c.linear_constraints.get_senses(r) == 'E': pass elif c.linear_constraints.get_senses(r) == 'R': pass elif c.linear_constraints.get_senses(r) == 'L': if rpi[r] > ZEROTOL: raise Exception("Dual feasibility test failed") elif c.linear_constraints.get_senses(r) == 'G': if rpi[r] < -ZEROTOL: raise Exception("Dual feasibility test failed") for q in c.quadratic_constraints.get_names(): if c.quadratic_constraints.get_senses(q) == 'E': pass elif c.quadratic_constraints.get_senses(q) == 'L': if qpi[q] > ZEROTOL: raise Exception("Dual feasibility test failed") elif c.quadratic_constraints.get_senses(q) == 'G': if qpi[q] < -ZEROTOL: raise Exception("Dual feasibility test failed") # Complementary slackness. # For any constraint the product of primal slack and dual multiplier # must be 0. for r in c.linear_constraints.get_names(): if ((not c.linear_constraints.get_senses(r) == 'E') and fabs(slack[r] * rpi[r]) > ZEROTOL): raise Exception("Complementary slackness test failed") for q in c.quadratic_constraints.get_names(): if ((not c.quadratic_constraints.get_senses(q) == 'E') and fabs(qslack[q] * qpi[q]) > ZEROTOL): raise Exception("Complementary slackness test failed") for j in c.variables.get_names(): if c.variables.get_upper_bounds(j) < cplex.infinity: slk = c.variables.get_upper_bounds(j) - x[j] if cpi[j] < -ZEROTOL: dual = cpi[j] else: dual = 0.0 if fabs(slk * dual) > ZEROTOL: raise Exception("Complementary slackness test failed") if c.variables.get_lower_bounds(j) > -cplex.infinity: slk = x[j] - c.variables.get_lower_bounds(j) if cpi[j] > ZEROTOL: dual = cpi[j] else: dual = 0.0 if fabs(slk * dual) > ZEROTOL: raise Exception("Complementary slackness test failed") # Stationarity. # The difference between objective function and gradient at optimal # solution multiplied by dual multipliers must be 0, i.e., for the # optimal solution x # 0 == c # - sum(r in rows) r'(x)*rpi[r] # - sum(q in quads) q'(x)*qpi[q] # - sum(c in cols) b'(x)*cpi[c] # where r' and q' are the derivatives of a row or quadratic constraint, # x is the optimal solution and rpi[r] and qpi[q] are the dual # multipliers for row r and quadratic constraint q. # b' is the derivative of a bound constraint and cpi[c] the dual bound # multiplier for column c. # Objective function kktsum = dict(zip(c.variables.get_names(), c.objective.get_linear())) # Linear constraints. # The derivative of a linear constraint ax - b (<)= 0 is just a. for r in c.linear_constraints.get_names(): row = c.linear_constraints.get_rows(r) for (var, val) in zip(map(v2name, row.ind), row.val): kktsum[var] -= rpi[r] * val # Quadratic constraints. # The derivative of a constraint xQx + ax - b <= 0 is # Qx + Q'x + a. for q in c.quadratic_constraints.get_names(): lin = c.quadratic_constraints.get_linear_components(q) for (var, val) in zip(map(v2name, lin.ind), lin.val): kktsum[var] -= qpi[q] * val quad = c.quadratic_constraints.get_quadratic_components(q) for (row, col, val) in zip(map(v2name, quad.ind1), map(v2name, quad.ind2), quad.val): kktsum[row] -= qpi[q] * x[col] * val kktsum[col] -= qpi[q] * x[row] * val # Bounds. # The derivative for lower bounds is -1 and that for upper bounds # is 1. # CPLEX already returns dj with the appropriate sign so there is # no need to distinguish between different bound types here. for v in c.variables.get_names(): kktsum[v] -= cpi[v] for v in c.variables.get_names(): if fabs(kktsum[v]) > ZEROTOL: raise Exception("Stationarity test failed") # KKT conditions satisfied. Dump out solution and dual values. print("Optimal solution satisfies KKT conditions.") print(" x[] = [", end=' ') for n in c.variables.get_names(): print(" %7.3f" % x[n], end=' ') print(" ]") print("cpi[] = [", end=' ') for n in c.variables.get_names(): print(" %7.3f" % cpi[n], end=' ') print(" ]") print("rpi[] = [", end=' ') for n in c.linear_constraints.get_names(): print(" %7.3f" % rpi[n], end=' ') print(" ]") print("qpi[] = [", end=' ') for n in c.quadratic_constraints.get_names(): print(" %7.3f" % qpi[n], end=' ') print(" ]")
def Chebyshev_CG(w): ######### Master Problem ########### M = cplex.Cplex() # Parameters var = list(range(len(w))) alpha = 1.0 init_pi = sum(w) / 150 epsilon = 0.1 # decision varialbes types=["C"]*len(var) M.variables.add(obj=[1] * len(var), names=['x_' + str(i) for i in var]) M.variables.add(obj=[-init_pi], names='z') M.variables.add(names=['y_' + str(i) for i in list(range(len(w)))]) # pattern constraints vals = np.zeros((len(w), len(var))) np.fill_diagonal(vals, 1) M.linear_constraints.add(lin_expr=[ cplex.SparsePair(ind=['x_' + str(j) for j in var] + ['y_' + str(i)] + ['z'], val=list(vals[i]) + [-1.0] + [-1.0]) for i in range(len(w)) ], senses=["G" for i in w], rhs=[0 for i in w]) # chebyshev constraint M.linear_constraints.add(lin_expr=[ cplex.SparsePair( ind=['x_' + str(j) for j in var] + ['y_' + str(i) for i in range(len(w))] + ['z'], val=[1.0 for k in var] + [1.0 for l in w] + [alpha * 120**(1 / 2)]) ], senses=["G"], rhs=[1.0]) #list(np.linalg.norm(vals,1)) # set objective M.objective.set_sense(M.objective.sense.minimize) ######### Separation Problem ########### S = cplex.Cplex() S.variables.add(types=[S.variables.type.integer] * len(w), obj=[1] * len(w)) totalsize = SparsePair(ind=list(range(len(w))), val=w) S.linear_constraints.add(lin_expr=[totalsize], senses=["L"], rhs=[150]) S.objective.set_sense(S.objective.sense.maximize) ite = 0 while True: ite += 1 M.write('cheby_m.lp') M.set_log_stream(None) M.set_error_stream(None) M.set_warning_stream(None) M.set_results_stream(None) M.solve() price = [ pie for pie in M.solution.get_dual_values(list(range(len(w)))) ] S.objective.set_linear(list(zip(list(range(len(w))), price))) S.write('cheby_s.lp') S.set_log_stream(None) S.set_error_stream(None) S.set_warning_stream(None) S.set_results_stream(None) S.solve() if M.solution.get_objective_value( ) < epsilon * M.solution.get_values('z'): break if S.solution.get_objective_value() < 1 + 1.0e-6: newsub = S.solution.get_values() idx = M.variables.get_num() M.variables.add(obj=[1.0]) M.linear_constraints.set_coefficients( list(zip(list(range(len(w))), [idx] * len(var), newsub))) var.append(idx) else: new_pi = M.solution.get_dual_values() M.objective.set_linear('z', -sum(new_pi)) M.variables.set_types( list(zip(var, [M.variables.type.continuous] * len(var)))) M.solve() return ite, M, S
def General_CG(w): M = cplex.Cplex() var = list(range(len(w))) M.variables.add(obj=[1] * len(var)) M.linear_constraints.add(lin_expr=[SparsePair()] * len(w), senses=["G"] * len(w), rhs=[1] * len(w)) for i in range(len(w)): M.linear_constraints.set_coefficients(i, i, 1) M.objective.set_sense(M.objective.sense.minimize) S = cplex.Cplex() S.variables.add(types=[S.variables.type.integer] * len(w), obj=[1] * len(w)) totalsize = SparsePair(ind=list(range(len(w))), val=w) S.linear_constraints.add(lin_expr=[totalsize], senses=["L"], rhs=[150]) S.objective.set_sense(S.objective.sense.maximize) ite = 0 while True: ite += 1 M.write('m.lp') M.set_log_stream(None) M.set_error_stream(None) M.set_warning_stream(None) M.set_results_stream(None) M.solve() price = [ pie for pie in M.solution.get_dual_values(list(range(len(w)))) ] S.objective.set_linear(list(zip(list(range(len(w))), price))) # S.write('s.lp') S.set_log_stream(None) S.set_error_stream(None) S.set_warning_stream(None) S.set_results_stream(None) S.solve() if S.solution.get_objective_value() < 1 + 1.0e-6: break newsub = S.solution.get_values() idx = M.variables.get_num() M.variables.add(obj=[1.0]) M.linear_constraints.set_coefficients( list(zip(list(range(len(w))), [idx] * len(var), newsub))) var.append(idx) M.variables.set_types(list(zip(var, [M.variables.type.integer] * len(var)))) M.solve() return ite, M, S
def cutstock(datafile): # Input data. If no file is given on the command line then use a # default file name. The data read is # width - the width of the the roll, # size - the sie of each strip, # amount - the demand for each strip. width, size, amount = read_dat_file(datafile) # Setup cutting optimization (master) problem. # This is the problem to which columns will be added in the loop # below. cut = cplex.Cplex() cutcons = list(range(len(amount))) # constraint indices cutvars = list(range(len(size))) # variable indices cut.variables.add(obj=[1] * len(cutvars)) # Add constraints. They have empty left-hand side initially. The # left-hand side is filled in the next loop. cut.linear_constraints.add(lin_expr=[SparsePair()] * len(cutcons), senses=["G"] * len(cutcons), rhs=amount) for v in cutvars: cut.linear_constraints.set_coefficients(v, v, int(width / size[v])) # Setup pattern generation (worker) problem. # The constraints and variables in this problem always stay the same # but the objective function will change during the column generation # loop. pat = cplex.Cplex() use = list(range(len(size))) # variable indices pat.variables.add(types=[pat.variables.type.integer] * len(use)) # Add a constant 1 to the objective. pat.variables.add(obj=[1], lb=[1], ub=[1]) # Single constraint: total size must not exceed the width. totalsize = SparsePair(ind=use, val=size) pat.linear_constraints.add(lin_expr=[totalsize], senses=["L"], rhs=[width]) pat.objective.set_sense(pat.objective.sense.minimize) # Column generation procedure while True: # Optimize over current patterns cut.solve() report1(cut) # Find and add new pattern. The objective function of the # worker problem is constructed from the dual values of the # constraints of the master problem. price = [-d for d in cut.solution.get_dual_values(cutcons)] pat.objective.set_linear(list(zip(use, price))) pat.solve() report2(pat, use) # If reduced cost (worker problem objective function value) is # non-negative we are optimal. Otherwise we found a new column # to be added. Coefficients of the new column are given by the # optimal solution vector to the worker problem. if pat.solution.get_objective_value() > -RC_EPS: break newpat = pat.solution.get_values(use) # The new pattern constitutes a new variable in the cutting # optimization problem. Create that variable and add it to all # constraints with the coefficients read from the optimal solution # of the pattern generation problem. idx = cut.variables.get_num() cut.variables.add(obj=[1.0]) cut.linear_constraints.set_coefficients( list(zip(cutcons, [idx] * len(use), newpat))) cutvars.append(idx) # Perform a final solve on the cutting optimization problem. # Turn all variables into integers before doing that. cut.variables.set_types( list(zip(cutvars, [cut.variables.type.integer] * len(cutvars)))) cut.solve() report3(cut) print("Solution status = ", cut.solution.get_status())
rhs = [width]) pat.objective.set_sense(pat.objective.sense.minimize) # Column generation procedure while True: # Optimize over current patterns cut.solve() report1(cut) # Find and add new pattern. The objective function of the # worker problem is constructed from the dual values of the # constraints of the master problem. price = [-d for d in cut.solution.get_dual_values(cutcons)] pat.objective.set_linear(list(zip(use, price))) pat.solve() report2(pat, use) # If reduced cost (worker problem objective function value) is # non-negative we are optimal. Otherwise we found a new column # to be added. Coefficients of the new column are given by the # optimal solution vector to the worker problem. if pat.solution.get_objective_value() > -RC_EPS: break newpat = pat.solution.get_values(use) # The new pattern constitutes a new variable in the cutting # optimization problem. Create that variable and add it to all # constraints with the coefficients read from the optimal solution # of the pattern generation problem.
def Kelly(self): w = self.w I = range(len(w)) M = cplex.Cplex() var = list(range(len(w))) M.variables.add(obj=[1] * len(var), lb=[0] * len(var)) M.linear_constraints.add(lin_expr=[SparsePair()] * len(w), senses=["G"] * len(w), rhs=[1] * len(w)) for i in range(len(w)): M.linear_constraints.set_coefficients(i, i, 1) M.objective.set_sense(M.objective.sense.minimize) M.set_log_stream(None) M.set_error_stream(None) M.set_warning_stream(None) M.set_results_stream(None) start = time.time() mt = 0 st = 0 ite = 0 solutions = [] iterations = [] criteria = True while criteria: ite += 1 M.set_problem_type(M.problem_type.LP) ct = time.time() M.solve() solutions.append(float(M.solution.get_objective_value())) iterations.append( float(cplex._internal._procedural.getitcnt(M._env._e, M._lp))) mt += time.time() - ct pi = list(M.solution.get_dual_values())[:len(w)] dual = list(M.solution.get_dual_values()) v = pi W = self.W pt = time.time() # print(w) S_obj, sol = binpacking.KnapsackBnB(v, w, W) # print(S_obj, sol) st += time.time() - pt if S_obj - 0.000001 > 1.: criteria = True newsub = sol idx = M.variables.get_num() M.variables.add(obj=[1.0]) M.linear_constraints.set_coefficients( list(zip(list(range(len(w))), [idx] * len(var), newsub))) var.append(idx) else: criteria = False M.set_problem_type(M.problem_type.LP) ct = time.time() M.solve() # M.write('kelly.lp') solutions.append(float(M.solution.get_objective_value())) iterations.append( float(cplex._internal._procedural.getitcnt(M._env._e, M._lp))) mt += time.time() - ct tt = time.time() - start self.Kelly_M = M self.Kelly_result = [ 'Kelly', ite, mt, st, tt, mt / (st + mt), solutions, np.average(np.array(iterations)) ]
def Kelly_CG(self, M, tolerence, var): w = self.w mt = 0 st = 0 ite = 0 solutions = [] iterations = [] criteria = True while criteria: ite += 1 M.set_problem_type(M.problem_type.LP) ct = time.time() M.solve() solutions.append(float(M.solution.get_objective_value())) iterations.append( float(cplex._internal._procedural.getitcnt(M._env._e, M._lp))) mt += time.time() - ct pi = list(M.solution.get_dual_values())[:len(w)] dual = list(M.solution.get_dual_values()) v = pi W = self.W pt = time.time() # print(w) S_obj, sol = binpacking.KnapsackBnB(v, w, W) # print(S_obj, sol) st += time.time() - pt if S_obj - tolerence > 1.: criteria = True newsub = sol idx = M.variables.get_num() M.variables.add(obj=[1.0]) M.linear_constraints.set_coefficients( list(zip(list(range(len(w))), [idx] * len(var), newsub))) var.append(idx) else: criteria = False M.set_problem_type(M.problem_type.LP) ct = time.time() M.solve() # M.write('kelly.lp') solutions.append(float(M.solution.get_objective_value())) iterations.append( float(cplex._internal._procedural.getitcnt(M._env._e, M._lp))) mt += time.time() - ct # tt = time.time() - start self.Kelly_M = M Kelly_result = [ite, mt, st, solutions, iterations] return Kelly_result
def chevy(self): w = self.w ######### Master Problem ########### M = cplex.Cplex() # Parameters var = list(range(len(w))) alpha = 1.0 init_pi = sum(w) / 150 epsilon = 0.1 # decision varialbes types=["C"]*len(var) M.variables.add(obj=[1] * len(var), names=['x_' + str(i) for i in var], lb=[0] * len(var)) M.variables.add(obj=[-init_pi], names='z', lb=[0]) M.variables.add(names=['y_' + str(i) for i in list(range(len(w)))], lb=[0] * len(w)) # pattern constraints vals = np.zeros((len(w), len(var))) np.fill_diagonal(vals, 1) M.linear_constraints.add(lin_expr=[ cplex.SparsePair(ind=['x_' + str(j) for j in var] + ['y_' + str(i)] + ['z'], val=list(vals[i]) + [-1.0] + [-1.0]) for i in range(len(w)) ], senses=["G" for i in w], rhs=[0 for i in w]) # chebyshev constraint M.linear_constraints.add(lin_expr=[ cplex.SparsePair( ind=['x_' + str(j) for j in var] + ['y_' + str(i) for i in range(len(w))] + ['z'], val=[1.0 for k in var] + [1.0 for l in w] + [alpha * len(w)**(1 / 2)]) ], senses=["G"], rhs=[1.0]) M.objective.set_sense(M.objective.sense.minimize) M.write('cheby.lp') ite = 0 while True: ite += 1 # M.write('cheby_m.lp') M.set_log_stream(None) M.set_error_stream(None) M.set_warning_stream(None) M.set_results_stream(None) # M.write('cheby.lp') self.chevy_M = M M.solve() v = [ pie for pie in M.solution.get_dual_values(list(range(len(w)))) ] # S.objective.set_linear(list(zip(list(range(len(w))), price))) # # S.write('cheby_s.lp') # S.set_log_stream(None) # S.set_error_stream(None) # S.set_warning_stream(None) # S.set_results_stream(None) # S.solve() S_obj, sol = binpacking.KnapsackBnB(v, w, W) print(sol) if M.solution.get_objective_value( ) < epsilon * M.solution.get_values('z'): break if S_obj < 1 + 1.0e-6: newsub = sol idx = M.variables.get_num() M.variables.add(obj=[1.0]) M.linear_constraints.set_coefficients( list(zip(list(range(len(w))), [idx] * len(var), newsub))) var.append(idx) else: new_pi = M.solution.get_dual_values() M.objective.set_linear('z', -sum(new_pi)) M.variables.set_types( list(zip(var, [M.variables.type.continuous] * len(var)))) M.solve() self.chevy_M = M self.chevy_result = [ 'Separation %s' % (self.sep), ite, mt, st, tt, mt / (st + mt), solutions, np.average(np.array(iterations)) ]
def Separation(self): # sep = 2 w = self.w I = range(len(w)) Is = list(np.array_split(I, self.sep)) BigM = 100000 M = cplex.Cplex() var = list(range(len(w))) vals = np.zeros((len(w), len(var))) np.fill_diagonal(vals, 1) x_p = lambda p: 'x_%d' % (p) x = [x_p(p) for p in range(len(var))] M.variables.add(lb=[0] * len(x), ub=[cplex.infinity] * len(x), names=x, obj=[1.] * len(x), types=['C'] * len(x)) y_i = lambda i: 'y_%d' % (i) y = [y_i(i) for i in I] ys = [[y[i] for i in Is[j]] for j in range(self.sep)] M.variables.add( # lb=[0] * len(y), # ub=[cplex.infinity] * len(y), names=y, obj=[BigM] * len(y), types=['C'] * len(y)) M.linear_constraints.add(lin_expr=[ cplex.SparsePair(ind=x + [y[i]], val=list(vals[i]) + [1.0]) for i in I ], senses=["G" for i in w], rhs=[1. for i in w]) M.objective.set_sense(M.objective.sense.minimize) M.set_log_stream(None) M.set_error_stream(None) M.set_warning_stream(None) M.set_results_stream(None) start = time.time() mt = 0 st = 0 ite = 0 s2 = 0 solutions = [] iterations = [] penalty = 0.6 for twice in range(2): for sec in range(self.sep): print(ite) criteria = True y_fix = list(set(y) - set(list(ys[sec]))) I_fix = list(set(I) - set(list(Is[sec]))) # M.objective.set_linear(zip(y_fix, [BigM] * len(y_fix))) M.variables.set_upper_bounds(zip(y_fix, np.zeros(len(y_fix)))) M.variables.set_lower_bounds(zip(y_fix, [0] * len(y_fix))) while criteria: ite += 1 if ite % 500 == 0: penalty = penalty * 0.6 if penalty < 0.1: penalty = 100 # print(penalty) M.set_problem_type(M.problem_type.LP) ct = time.time() M.solve() solutions.append(float(M.solution.get_objective_value())) iterations.append( float( cplex._internal._procedural.getitcnt( M._env._e, M._lp))) mt += time.time() - ct dual = list(M.solution.get_dual_values()) pi = dual pi_ = [dual[i] * penalty for i in I_fix] v = pi W = self.W pt = time.time() ##################################################### # if ite >= 51 and ite<=162: # S_obj, sol =binpacking.Knapsack2(v,w,W) # s2 += time.time() - pt # # else: # aa = time.time() # S_obj, sol = binpacking.KnapsackBnB(v, w, W) # st += time.time() - pt ##################################################### S_obj, sol = binpacking.KnapsackBnB(v, w, W) if ite == 1000: print(v, w, W) print(time.time() - pt) st += time.time() - pt if S_obj - 0.00001 > 1.: criteria = True M.objective.set_linear( list( zip( list( map(lambda x: int(x + len(w)), Is[sec])), pi_))) # M.objective.set_linear(zip(y_fix, [BigM] * len(y_fix))) newsub = sol idx = M.variables.get_num() M.variables.add(obj=[1.0]) M.linear_constraints.set_coefficients( list( zip(list(range(len(w))), [idx] * len(var), newsub))) var.append(idx) # if ite >= 50: # print('2') else: criteria = False # M.write('1.lp') if M.solution.get_values(ys[sec]) == [0] * len(ys[sec]): print('dddd') break else: continue break M.set_problem_type(M.problem_type.LP) ct = time.time() M.solve() # M.write('sep.lp') solutions.append(float(M.solution.get_objective_value())) iterations.append( float(cplex._internal._procedural.getitcnt(M._env._e, M._lp))) print(s2) mt += time.time() - ct tt = time.time() - start self.Separation_M = M self.Separation_result = [ 'Separation %s' % (self.sep), ite, mt, st, tt, mt / (st + mt), solutions, np.average(np.array(iterations)) ]
def Stabilization(self): eps = 0.1 w = self.w I = range(len(w)) M = cplex.Cplex() var = list(range(len(w))) vals = np.zeros((len(w), len(var))) np.fill_diagonal(vals, 1) x_p = lambda p: 'x_%d' % (p) x = [x_p(p) for p in range(len(var))] M.variables.add(lb=[0] * len(x), ub=[cplex.infinity] * len(x), names=x, obj=[1.] * len(x), types=['C'] * len(x)) dp_i = lambda i: 'dp_%d' % (i) dp = [dp_i(i) for i in I] M.variables.add(lb=[0] * len(dp), ub=[eps] * len(dp), names=dp, obj=[0] * len(dp), types=['C'] * len(dp)) dm_i = lambda i: 'dm_%d' % (i) dm = [dm_i(i) for i in I] M.variables.add(lb=[0] * len(dm), ub=[eps] * len(dm), names=dm, obj=[0] * len(dm), types=['C'] * len(dm)) M.linear_constraints.add(lin_expr=[ cplex.SparsePair(ind=x + [dm[i]] + [dp[i]], val=list(vals[i]) + [-1.0] + [1.0]) for i in I ], senses=["G" for i in w], rhs=[1. for i in w]) # M.linear_constraints.add(lin_expr=[SparsePair()] * len(w), # senses=["G"] * len(w), # rhs=[1] * len(w)) # for i in range(len(w)): # M.linear_constraints.set_coefficients(i, i, 1) M.objective.set_sense(M.objective.sense.minimize) M.set_log_stream(None) M.set_error_stream(None) M.set_warning_stream(None) M.set_results_stream(None) start = time.time() mt = 0 st = 0 ite = 0 solutions = [] iterations = [] criteria = True while criteria: ite += 1 M.set_problem_type(M.problem_type.LP) ct = time.time() M.solve() solutions.append(float(M.solution.get_objective_value())) iterations.append( float(cplex._internal._procedural.getitcnt(M._env._e, M._lp))) mt += time.time() - ct pi = list(M.solution.get_dual_values())[:len(w)] dual = list(M.solution.get_dual_values()) v = pi W = self.W pt = time.time() # print(w) S_obj, sol = binpacking.KnapsackBnB(v, w, W) # print(S_obj, sol) st += time.time() - pt if S_obj - 0.000001 > 1. or eps != 0: criteria = True newsub = sol idx = M.variables.get_num() M.variables.add(obj=[1.0]) M.linear_constraints.set_coefficients( list(zip(list(range(len(w))), [idx] * len(var), newsub))) var.append(idx) if ite % 100 == 0: eps *= 0.1 if ite == 600: eps = 0 for dv in dm + dp: M.variables.set_upper_bounds(dv, eps) else: criteria = False M.set_problem_type(M.problem_type.LP) ct = time.time() M.solve() # M.write('kelly.lp') solutions.append(float(M.solution.get_objective_value())) iterations.append( float(cplex._internal._procedural.getitcnt(M._env._e, M._lp))) mt += time.time() - ct tt = time.time() - start self.Stab_M = M self.Stab_Result = [ 'Stabilization', ite, mt, st, tt, mt / (st + mt), solutions, np.average(np.array(iterations)) ]
def qcpdual(): # ###################################################################### # # # # S E T U P P R O B L E M # # # # ###################################################################### # c = cplex.Cplex() c.variables.add(obj=Data.obj, lb=Data.lb, ub=Data.ub, names=Data.cname) c.linear_constraints.add(lin_expr=Data.rows, senses=Data.sense, rhs=Data.rhs, names=Data.rname) for q in range(0, len(Data.qname)): c.quadratic_constraints.add(lin_expr=Data.qlin[q], quad_expr=Data.quad[q], sense=Data.qsense[q], rhs=Data.qrhs[q], name=Data.qname[q]) # ###################################################################### # # # # O P T I M I Z E P R O B L E M # # # # ###################################################################### # c.parameters.barrier.qcpconvergetol.set(1e-10) c.solve() if not c.solution.get_status() == c.solution.status.optimal: raise Exception("No optimal solution found") # ###################################################################### # # # # Q U E R Y S O L U T I O N # # # # ###################################################################### # # We store all results in a dictionary so that we can easily access # them by name. # Optimal solution and slacks for linear and quadratic constraints. x = dict(zip(c.variables.get_names(), c.solution.get_values())) slack = dict(zip(c.linear_constraints.get_names(), c.solution.get_linear_slacks())) qslack = dict(zip(c.quadratic_constraints.get_names(), c.solution.get_quadratic_slacks())) # Dual multipliers for constraints. cpi = dict(zip(c.variables.get_names(), c.solution.get_reduced_costs())) rpi = dict(zip(c.linear_constraints.get_names(), c.solution.get_dual_values())) qpi = getqconstrmultipliers(c, ZEROTOL) # Some CPLEX functions return results by index instead of by name. # Define a function that translates from index to name. v2name = lambda x: c.variables.get_names(x) # ###################################################################### # # # # C H E C K K K T C O N D I T I O N S # # # # Here we verify that the optimal solution computed by CPLEX (and # # the qpi[] values computed above) satisfy the KKT conditions. # # # # ###################################################################### # # Primal feasibility: This example is about duals so we skip this test. # # Dual feasibility: We must verify # - for <= constraints (linear or quadratic) the dual # multiplier is non-positive. # - for >= constraints (linear or quadratic) the dual # multiplier is non-negative. for r in c.linear_constraints.get_names(): if c.linear_constraints.get_senses(r) == 'E': pass elif c.linear_constraints.get_senses(r) == 'R': pass elif c.linear_constraints.get_senses(r) == 'L': if rpi[r] > ZEROTOL: raise Exception("Dual feasibility test failed") elif c.linear_constraints.get_senses(r) == 'G': if rpi[r] < -ZEROTOL: raise Exception("Dual feasibility test failed") for q in c.quadratic_constraints.get_names(): if c.quadratic_constraints.get_senses(q) == 'E': pass elif c.quadratic_constraints.get_senses(q) == 'L': if qpi[q] > ZEROTOL: raise Exception("Dual feasibility test failed") elif c.quadratic_constraints.get_senses(q) == 'G': if qpi[q] < -ZEROTOL: raise Exception("Dual feasibility test failed") # Complementary slackness. # For any constraint the product of primal slack and dual multiplier # must be 0. for r in c.linear_constraints.get_names(): if ((not c.linear_constraints.get_senses(r) == 'E') and fabs(slack[r] * rpi[r]) > ZEROTOL): raise Exception("Complementary slackness test failed") for q in c.quadratic_constraints.get_names(): if ((not c.quadratic_constraints.get_senses(q) == 'E') and fabs(qslack[q] * qpi[q]) > ZEROTOL): raise Exception("Complementary slackness test failed") for j in c.variables.get_names(): if c.variables.get_upper_bounds(j) < cplex.infinity: slk = c.variables.get_upper_bounds(j) - x[j] if cpi[j] < -ZEROTOL: dual = cpi[j] else: dual = 0.0 if fabs(slk * dual) > ZEROTOL: raise Exception("Complementary slackness test failed") if c.variables.get_lower_bounds(j) > -cplex.infinity: slk = x[j] - c.variables.get_lower_bounds(j) if cpi[j] > ZEROTOL: dual = cpi[j] else: dual = 0.0 if fabs(slk * dual) > ZEROTOL: raise Exception("Complementary slackness test failed") # Stationarity. # The difference between objective function and gradient at optimal # solution multiplied by dual multipliers must be 0, i.e., for the # optimal solution x # 0 == c # - sum(r in rows) r'(x)*rpi[r] # - sum(q in quads) q'(x)*qpi[q] # - sum(c in cols) b'(x)*cpi[c] # where r' and q' are the derivatives of a row or quadratic constraint, # x is the optimal solution and rpi[r] and qpi[q] are the dual # multipliers for row r and quadratic constraint q. # b' is the derivative of a bound constraint and cpi[c] the dual bound # multiplier for column c. # Objective function kktsum = dict(zip(c.variables.get_names(), c.objective.get_linear())) # Linear constraints. # The derivative of a linear constraint ax - b (<)= 0 is just a. for r in c.linear_constraints.get_names(): row = c.linear_constraints.get_rows(r) for (var, val) in zip(map(v2name, row.ind), row.val): kktsum[var] -= rpi[r] * val # Quadratic constraints. # The derivative of a constraint xQx + ax - b <= 0 is # Qx + Q'x + a. for q in c.quadratic_constraints.get_names(): lin = c.quadratic_constraints.get_linear_components(q) for (var, val) in zip(map(v2name, lin.ind), lin.val): kktsum[var] -= qpi[q] * val quad = c.quadratic_constraints.get_quadratic_components(q) for (row, col, val) in zip(map(v2name, quad.ind1), map(v2name, quad.ind2), quad.val): kktsum[row] -= qpi[q] * x[col] * val kktsum[col] -= qpi[q] * x[row] * val # Bounds. # The derivative for lower bounds is -1 and that for upper bounds # is 1. # CPLEX already returns dj with the appropriate sign so there is # no need to distinguish between different bound types here. for v in c.variables.get_names(): kktsum[v] -= cpi[v] for v in c.variables.get_names(): if fabs(kktsum[v]) > ZEROTOL: raise Exception("Stationarity test failed") # KKT conditions satisfied. Dump out solution and dual values. print("Optimal solution satisfies KKT conditions.") print(" x[] = [", end=' ') for n in c.variables.get_names(): print(" %7.3f" % x[n], end=' ') print(" ]") print("cpi[] = [", end=' ') for n in c.variables.get_names(): print(" %7.3f" % cpi[n], end=' ') print(" ]") print("rpi[] = [", end=' ') for n in c.linear_constraints.get_names(): print(" %7.3f" % rpi[n], end=' ') print(" ]") print("qpi[] = [", end=' ') for n in c.quadratic_constraints.get_names(): print(" %7.3f" % qpi[n], end=' ') print(" ]")
def cutstock(datafile): # Input data. If no file is given on the command line then use a # default file name. The data read is # width - the width of the the roll, # size - the sie of each strip, # amount - the demand for each strip. width, size, amount = read_dat_file(datafile) # Setup cutting optimization (master) problem. # This is the problem to which columns will be added in the loop # below. cut = cplex.Cplex() cutcons = list(range(len(amount))) # constraint indices cutvars = list(range(len(size))) # variable indices cut.variables.add(obj=[1] * len(cutvars)) # Add constraints. They have empty left-hand side initially. The # left-hand side is filled in the next loop. cut.linear_constraints.add(lin_expr=[SparsePair()] * len(cutcons), senses=["G"] * len(cutcons), rhs=amount) for v in cutvars: cut.linear_constraints.set_coefficients(v, v, int(width / size[v])) # Setup pattern generation (worker) problem. # The constraints and variables in this problem always stay the same # but the objective function will change during the column generation # loop. pat = cplex.Cplex() use = list(range(len(size))) # variable indices pat.variables.add(types=[pat.variables.type.integer] * len(use)) # Add a constant 1 to the objective. pat.variables.add(obj=[1], lb=[1], ub=[1]) # Single constraint: total size must not exceed the width. totalsize = SparsePair(ind=use, val=size) pat.linear_constraints.add(lin_expr=[totalsize], senses=["L"], rhs=[width]) pat.objective.set_sense(pat.objective.sense.minimize) # Column generation procedure while True: # Optimize over current patterns cut.solve() report1(cut) # Find and add new pattern. The objective function of the # worker problem is constructed from the dual values of the # constraints of the master problem. price = [-d for d in cut.solution.get_dual_values(cutcons)] pat.objective.set_linear(list(zip(use, price))) pat.solve() report2(pat, use) # If reduced cost (worker problem objective function value) is # non-negative we are optimal. Otherwise we found a new column # to be added. Coefficients of the new column are given by the # optimal solution vector to the worker problem. if pat.solution.get_objective_value() > -RC_EPS: break newpat = pat.solution.get_values(use) # The new pattern constitutes a new variable in the cutting # optimization problem. Create that variable and add it to all # constraints with the coefficients read from the optimal solution # of the pattern generation problem. idx = cut.variables.get_num() cut.variables.add(obj=[1.0]) cut.linear_constraints.set_coefficients(list(zip(cutcons, [idx] * len(use), newpat))) cutvars.append(idx) # Perform a final solve on the cutting optimization problem. # Turn all variables into integers before doing that. cut.variables.set_types( list(zip(cutvars, [cut.variables.type.integer] * len(cutvars)))) cut.solve() report3(cut) print("Solution status = ", cut.solution.get_status())