class LinearProgram: def __init__(self, hbw_task=None): if hbw_task is None: return self.model = Model(hbw_task.instance_name + '.lp') # LP variables self.variables = [] # was all_variables self.make_model_vars(hbw_task) self.model.update() # LP Constraints self.constraints = [] self.make_model_constraints(hbw_task) self.model.update() self.goal_constraints = set() #self.model.write( 'tmp.lp' ) def copy(self): from copy import deepcopy new_lp = LinearProgram() new_lp.model = self.model.copy() new_lp.constraints = [] new_lp.goal_constraints = set() for index, ext_constraint in enumerate(self.constraints): new_lp.constraints.append((deepcopy(ext_constraint[0]), new_lp.model.getConstrs()[index])) return new_lp def make_model_vars(self, hbw_task): # each cylinder contains a piston of negl. weight and cross # section area equal to the cylinder's. In general, there will # be a fluid column below it self.fluid_column_heights = [ ] # the k-th element of this list corresponds with d_k for x in xrange(hbw_task.objects.num_cylinders()): varname = 'd_{0}'.format(x) fch_i = self.model.addVar(vtype=GRB.CONTINUOUS, name=varname, lb=0.0, ub=hbw_task.objects.cylinders[x].height) self.fluid_column_heights.append(fch_i) self.variables += self.fluid_column_heights # modeling the partial wts of blocks on the piston as a list of # LP variables -- pw_i = partial wt of blocks in the range [0,i-1] that are in the cylinder # base case -- pw_0 = 0 self.partial_wts = {} # this contains vars w_i,k for i in xrange(hbw_task.objects.num_blocks()): for j in xrange(hbw_task.objects.num_cylinders()): varname = 'w_{0},{1}'.format(i, j) pw_ij = self.model.addVar(vtype=GRB.CONTINUOUS, name=varname, lb=0.0, ub=hbw_task.objects.blocks[i].weight) self.partial_wts[(i, j)] = pw_ij self.variables.append(pw_ij) # wt of columns (fluid + blocks) in each cylinder self.column_wts = [] for x in xrange(hbw_task.objects.num_cylinders()): varname = 'f_{0}'.format(x) cw_x = self.model.addVar(vtype=GRB.CONTINUOUS, name=varname) self.column_wts.append(cw_x) self.variables += self.column_wts def get_all_variables(self): return self.variables def get_all_constraints(self): return self.constraints def make_model_constraints(self, hbw_task): self.constraints = self.make_partial_wt_constraints(hbw_task) self.constraints += self.make_column_wt_constraints(hbw_task) self.constraints += self.make_pressure_balancing_constraints(hbw_task) self.constraints += self.make_fluid_balancing_constraint(hbw_task) def make_partial_wt_constraints(self, hbw_task): wt_constraints = [] for cylId in xrange(hbw_task.objects.num_cylinders()): wt_constraints += self.make_wt_constraints_for_cylinder( cylId, hbw_task) return wt_constraints def make_wt_constraints_for_cylinder(self, cylId, hbw_task): #hbw_obj, vars): from hbw3.planning import in_cylinder wt_constraints = [] cylinder = hbw_task.objects.cylinders[cylId] for i in xrange(0, hbw_task.objects.num_blocks()): var = hbw_task.block_container[i] var_valuation = [ (var.index, hbw_task.block_container_values[in_cylinder(cylId)]) ] constraint = (var_valuation, self.model.addConstr( self.partial_wts[(i, cylId)] - hbw_task.objects.blocks[i].weight == 0)) wt_constraints.append(constraint) for name, v in hbw_task.block_container_values.iteritems(): if name == in_cylinder(cylId): continue var_valuation = [(var.index, v)] constraint = (var_valuation, self.model.addConstr( self.partial_wts[(i, cylId)] == 0)) wt_constraints.append(constraint) return wt_constraints # this function adds equality constraints to calculate the total downward force # at the bottom of the cylinder (column_wt = total weight of all blocks + wt of fluid column) def make_column_wt_constraints_for_cylinder(self, cylId, hbw_task): density = hbw_task.objects.get_fluid_density() cross_section_area_of_cyl = hbw_task.objects.cylinders[cylId].area constr = ( [], # unconditional self.model.addConstr( self.column_wts[cylId] - density * cross_section_area_of_cyl * self.fluid_column_heights[cylId] - quicksum(self.partial_wts[(i, cylId)] for i in xrange(hbw_task.objects.num_blocks())) == 0)) return constr def make_column_wt_constraints(self, hbw_task): return [ self.make_column_wt_constraints_for_cylinder(cylId, hbw_task) for cylId in xrange(hbw_task.objects.num_cylinders()) ] def make_pressure_balancing_constraints(self, hbw_task): pb_constraints = [] # we have 'numCylinders -1' constraints - one for each consecutive pair for cylId in xrange(hbw_task.objects.num_cylinders() - 1): # constr for cylId & cylId + 1 reciprocal_area_1 = 1.0 / hbw_task.objects.cylinders[cylId].area reciprocal_area_2 = 1.0 / hbw_task.objects.cylinders[cylId + 1].area lpconstr = ( [], # unconditional self.model.addConstr( reciprocal_area_1 * self.column_wts[cylId] - reciprocal_area_2 * self.column_wts[cylId + 1] == 0)) pb_constraints.append(lpconstr) return pb_constraints def make_fluid_balancing_constraint(self, hbw_task): constr = ( [], # unconditional self.model.addConstr( quicksum(hbw_task.objects.cylinders[cylId].area * self.fluid_column_heights[cylId] for cylId in xrange(hbw_task.objects.num_cylinders())) == hbw_task.objects.fluid.total_fluid_in_cylinders)) return [constr] def print_constraints(self): for phi, gamma in self.constraints: if len(phi) == 0: print(' True -> %s' % gamma) else: head = ' & '.join(['%s = %s' % (x, v) for x, v in phi]) print(' %s -> %s' % (head, gamma))
class LinearProgram: def __init__(self, psr_task=None): if psr_task is None: return self.model = Model(psr_task.instance_name + '.lp') # LP variables self.variables = [] # was all_variables self.make_model_vars(psr_task) self.model.update() # LP Constraints self.constraints = [] self.make_model_constraints(psr_task) self.model.update() self.goal_constraints = set() def copy(self): from copy import deepcopy new_lp = LinearProgram() new_lp.variables = deepcopy(self.variables) new_lp.goal_constraints = deepcopy(self.goal_constraints) new_lp.model = self.model.copy() new_lp.constraints = [] for index, ext_constraint in enumerate(self.constraints): new_lp.constraints.append((deepcopy(ext_constraint[0]), new_lp.model.getConstrs()[index])) return new_lp def __make_power_vars(self, task): for cb in task.network.TECircuitbreakerLines: # it is always zero # NOTE: if it always zero, this should simplify some constraints - and this variable should # not be in the model cb.power = self.model.addVar(name='p_{0}'.format(cb.name), lb=0.0, ub=0.0, vtype=GRB.CONTINUOUS) for r_line in task.network.TERDeviceLines: r_line.power = self.model.addVar(name='p_{0}'.format(r_line.name), lb=-r_line.capacity, ub=r_line.capacity, vtype=GRB.CONTINUOUS) for m_line in task.network.TEMDeviceLines: m_line.power = self.model.addVar(name='p_{0}'.format(m_line.name), lb=-m_line.capacity, ub=m_line.capacity, vtype=GRB.CONTINUOUS) for bus in task.network.TEBuses: lb_fed = 1.0 if ((bus.generation_max > 0) and not bus.fault) else 0.0 bus.fed = self.model.addVar(name='b_{0}_fed'.format(bus.name), lb=lb_fed, ub=1.0, vtype=GRB.CONTINUOUS) bus.generation = self.model.addVar(name='b_{0}_gen'.format( bus.name), lb=0.0, ub=bus.generation_max, vtype=GRB.CONTINUOUS) # theta is an angle, so it should be bounded by a full circle # assuming unit is degrees, that is, between -180 and +180 bus.theta = self.model.addVar(name='b_{0}_theta'.format(bus.name), lb=-180.0, ub=180.0, vtype=GRB.CONTINUOUS) #self.model.setObjective(quicksum([bus.generation for bus in task.network.TEBuses]), GRB.MAXIMIZE) def make_model_vars(self, task): self.__make_power_vars(task) def __make_kirchoff_constraints(self, task): for bus in task.network.TEBuses: c = self.model.addConstr( bus.generation + quicksum([l.power for l in bus.connections_in]), GRB.EQUAL, bus.load_max * bus.fed + quicksum([l.power for l in bus.connections_out])) #c = self.model.addConstr( bus.generation + quicksum( [ l.power for l in bus.connections_in ] ) # - bus.load_max * bus.fed + quicksum( [ -l.power for l in bus.connections_out ] ) # == 0.0 ) self.constraints.append(([], c)) # Corresponds with TEcreate_default_goal def make_bus_feeding_constraints(self, task): bus_feeding_constraints = [] for bus in task.network.TEBuses: if bus.fault: c = self.model.addConstr(bus.fed, GRB.EQUAL, 0.0) self.constraints.append(([], c)) bus_feeding_constraints.append(len(self.constraints) - 1) else: if bus.generation_max > 0.0: # is a generator c = self.model.addConstr(bus.fed, GRB.EQUAL, 1.0) self.constraints.append(([], c)) bus_feeding_constraints.append(len(self.constraints) - 1) self.model.update() print('Constraints in goal: {0}'.format(len(bus_feeding_constraints))) self.goal_constraints = set(bus_feeding_constraints) return self.goal_constraints # Corresponds with TEpowerlines_create_goal def make_bus_feeding_constraints_2(self, task): bus_feeding_constraints = [] for bus in task.network.TEBuses: if bus.fault: c = self.model.addConstr(bus.fed, GRB.EQUAL, 0.0) self.constraints.append(([], c)) #bus_feeding_constraints.append( len(self.constraints)-1 ) continue if not bus.feed: continue c = self.model.addConstr(bus.fed, GRB.EQUAL, 1.0) self.constraints.append(([], c)) bus_feeding_constraints.append(len(self.constraints) - 1) self.model.update() print('Constraints in goal: {0}'.format(len(bus_feeding_constraints))) #print( 'Constraints in self {0}, constraints in Gurobi obj {1}'.format( len(self.constraints), len(self.model.getConstrs()) ) ) self.goal_constraints = set(bus_feeding_constraints) return self.goal_constraints def __make_line_constraints(self, task): for line in task.network.TERDeviceLines + task.network.TEMDeviceLines: c_dc_flow_model_closed = self.model.addConstr( line.power, GRB.EQUAL, -line.susceptance * (line.connections[0].theta - line.connections[1].theta)) self.constraints.append( ([(line.closed.index, True)], c_dc_flow_model_closed)) c_dc_flow_model_open = self.model.addConstr( line.power, GRB.EQUAL, 0.0) self.constraints.append( ([(line.closed.index, False)], c_dc_flow_model_open)) c_bus_feeding_state = self.model.addConstr(line.connections[0].fed, GRB.EQUAL, line.connections[1].fed) #c_bus_feeding_state = self.model.addConstr( line.connections[0].fed - line.connections[1].fed == 0.0 ) self.constraints.append( ([(line.closed.index, True)], c_bus_feeding_state)) def __make_circuit_breaker_constraints(self, task): for breaker in task.network.TECircuitbreakerLines: c_if_closed_always_fed = self.model.addConstr( breaker.connections[0].fed, GRB.EQUAL, 1.0) self.constraints.append( ([(breaker.closed.index, True)], c_if_closed_always_fed)) c_if_open_not_fed = self.model.addConstr( breaker.connections[0].fed, GRB.EQUAL, 0.0) self.constraints.append( ([(breaker.closed.index, False)], c_if_open_not_fed)) # March 2015: This follows from Sylvie's IJCAI-13 paper on PSR # see constraints on circuit breakers if_open_kill_gen_in_connected_bus = self.model.addConstr( breaker.connections[0].generation, GRB.EQUAL, 0.0) self.constraints.append(([(breaker.closed.index, False)], if_open_kill_gen_in_connected_bus)) def make_model_constraints(self, task): self.__make_kirchoff_constraints(task) self.__make_line_constraints(task) self.__make_circuit_breaker_constraints(task) #self.make_bus_feeding_constraints( task ) def print_constraints(self): for phi, gamma in self.constraints: if len(phi) == 0: print(' True -> %s' % gamma) else: head = ' & '.join(['%s = %s' % (x, v) for x, v in phi]) print(' %s -> %s' % (head, gamma))
def populate_dual_subproblem(data, upper_cost=None, flow_cost=None): """ Function that populates the Benders Dual Subproblem, as suggested by the paper "Minimal Infeasible Subsystems and Bender's cuts" by Fischetti, Salvagnin and Zanette. :param data: Problem data structure :param upper_cost: Link setup decisions fixed in the master :param flow_cost: This is the cost of the continuous variables of the master problem, as explained in the paper :return: Numpy array of Gurobi model objects """ # Gurobi model objects subproblems = np.empty(shape=(data.periods, data.commodities), dtype=object) # Construct model for period/commodity 0. # Then, copy this and change the coefficients dual_subproblem = Model('dual_subproblem_(0,0)') # Ranges we are going to need arcs, periods, commodities = xrange(data.arcs.size), xrange( data.periods), xrange(data.commodities) # Origins and destinations of commodities origins, destinations = data.origins, data.destinations # We use arrays to store variable indexes and variable objects. Why use # both? Gurobi wont let us get the values of individual variables # within a callback.. We just get the values of a large array of # variables, in the order they were initially defined. To separate them # in variable categories, we will have to use index arrays flow_index = np.zeros(shape=data.nodes, dtype=int) flow_duals = np.empty_like(flow_index, dtype=object) ubounds_index = np.zeros(shape=len(arcs), dtype=int) ubounds_duals = np.empty_like(ubounds_index, dtype=object) # Makes sure we don't add variables more than once flow_duals_names = set() if upper_cost is None: upper_cost = np.zeros(shape=(len(periods), len(arcs)), dtype=float) if flow_cost is None: flow_cost = np.zeros(shape=(len(periods), len(commodities)), dtype=float) # Populate all variables in one loop, keep track of their indexes # Data for period = 0, com = 0 count = 0 for arc in arcs: ubounds_duals[arc] = dual_subproblem.addVar( obj=-upper_cost[0, arc], lb=0., name='ubound_dual_a{}'.format(arc)) ubounds_index[arc] = count count += 1 start_node, end_node = get_2d_index(data.arcs[arc], data.nodes) start_node, end_node = start_node - 1, end_node - 1 for node in (start_node, end_node): var_name = 'flow_dual_n{}'.format(node) if var_name not in flow_duals_names: flow_duals_names.add(var_name) obj, ub = 0., GRB.INFINITY if data.origins[0] == node: obj = 1. if data.destinations[0] == node: obj = -1. ub = 0. flow_duals[node] = \ dual_subproblem.addVar( obj=obj, lb=0., name=var_name) flow_index[node] = count count += 1 opt_var = dual_subproblem.addVar(obj=-flow_cost[0, 0], lb=0., name='optimality_var') dual_subproblem.params.threads = 2 dual_subproblem.params.LogFile = "" dual_subproblem.update() # Add constraints demand = data.demand[0, 0] for arc in arcs: start_node, end_node = get_2d_index(data.arcs[arc], data.nodes) start_node, end_node = start_node - 1, end_node - 1 lhs = flow_duals[start_node] - flow_duals[end_node] \ - ubounds_duals[arc] - \ opt_var * data.variable_cost[arc] * demand dual_subproblem.addConstr(lhs <= 0., name='flow_a{}'.format(arc)) # Original Fischetti model lhs = quicksum(ubounds_duals) + opt_var dual_subproblem.addConstr(lhs == 1, name='normalization_constraint') # Store variable indices dual_subproblem._ubounds_index = ubounds_index dual_subproblem._flow_index = flow_index dual_subproblem._all_variables = np.array(dual_subproblem.getVars()) dual_subproblem._flow_duals = np.take(dual_subproblem._all_variables, flow_index) dual_subproblem._ubound_duals = np.take(dual_subproblem._all_variables, ubounds_index) dual_subproblem.setParam('OutputFlag', 0) dual_subproblem.modelSense = GRB.MAXIMIZE dual_subproblem.update() subproblems[0, 0] = dual_subproblem for period, com in product(periods, commodities): if (period, com) != (0, 0): model = dual_subproblem.copy() optimality_var = model.getVarByName('optimality_var') optimality_var.Obj = -flow_cost[period, com] demand = data.demand[period, com] for node in xrange(data.nodes): variable = model.getVarByName('flow_dual_n{}'.format(node)) if origins[com] == node: obj = 1. elif destinations[com] == node: obj = -1. else: obj = 0. variable.obj = obj for arc in arcs: variable = model.getVarByName('ubound_dual_a{}'.format(arc)) variable.Obj = -np.sum(upper_cost[:period + 1, arc]) constraint = model.getConstrByName('flow_a{}'.format(arc)) model.chgCoeff(constraint, optimality_var, -demand * data.variable_cost[arc]) model._all_variables = np.array(model.getVars()) model.update() subproblems[period, com] = model return subproblems
class LinearProgram: def __init__(self, hbw_task=None): if hbw_task is None: return self.model = Model(hbw_task.instance_name + '.lp') # LP variables self.variables = [] # was all_variables self.make_model_vars(hbw_task) self.model.update() # LP Constraints self.constraints = [] self.make_model_constraints(hbw_task) self.model.update() self.goal_constraints = set() def copy(self): from copy import deepcopy new_lp = LinearProgram() new_lp.model = self.model.copy() new_lp.constraints = [] new_lp.goal_constraints = set() for index, ext_constraint in enumerate(self.constraints): new_lp.constraints.append((deepcopy(ext_constraint[0]), new_lp.model.getConstrs()[index])) return new_lp def make_model_vars(self, hbw_task): # each cylinder contains a piston of negl. weight and cross # section area equal to the cylinder's. In general, there will # be a fluid column below it self.fluid_column_heights = [] for x in xrange(hbw_task.objects.num_cylinders()): varname = 'fluid_col_ht_cyl_%d' % x fch_i = self.model.addVar(vtype=GRB.CONTINUOUS, name=varname, lb=0.0, ub=hbw_task.objects.cylinders[x].height) self.fluid_column_heights.append(fch_i) self.variables += self.fluid_column_heights # modeling the partial wts of blocks on the piston as a list of # LP variables -- pw_i = partial wt of blocks in the range [0,i-1] that are in the cylinder # base case -- pw_0 = 0 self.partial_wts = {} for i in xrange(hbw_task.objects.num_blocks() + 1): for j in xrange(hbw_task.objects.num_cylinders()): varname = 'pw_%d_cyl_%d' % (i, j) pw_ij = self.model.addVar(vtype=GRB.CONTINUOUS, name=varname) self.partial_wts[(i, j)] = pw_ij self.variables.append(pw_ij) # wt of columns (fluid + blocks) in each cylinder self.column_wts = [] for x in xrange(hbw_task.objects.num_cylinders()): varname = 'col_wt_cyl_%d' % x cw_x = self.model.addVar(vtype=GRB.CONTINUOUS, name=varname) self.column_wts.append(cw_x) self.variables += self.column_wts def get_all_variables(self): return self.variables def get_all_constraints(self): return self.constraints def make_model_constraints(self, hbw_task): self.constraints = self.make_partial_wt_constraints(hbw_task) self.constraints += self.make_column_wt_constraints(hbw_task) self.constraints += self.make_pressure_balancing_constraints(hbw_task) self.constraints += self.make_fluid_balancing_constraint(hbw_task) def make_partial_wt_constraints(self, hbw_task): wt_constraints = [] for cylId in xrange(hbw_task.objects.num_cylinders()): wt_constraints += self.make_wt_constraints_for_cylinder( cylId, hbw_task) return wt_constraints def make_wt_constraints_for_cylinder(self, cylId, hbw_task): #hbw_obj, vars): wt_constraints = [] cylinder = hbw_task.objects.cylinders[cylId] # base case constraint base_constraint = ([], self.model.addConstr( self.partial_wts[(0, cylId)] == 0)) wt_constraints.append(base_constraint) for blockId in xrange(hbw_task.objects.num_blocks()): # for the inductive case, two switched constraints var = hbw_task.block_in_cylinder[(blockId, cylId)] var_valuation = [(var.index, False)] # for the below, note that blocks are indexed from 0 to n-1 # but the corr. LP vars are indexed from 1 to n # since 0 is the base-case var # for var = False false_constraint = (var_valuation, self.model.addConstr( self.partial_wts[(blockId + 1, cylId)] - self.partial_wts[(blockId, cylId)] == 0)) wt_constraints.append(false_constraint) # same for var being True block = hbw_task.objects.blocks[blockId] var_valuation = [(var.index, True)] true_constraint = ( var_valuation, self.model.addConstr( self.partial_wts[(blockId + 1, cylId)] - self.partial_wts[(blockId, cylId)] == block.weight)) wt_constraints.append(true_constraint) return wt_constraints # this function adds equality constraints to calculate the total downward force # at the bottom of the cylinder (column_wt = total weight of all blocks + wt of fluid column) def make_column_wt_constraints_for_cylinder(self, cylId, hbw_task): density = hbw_task.objects.get_fluid_density() cross_section_area_of_cyl = hbw_task.objects.cylinders[cylId].area lpvar_wt_of_blocks = self.partial_wts[( hbw_task.objects.num_blocks(), cylId)] #element corresponding to total wt of blocks constr = ( [], # unconditional self.model.addConstr( self.column_wts[cylId] - density * cross_section_area_of_cyl * self.fluid_column_heights[cylId] - lpvar_wt_of_blocks == 0)) return constr def make_column_wt_constraints(self, hbw_task): return [ self.make_column_wt_constraints_for_cylinder(cylId, hbw_task) for cylId in xrange(hbw_task.objects.num_cylinders()) ] def make_pressure_balancing_constraints(self, hbw_task): pb_constraints = [] # we have 'numCylinders -1' constraints - one for each consecutive pair for cylId in xrange(hbw_task.objects.num_cylinders() - 1): # constr for cylId & cylId + 1 reciprocal_area_1 = 1.0 / hbw_task.objects.cylinders[cylId].area reciprocal_area_2 = 1.0 / hbw_task.objects.cylinders[cylId + 1].area lpconstr = ( [], # unconditional self.model.addConstr( reciprocal_area_1 * self.column_wts[cylId] - reciprocal_area_2 * self.column_wts[cylId + 1] == 0)) pb_constraints.append(lpconstr) return pb_constraints def make_fluid_balancing_constraint(self, hbw_task): constr = ( [], # unconditional self.model.addConstr( quicksum(hbw_task.objects.cylinders[cylId].area * self.fluid_column_heights[cylId] for cylId in xrange(hbw_task.objects.num_cylinders())) == hbw_task.objects.fluid.total_fluid_in_cylinders)) return [constr] def print_constraints(self): for phi, gamma in self.constraints: if len(phi) == 0: print(' True -> %s' % gamma) else: head = ' & '.join(['%s = %s' % (x, v) for x, v in phi]) print(' %s -> %s' % (head, gamma))
class LinearProgram : def __init__( self, task = None ) : if task is None: return self.task = task self.model = Model( task.instance_name + '.lp' ) # LP variables self.variables = [] # was all_variables self.make_model_vars( task ) self.model.update() # LP Constraints self.constraints = [] self.make_model_constraints( task ) self.model.update() self.goal_constraints = set( range(self.first_goal_constraint, len(self.constraints)) ) #self.model.write( 'tmp.lp' ) def copy( self ) : from copy import deepcopy new_lp = LinearProgram( ) new_lp.model = self.model.copy() new_lp.constraints = [] for index, ext_constraint in enumerate(self.constraints) : new_lp.constraints.append( ( deepcopy(ext_constraint[0]), new_lp.model.getConstrs()[index] ) ) new_lp.first_goal_constraint = self.first_goal_constraint new_lp.goal_constraints = set( range (new_lp.first_goal_constraint, len(new_lp.constraints)) ) return new_lp def make_model_vars( self, task ) : # vars are x_{v,l,t} for vehicle v, (non-depot) location l # and goods type t in {ambient, chilled}; if the vehicle # is non-refridgerated, only t = ambient should be created. self.varindex = {} for (vname, _, cap, chilled) in task.vehicles: for loc in task.locations[1:]: varname = 'x_{0}_{1}_ambient'.format(vname, loc) x = self.model.addVar(vtype = GRB.CONTINUOUS, name = varname, lb = 0.0, ub = cap) self.variables.append(x) self.varindex[(vname, loc, 'ambient')] = x if chilled: varname = 'x_{0}_{1}_chilled'.format(vname, loc) x = self.model.addVar(vtype = GRB.CONTINUOUS, name = varname, lb = 0.0, ub = cap) self.variables.append(x) self.varindex[(vname, loc, 'chilled')] = x def get_all_variables (self): return self.variables def get_all_constraints (self): return self.constraints def make_model_constraints (self, task): self.constraints = [] # vehicle capacity constraints: sum of allocations # to all locations and goods types must be within # vehicle capacity. for (vname, _, cap, chilled) in task.vehicles: avars = [ self.varindex[(vname, loc, 'ambient')] for loc in task.locations[1:] ] if chilled: avars += [ self.varindex[(vname, loc, 'chilled')] for loc in task.locations[1:] ] c = self.model.addConstr( quicksum( avars ), GRB.LESS_EQUAL, float(cap) ) constraint = ( [], c ) self.constraints.append( constraint ) # switched constraints: # visited[v,l] = False -> x_v_l_{a,c} = 0, # for all v and l for (vname, _, _, chilled) in task.vehicles: for loc in task.locations[1:]: var = task.visited[(vname, loc)] trigger = [ ( var.index, False ) ] constraint = ( trigger, self.model.addConstr( self.varindex[(vname, loc, 'ambient')], GRB.EQUAL, 0.0) ) self.constraints.append(constraint) if chilled: constraint = ( trigger, self.model.addConstr( self.varindex[(vname, loc, 'chilled')], GRB.EQUAL, 0.0) ) self.constraints.append(constraint) # goal constraints self.first_goal_constraint = len(self.constraints) for loc in task.locations[1:]: (qc, qa) = task.demand[loc] avars = [] cvars = [] for (vname, _, _, chilled) in task.vehicles: avars.append(self.varindex[ (vname, loc, 'ambient') ]) if chilled: cvars.append(self.varindex[ (vname, loc, 'chilled') ]) c = self.model.addConstr( quicksum( avars ), GRB.EQUAL, float(qa) ) #print(loc, c, qa) self.constraints.append( ( [], c ) ) c = self.model.addConstr( quicksum( cvars ), GRB.EQUAL, float(qc) ) #print(loc, c, qc) self.constraints.append( ( [], c ) ) def print_constraints (self): for index,(phi, gamma) in enumerate(self.constraints): if len(phi) == 0 : print( index, ' True -> %s'%gamma ) else : head = ' & '.join( [ '%s = %s'%(self.task.state_vars[x].name,v) for x,v in phi ] ) print ( index, ' %s -> %s'%( head, gamma ) )
class LinearProgram: def __init__(self, counters_task): self.model = Model(counters_task.instance_name + '.lp') # LP Vars self.variables = [] self.make_model_vars(counters_task) self.model.update() # LP Constraints self.constraints = [] self.make_model_constraints(counters_task) self.model.update() self.goal_constraints = set() def copy(self): from copy import deepcopy new_lp = LinearProgram() new_lp.variables = deepcopy(self.variables) new_lp.goal_constraints = deepcopy(self.goal_constraints) new_lp.model = self.model.copy() new_lp.constraints = [] for index, ext_constraint in enumerate(self.constraints): new_lp.constraints.append((deepcopy(ext_constraint[0]), new_lp.model.getConstrs()[index])) return new_lp def make_model_vars(self, task): for x_c in task.counter_values: x_c_2 = self.model.addVar(name=x_c.name, lb=min(x_c.domain), ub=max(x_c.domain), vtype=GRB.INTEGER) self.variables.append(x_c_2) def make_model_constraints(self, task): for i in range(0, len(self.variables)): x = task.counter_values[i] x2 = self.variables[i] # Equivalence constraints for v in x.domain: c = self.model.addConstr(x2 == v) self.constraints.append(([(x.index, v)], c)) # Bounds constraints gt_zero = self.model.addConstr(x2 >= 0) lt_max = self.model.addConstr(x2, GRB.LESS_EQUAL, task.max_value) self.constraints.append(([], gt_zero)) self.constraints.append(([], lt_max)) def make_goal_constraints(self, goal_condition): goal_constraints = [] for cond, lhs, rhs in goal_condition: x_lhs = None x_rhs = None for x in self.model.getVars(): if lhs in x.getAttr('VarName'): x_lhs = x if rhs in x.getAttr('VarName'): x_rhs = x assert x_lhs is not None assert x_rhs is not None c = None if cond == '<': c = ([], self.model.addConstr(x_lhs <= x_rhs)) self.constraints.append(c) goal_constraints.append(len(self.constraints) - 1) c = ([], self.model.addConstr(x_lhs <= x_rhs - 1)) self.constraints.append(c) goal_constraints.append(len(self.constraints) - 1) elif cond == '>': c = ([], self.model.addConstr(x_lhs >= x_rhs)) self.constraints.append(c) goal_constraints.append(len(self.constraints) - 1) c = ([], self.model.addConstr(x_lhs - 1 >= x_rhs)) self.constraints.append(c) goal_constraints.append(len(self.constraints) - 1) elif cond == '=': c = ([], self.model.addConstr(x_lhs == x_rhs)) self.constraints.append(c) goal_constraints.append(len(self.constraints) - 1) else: assert False self.model.update() self.goal_constraints = set(goal_constraints) return self.goal_constraints
class LinearProgram: def __init__(self, counters_task=None): if counters_task is None: return self.model = Model(counters_task.instance_name + '.lp') self.var_map = {} # LP Vars self.variables = [] self.make_model_vars(counters_task) self.model.update() # LP Constraints self.constraints = [] self.make_model_constraints(counters_task) self.model.update() self.goal_constraints = set() def copy(self): from copy import deepcopy new_lp = LinearProgram() new_lp.goal_constraints = deepcopy(self.goal_constraints) new_lp.model = self.model.copy() new_lp.constraints = [] for index, ext_constraint in enumerate(self.constraints): new_lp.constraints.append((deepcopy(ext_constraint[0]), new_lp.model.getConstrs()[index])) return new_lp def make_model_vars(self, task): for x_c in task.counter_values: x_c_2 = self.model.addVar(name=x_c, lb=0, ub=task.max_value - 1) ## , vtype = GRB.INTEGER self.variables.append(x_c_2) self.var_map[x_c] = x_c_2 def make_model_constraints(self, task): for counter in task.counter_values: for k in xrange(0, task.max_value): x = self.var_map[counter] c = self.model.addConstr(k <= x) self.constraints.append( ([(task.counter_values[counter][k].index, True)], c)) c = self.model.addConstr(x <= k - 1) self.constraints.append( ([(task.counter_values[counter][k].index, False)], c)) def make_goal_constraints(self, goal_condition): goal_constraints = [] # print(goal_condition) # print(self.model.variables) # print(self.var_map) for cond, lhs, rhs in goal_condition: # x_lhs = None # x_rhs = None # for x in self.model.getVars() : # if lhs in x.getAttr('VarName') : # x_lhs = x # if rhs in x.getAttr('VarName') : # x_rhs = x # assert x_lhs is not None # assert x_rhs is not None x_lhs = self.var_map[lhs] x_rhs = self.var_map[rhs] c = None if cond == '<': c = ([], self.model.addConstr(x_lhs <= x_rhs - 1)) self.constraints.append(c) goal_constraints.append(len(self.constraints) - 1) elif cond == '>': c = ([], self.model.addConstr(x_lhs - 1 >= x_rhs)) self.constraints.append(c) goal_constraints.append(len(self.constraints) - 1) elif cond == '=': c = ([], self.model.addConstr(x_lhs == x_rhs)) self.constraints.append(c) goal_constraints.append(len(self.constraints) - 1) else: assert False self.model.update() self.goal_constraints = set(goal_constraints) return self.goal_constraints
def populate_dual_subproblem(data): """ Function that populates the Benders Dual Subproblem, as suggested by the paper "Minimal Infeasible Subsystems and Bender's cuts" by Fischetti, Salvagnin and Zanette. :param data: Problem data structure :param upper_cost: Link setup decisions fixed in the master :param flow_cost: This is the cost of the continuous variables of the master problem, as explained in the paper :return: Numpy array of Gurobi model objects """ # Gurobi model objects subproblems = np.empty(shape=(data.periods, data.commodities), dtype=object) # Construct model for period/commodity 0. # Then, copy this and change the coefficients subproblem = Model('subproblem_(0,0)') # Ranges we are going to need arcs, periods, commodities, nodes = xrange(data.arcs.size), xrange( data.periods), xrange(data.commodities), xrange(data.nodes) # Other data demand, var_cost = data.demand, data.variable_cost # Origins and destinations of commodities origins, destinations = data.origins, data.destinations # We use arrays to store variable indexes and variable objects. Why use # both? Gurobi wont let us get the values of individual variables # within a callback.. We just get the values of a large array of # variables, in the order they were initially defined. To separate them # in variable categories, we will have to use index arrays flow_vars = np.empty_like(arcs, dtype=object) # Populate all variables in one loop, keep track of their indexes # Data for period = 0, com = 0 for arc in arcs: flow_vars[arc] = subproblem.addVar(obj=demand[0, 0] * var_cost[arc], lb=0., ub=1., name='flow_a{}'.format(arc)) subproblem.update() # Add constraints for node in nodes: out_arcs = get_2d_index(data.arcs, data.nodes)[0] == node + 1 in_arcs = get_2d_index(data.arcs, data.nodes)[1] == node + 1 lhs = quicksum(flow_vars[out_arcs]) - quicksum(flow_vars[in_arcs]) subproblem.addConstr(lhs == 0., name='flow_bal{}'.format(node)) subproblem.update() # Store variables subproblem._all_variables = flow_vars.tolist() # Set parameters subproblem.setParam('OutputFlag', 0) subproblem.modelSense = GRB.MINIMIZE subproblem.params.threads = 2 subproblem.params.LogFile = "" subproblem.update() subproblems[0, 0] = subproblem for period, com in product(periods, commodities): if (period, com) != (0, 0): model = subproblem.copy() model.ModelName = 'subproblem_({},{})'.format(period, com) flow_cost = data.demand[period, com] * var_cost model.setObjective(LinExpr(flow_cost.tolist(), model.getVars())) model.setAttr('rhs', model.getConstrs(), [0.0] * data.nodes) model._all_variables = model.getVars() model.update() subproblems[period, com] = model return subproblems
m.setAttr('ModelSense', GRB.MAXIMIZE) m.update() m.addConstr(x1 + x2 <= 6, "c0") m.addConstr(9*x1 + 5*x2 <= 45, "c1") m.setParam( 'OutputFlag', False ) m.optimize() logStatus(m) root = bn.BBNode(None, '', '', 0) node1 = bn.BBNode(root,'x1','ub',3) m1 = m.copy() m1.addConstr(m1.getVars()[0] <= 3, "node1") m1.optimize() logStatus(m1) node2 = bn.BBNode(root,'x1','lb',4) m2 = m.copy() m2.addConstr(m2.getVars()[0] >= 4, "node2") m2.optimize() logStatus(m2) node21 = bn.BBNode(node2,'x2','ub', 1) m21 = m2.copy() m21.addConstr(m21.getVars()[1] <= 1,"node21") m21.optimize() logStatus(m21)