def subproblem(xhat): '''子问题求解 ''' # 初始化问题 r = xp.problem() # 定义变量 y = [xp.var() for i in range(n2)] z = [xp.var(lb=-xp.infinity) for i in range(n1)] epsilon = xp.var(lb=-xp.infinity) r.addVariable(y, z, epsilon) # 定义约束 dummy1 = [z[i] == xhat[i] for i in range(n1)] dummy2 = epsilon == 1 constr=[xp.Sum(A1[ii][jj]*z[jj] for jj in range(n1)) + \ xp.Sum(A2[ii][jj]*y[jj] for jj in range(n2)) \ - epsilon*b[ii]<=0 for ii in range(m)] r.addConstraint(constr, dummy1, dummy2) # 定义目标函数 r.setObjective(xp.Sum(c2[jj] * y[jj] for jj in range(n2)), sense=xp.minimize) r.setControl({"presolve": 0}) # 解该问题 r.setControl('outputlog', 0) r.solve() # 寻找切片 xind1 = [r.getIndex(dummy1[ii]) for ii in range(n1)] # 根据自问题的解的情况判断下一步 if r.getProbStatus() == xp.lp_optimal: # 获得了最优解 print("Optimal Subproblem") dualmult = r.getDual() lamb = [dualmult[ii] for ii in xind1] beta = r.getObjVal() return (lamb, beta, 'Optimal') elif r.getProbStatus() == xp.lp_infeas: # 子问题不可行 print("Infeasible Subproblem") if not r.hasdualray(): print("Could not retrieve a dual ray, return no good cut instead:") xhatones = set(ii for ii in range(n1) if xhat[ii] >= 0.5) lamb = [2 * xhat[ii] - 1 for ii in range(n1)] beta = -sum(xhat) + 1 else: # 求解对偶 dray = [] r.getdualray(dray) print("Dual Ray:", dray) lamb = [dray[ii] for ii in xind1] beta = dray[r.getIndex(dummy2)] return (lamb, beta, 'Infeasible') else: print("ERROR: Subproblem not optimal or infeasible. Terminating.") sys.exit()
def no_linearization(quad, **kwargs): """ Solve a problem using the solver's default approach to quadratics (for cplex, this is the std linearization) """ start = timer() n = quad.n c = quad.c m = xp.problem(name='no_linearization') x = np.array([xp.var(vartype=xp.binary) for i in range(n)]) m.addVariable(x) if type(quad) is Knapsack: #HSP and UQP don't have cap constraint #add capacity constraint(s) for k in range(quad.m): m.addConstraint(xp.Sum(x[i]*quad.a[k][i] for i in range(n)) <= quad.b[k]) #k_item constraint if necessary (if KQKP or HSP) if quad.num_items > 0: m.addConstraint(xp.Sum(x[i] for i in range(n)) == quad.num_items) #compute quadratic values contirbution to obj quadratic_values = 0 for i in range(n): for j in range(i+1,n): quadratic_values = quadratic_values + (x[i]*x[j]*quad.C[i,j]) #set objective function linear_values = xp.Sum(x[i]*c[i] for i in range(n)) m.setObjective(linear_values + quadratic_values, sense=xp.maximize) end = timer() setup_time = end-start return [m, setup_time]
def optimize(self, goal='cost'): ''' Optimizes the micro grid with the given objective goal Raise a TimoutError, if the time limit is reached ''' self.__addVariablesToModel() self.__createContraints() if goal == 'cost': self.model.setObjective( (xp.Sum(self.buy[t]*(self.total_demand[t]- self.total_supply[t]) for t in self.time_steps)), #(xp.Sum( #((self.total_supply[t] - self.total_demand[t])*self.sell[t]*self.sell_prices[t]) # + ((self.total_supply[t]- self.total_demand[t])*self.buy[t]*self.buy_prices[t]) #for t in self.time_steps))/10**6 , sense=xp.maximize) elif goal == 'independent': obj_independent = (xp.Sum(self.buy[t]*(1- self.total_demand[t]- self.total_supply[t]) + self.sell[t]*(self.total_supply[t] - self.total_demand[t]) for t in self.time_steps)) self.model.setObjective(obj_independent, sense=xp.minimize) elif goal == 'xp.minbuy': obj_independent = (xp.Sum(self.buy[t]*(self.total_demand[t]- self.total_supply[t]) for t in self.time_steps)) self.model.setObjective(obj_independent, sense=xp.minimize) else: raise ValueError('Unkown objective goal') self.model.solve() print("Status: ", self.model.getProbStatusString()) print("Solution: ", self.model.getSolution()) if self.model.getProbStatus() == GRB.TIME_LIMIT: raise TimeoutError()
def set_objective(self): self.p.setObjective(xp.Sum( xp.Sum(self.e[a, t] for t in self._periods.keys()) for a in range(len(self._areasList))) + 0.001 * xp.Sum( xp.Sum( xp.Sum(self.y[a, b, t] for t in self._periods.keys()) for a in range(len(self._areasList))) for b in range(len(self._areasList))), sense=xp.minimize)
def set_constraints(self): for i in self.emptySlots: for j in self.slots: self.m.addConstraint(self.x[i, j] == 0) for flight in self.flights: if not self.f_in_matched(flight): self.m.addConstraint(self.x[flight.slot.index, flight.slot.index] == 1) else: self.m.addConstraint( xp.Sum(self.x[flight.slot.index, j.index] for j in flight.compatibleSlots) == 1) for j in self.slots: self.m.addConstraint( xp.Sum(self.x[i.index, j.index] for i in self.slots) <= 1) for flight in self.flights: for j in flight.notCompatibleSlots: self.m.addConstraint(self.x[flight.slot.index, j.index] == 0) for flight in self.flights_in_matches: self.m.addConstraint(xp.Sum(self.x[flight.slot.index, slot_to_swap.index] for slot_to_swap in [s for s in self.slots if s != flight.slot]) \ == xp.Sum([self.c[j] for j in self.get_match_for_flight(flight)])) for flight in self.flights: for other_flight in flight.airline.flights: if flight != other_flight: self.m.addConstraint(self.x[flight.slot.index, other_flight.slot.index] == 0) k = 0 for match in self.matches: pairA = match[0] pairB = match[1] self.m.addConstraint(xp.Sum(self.x[i.slot.index, j.slot.index] for i in pairA for j in pairB) + \ xp.Sum(self.x[i.slot.index, j.slot.index] for i in pairB for j in pairA) >= \ (self.c[k]) * 4) self.m.addConstraint( xp.Sum(self.x[i.slot.index, j.slot.index] * i.costFun(i, j.slot) for i in pairA for j in pairB) - \ (1 - self.c[k]) * 100000 \ <= xp.Sum(self.x[i.slot.index, j.slot.index] * i.costFun(i, i.slot) for i in pairA for j in pairB) - \ self.epsilon) self.m.addConstraint( xp.Sum(self.x[i.slot.index, j.slot.index] * i.costFun(i, j.slot) for i in pairB for j in pairA) - \ (1 - self.c[k]) * 100000 \ <= xp.Sum(self.x[i.slot.index, j.slot.index] * i.costFun(i, i.slot) for i in pairB for j in pairA) - \ self.epsilon) k += 1
def _set_objective_function(self): # Similar to constraints, saving the costs expressions as attributes # can give you the chance to retrieve their values at the end of the optimization self.total_holding_cost = self.input_params['holding_cost'] * xp.Sum( self.inventory_variables) self.total_production_cost = xp.Sum( row['production_cost'] * self.production_variables[index] for index, row in self.input_data.iterrows()) objective = self.total_holding_cost + self.total_production_cost self.model.setObjective(objective, sense=xp.minimize)
def standard_linearization(quad, lhs_constraints=True, **kwargs): """ Apply the standard linearization to a QP and return resulting model param lhs_constraints: can choose to include optional lower bound constraints (or upper bound if mixed_sign) """ n = quad.n c = quad.c C = quad.C a = quad.a b = quad.b # create model and add variables m = xp.problem(name='standard_linearization') x = np.array([xp.var(vartype=xp.binary) for i in range(n)]) w = np.array([[xp.var(vartype=xp.continuous) for i in range(n)] for i in range(n)]) m.addVariable(x,w) if type(quad) is Knapsack: # HSP and UQP don't have cap constraint # add capacity constraint(s) for k in range(quad.m): m.addConstraint(xp.Sum(x[i]*a[k][i] for i in range(n)) <= b[k]) #k_item constraint if necessary (if KQKP or HSP) if quad.num_items > 0: m.addConstraint(xp.Sum(x[i] for i in range(n)) == quad.num_items) # add auxiliary constraints for i in range(n): for j in range(i+1, n): if lhs_constraints: if C[i,j]+C[j,i] > 0: m.addConstraint(w[i,j] <= x[i]) m.addConstraint(w[i,j] <= x[j]) else: m.addConstraint(x[i]+x[j]-1 <= w[i,j]) m.addConstraint(w[i,j] >= 0) else: m.addConstraint(w[i,j] <= x[i]) m.addConstraint(w[i,j] <= x[j]) m.addConstraint(x[i]+x[j]-1 <= w[i,j]) m.addConstraint(w[i,j] >= 0) #compute quadratic values contirbution to obj quadratic_values = 0 for i in range(n): for j in range(i+1, n): quadratic_values = quadratic_values + (w[i, j]*(C[i, j]+C[j, i])) # set objective function linear_values = sum(x[i]*c[i] for i in range(n)) m.setObjective(linear_values + quadratic_values, sense=xp.maximize) # return model + setup time return [m, 0]
def set_constraints(self): flight: modFl.Flight airline: air.Airline for flight in self.flights: self.m.addConstraint( xp.Sum(self.x[flight.slot.index, slot.index] for slot in flight.compatibleSlots) == 1) for slot in self.slots: self.m.addConstraint( xp.Sum(self.x[flight.slot.index, slot.index] for flight in self.flights) <= 1)
def extended_linear_formulation(quad, **kwargs): """ Apply the extended linear formulation to a QP and return resulting model """ start = timer() n = quad.n c = quad.c C = quad.C a = quad.a b = quad.b #create model and add variables m = xp.problem(name='extended_linear_formulation') x = np.array([xp.var(vartype=xp.binary) for i in range(n)]) z = np.array([[[xp.var(vartype=xp.continuous, lb=-xp.infinity) for i in range(n)] for j in range(n)] for k in range(n)]) m.addVariable(x,z) if type(quad) is Knapsack: #HSP and UQP don't have cap constraint #add capacity constraint(s) for k in range(quad.m): m.addConstraint(xp.Sum(x[i]*a[k][i] for i in range(n)) <= b[k]) #k_item constraint if necessary (if KQKP or HSP) if quad.num_items > 0: m.addConstraint(xp.Sum(x[i] for i in range(n)) == quad.num_items) #add auxiliary constraints for i in range(n): for j in range(i+1,n): m.addConstraint(z[i,i,j]+z[j,i,j] <= 1) if C[i,j] < 0: m.addConstraint(x[i] + z[i,i,j] <= 1) m.addConstraint(x[j] + z[j,i,j] <= 1) elif C[i,j] > 0: m.addConstraint(x[i] + z[i,i,j] + z[j,i,j] >= 1) m.addConstraint(x[j] + z[i,i,j] + z[j,i,j] >= 1) #compute quadratic values contirbution to obj constant = 0 quadratic_values = 0 for i in range(n): for j in range(i+1,n): constant = constant + C[i,j] quadratic_values = quadratic_values + (C[i,j]*(z[i,i,j]+z[j,i,j])) #set objective function linear_values = xp.Sum(x[i]*c[i] for i in range(n)) m.setObjective(linear_values + constant - quadratic_values, sense=xp.maximize) end = timer() setup_time = end-start #return model + setup time return [m, setup_time]
def force_auxiliary_variables_1(self, doctor, total_work_time): aux_definition1 = xp.constraint( xp.Sum(p.shift.duration_in_hours * self.x[p] for p in self.x if p.doctor == doctor) - doctor.allocation * total_work_time <= self.t[doctor], name=f'Auxiliary 1 for {doctor}') self.model.addConstraint(aux_definition1)
def _get_expr_from_pyomo_repn(self, repn, max_degree=2): referenced_vars = ComponentSet() degree = repn.polynomial_degree() if (degree is None) or (degree > max_degree): raise DegreeError( 'XpressDirect does not support expressions of degree {0}.'. format(degree)) # NOTE: xpress's python interface only allows for expresions # with native numeric types. Others, like numpy.float64, # will cause an exception when constructing expressions if len(repn.linear_vars) > 0: referenced_vars.update(repn.linear_vars) new_expr = xpress.Sum( float(coef) * self._pyomo_var_to_solver_var_map[var] for coef, var in zip(repn.linear_coefs, repn.linear_vars)) else: new_expr = 0.0 for coef, (x, y) in zip(repn.quadratic_coefs, repn.quadratic_vars): new_expr += float(coef) * self._pyomo_var_to_solver_var_map[ x] * self._pyomo_var_to_solver_var_map[y] referenced_vars.add(x) referenced_vars.add(y) new_expr += repn.constant return new_expr, referenced_vars
def force_shift_fulfillment(self, shift, hospital): shift_fulfillment = xp.constraint( xp.Sum(self.x[p] for p in self.x if p.shift == shift and p.hospital == hospital) == 1, name= f'Force shift fulfillment for shift {shift} and hospital {hospital}' ) self.model.addConstraint(shift_fulfillment)
def prlt1_linearization(quad): # only called from within reformulate_glover (make inner func?) n = quad.n c = quad.c C = quad.C a = quad.a b = quad.b # create model and add variables m = xp.problem(name='PRLT-1_linearization') x = m.addVars(n, lb=0, ub=1, vtype=GRB.CONTINUOUS) w = m.addVars(n, n, vtype=GRB.CONTINUOUS) if type(quad) is Knapsack: # HSP and UQP don't have cap constraint # add capacity constraint(s) for k in range(quad.m): m.addConstraint(xp.Sum(x[i]*a[k][i] for i in range(n)) <= b[k]) #k_item constraint if necessary (if KQKP or HSP) if quad.num_items > 0: m.addConstraint(xp.Sum(x[i] for i in range(n)) == quad.num_items) # add auxiliary constraints for i in range(n): for j in range(i+1, n): m.addConstraint(w[i, j] == w[j, i], name='con16'+str(i)+str(j)) for k in range(quad.m): for j in range(n): m.addConstraint(xp.Sum(a[k][i]*w[i, j] for i in range(n) if i != j) <= (b[k]-a[k][j])*x[j]) for i in range(n): m.addConstraint(w[i, j] <= x[j]) # add objective function quadratic_values = 0 for j in range(n): for i in range(n): if(i == j): continue quadratic_values = quadratic_values + (C[i, j]*w[i, j]) m.setObjective(linear_values + quadratic_values, sense=xp.maximize) linear_values = xp.Sum(x[j]*c[j] for j in range(n)) # return model return m
def set_objective(self): # self.m.setObjective( # xp.Sum(self.x[flight.slot.index, j.index] * self.score(flight, j) # for flight in self.flights for j in self.slots), sense=xp.minimize) self.m.setObjective( xp.Sum(self.x[flight.slot.index, j.index] * flight.costFun(flight, j) for flight in self.flights for j in self.slots), sense=xp.minimize)
def max_time_working(self, doctor, shift_day, day_limit): time_working = xp.constraint( xp.Sum(self.x[p] for p in self.x for j in range(day_limit + 1) if p.doctor == doctor and p.shift.start_time.date() == (shift_day + timedelta(days=j)).date()) <= day_limit, name= f'Max working time for day {shift_day} and doctor {doctor.name}') self.model.addConstraint(time_working)
def force_nightshift_work(self, nightshift, n_days_working): nightshift_work = xp.constraint( xp.Sum(self.x[p] for p in self.x for k in range(n_days_working) if p.doctor == nightshift.doctor and p.shift.start_time. date() == (nightshift.day.date() + timedelta(days=k))) >= 3 * self.y[nightshift], name= f'Force nightshift for doctor {nightshift.doctor} at day {nightshift.day}' ) self.model.addConstraint(nightshift_work)
def set_constraints(self): flight: modFl.Flight airline: air.Airline for flight in self.flights: self.m.addConstraint( xp.Sum(self.x[flight.slot.index, slot.index] for slot in flight.compatibleSlots) == 1) for slot in self.slots: self.m.addConstraint( xp.Sum(self.x[flight.slot.index, slot.index] for flight in self.flights) <= 1) for airline in self.airlines: self.m.addConstraint( xp.Sum(flight.costFun(flight, flight.slot) for flight in airline.flights) >= \ xp.Sum(self.x[flight.slot.index, slot.index] * flight.costFun(flight, slot) for flight in airline.flights for slot in self.slots) )
def UDPPlocalOpt(airline: air.UDPPairline, slots: List[sl.Slot], xp_problem=None): if xp_problem is None: m = xp.problem() else: m = xp_problem x = np.array([[xp.var(vartype=xp.binary) for j in slots] for i in airline.flights]) m.addVariable(x) flight: fl.UDPPflight for k in range(airline.numFlights): #one x max for slot m.addConstraint( xp.Sum(x[flight.localNum, k] for flight in airline.flights) == 1) for flight in airline.flights: # flight assignment m.addConstraint( xp.Sum(x[flight.localNum, k] for k in range(eta_limit_slot(flight, airline.AUslots), airline.numFlights)) == 1) m.setObjective( xp.Sum( x[flight.localNum][k] * flight.costFun(flight, airline.AUslots[k]) for flight in airline.flights for k in range(airline.numFlights))) m.solve() # print("airline ",airline) for flight in airline.flights: for k in range(airline.numFlights): if m.getSolution(x[flight.localNum, k]) > 0.5: flight.newSlot = airline.flights[k].slot flight.priorityNumber = k flight.priorityValue = "N"
def solve(self, X): """ Implementation of the dual of SVDD problem Params: - X, the dataset without labels Returns: None """ m = X.shape[0] # Define Variables of the problem alphas = [xp.var(lb=0, ub=self.C) for i in range(m)] self.p.addVariable(alphas) # Define Constraints # constr1 = [alpha <= C for alpha in alphas] # p.addConstraint(constr1) # already defined in the definition of the variable as a upper bound # Define objective function obj1 = xp.Sum(alphas[i] * np.sum( self.kernel(X[i].reshape(1, -1), X[i].reshape(1, -1))[0][0]) for i in range(m)) obj2 = xp.Sum(alphas[i] * alphas[j] * np.sum( self.kernel(X[i].reshape(1, -1), X[j].reshape(1, -1))[0][0]) for i in range(m) for j in range(m)) obj = obj1 - obj2 self.p.setObjective(obj, sense=xp.maximize) #set maximization problem # solve the problem self.p.solve() self.opt = np.array( self.p.getSolution(alphas)) #save the values of alphas self.c0 = np.sum(self.opt.reshape(-1, 1) * X, axis=0) indices_support_vectors = self.select_support_vectors( ) #choose a point as support vector self.support_vectors = X[indices_support_vectors]
def force_rest(self, nightshift, n_days_working, n_days_rest): force_rest = xp.constraint( xp.Sum( self.x[p] for p in self.x for k in range(n_days_working, n_days_working + n_days_rest) if p.doctor == nightshift.doctor and p.shift.start_time.date() == (nightshift.day.date() + timedelta(days=k))) <= 3 * (1 - self.y[nightshift]), name= f'Force rest for doctor {nightshift.doctor} at day {nightshift.day}' ) self.model.addConstraint(force_rest)
def set_constraints(self): for i in self.emptySlots: for j in self.slots: self.m.addConstraint(self.x[i, j] == 0) for flight in self.flights: if not self.f_in_matched(flight): self.m.addConstraint(self.x[flight.slot.index, flight.slot.index] == 1) else: self.m.addConstraint(xp.Sum(self.x[flight.slot.index, j.index] for j in flight.compatibleSlots) == 1) for j in self.slots: self.m.addConstraint(xp.Sum(self.x[i.index, j.index] for i in self.slots) <= 1) # for flight in self.flights: # for j in flight.notCompatibleSlots: # self.m.addConstraint(self.x[flight.slot.index, j.index] == 0) for flight in self.flights_in_matches: self.m.addConstraint( xp.Sum(self.x[flight.slot.index, slot.index] for slot in self.slots if slot != flight.slot) \ <= xp.Sum([self.c[j] for j in self.get_match_for_flight(flight)])) self.m.addConstraint(xp.Sum([self.c[j] for j in self.get_match_for_flight(flight)]) <= 1) k = 0 for match in self.matches: flights = [flight for pair in match for flight in pair] self.m.addConstraint(xp.Sum(xp.Sum(self.x[i.slot.index, j.slot.index] for i in pair for j in flights) for pair in match) >= (self.c[k]) * len(flights)) for pair in match: self.m.addConstraint( xp.Sum(self.x[i.slot.index, j.slot.index] * i.costFun(i, j.slot) for i in pair for j in flights) - (1 - self.c[k]) * 10000000 \ <= xp.Sum(self.x[i.slot.index, j.slot.index] * i.costFun(i, i.slot) for i in pair for j in flights) - \ self.epsilon) k += 1
def set_constraints(self): # t is the index of the time period for acc in self.accs: # no self colab for t in range(self.intNum): self.p.addConstraint(self.x[acc.index, acc.index, t] == 0) self.p.addConstraint(xp.Sum(self.m[k] for k in self.get_acc_matches(acc)) <= 1) # colab with only one for each interval for acc_A in self.accs: for t in range(self.intNum): self.p.addConstraint( xp.Sum(self.x[acc_A.index, acc_B.index, t] for acc_B in self.accs) <= 1 ) k = 0 for match in self.matches: acc_A, acc_B = match[0], match[1] self.p.addConstraint( xp.Sum(self.x[acc_A.index, acc_B.index, t] for t in range(self.intNum)) <= self.m[k] ) k += 1
def set_constraints(self): for a in range(len(self._areasList)): for t in self._periods.keys(): self.p.addConstraint( self._areasList[a].demand[self._periods[t]] - self._areasList[a].capacity[self._periods[t]] - xp.Sum(self.y[a, other_area, t] for other_area in range(len(self._areasList))) + xp.Sum(self.y[other_area, a, t] for other_area in range(len(self._areasList))) <= self.e[a, t]) available = 1 if ( self._areasList[a].demand[self._periods[t]] - self._areasList[a].capacity[self._periods[t]]) < 0 else 0 self.p.addConstraint( xp.Sum(self.y[j, a, t] for j in range(len(self._areasList))) <= 10000 * available) self.p.addConstraint( xp.Sum(self.y[a, j, t] for j in range(len(self._areasList))) <= 10000 * (1 - available)) idxs = [ i for i in range(len(self._matches)) if self._matches[i][0].name == self._areasList[a].name or self._matches[i][1].name == self._areasList[a].name ] self.p.addConstraint(xp.Sum(self.m[i] for i in idxs) <= 1) for i in range(len(self._matches)): a = self._matches[i][0].index b = self._matches[i][1].index self.p.addConstraint( xp.Sum(self.y[a, b, t] + self.y[b, a, t] for t in self._periods.keys()) <= 10000 * self.m[i])
# The objective function is the total area of the polygon. Considering # the segment S[i] joining the center to the i-th vertex and A(i,j) # the area of the triangle defined by the two segments S[i] and S[j], # the objective function is # # A[0,1] + A[1,2] + ... + A[N-1,0] # # Where A[i,i+1] is given by # # 1/2 * rho[i] * rho[i+1] * sin (theta[i+1] - theta[i]) p.setObjective( 0.5 * ( xp.Sum(rho[i] * rho[i - 1] * xp.sin(theta[i] - theta[i - 1]) for i in Vertices if i != 0) # sum of the first N-1 triangle areas + rho[0] * rho[N - 1] * xp.sin(theta[0] - theta[N - 1])), sense=xp.maximize) # plus area between segments N and 1 # Angles are in increasing order, and should be different (the solver # finds a bad local optimum otherwise p.addConstraint(theta[i] >= theta[i - 1] + 1e-4 for i in Vertices if i != 0) # solve the problem p.solve() # The following command saves the final problem onto a file #
def solve(self): ### Params ### self.n_plyrs_per_game = [ self.game_list[i][1] for i in range(len(self.game_list)) ] req_plyrs_per_round = sum([ct * 2 for ct in self.n_plyrs_per_game]) if self.trim_gms & (req_plyrs_per_round > self.n_plyrs): if self.debug: st.text("Removing games to fit the number of passed players") while req_plyrs_per_round > self.n_plyrs: self.game_list = self.game_list[:-1] self.n_plyrs_per_game = [ self.game_list[i][1] for i in range(len(self.game_list)) ] req_plyrs_per_round = sum( [ct * 2 for ct in self.n_plyrs_per_game]) print(req_plyrs_per_round, self.n_plyrs) # max number of times a player can play a given game self.max_gm_plays = max( 2, math.ceil(req_plyrs_per_round * self.n_rounds / self.n_plyrs)) st.text(f'Final game list: {self.game_list}') if not self.max_gm_plays_soft: st.text( f'Max times a player is allowed to play same game: {self.max_gm_plays}' ) self.n_games = len(self.game_list) ## Instantiate problem ## self.model = xp.problem() ################# ### Variables ### ################# # Player-game-round-team plyrs = {} for p in range(self.n_plyrs): plyrs[p] = {} for g in range(self.n_games): plyrs[p][g] = {} for r in range(self.n_rounds): plyr_var_list = [ xp.var(name=f"p{p}_g{g}_r{r}_t{t}", vartype=xp.binary) for t in range(self.n_tms) ] plyrs[p][g][r] = plyr_var_list self.model.addVariable(plyr_var_list) # Team assignments tms = {} for t in range(self.n_tms): tm_var_list = [ xp.var(name=f"p{p}_tm{t}", vartype=xp.binary) for p in range(self.n_plyrs) ] tms[t] = tm_var_list self.model.addVariable(tm_var_list) # Variable set greater_than/equal_to game_played count for all players for all games - this quantity is minimized max_gm_plays_global = xp.var(name='max_gm_plays_global', lb=0) self.model.addVariable(max_gm_plays_global) ################### ### Constraints ### ################### # Correct number of plyrs per game per team ## Why need both less/greater than and not equal to? for g in range(self.n_games): for r in range(self.n_rounds): for t in range(self.n_tms): suff_plyrs_tm_1 = xp.Sum(plyrs[p][g][r][t] for p in range( self.n_plyrs)) >= self.n_plyrs_per_game[g] suff_plyrs_tm_2 = xp.Sum(plyrs[p][g][r][t] for p in range( self.n_plyrs)) <= self.n_plyrs_per_game[g] self.model.addConstraint( xp.constraint(suff_plyrs_tm_1, name=f'gteq_plyrs_tm{t}_rnd{r}_gm{g}')) self.model.addConstraint( xp.constraint(suff_plyrs_tm_2, name=f'lteq_plyrs_tm{t}_rnd{r}_gm{g}')) # One game per time per player for p in range(self.n_plyrs): for r in range(self.n_rounds): for t in range(self.n_tms): one_game_per_round_per_plyr = xp.Sum( plyrs[p][g][r][t] for g in range(self.n_games)) <= 1 self.model.addConstraint( xp.constraint( one_game_per_round_per_plyr, name=f"one_gm_in_rnd{r}_for_plyr{p}_tm{t}")) # Team assignment constraints for p in range(self.n_plyrs): # One team per player tm_lb = xp.Sum(tms[t][p] for t in range(self.n_tms)) <= 1 tm_ub = xp.Sum(tms[t][p] for t in range(self.n_tms)) >= 1 self.model.addConstraint( xp.constraint(tm_lb, name=f'plyr{p}_lteq_1_tm')) self.model.addConstraint( xp.constraint(tm_ub, name=f'plyr{p}_gteq_1_tm')) for t in range(self.n_tms): # Forcing tm variables to be flipped when player selected tm_enforce = xp.Sum( plyrs[p][g][r][t] for g in range(self.n_games) for r in range(self.n_rounds)) <= tms[t][p] * self.n_rounds self.model.addConstraint( xp.constraint(tm_enforce, name=f'plyr{p}_tm{t}_enforce')) # Teams evenly split tms_even_1 = xp.Sum(tms[0][p] for p in range(self.n_plyrs)) - xp.Sum( tms[1][p] for p in range(self.n_plyrs)) <= 1 tms_even_2 = xp.Sum(tms[1][p] for p in range(self.n_plyrs)) - xp.Sum( tms[0][p] for p in range(self.n_plyrs)) <= 1 self.model.addConstraint(tms_even_1) self.model.addConstraint(tms_even_2) # Each player plays each game at most 'self.max_gm_plays' for p in range(self.n_plyrs): for g in range(self.n_games): for t in range(self.n_tms): max_rds_per_game_per_plyr = xp.Sum( plyrs[p][g][r][t] for r in range(self.n_rounds)) if not self.max_gm_plays_soft: self.model.addConstraint( xp.constraint( max_rds_per_game_per_plyr <= self.max_gm_plays, name= f"plyr{p}_plays_gm{g}_max_{self.max_gm_plays}_times_tm{t}" )) self.model.addConstraint( xp.constraint( max_gm_plays_global >= max_rds_per_game_per_plyr, name=f'max_gm_plays_global_gteq_p{p}_g{g}_t{t}')) # Each player plays at most once more than every other player for p1 in range(self.n_plyrs): n_plays = xp.Sum(plyrs[p1][g][r][t] for g in range(self.n_games) for r in range(self.n_rounds) for t in range(self.n_tms)) for p2 in range(p1 + 1, self.n_plyrs): n_plays_ = xp.Sum(plyrs[p2][g][r][t] for g in range(self.n_games) for r in range(self.n_rounds) for t in range(self.n_tms)) self.model.addConstraint( (n_plays - n_plays_) <= self.max_gm_gap) self.model.addConstraint( (n_plays_ - n_plays) <= self.max_gm_gap) # Objective self.model.setObjective(max_gm_plays_global, sense=xp.minimize) self.model.solve() self.check_model_feasibility() return plyrs, tms
def xpress_sum(terms): """ Sum the given terms """ return xp.Sum(terms)
# the Xpress API routines to amend the problem with rows and quadratic # terms. # p = xp.problem () N = 5 S = range (N) x = [xp.var (vartype = xp.binary) for i in S] p.addVariable (x) # vectors can be used whole or addressed with their index c0 = xp.Sum (x) <= 10 cc = [x[i]/1.1 <= x[i+1]*2 for i in range (N-1)] p.addConstraint (c0, cc) p.setObjective (3 - x[0]) mysol = [0, 0, 1, 1, 1, 1.4] # add a variable with its coefficients p.addcols ([4], [0,3], [c0,4,2], [-3, 2.4, 1.4], [0], [2], ['YY'], ['B']) p.write ("problem1", "lp") # load a MIP solution p.loadmipsol ([0,0,1,1,1,1.4])
def solve_via_data(self, data, warm_start, verbose, solver_opts, solver_cache=None): import xpress if 'no_qp_reduction' in solver_opts.keys( ) and solver_opts['no_qp_reduction'] is True: self.translate_back_QP_ = True c = data[s.C] # objective coefficients dims = dims_to_solver_dict( data[s.DIMS]) # contains number of columns, rows, etc. nrowsEQ = dims[s.EQ_DIM] nrowsLEQ = dims[s.LEQ_DIM] nrows = nrowsEQ + nrowsLEQ # linear constraints b = data[s.B][:nrows] # right-hand side A = data[s.A][:nrows] # coefficient matrix # Problem self.prob_ = xpress.problem() mstart = makeMstart(A, len(c), 1) varGroups = { } # If origprob is passed, used to tie IIS to original constraints transf2Orig = { } # Ties transformation constraints to originals via varGroups nOrigVar = len(c) # Uses flat naming. Warning: this mixes # original with auxiliary variables. varnames = ['x_{0:05d}'.format(i) for i in range(len(c))] linRownames = ['lc_{0:05d}'.format(i) for i in range(len(b))] self.prob_.loadproblem( "CVXproblem", ['E'] * nrowsEQ + ['L'] * nrowsLEQ, # qrtypes None, # range c, # obj coeff mstart, # mstart None, # mnel A.indices, # row indices A.data, # coefficients [-xpress.infinity] * len(c), # lower bound [xpress.infinity] * len(c), # upper bound colnames=varnames, # column names rownames=linRownames) # row names x = np.array(self.prob_.getVariable()) # get whole variable vector # Set variable types for discrete variables self.prob_.chgcoltype( data[s.BOOL_IDX] + data[s.INT_IDX], 'B' * len(data[s.BOOL_IDX]) + 'I' * len(data[s.INT_IDX])) currow = nrows iCone = 0 auxVars = set(range(nOrigVar, len(c))) # Conic constraints # # Quadratic objective and constraints fall in this category, # as all quadratic stuff is converted into a cone via a linear transformation for k in dims[s.SOC_DIM]: # k is the size of the i-th cone, where i is the index # within dims [s.SOC_DIM]. The cone variables in # CVXOPT, apparently, are separate variables that are # marked as conic but not shown in a cone explicitly. A = data[s.A][currow:currow + k].tocsr() b = data[s.B][currow:currow + k] currow += k if self.translate_back_QP_: # Conic problem passed by CVXPY is translated back # into a QP problem. The problem is passed to us # as follows: # # min c'x # s.t. Ax <>= b # y[i] = P[i]' * x + b[i] # ||y[i][1:]||_2 <= y[i][0] # # where P[i] is a matrix, b[i] is a vector. Get # rid of the y variables by explicitly rewriting # the conic constraint as quadratic: # # y[i][1:]' * y[i][1:] <= y[i][0]^2 # # and hence # # (P[i][1:]' * x + b[i][1:])^2 <= (P[i][0]' * x + b[i][0])^2 Plhs = A[1:] Prhs = A[0] indRowL, indColL = Plhs.nonzero() indRowR, indColR = Prhs.nonzero() coeL = Plhs.data coeR = Prhs.data lhs = list(b[1:]) rhs = b[0] for i in range(len(coeL)): lhs[indRowL[i]] -= coeL[i] * x[indColL[i]] for i in range(len(coeR)): rhs -= coeR[i] * x[indColR[i]] self.prob_.addConstraint( xpress.Sum([lhs[i]**2 for i in range(len(lhs))]) <= rhs**2) else: # Create new (cone) variables and add them to the problem conevar = np.array([ xpress.var(name='cX{0:d}_{1:d}'.format(iCone, i), lb=-xpress.infinity if i > 0 else 0) for i in range(k) ]) self.prob_.addVariable(conevar) initrow = self.prob_.attributes.rows mstart = makeMstart(A, k, 0) trNames = [ 'linT_qc{0:d}_{1:d}'.format(iCone, i) for i in range(k) ] # Linear transformation for cone variables <--> original variables self.prob_.addrows( ['E'] * k, # qrtypes b, # rhs mstart, # mstart A.indices, # ind A.data, # dmatval names=trNames) # row names self.prob_.chgmcoef([initrow + i for i in range(k)], conevar, [1] * k) conename = 'cone_qc{0:d}'.format(iCone) # Real cone on the cone variables (if k == 1 there's no # need for this constraint as y**2 >= 0 is redundant) if k > 1: self.prob_.addConstraint( xpress.constraint(constraint=xpress.Sum( conevar[i]**2 for i in range(1, k)) <= conevar[0]**2, name=conename)) auxInd = list(set(A.indices) & auxVars) if len(auxInd) > 0: group = varGroups[varnames[auxInd[0]]] for i in trNames: transf2Orig[i] = group transf2Orig[conename] = group iCone += 1 # Objective. Minimize is by default both here and in CVXOPT self.prob_.setObjective(xpress.Sum(c[i] * x[i] for i in range(len(c)))) # End of the conditional (warm-start vs. no warm-start) code, # set options, solve, and report. # Set options # # The parameter solver_opts is a dictionary that contains only # one key, 'solver_opt', and its value is a dictionary # {'control': value}, matching perfectly the format used by # the Xpress Python interface. if verbose: self.prob_.controls.miplog = 2 self.prob_.controls.lplog = 1 self.prob_.controls.outputlog = 1 else: self.prob_.controls.miplog = 0 self.prob_.controls.lplog = 0 self.prob_.controls.outputlog = 0 if 'solver_opts' in solver_opts.keys(): self.prob_.setControl(solver_opts['solver_opts']) self.prob_.setControl({ i: solver_opts[i] for i in solver_opts.keys() if i in xpress.controls.__dict__.keys() }) # Solve self.prob_.solve() results_dict = { 'problem': self.prob_, 'status': self.prob_.getProbStatus(), 'obj_value': self.prob_.getObjVal(), } status_map_lp, status_map_mip = self.get_status_maps() if self.is_mip(data): status = status_map_mip[results_dict['status']] else: status = status_map_lp[results_dict['status']] results_dict[s.XPRESS_TROW] = transf2Orig results_dict[ s.XPRESS_IIS] = None # Return no IIS if problem is feasible if status in s.SOLUTION_PRESENT: results_dict['x'] = self.prob_.getSolution() if not (data[s.BOOL_IDX] or data[s.INT_IDX]): results_dict['y'] = self.prob_.getDual() elif status == s.INFEASIBLE: # Retrieve all IIS. For LPs there can be more than one, # but for QCQPs there is only support for one IIS. iisIndex = 0 self.prob_.iisfirst(0) # compute all IIS row, col, rtype, btype, duals, rdcs, isrows, icols = [], [], [], [], [], [], [], [] self.prob_.getiisdata(0, row, col, rtype, btype, duals, rdcs, isrows, icols) origrow = [] for iRow in row: if iRow.name in transf2Orig.keys(): name = transf2Orig[iRow.name] else: name = iRow.name if name not in origrow: origrow.append(name) results_dict[s.XPRESS_IIS] = [{ 'orig_row': origrow, 'row': row, 'col': col, 'rtype': rtype, 'btype': btype, 'duals': duals, 'redcost': rdcs, 'isolrow': isrows, 'isolcol': icols }] while self.prob_.iisnext() == 0: iisIndex += 1 self.prob_.getiisdata(iisIndex, row, col, rtype, btype, duals, rdcs, isrows, icols) results_dict[s.XPRESS_IIS].append( (row, col, rtype, btype, duals, rdcs, isrows, icols)) # Generate solution. solution = {} if results_dict["status"] != s.SOLVER_ERROR: self.prob_ = results_dict['problem'] vartypes = [] self.prob_.getcoltype(vartypes, 0, len(data[s.C]) - 1) status_map_lp, status_map_mip = self.get_status_maps() if data[s.BOOL_IDX] or data[s.INT_IDX]: solution[s.STATUS] = status_map_mip[results_dict['status']] else: solution[s.STATUS] = status_map_lp[results_dict['status']] if solution[s.STATUS] in s.SOLUTION_PRESENT: solution[s.PRIMAL] = results_dict['x'] solution[s.VALUE] = results_dict['obj_value'] if not (data[s.BOOL_IDX] or data[s.INT_IDX]): solution[s.EQ_DUAL] = [-v for v in results_dict['y']] solution[s.XPRESS_IIS] = results_dict[s.XPRESS_IIS] solution[s.XPRESS_TROW] = results_dict[s.XPRESS_TROW] return solution
def max_flow(): global graph global vertices_no global vertices global weight thres = 0.4 # density of network thresdem = 0.8 # density of demand mesh dem = [] for i in range(vertices_no): for j in range(vertices_no): if i != j and np.random.random() < thresdem: dem.append((vertices[i], vertices[j], math.ceil(200 * np.random.random()))) print("This is a random demand for each node", dem) for i in range(vertices_no): for j in range(vertices_no): if graph[i][j] != 0: pair = vertices[i], vertices[j] print(pair) #*********************************************************************************************************************** #flow variables f = {(i, j, d): xp.var(name='f_{0}_{1}_{2}_{3}'.format(i, j, dem[d][0], dem[d][1])) for (i, j) in pair for d in range(len(dem))} # capacity variables x = {(i, j): xp.var(vartype=xp.integer, name='cap_{0}_{1}'.format(i, j)) for (i, j) in pair} print("this is x", x) p = xp.problem('network flow') p.addVariable(f, x) def demand(i, d): if dem[d][0] == i: # source return 1 elif dem[d][1] == i: # destination return -1 else: return 0 # Flow conservation constraints: total flow balance at node i for each demand d # must be 0 if i is an intermediate node, 1 if i is the source of demand d, and # -1 if i is the destination. flow = {(i, d): xp.constraint( constraint=xp.Sum(f[vertices[i], vertices[j], d] for j in range(vertices_no) if (vertices[i], vertices[j]) in weight) - xp.Sum(f[vertices[j], vertices[i], d] for j in range(vertices_no) if (vertices[j], vertices[i]) in weight) == demand( vertices[i], d), name='cons_{0}_{1}_{2}'.format(i, dem[d][0], dem[d][1])) for d in range(len(dem)) for i in range(vertices_no)} # Capacity constraints: weighted sum of flow variables must be contained in the # total capacity installed on the arc (i, j) capacity = { (i, j): xp.constraint(constraint=xp.Sum( dem[d][2] * f[vertices[i], vertices[j], d] for d in range(len(dem))) <= x[vertices[i], vertices[j]], name='capacity_{0}_{1}'.format(vertices[i], vertices[j])) for (i, j) in weight } p.addConstraint(flow, capacity) p.setObjective(xp.Sum(x[i, j] for (i, j) in weight)) p.solve() p.getSolution()
def enforce_hospital_limit(self, doctor): hospital_limit = xp.constraint( xp.Sum(self.x[p] for p in self.x if p.doctor == doctor and p.hospital not in doctor.work_locations) == 0, name=f'Enforce hospital limits for doctor {doctor.name}') self.model.addConstraint(hospital_limit)