def get_pricing(m, w, L): # creating the pricing problem pricing = Model() # creating pricing variables a = [] for i in range(m): a.append( pricing.add_var(obj=0, var_type=INTEGER, name='a_%d' % (i + 1))) # creating pricing constraint pricing.add_constr(xsum(w[i] * a[i] for i in range(m)) <= L, 'bar_length') pricing.write('pricing.lp') return a, pricing
def kantorovich(): """ Simple implementation of the compact formulation from Kantorovich for the problem """ N = 10 # maximum number of bars L = 250 # bar length m = 4 # number of requests w = [187, 119, 74, 90] # size of each item b = [1, 2, 2, 1] # demand for each item # creating the model (note that the linear relaxation is solved) model = Model(SOLVER) x = {(i, j): model.add_var(obj=0, var_type=CONTINUOUS, name="x[%d,%d]" % (i, j)) for i in range(m) for j in range(N)} y = {j: model.add_var(obj=1, var_type=CONTINUOUS, name="y[%d]" % j) for j in range(N)} # constraints for i in range(m): model.add_constr(xsum(x[i, j] for j in range(N)) >= b[i]) for j in range(N): model.add_constr(xsum(w[i] * x[i, j] for i in range(m)) <= L * y[j]) # additional constraint to reduce symmetry for j in range(1, N): model.add_constr(y[j - 1] >= y[j]) # optimizing the model and printing solution model.optimize() print_solution(model)
def test_tsp_mipstart(solver: str): """tsp related tests""" announce_test("TSP - MIPStart", solver) N = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] n = len(N) i0 = N[0] A = { ('a', 'd'): 56, ('d', 'a'): 67, ('a', 'b'): 49, ('b', 'a'): 50, ('d', 'b'): 39, ('b', 'd'): 37, ('c', 'f'): 35, ('f', 'c'): 35, ('g', 'b'): 35, ('b', 'g'): 25, ('a', 'c'): 80, ('c', 'a'): 99, ('e', 'f'): 20, ('f', 'e'): 20, ('g', 'e'): 38, ('e', 'g'): 49, ('g', 'f'): 37, ('f', 'g'): 32, ('b', 'e'): 21, ('e', 'b'): 30, ('a', 'g'): 47, ('g', 'a'): 68, ('d', 'c'): 37, ('c', 'd'): 52, ('d', 'e'): 15, ('e', 'd'): 20 } # input and output arcs per node Aout = {n: [a for a in A if a[0] == n] for n in N} Ain = {n: [a for a in A if a[1] == n] for n in N} m = Model(solver_name=solver) m.verbose = 0 x = { a: m.add_var(name='x({},{})'.format(a[0], a[1]), var_type=BINARY) for a in A } m.objective = xsum(c * x[a] for a, c in A.items()) for i in N: m += xsum(x[a] for a in Aout[i]) == 1, 'out({})'.format(i) m += xsum(x[a] for a in Ain[i]) == 1, 'in({})'.format(i) # continuous variable to prevent subtours: each # city will have a different "identifier" in the planned route y = {i: m.add_var(name='y({})'.format(i), lb=0.0) for i in N} # subtour elimination for (i, j) in A: if i0 not in [i, j]: m.add_constr(y[i] - (n + 1) * x[(i, j)] >= y[j] - n) route = ['a', 'g', 'f', 'c', 'd', 'e', 'b', 'a'] m.start = [(x[route[i - 1], route[i]], 1.0) for i in range(1, len(route))] m.optimize() check_result("mip model status", m.status == OptimizationStatus.OPTIMAL) check_result("mip model objective", (abs(m.objective_value - 262)) <= 0.0001) print('')
def cg(): """ Simple column generation implementation for a Cutting Stock Problem """ L = 250 # bar length m = 4 # number of requests w = [187, 119, 74, 90] # size of each item b = [1, 2, 2, 1] # demand for each item # creating models and auxiliary lists master = Model() lambdas = [] constraints = [] # creating an initial pattern (which cut one item per bar) # to provide the restricted master problem with a feasible solution for i in range(m): lambdas.append(master.add_var(obj=1, name='lambda_%d' % (len(lambdas) + 1))) # creating constraints for i in range(m): constraints.append(master.add_constr(lambdas[i] >= b[i], name='i_%d' % (i + 1))) # creating the pricing problem pricing = Model(SOLVER) # creating pricing variables a = [] for i in range(m): a.append(pricing.add_var(obj=0, var_type=INTEGER, name='a_%d' % (i + 1))) # creating pricing constraint pricing.add_constr(xsum(w[i] * a[i] for i in range(m)) <= L, 'bar_length') pricing.write('pricing.lp') new_vars = True while new_vars: ########## # STEP 1: solving restricted master problem ########## master.optimize() # printing dual values print_solution(master) print('pi = ', end='') print([constraints[i].pi for i in range(m)]) print('') ########## # STEP 2: updating pricing objective with dual values from master ########## pricing.objective = 1 for i in range(m): a[i].obj = -constraints[i].pi # solving pricing problem pricing.optimize() # printing pricing solution z_val = pricing.objective_value print('Pricing:') print(' z = {z_val}'.format(**locals())) print(' a = ', end='') print([v.x for v in pricing.vars]) print('') ########## # STEP 3: adding the new columns ########## # checking if columns with negative reduced cost were produced and # adding them into the restricted master problem if 1 + pricing.objective_value < - EPS: coeffs = [a[i].x for i in range(m)] column = Column(constraints, coeffs) lambdas.append(master.add_var(obj=1, column=column, name='lambda_%d' % (len(lambdas) + 1))) print('new pattern = {coeffs}'.format(**locals())) # if no column with negative reduced cost was produced, then linear # relaxation of the restricted master problem is solved else: new_vars = False pricing.write('pricing.lp') print_solution(master)
class UnitCommitment: def __init__(self, generators, demand): self.generators = generators self.demand = demand self.period = range(1, len(self.demand) + 1) self.model = Model(name='UnitCommitment') self.p, self.u = {}, {} def build_model(self, fixed=False, u_fixed=None): for t in self.period: for g in self.generators.index: if fixed: self.u[t, g] = self.model.add_var(lb=u_fixed[t, g], ub=u_fixed[t, g]) else: self.u[t, g] = self.model.add_var(var_type=BINARY) self.p[t, g] = self.model.add_var() # Max/min power for t in self.period: for g in self.generators.index: self.model.add_constr( self.p[t, g] <= self.generators.loc[g, 'p_max'] * self.u[t, g], name=f'pmax_constr[{t},{g}]') self.model.add_constr( self.p[t, g] >= self.generators.loc[g, 'p_min'] * self.u[t, g], name=f'pmin_constr[{t},{g}]') # Power balance for t in self.period: self.model.add_constr(xsum( self.p[t, g] for g in self.generators.index) == self.demand.loc[t, 'demand'], name=f'power_bal_constr[{t}]') # Min on for t in self.period[1:]: for g in self.generators.index: min_on_time = min(t + self.generators.loc[g, 'min_on'] - 1, len(self.period)) for tau in range(t, min_on_time + 1): self.model.add_constr( self.u[tau, g] >= self.u[t, g] - self.u[t - 1, g], name=f'min_on_constr[{t},{g}]') # Min off for t in self.period[1:]: for g in self.generators.index: min_off_time = min(t + self.generators.loc[g, 'min_off'] - 1, len(self.period)) for tau in range(t, min_off_time + 1): self.model.add_constr( 1 - self.u[tau, g] >= self.u[t - 1, g] - self.u[t, g], name=f'min_off_constr[{t},{g}]') # Objective function self.model.objective = minimize( xsum( xsum(self.p[t, g] * self.generators.loc[g, 'c_var'] + self.u[t, g] * self.generators.loc[g, 'c_fix'] for g in self.generators.index) for t in self.period)) def optimize(self): self.model.optimize() return self.u, self.p, self.model.objective.x, self.model.status.name def get_prices(self): u_fixed = {(t, g): self.u[t, g].x for g in self.generators.index for t in self.period} self.model.clear() self.build_model(fixed=True, u_fixed=u_fixed) self.optimize() return [ self.model.constr_by_name(f'power_bal_constr[{t}]').pi for t in self.period ]