def update_linear_program(self): self.compute_constraints(self.aa_ori, self.model.bpt) solver = os.getenv('SOLVER', 'cplex') if solver == 'glpk': import pymprog self.lp = pymprog.model('lp_elecre_tri_weights') self.lp.verb = verbose self.add_variables_glpk() self.add_constraints_glpk() self.add_objective_glpk() self.solve_function = self.solve_glpk elif solver == 'scip': from zibopt import scip self.lp = scip.solver(quiet=not verbose) self.add_variables_scip() self.add_constraints_scip() self.add_objective_scip() self.solve_function = self.solve_scip elif solver == 'cplex': import cplex solver_max_threads = int(os.getenv('SOLVER_MAX_THREADS', 0)) self.lp = cplex.Cplex() self.lp.parameters.threads.set(solver_max_threads) if verbose is False: self.lp.set_log_stream(None) self.lp.set_results_stream(None) # self.lp.set_warning_stream(None) # self.lp.set_error_stream(None) self.add_variables_cplex() self.add_constraints_cplex() self.add_objective_cplex() self.solve_function = self.solve_cplex else: raise NameError('Invalid solver selected')
def __init__(self, c, cs, cat, gi_worst, gi_best): self.criteria = c self.cs = cs self.cat = { cat: i+1 \ for i, cat in enumerate(cat.get_ordered_categories()) } self.gi_worst = gi_worst self.gi_best = gi_best self.__compute_abscissa() solver = os.getenv('SOLVER', 'cplex') if solver == 'cplex': import cplex solver_max_threads = int(os.getenv('SOLVER_MAX_THREADS', 0)) self.lp = cplex.Cplex() self.lp.parameters.threads.set(solver_max_threads) self.encode_constraints = self.encode_constraints_cplex self.add_objective = self.add_objective_cplex self.solve_function = self.solve_cplex if verbose is False: self.lp.set_log_stream(None) self.lp.set_results_stream(None) elif solver == 'glpk': import pymprog self.lp = pymprog.model('lp_avfsort') self.lp.verb = verbose self.encode_constraints = self.encode_constraints_glpk self.add_objective = self.add_objective_glpk self.solve_function = self.solve_glpk else: raise NameError('Invalid solver selected')
def solve(COURSES, STUDENTS): print("COURSES = " + str(COURSES)) print("STUDENTS = " + str(STUDENTS)) # indexing the set of students S = range(len(STUDENTS)) # indexing the set of courses C = range(len(COURSES)) # Counting vows # vow_set = set() # for s in S: # for vow in STUDENTS[s].vows: # vow_set.add(vow) NB_VOWS = 2 print("NB_VOWS = " + str(NB_VOWS)) # indexation of wishes for cartesian product V = range(NB_VOWS) # cartesian product of S and W SxV = iprod(S, V) # solving linear problem assignment_model = model("assign") # Variables course_is_open = assignment_model.var("course is open", C) course_headcount = assignment_model.var("course headcount", C) student_gets_vow = assignment_model.var("student gets wish", SxV) # Objective function assignment_model.min( sum(STUDENTS[s].vows[v].weight * student_gets_vow[s, v] for s, v in SxV)) # Constraints for student in S: sum(student_gets_vow[student, v] for v in V) == 1 for course in C: course_is_open[course] <= 1 sum(student_gets_vow[s, v] * int(COURSES[course] in STUDENTS[s].vows[v].courses) for s, v in SxV) == course_headcount[course] COURSES[course].min_students * course_is_open[ course] <= course_headcount[course] COURSES[course].max_students * course_is_open[ course] >= course_headcount[course] # Solve assignment_model.solve() # building results result = [0 for student in S] for student, vow in SxV: if student_gets_vow[student, vow].primal != 0: result[student] = vow STUDENTS[student].courses = STUDENTS[student].vows[vow].courses return result
def _minimize_skillqueue(self, sp_queue, implant_level): attribute_sum = (20 + implant_level) * len(EveSkillQueue.attribute_ids) p = pymprog.model(self.__class__.__name__) p.verbose(False) x = p.var('x', len(EveSkillQueue.attribute_ids), int, bounds=(5 + implant_level, 35 + implant_level)) p.maximize( sum( sum(elem[i] * x[i] for i in range(len(EveSkillQueue.attribute_ids))) for elem in sp_queue)) sum([x[i] for i in range(len(EveSkillQueue.attribute_ids)) ]) == attribute_sum p.solver('intopt', msg_lev=pymprog.glpk.GLP_MSG_OFF) p.solve() opt_profile = [0] * len(EveSkillQueue.attribute_ids) if p.get_status() in [pymprog.glpk.GLP_OPT, pymprog.glpk.GLP_FEAS]: opt_profile = [ int(x[i].primal) - implant_level for i in range(len(EveSkillQueue.attribute_ids)) ] p.end() return opt_profile
def solveLP(n, data, returnBack): startTime = datetime.datetime.now() V = range(1, n+1) E = data.keys() p = pymprog.model("tsp") x = p.var(E, 'x', bool) # x created over index set E. v = p.var(V, 'v', bool) # v[k] == 1 implies last node, sum(y[k, i] = 0) # minize the total travel distance p.min(sum([data[t]*x[t] for t in E] + [v[i] for i in V]), 'totaldist') # subject to: leave each city exactly once p.st([sum([x[k,j] for j in V if (k,j) in E] + [v[k]])==1 for k in V], 'leave') # subject to: enter each city exactly once if returnBack: p.st([sum(x[i,k] for i in V if (i,k) in E)==1 for k in V], 'enter') else: p.st([sum(x[i,k] for i in V if (i,k) in E)==1 for k in V[1:]], 'enter') # We then need some flow constraints to eliminate subtours. # y: the number of cars carried: endowed with n at city 1. # exactly one car will be sold at each city. y=p.var(E, 'y') p.st([(n-1)*x[t] >= y[t] for t in E], 'cap') p.st([sum(y[i,k] for i in V if (i,k) in E) + (n if k==1 else 0) ==sum(y[k,j] for j in V if (k,j) in E) + 1 for k in V], 'sale') if not returnBack: # Without this, simply all v[i] == 0 # v[i] set only when sum(y[i,j] for j in V) p.st([sum([y[i,j] for j in V] + [v[i]]) >= 1 for k in V], 'open') p.solve(float) #solve as LP only. #print "simplex done:", p.status() p.solve(int) #solve the IP problem print(p.vobj()) # print "Edges:" # for t in E: # print "x%s = %d" % (t, x[t].primal) # print "T's:" # for t in E: # print "y%s = %d" % (t, y[t].primal) # print "vs:" # for d in V: # print "v[%d] = %d" % (d, v[d].primal) tour = [t for t in E if x[t].primal>.5] # list of tuples (i,j) tour.sort(key=lambda t: n-y[t].primal-1) # organize links by car number in reverse # automatically includes trailing [1] if returnBack itinerary = [1] + [v[1] for v in tour] endTime = datetime.datetime.now() print "Time required to solve LP: %f" % (endTime - startTime).total_seconds() return itinerary
def setInequalityConstraints(self, Aineq=None, bineq=None): """ set inequality constraints for optimization (or unset if None) """ nCols = len(self.lb) if Aineq is None: nIneq = 0 else: nIneq = len(Aineq) if nIneq != len(bineq): raise ValueError( "Error: Matrix and vector dimensions disagree " "in inequality constraints") if nIneq > 0 and len(Aineq[0]) != nCols: raise ValueError("Error: Matrix Aineq has wrong number of " "columns.") self.Aineq = Aineq self.bineq = bineq if self.solver == _GLPK: # Construct new model object (because constraints can't be unset) rangeCols = range(nCols) self.glpkP = pymprog.model('LP') self.glpkVar = self.glpkP.var('flux', rangeCols, bounds=(None, None)) self.glpkP.solver("simplex", msg_lev=pymprog.glpk.GLP_MSG_ERR) nRows = len(self.Aeq) # Mass balance constraints self.glpkP.st( sum(self.glpkVar[j] * float(self.Aeq[i][j]) for j in rangeCols if self.Aeq[i][j] != 0.) == float(self.beq[i]) for i in range(nRows)) # Flux bounds constraints self.glpkP.st([ self.glpkVar[i] >= float(self.lb[i]) for i in rangeCols if not isinf(self.lb[i]) ]) self.glpkP.st([ self.glpkVar[i] <= float(self.ub[i]) for i in rangeCols if not isinf(self.ub[i]) ]) if nIneq: # Inequality constraints self.glpkP.st( sum(self.glpkVar[j] * float(Aineq[i][j]) for j in rangeCols if Aineq[i][j] != 0.) <= float(bineq[i]) for i in range(nIneq))
def initVariables(self,necessary=True, maxEpsilon=0): self.solver = pymprog.model('electreHROR') self.epsilon = self.solver.var() self.v = self.solver.var(self.EL, 'veto', int) #słowo lambda jest zarezerowane w pythonie self.lamba = self.solver.var(self.TreeWithoutLeaves, 'lambda') mrBoard = pymprog.iprod(self.TreeWithoutLeaves,self.alternativesIDs, self.alternativesIDs) self.Mr = self.solver.var(mrBoard, 'Mr', bool) self.delta = self.solver.var(self.TreeWithoutLeaves, 'delta', int) mtBoard = pymprog.iprod(self.EL, self.alternativesIDs, self.alternativesIDs) self.Mt = self.solver.var(mtBoard, 'Mt', bool) aBoard = pymprog.iprod(self.EL, self.alternativesIDs, self.alternativesIDs) self.psi = self.solver.var(aBoard, 'psi', float) #self.solver.min(self.epsilon) self.solver.max(self.epsilon) self.addConstraints(necessary)
def solve(map): ''' Solves the problem, given a map as described in http://wiki.dropbox.com/Drew. ''' # First begin by dissecting the map. length holds the total number # of blocks and space on the map. length = len(map) # Edge case. if length <= 1: return '' # Make a rough estimate of the worst case time needed to complete the map. # I calculated worst case using maps like x.5x and x.5x.5x. They yielded # approximates a traversal time that was twice the number of blocks. To # be safe, we'll allocate three times the amount. time = 3*length # Create the model. model = pymprog.model('megaman') # The board has axis of blocks and time. board = model.var(pymprog.iprod(range(time),range(length)),'Board',bool) # Add the constraints from the map. addMapConstraints(model,board,map,time) # Add the constraints from the baord. addBoardConstraints(model,board,length,time) # Prefer the shorter solutions. model.min(sum(i*board[i,j]/(j+1) for i in range(time) for j in range(length))) # Solve the problem. model.solvopt(method='exact', verbosity=2) model.solve(int) # Print some results. for i in range(time): for j in range(length): print int(board[i,j].primal) , print
def initVariables(self, necessary=True, maxEpsilon=0): self.solver = pymprog.model('electreHROR') self.epsilon = self.solver.var() self.v = self.solver.var(self.EL, 'veto', int) #słowo lambda jest zarezerowane w pythonie self.lamba = self.solver.var(self.TreeWithoutLeaves, 'lambda') mrBoard = pymprog.iprod(self.TreeWithoutLeaves, self.alternativesIDs, self.alternativesIDs) self.Mr = self.solver.var(mrBoard, 'Mr', bool) self.delta = self.solver.var(self.TreeWithoutLeaves, 'delta', int) mtBoard = pymprog.iprod(self.EL, self.alternativesIDs, self.alternativesIDs) self.Mt = self.solver.var(mtBoard, 'Mt', bool) aBoard = pymprog.iprod(self.EL, self.alternativesIDs, self.alternativesIDs) self.psi = self.solver.var(aBoard, 'psi', float) #self.solver.min(self.epsilon) self.solver.max(self.epsilon) self.addConstraints(necessary)
def solve_vrp(self): n, c, k, d, C, V, E = self.n, self.c, self.k, self.d, self.C, self.V, self.E def b(s): return int(ceil(float(sum(d[i] for i in s)) / C)) p = pymprog.model("vrp") x = p.var(E, 'x') # x created over index set E # set bounds for e in E: if 1 in e: 0 <= x[e] <= 2 else: 0 <= x[e] <= 1 # minimize the total travel distance p.min(sum(c[e] * x[e] for e in E), 'totaldist') p.st(sum(x[e] for e in E if e[0] == 1) == 2 * k) for i in V[1:]: adj = [e for e in E if i in e] if not adj: continue p.st(sum(x[e] for e in adj) == 2) for s in filter(lambda s: len(s) > 1, powerset(V[1:])): p.st( sum(x[e] for e in E if (e[0] in s and e[1] not in s) or (e[0] not in s and e[1] in s)) >= 2 * b(s)) p.solve(float) if options.verbose: print "simplex done:", p.status() p.solve(int) if options.verbose: print(p.vobj()) for e in E: if x[e].primal > 0: print x[e].name, x[e].primal self.x = x
def solve_vrp(self): n, c, k, d, C, V, E = self.n, self.c, self.k, self.d, self.C, self.V, self.E def b(s): return int(ceil(float(sum(d[i] for i in s))/C)) p = pymprog.model("vrp") x = p.var(E, 'x') # x created over index set E # set bounds for e in E: if 1 in e: 0 <= x[e] <= 2 else: 0 <= x[e] <= 1 # minimize the total travel distance p.min(sum(c[e]*x[e] for e in E), 'totaldist') p.st(sum(x[e] for e in E if e[0]==1) == 2*k) for i in V[1:]: adj = [e for e in E if i in e] if not adj: continue p.st(sum(x[e] for e in adj) == 2) for s in filter(lambda s: len(s)>1, powerset(V[1:])): p.st(sum(x[e] for e in E if (e[0] in s and e[1] not in s) or (e[0] not in s and e[1] in s)) >= 2*b(s)) p.solve(float) if options.verbose: print "simplex done:", p.status() p.solve(int) if options.verbose: print(p.vobj()) for e in E: if x[e].primal > 0: print x[e].name, x[e].primal self.x = x
XLnXS_i_t_DF.loc[DFrowLIST[counter-1],'Cost_i_t'] = int(xs[i].primal)*75 + int(xl[i].primal)*200 #3 : 'Cost_i_t' Total_Cost = Total_Cost + int(xs[i].primal)*75 + int(xl[i].primal)*200 print("\n") print(XLnXS_i_t_DF.loc[DFrowLIST[counter-1],'Road_ID_t'] ," =(?)=", p.get_col_name(counter) ) #," =(?)=", counter += 1 XLnXS_i_t_DF.name = ("Total Cost ="+str(Total_Cost)) print("\n\nTotal Cost = ",XLnXS_i_t_DF.name," =(?)= ",p.vobj(),"\n",XLnXS_i_t_DF) return XLnXS_i_t_DF p = model("5 roads and 1 period_AGE_is_a_Variable") print(p.get_prob_name()) p.verbose(True) from __future__ import print_function import pymprog as PYM # from pymprog import * np.set_printoptions(precision = 2, linewidth = 400) from random import Random ## Model Data ## rand = Random() roads =5 time = 1 # initializing indices M = [(i,t) for i in range(roads) for t in range(time+1)]
from pymprog import model p = model("5 roads and 1 period") print(p.get_prob_name()) p.verbose(True) from __future__ import print_function import numpy as np import pymprog as PYM # from pymprog import * np.set_printoptions(precision = 2, linewidth = 400) from random import Random rand = Random() roads =5 time = 1 # initializing indices M = [(i,t) for i in range(roads) for t in range(time+1)] age_i_0 = np.array(np.random.randint(1,11,(5,1))) PYM.begin(p) # p.solver(int, br_tech=PYM.glpk.GLP_BR_PCH) # begin("5 roads and 1 period") #action variables xl is large action, xs is small action ## Slice notation a[start_index:end_index:step] xl = p.var('xl', M[1::2], bool) xs = p.var('xs', M[1::2], bool) #age variables not above 10 years old # age_i_t = p.var('age',M, bounds = (0,10)) print("\n*Variables*\n"),xl, print("\n**\n"),xs, print("\n**\n")#,age_i_t #Setting objective function p.minimize(sum(xl[i]*200+xs[i]*75 for i in M[1::2]),'Cost')
dispGASlotCount = False dispOffSch = False ## Problem Config ## gaList = ['Appurv', 'Ashley', 'Bianca', 'Kristy', 'Rob', 'Jason', 'Davona', 'Rocky', 'Taylor', 'Praveen','Alex', 'Laurel', 'Harshita', 'Thomas','Chris', 'Tyler', 'Angela'] reqSlotsPerGA = [18,9,9,9,9,9,9,9,9,9,9,9,4,4,4,4,4] officeList = ['RiverKnoll', 'Colony'] daysList = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'] slotsList = ['9am - 10am', '10am - 11am', '11am - 12pm', '12pm - 1pm', '1pm - 2pm', '2pm - 3pm', '3pm - 4pm', '4pm - 5pm'] ## Mathematical Model ## p = math.model('Shift Assignment') # Starting the model #math.verbose(True) ## Sets ## GAs = range(len(gaList)) # Set of student staff SLOTS = range(len(slotsList)) # Set of shift slots per day (1/2 hour each) OFFICES = range(len(officeList)) # Set of offices DAYS = range(len(daysList)) # Set of days when office is open ## Params ## alpha = [[[1 for k in DAYS] for j in SLOTS] for i in GAs] kappa = [0 for k in GAs] maxSlots = 5 # Max consecutive slots allowed in an assignment minSlots = 2 # Min consecutive slots required in an assignment
def allocate_cpu_and_start_stretch(numhosts, cputotals, jobhosts, jobcpus, next_per_mcb8_time, target): global time T = 1000 * (next_per_mcb8_time - time) allocs = set() if not jobhosts: return allocs if target == "minmaxstretch": allocs = set(Alloc(job, 1, hosts) for job, hosts in jobhosts.iteritems()) cpuloads = [0] * numhosts for alloc in allocs: for host, count in alloc.hosts.iteritems(): cpuloads[host] += count improvableallocs = set(alloc for alloc in allocs if alloc.cpu < alloc.job.cpu) avgyields = dict((alloc.job, calcavgyield(alloc.job, alloc.cpu, T)) for alloc in improvableallocs) while improvableallocs: minavgyield = min(avgyields.values()) lowallocs = sorted((alloc for alloc in improvableallocs if avgyields[alloc.job] == minavgyield), key=(lambda a: invpri(a.job))) for alloc in lowallocs: alloc.cpu += 1 for host, count in alloc.hosts.iteritems(): cpuloads[host] += count if max(cpuloads) > 100: improvableallocs.remove(alloc) del avgyields[alloc.job] alloc.cpu -= 1 for host, count in alloc.hosts.iteritems(): cpuloads[host] -= count elif alloc.cpu == alloc.job.cpu: improvableallocs.remove(alloc) del avgyields[alloc.job] else: avgyields[alloc.job] = calcavgyield(alloc.job, alloc.cpu, T) if max(cpuloads) > 100: print "ERROR: invalid set of allocations at time:", time for alloc in allocs: start_alloc(alloc) return allocs prob = pymprog.model('maximize total avg yield') cols = prob.var(jobhosts.keys()) prob.max(sum((job.vtmsecs() + cols[job] * T) / (job.ftmsecs() + T) for job in jobhosts), 'total time weighted average yield') prob.st(jobcpus[job] <= cols[job] <= job.cpu for job in jobhosts) prob.st(sum(cols[job] * jobhosts[job][host] for job in jobhosts) <= 100 for host in range(numhosts)) prob.solve() cpuloads = [0] * numhosts for job, hosts in jobhosts.iteritems(): alloc = Alloc(job, max(1, int(cols[job].primal)), hosts) allocs.add(alloc) start_alloc(alloc) for host, count in hosts.iteritems(): cpuloads[host] += alloc.cpu * count if max(cpuloads) > 100: print "ERROR: invalid set of allocations at time:", time return allocs
# Regionalize demand stationDemand = [[100][400][300][100][250][600][50][200][75] [30][600][400][100][350][125][500][700][10] [40][400][200][75][500]] # Set up capacity constraints - cost per unit of energy energy delivered by mile - to fix pipeCapacity = 5 truckCapacity = 2 # Create empty problem instance p = pymprog.model('FuelNetworkCosts') #create variables: (set up #legs as binary variables to either be truck or pipeline binaryLegs = [] # set up minimization as objective function - minimizing cost with either option p.min(numpy.outer(binaryLegs, latLongDistArray)*pipeCapacity - numpy.outer((1-binaryLegs)*stationDemand/truckCapacity), 'myobj') #constraints r=p.st( binaryLegs = (0 or 1): for each i in range(0, x): binaryLegs[x, 0]*pipeCapacity*latLongDistArray[x,0] - (1-binaryLegs[x,0])*stationDemand/truckCapacity >= stationDemand[x,0] )
def __init__(self, model, pt, aa, epsilon = 0.0001): self.pt = pt self.aa = aa self.model = model self.criteria = model.criteria.get_active() self.cps = model.categories_profiles self.epsilon = epsilon self.__profiles = self.cps.get_ordered_profiles() self.__categories = self.cps.get_ordered_categories() self.pt.update_direction(model.criteria) if self.model.bpt is not None: self.model.bpt.update_direction(model.criteria) tmp_pt = self.pt.copy() for bp in self.model.bpt: tmp_pt.append(bp) self.ap_min = tmp_pt.get_min() self.ap_max = tmp_pt.get_max() self.ap_range = tmp_pt.get_range() else: self.ap_min = self.pt.get_min() self.ap_max = self.pt.get_max() self.ap_range = self.pt.get_range() for c in self.criteria: self.ap_min.performances[c.id] -= self.epsilon self.ap_max.performances[c.id] += self.epsilon self.ap_range.performances[c.id] += 2 * self.epsilon * 100 solver = os.getenv('SOLVER', 'cplex') if solver == 'glpk': import pymprog self.lp = pymprog.model('lp_elecre_tri_weights') self.lp.verb = verbose self.add_variables_glpk() self.add_constraints_glpk() self.add_extra_constraints_glpk() self.add_objective_glpk() self.solve_function = self.solve_glpk elif solver == 'cplex': import cplex solver_max_threads = int(os.getenv('SOLVER_MAX_THREADS', 0)) self.lp = cplex.Cplex() self.lp.parameters.threads.set(solver_max_threads) self.add_variables_cplex() self.add_constraints_cplex() self.add_extra_constraints_cplex() self.add_objective_cplex() self.solve_function = self.solve_cplex if verbose is False: self.lp.set_log_stream(None) self.lp.set_results_stream(None) # self.lp.set_warning_stream(None) # self.lp.set_error_stream(None) else: raise NameError('Invalid solver selected') self.pt.update_direction(model.criteria) if self.model.bpt is not None: self.model.bpt.update_direction(model.criteria)
def __init__(self, model, pt, aa, epsilon=0.0001): self.pt = pt self.aa = aa self.model = model self.criteria = model.criteria.get_active() self.cps = model.categories_profiles self.epsilon = epsilon self.__profiles = self.cps.get_ordered_profiles() self.__categories = self.cps.get_ordered_categories() self.pt.update_direction(model.criteria) if self.model.bpt is not None: self.model.bpt.update_direction(model.criteria) tmp_pt = self.pt.copy() for bp in self.model.bpt: tmp_pt.append(bp) self.ap_min = tmp_pt.get_min() self.ap_max = tmp_pt.get_max() self.ap_range = tmp_pt.get_range() else: self.ap_min = self.pt.get_min() self.ap_max = self.pt.get_max() self.ap_range = self.pt.get_range() for c in self.criteria: self.ap_min.performances[c.id] -= self.epsilon self.ap_max.performances[c.id] += self.epsilon self.ap_range.performances[c.id] += 2 * self.epsilon * 100 solver = os.getenv('SOLVER', 'cplex') if solver == 'glpk': import pymprog self.lp = pymprog.model('lp_elecre_tri_weights') self.lp.verb = verbose self.add_variables_glpk() self.add_constraints_glpk() self.add_extra_constraints_glpk() self.add_objective_glpk() self.solve_function = self.solve_glpk elif solver == 'cplex': import cplex solver_max_threads = int(os.getenv('SOLVER_MAX_THREADS', 0)) self.lp = cplex.Cplex() self.lp.parameters.threads.set(solver_max_threads) self.add_variables_cplex() self.add_constraints_cplex() self.add_extra_constraints_cplex() self.add_objective_cplex() self.solve_function = self.solve_cplex if verbose is False: self.lp.set_log_stream(None) self.lp.set_results_stream(None) # self.lp.set_warning_stream(None) # self.lp.set_error_stream(None) else: raise NameError('Invalid solver selected') self.pt.update_direction(model.criteria) if self.model.bpt is not None: self.model.bpt.update_direction(model.criteria)
def allocate_cpu_and_start_stretch(numhosts, cputotals, jobhosts, jobcpus, next_per_mcb8_time, target): global time T = 1000 * (next_per_mcb8_time - time) allocs = set() if not jobhosts: return allocs if target == "minmaxstretch": allocs = set( Alloc(job, 1, hosts) for job, hosts in jobhosts.iteritems()) cpuloads = [0] * numhosts for alloc in allocs: for host, count in alloc.hosts.iteritems(): cpuloads[host] += count improvableallocs = set(alloc for alloc in allocs if alloc.cpu < alloc.job.cpu) avgyields = dict((alloc.job, calcavgyield(alloc.job, alloc.cpu, T)) for alloc in improvableallocs) while improvableallocs: minavgyield = min(avgyields.values()) lowallocs = sorted((alloc for alloc in improvableallocs if avgyields[alloc.job] == minavgyield), key=(lambda a: invpri(a.job))) for alloc in lowallocs: alloc.cpu += 1 for host, count in alloc.hosts.iteritems(): cpuloads[host] += count if max(cpuloads) > 100: improvableallocs.remove(alloc) del avgyields[alloc.job] alloc.cpu -= 1 for host, count in alloc.hosts.iteritems(): cpuloads[host] -= count elif alloc.cpu == alloc.job.cpu: improvableallocs.remove(alloc) del avgyields[alloc.job] else: avgyields[alloc.job] = calcavgyield( alloc.job, alloc.cpu, T) if max(cpuloads) > 100: print "ERROR: invalid set of allocations at time:", time for alloc in allocs: start_alloc(alloc) return allocs prob = pymprog.model('maximize total avg yield') cols = prob.var(jobhosts.keys()) prob.max( sum((job.vtmsecs() + cols[job] * T) / (job.ftmsecs() + T) for job in jobhosts), 'total time weighted average yield') prob.st(jobcpus[job] <= cols[job] <= job.cpu for job in jobhosts) prob.st( sum(cols[job] * jobhosts[job][host] for job in jobhosts) <= 100 for host in range(numhosts)) prob.solve() cpuloads = [0] * numhosts for job, hosts in jobhosts.iteritems(): alloc = Alloc(job, max(1, int(cols[job].primal)), hosts) allocs.add(alloc) start_alloc(alloc) for host, count in hosts.iteritems(): cpuloads[host] += alloc.cpu * count if max(cpuloads) > 100: print "ERROR: invalid set of allocations at time:", time return allocs
def allocate_cpu_and_start(numhosts, cputotals, jobhosts, target): global time allocs = set() if not jobhosts: return allocs if (target == "none"): minyield = float(10000 / max(100, *cputotals)) / 100 for job, hosts in jobhosts.iteritems(): alloc = Alloc(job, max(1, int(minyield * job.cpu)), hosts) allocs.add(alloc) start_alloc(alloc) return allocs elif (target == "maxminyield"): allocs = set( Alloc(job, 1, hosts) for job, hosts in jobhosts.iteritems()) improvableallocs = allocs.copy() unfilledhosts = set(range(numhosts)) cpuneeds = cputotals[:] cpuloads = [0] * numhosts while improvableallocs: unfilledhosts = set(h for h in unfilledhosts if cpuneeds[h]) minyields = dict(( host, min(1.0, float(100 * (100 - cpuloads[host]) / cpuneeds[host]) / 100)) for host in unfilledhosts) minyield = min(minyields.values()) filledhosts = set(h for h in unfilledhosts if minyields[h] == minyield) boundallocs = set(alloc for alloc in improvableallocs if alloc.hosts.hostset() & filledhosts) for alloc in boundallocs: alloc.cpu = max(1, int(minyield * alloc.job.cpu)) for host, count in alloc.hosts.iteritems(): cpuloads[host] += alloc.cpu * count cpuneeds[host] -= alloc.job.cpu * count unfilledhosts -= filledhosts improvableallocs -= boundallocs if max(cpuloads) > 100: print "ERROR: invalid set of allocations at time:", time for alloc in allocs: start_alloc(alloc) return allocs minyield = float(10000 / max(100, *cputotals)) / 100 prob = pymprog.model('2nd phase linear program') cols = prob.var(jobhosts.keys(), 'alloc') if (target == "util"): prob.max(sum(cols[job] for job in jobhosts), 'total utilization') else: prob.max(sum(cols[job] / job.cpu for job in jobhosts), 'total yield') prob.st( max(1, int(minyield * job.cpu)) <= cols[job] <= job.cpu for job in jobhosts) prob.st( sum(cols[job] * jobhosts[job][host] for job in jobhosts) <= 100 for host in range(numhosts)) #prob.solvopt(method='interior') prob.solve() status = prob.status() if status != "opt": print "ERROR: ", time, status cpuloads = [0] * numhosts for job, hosts in jobhosts.iteritems(): alloc = Alloc(job, max(1, int(cols[job].primal)), hosts) allocs.add(alloc) start_alloc(alloc) for host, count in hosts.iteritems(): cpuloads[host] += alloc.cpu * count if max(cpuloads) > 100: print "ERROR: invalid set of allocations at time:", time return allocs
from pymprog import * from pymprog import model c = (10, 6, 4) A = [(1, 1, 1), (9, 4, 5), (2, 2, 6)] b = (10, 60, 30) p = model('basic') p.verbose(True) x = p.var('x', 3) p.maximize(sum(c[i] * x[i] for i in range(3))) for i in range(3): sum(A[i][j] * x[j] for j in range(3)) <= b[i] p.solve() # solve the model p.sensitivity() # sensitivity report p.end()
def allocate_cpu_and_start(numhosts, cputotals, jobhosts, target): global time allocs = set() if not jobhosts: return allocs if (target == "none"): minyield = float(10000 / max(100, *cputotals)) / 100 for job, hosts in jobhosts.iteritems(): alloc = Alloc(job, max(1, int(minyield * job.cpu)), hosts) allocs.add(alloc) start_alloc(alloc) return allocs elif (target == "maxminyield"): allocs = set(Alloc(job, 1, hosts) for job, hosts in jobhosts.iteritems()) improvableallocs = allocs.copy() unfilledhosts = set(range(numhosts)) cpuneeds = cputotals[:] cpuloads = [0] * numhosts while improvableallocs: unfilledhosts = set(h for h in unfilledhosts if cpuneeds[h]) minyields = dict((host, min(1.0, float(100 * (100 - cpuloads[host]) / cpuneeds[host]) / 100)) for host in unfilledhosts) minyield = min(minyields.values()) filledhosts = set( h for h in unfilledhosts if minyields[h] == minyield) boundallocs = set(alloc for alloc in improvableallocs if alloc.hosts.hostset() & filledhosts) for alloc in boundallocs: alloc.cpu = max(1, int(minyield * alloc.job.cpu)) for host, count in alloc.hosts.iteritems(): cpuloads[host] += alloc.cpu * count cpuneeds[host] -= alloc.job.cpu * count unfilledhosts -= filledhosts improvableallocs -= boundallocs if max(cpuloads) > 100: print "ERROR: invalid set of allocations at time:", time for alloc in allocs: start_alloc(alloc) return allocs minyield = float(10000 / max(100, *cputotals)) / 100 prob = pymprog.model('2nd phase linear program') cols = prob.var(jobhosts.keys(), 'alloc') if (target == "util"): prob.max(sum(cols[job] for job in jobhosts), 'total utilization') else: prob.max(sum(cols[job] / job.cpu for job in jobhosts), 'total yield') prob.st(max(1, int(minyield * job.cpu)) <= cols[job] <= job.cpu for job in jobhosts) prob.st(sum(cols[job] * jobhosts[job][host] for job in jobhosts) <= 100 for host in range(numhosts)) #prob.solvopt(method='interior') prob.solve() status = prob.status() if status != "opt": print "ERROR: ", time, status cpuloads = [0] * numhosts for job, hosts in jobhosts.iteritems(): alloc = Alloc(job, max(1, int(cols[job].primal)), hosts) allocs.add(alloc) start_alloc(alloc) for host, count in hosts.iteritems(): cpuloads[host] += alloc.cpu * count if max(cpuloads) > 100: print "ERROR: invalid set of allocations at time:", time return allocs
def __init__(self, Aeq, beq=None, Aineq=None, bineq=None, lb=None, ub=None, solver=solvers['default']): """ initialize the linear problem Keyword arguments: Aeq -- matrix of equality constrains (Aeq * v = beq) beq -- right-hand side vector of equality constraints Aineq -- matrix of inequality constrains (Aineq * v <= bineq) bineq -- right-hand side vector of inequality constraints lb -- list of lower bounds (indexed like matrix columns) ub -- list of upper bounds (indexed like matrix columns) solver -- solver to be used for Linear Programming """ """ with open("parameters.txt", "w") as file: string = str(self) + "\n\n Aeq:\n" + str(Aeq) + "\n\n beq:\n" + str(beq) + "\n\n Aineq:\n" + str(Aineq) + "\n\n bineq:\n" + str(bineq) + "\n\n lb:\n" + str(lb) + "\n\n ub:\n" + str(ub) + "\n\n solver:\n" + str(solver) + "\n" file.write(string) """ try: self.solver = self.solvers[solver.lower()] except KeyError: raise SolverError("Unknown solver '%s'. Try one of the following:" "\n%s" % (solver, self.solvers.keys())) self.Aeq = Aeq self.Aineq = Aineq self.bineq = bineq self.istop = None self.status = SolverStatus.PREPARED # Check matrix and lb/ub vectors nRows = len(self.Aeq) if not nRows: raise ValueError("Error: Matrix Aeq has no rows") nCols = len(self.Aeq[0]) if nCols == 0: raise ValueError("Error: Matrix Aeq has no columns") if lb is None: self.lb = [-inf] * nCols elif len(lb) != nCols: raise ValueError("Error: Vector of lower bounds has wrong length") else: self.lb = lb if ub is None: self.ub = [inf] * nCols elif len(ub) != nCols: raise ValueError("Error: Vector of upper bounds has wrong length") else: self.ub = ub if beq is not None: if len(beq) != nRows: raise ValueError("Error: Right-hand side vector of equalities " "has wrong length") self.beq = beq else: # Default: Right-hand side is zero vector with the same number of # rows as matrix self.beq = [0.] * nRows if Aineq is None: nIneq = 0 else: nIneq = len(Aineq) if len(bineq) != nIneq: raise ValueError( "Error: Matrix and vector dimensions disagree " "in inequality constraints") if nIneq > 0 and len(Aineq[0]) != nCols: raise ValueError("Error: Matrix Aineq has wrong number of " "columns.") # Result values self.obj_value = 0. # value of objective function at solution self.solution = [] # solution vector if self.solver == _GLPK: rangeCols = range(nCols) self.glpkP = pymprog.model('LP') self.glpkVar = self.glpkP.var('flux', rangeCols, bounds=(None, None)) self.glpkP.solver("simplex", msg_lev=pymprog.glpk.GLP_MSG_ERR) # Mass balance and other equality constraints self.glpkP.st( sum(self.glpkVar[j] * float(Aeq[i][j]) for j in rangeCols if Aeq[i][j] != 0.) == float(beq[i]) for i in range(nRows)) # Flux bounds constraints self.glpkP.st([ self.glpkVar[i] >= float(lb[i]) for i in rangeCols if not isinf(lb[i]) ]) self.glpkP.st([ self.glpkVar[i] <= float(ub[i]) for i in rangeCols if not isinf(ub[i]) ]) # Inequality constraints if nIneq: self.glpkP.st( sum(self.glpkVar[j] * float(Aineq[i][j]) for j in rangeCols if Aineq[i][j] != 0.) <= float(bineq[i]) for i in range(nIneq))
(16, 7): 592, (16, 8): 206, (16, 9): 933, (16, 10): 610, (16, 11): 2248, (16, 12): 417, (16, 13): 406, (16, 14): 449, (16, 15): 636, } # set of arcs: (i,j) repr an arc from i to j E = c.keys() import pymprog p = pymprog.model("tsp") x = p.var(E, "x", bool) # x created over index set E. # minize the total travel distance p.min(sum(c[t] * x[t] for t in E), "totaldist") # subject to: leave each city exactly once p.st([sum(x[k, j] for j in V if (k, j) in E) == 1 for k in V], "leave") # subject to: enter each city exactly once p.st([sum(x[i, k] for i in V if (i, k) in E) == 1 for k in V], "enter") # We then need some flow constraints to eliminate subtours. # y: the number of cars carried: endowed with n at city 1. # exactly one car will be sold at each city. y = p.var(E, "y") p.st([(n - 1) * x[t] >= y[t] for t in E], "cap") p.st( [
def setupLP(minPurchaseFlag = False, geoFlag = False, assetFlag = False ): totalDollars = 100000 numFunds = len(fundsDict) fundFees = range(numFunds) fundMinInvest = range(numFunds) bigM = range(numFunds) cid, amounts,fundMins1,fundMins2,binaries = range(numFunds), range(numFunds),range(numFunds),range(numFunds),range(numFunds) #binaries = [0] * numFunds fundList = [] def pretty_print_portfolio(): fundLabels = list() fundWeights = list() fundTickers = list() fundDollars = list() for i in cid: if fundAmounts[i].primal > 0: l = textwrap.wrap(fundList[i]["Name"]+"\n"+fundList[i]["Ticker"], 16) fundTickers.append(fundList[i]["Ticker"]) fundLabels.append("\n".join(l)) fundDollars.append(fundAmounts[i].primal) fundWeights.append(fundAmounts[i].primal/dollars.primal) print("%g %s : %s : %g : binary: %g" % (i, fundList[i]["Ticker"], fundList[i]["Name"], fundAmounts[i].primal, fundMinBinary[i].primal) ) fig = PLT.figure() pie_chart_portfolio(fundLabels, fundWeights, (portfolio.vobj()/dollars.primal), fig) sum = sim_port(fundTickers,fundDollars, fig) print("Total Fees Are: %g" % (portfolio.vobj()/dollars.primal)) return sum for index,k in enumerate(sorted(fundsDict.keys()), start=0): f = fundsDict[k] f['index'] = index fundFees[index] = .01*(float(f['ExpenseRatio']) + float(f['PurchaseFee']) + float(f['RedemptionFee'])) fundMinInvest[index] = atof(f['MinInitialPurchase']) bigM[index] = 1e9 #if fundMinInvest[index] > totalDollars: # bigM[index] = 3*fundMinInvest[index] # else: # bigM[index] = 3*totalDollars # print("%g : %s min is %g" %(index, f['Ticker'], fundMinInvest[index])) fundList.append(f) numGeographies = len(set(fundsDict[k]['Geographical'] for k in fundsDict.keys())) portfolio = pymprog.model('portfolio') fundAmounts = portfolio.var(amounts, 'X') portfolio.min(sum(fundFees[i] * fundAmounts[i] for i in cid), 'port_objective') ######################## ## Constraint 1 ## Sum (Allocations) <= Total Dollars dollars=portfolio.st( ( totalDollars -100 ) <= sum(fundAmounts[i] for i in cid) <= totalDollars ) fundMinBinary = portfolio.var(binaries, 'Bool',bool) ######################################## END CONSTRAINT 1 ######################################## ## Constraint 2 ## For each fund, mininum allocation is met if minPurchaseFlag is True: for i in range(numFunds): # print("st:%g %s min is %g" % (i, fundList[i]['Ticker'], fundMinInvest[i])) fundMins1[i] = portfolio.st( fundAmounts[i] <= (fundMinBinary[i] * bigM[i])) fundMins2[i] = portfolio.st( fundAmounts[i] >= (fundMinInvest[i] - bigM[i] + (fundMinBinary[i] * bigM[i]))) #for i in range(numFunds): # print(fundMins1[i]) # print(fundMins2[i]) ################################ END CONSTRAINT 2 ################################ ## Constraint 3 ## Geo constraints if geoFlag is True: geoLabels = ['Developed', 'Emerging', 'Global', 'Asia', 'US'] numGeoLabels = len(geoLabels) geoWeights = [0.2, 0.3, 0, 0, 0.5] geoConstraints = range(numGeoLabels) fundGeoFlags = range(numGeoLabels) for g in range(numGeoLabels): fundGeoFlags[g] = range(numFunds) for f in range(numFunds): ### if this fund is in this geoGraphy if fundList[f]['Geographical'] == geoLabels[g]: fundGeoFlags[g][f] = 1 else: fundGeoFlags[g][f] = 0 geoConstraints[g] = portfolio.st ( sum ( fundGeoFlags[g][f] * fundAmounts[f] for f in cid ) <= (totalDollars* geoWeights[g])) ######################## END Constraint 3 ################################ ## Constraint 4 ## AssetClass constraints if assetFlag is True: assetClasses = ['Bond', 'RealEstate', 'Stock'] numAssetClasses = len(assetClasses) assetClassWeights = [ 0.2, 0.2, 0.6] assetClassConstraints = range(numAssetClasses) fundAssetFlags = range(numAssetClasses) for a in range(numAssetClasses): fundAssetFlags[a] = range(numFunds) for f in range(numFunds): ### if this fund is in this assetClass if fundList[f]['Asset'] == assetClasses[a]: fundAssetFlags[a][f] = 1 else: fundAssetFlags[a][f] = 0 assetClassConstraints[a] = portfolio.st ( sum ( fundAssetFlags[a][f] * fundAmounts[f] for f in cid ) <= (totalDollars* assetClassWeights[a])) #################################### END Constraint 4 portfolio.solvopt(integer='advanced') portfolio.solve() print "Solve status: ", portfolio.status() #pretty_print_portfolio ( fundAmounts, portfolio, dollars, cid, fundList ) return pretty_print_portfolio ()
0 0 5 0 1 0 3 0 0 * 5 1 4 7 6 9 3 8 2 一个构造精良的数独谜题应该包含有唯一解,且能够通过逻辑推断来解决,尽管有时可能必须通过“猜测并检验”来排除一些选项(这一要求目前还颇受争议)。 寻找答案的复杂度决定了题目的难度;上面这个谜题被认为是简单的谜题,因为我们可以通过直截了当的演绎推理来解决它。 在这个6K的文本文件sudoku.txt(右击并选择“目标另存为……”)中包含有50个不同难度的数独谜题,但保证它们都只有唯一解(文件中的第一个谜题就是上述样例)。 解开这50个谜题,找出每个谜题解答左上角的三个数字并连接起来,给出这些数的和; 举例来说,上述样例解答左上角的三个数字连接起来构成的数是483。 """ from pymprog import model, iprod g = [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 3, 0, 8, 5], [0, 0, 1, 0, 2, 0, 0, 0, 0], [0, 0, 0, 5, 0, 7, 0, 0, 0], [0, 0, 4, 0, 0, 0, 1, 0, 0], [0, 9, 0, 0, 0, 0, 0, 0, 0], [5, 0, 0, 0, 0, 0, 0, 7, 3], [0, 0, 2, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 4, 0, 0, 0, 9]] p = model("sudoku") I = range(1, 10) J = range(1, 10) K = range(1, 10) T = iprod(I, J, K) # 标记元组 x = p.var('x', T, bool) # x[i,j,k] = 1 means cell [i,j] is assigned number k # assign pre-defined numbers using the "givens" [ x[i, j, k] == (1 if g[i - 1][j - 1] == k else 0) for (i, j, k) in T if g[i - 1][j - 1] > 0 ] # each cell must be assigned exactly one number [sum(x[i, j, k] for k in K) == 1 for i in I for j in J]
n = 3 N = range(n) M = [(i, j) for i in N for j in N if i < j] D = (3, 4, 2) #duration of each job L = (0, 2, 0) #earliest start U = (9, 7, 8) #latest finish # from pymprog import * import pymprog as PYM js = PYM.model("job-scheduling") PYM.begin(js) x = js.var('x', N) #start time #MD[i,j] = (D[i]+D[j])/2.0 #T[i] = x[i] + D[i] #y[i,j]<= |T[i]-x[j]-MD[i,j]| #y[i,j] < MD[i,j] <==> overlap betw jobs i,j y = js.var('y', M) #w[i,j]: the 'OR' for |T[i]-x[j]-MD[i,j]| w = js.var('w', M, kind=bool) # z[i,j] >= MD[i,j] - y[i,j] z = js.var('z', M) js.minimize(sum(z[i, j] for i, j in M)) for i, j in M: ((D[i] + D[j]) / 2.0 - (x[i] + D[i] - x[j]) + (U[i] - L[j]) * w[i, j] >= y[i, j]) ((x[i] + D[i] - x[j]) - (D[i] + D[j]) / 2.0 + (U[j] - L[i]) *
def build_model(data, n_cliques = 0): # Load Data Format n = data['n'] r = data['r'] p = data['p'] s = data['s'] c = data['c'] h = data['h'] conflicts = data['conflicts'] locking_times = data['locking_times'] T = data['T'] model = pymprog.model("ExaminationScheduling") print("Building variables...") # x[i,k,l] = 1 if exam i is at time l in room k NxRxP = [ (i,k,l) for i in range(n) for k in range(r) for l in range(p) if T[k][l] == 1 ] x = model.var(NxRxP, 'x', bool) # y[i,l] = 1 if exam i is at time l NxP = itertools.product(range(n), range(p)) y = model.var(NxP, 'y', bool) # help variable z[i,j] and delta[i,j] for exam i and exam j # we are only interested in those exams i and j which have a conflict! NxN = [ (i,j) for i in range(n) for j in conflicts[i] ] z = model.var(NxN, 'z', bool) delta = model.var(NxN, 'delta', bool) # adding variables as found in MidTerm.pdf print("Building constraints...") print("c1: connecting variables x and y") for i in range(n): for l in range(p): model.st( sum([ x[i, k, l] for k in range(r) if T[k][l] == 1 ]) <= r * y[i, l], "c1a") model.st( sum([ x[i, k, l] for k in range(r) if T[k][l] == 1 ]) >= y[i, l], "c1b") print("c2: each exam at exactly one time") for i in range(n): model.st( sum([ y[i, l] for l in range(p) ]) == 1 , "c2") print("c3: avoid conflicts") for i in range(n): for l in range(p): # careful!! Big M changed! model.st(sum([ y[j,l] for j in conflicts[i] ]) <= (1 - y[i, l]) * sum(conflicts[i]), "c3") print("c4: seats for all students") for i in range(n): model.st( sum([ x[i, k, l] * c[k] for k in range(r) for l in range(p) if T[k][l] == 1 ]) >= s[i], "c4") print("c5: only one exam per room per period") for k in range(r): for l in range(p): if T[k][l] == 1: model.st( sum([ x[i, k, l] for i in range(n) ]) <= 1, "c5") print("c6: any multi room exam takes place at one moment in time") for i in range(n): for l in range(p): model.st(sum([ x[i, k, m] for k in range(r) for m in range(p) if m != l and T[k][m] == 1 ]) <= (1 - y[i, l]) * r, "c6") #print("c7: resolving the absolute value") #for i in range(n): #for j in conflicts[i]: #model.st( z[i, j] <= sum([ h[l]*(y[i,l] - y[j,l]) for l in range(p) ]) + delta[i,j] * (2*h[len(h)-1]), "c7a") #model.st( z[i, j] <= -sum([ h[l]*(y[i,l]-y[j,l]) for l in range(p) ]) + (1-delta[i,j]) * (2*h[len(h)-1]), "c7b") #model.st( z[i, j] >= sum([ h[l]*(y[i,l] - y[j,l]) for l in range(p) ]) , "c7c") #model.st( z[i, j] >= -sum([ h[l]*(y[i,l] - y[j,l]) for l in range(p) ]) , "c7d") print("c8: Building clique constraints") G = nx.Graph() for i in range(n): G.add_node(i) for i in range(n): for j in conflicts[i]: G.add_edge(i,j) cliques = nx.find_cliques(G) # generator for counter, clique in itertools.izip(range(n_cliques), cliques): for l in range(l): model.st( sum([ y[i, l] for i in clique ]) <= 1, "c_lique") print("OK") # objective: minimize number of used rooms and maximize the distance of exams print("Building Objective...") gamma = 1 obj1 = sum([ x[i,k,l] * s[i] for i,k,l in itertools.product(range(n), range(r), range(p)) if T[k][l] == 1 ]) obj2 = 1 #obj2 = -sum([ z[i,j] for i in range(n) for j in conflicts[i] ]) #print(x) #obj1 = sum(x.values()) #obj2 = -sum(z.values()) model.min(obj1 + gamma * obj2, 'ExaminationScheduling') return(glpkWrapper(model),y)
(16, 7): 592, (16, 8): 206, (16, 9): 933, (16, 10): 610, (16, 11): 2248, (16, 12): 417, (16, 13): 406, (16, 14): 449, (16, 15): 636 } #set of arcs: (i,j) repr an arc from i to j E = c.keys() from pymprog import model p = model("tsp") x = p.var('x', E, bool) # created over E. #minize the total travel distance p.min(sum(c[t] * x[t] for t in E), 'totaldist') #subject to: leave each city exactly once for k in V: sum(x[k, j] for j in V if (k, j) in E) == 1 #subject to: enter each city exactly once for k in V: sum(x[i, k] for i in V if (i, k) in E) == 1 #some flow constraints to eliminate subtours. #y: the number of cars carried: city 1 has n cars. #exactly one car will be distributed to each city. y = p.var('y', E) for t in E: