def _cut(self, model, val_func, cut_func): '''Returns true if a cut was added to the master''' problem = self.problem theta = self.theta x = self.x # Create subproblem. sub = Model() # y[ip,iq,s,c] = 1 if images ip & iq have a shared path through stage # s by running command c during s, 0 otherwise y = {} for (ip, iq), cmds in problem.shared_cmds.items(): for s, c in product(problem.shared_stages[ip, iq], cmds): y[ip, iq, s, c] = sub.addVar(name='y[%s,%s,%s,%s]' % (ip, iq, s, c)) sub.update() # Find shared paths among image pairs. constraints = defaultdict(list) for (ip, iq), cmds in problem.shared_cmds.items(): for s in problem.shared_stages[ip, iq]: for c in cmds: constraints[ip, s, c].append( sub.addConstr( y[ip, iq, s, c] <= val_func(model, x[ip, s, c]))) constraints[iq, s, c].append( sub.addConstr( y[ip, iq, s, c] <= val_func(model, x[iq, s, c]))) if s > 1: sub.addConstr( sum(y[ip, iq, s, c] for c in cmds) <= sum(y[ip, iq, s - 1, c] for c in cmds)) sub.setObjective( -sum(problem.commands[c] * y[ip, iq, s, c] for ip, iq, s, c in y), GRB.MINIMIZE) sub.optimize() # Add the dual prices for each variable pi = defaultdict(float) for isp, cons in constraints.iteritems(): for c in cons: pi[isp] += c.pi # Detect optimality if val_func(model, theta) >= sub.objVal: return False # no cuts to add # Optimality cut cut_func(model, theta >= sum(pi[isp] * x[isp] for isp in pi if pi[isp])) return True
def solve(self, problem, saver): # Construct model. self.problem = problem # Do recursive maximal clique detection. self.clique_data = clique_data = problem.cliques() self.model = model = Model() if self.time is not None: model.params.TimeLimit = 60 * int(self.time) # Each image needs to run all its commands. This keep track of # what variables run each command for each image. self.by_img_cmd = by_img_cmd = defaultdict(list) # Objective is the total cost of all the commands we run. self._obj = [] # x[i,c] = 1 if image i incurs the cost of command c directly self.x = x = {} for img, cmds in problem.images.items(): for cmd in cmds: name = 'x[%s,%s]' % (img, cmd) x[name] = v = model.addVar(vtype=GRB.BINARY, name=name) self._obj.append(problem.commands[cmd] * v) by_img_cmd[img, cmd].append(v) # cliques[i] = 1 if clique i is used, 0 otherwise self.cliques = {} self._update(clique_data) for c in clique_data['cliques']: print c # Each image has to run each of its commands. for img_cmd, vlist in by_img_cmd.items(): self.model.addConstr(sum(vlist) == 1) model.setObjective(sum(self._obj), GRB.MINIMIZE) model.optimize() # lambda *args: self._callback(saver, *args)) # TODO: callback # Translate the output of this to a schedule. schedule = defaultdict(list) self._translate(schedule, clique_data) for name, v in x.items(): img, cmd = name.replace('x[', '').replace(']', '').split(',') if v.x > 0.5: schedule[img].append(cmd) saver(schedule)
def _cut(self, model, val_func, cut_func): '''Returns true if a cut was added to the master''' problem = self.problem theta = self.theta x = self.x # Create subproblem. sub = Model() # y[ip,iq,s,c] = 1 if images ip & iq have a shared path through stage # s by running command c during s, 0 otherwise y = {} for (ip, iq), cmds in problem.shared_cmds.items(): for s, c in product(problem.shared_stages[ip, iq], cmds): y[ip,iq,s,c] = sub.addVar(name='y[%s,%s,%s,%s]' % (ip,iq,s,c)) sub.update() # Find shared paths among image pairs. constraints = defaultdict(list) for (ip, iq), cmds in problem.shared_cmds.items(): for s in problem.shared_stages[ip,iq]: for c in cmds: constraints[ip,s,c].append(sub.addConstr(y[ip,iq,s,c] <= val_func(model, x[ip,s,c]))) constraints[iq,s,c].append(sub.addConstr(y[ip,iq,s,c] <= val_func(model, x[iq,s,c]))) if s > 1: sub.addConstr(sum(y[ip,iq,s,c] for c in cmds) <= sum(y[ip,iq,s-1,c] for c in cmds)) sub.setObjective( -sum(problem.commands[c] * y[ip,iq,s,c] for ip,iq,s,c in y), GRB.MINIMIZE ) sub.optimize() # Add the dual prices for each variable pi = defaultdict(float) for isp, cons in constraints.iteritems(): for c in cons: pi[isp] += c.pi # Detect optimality if val_func(model, theta) >= sub.objVal: return False # no cuts to add # Optimality cut cut_func(model, theta >= sum(pi[isp]*x[isp] for isp in pi if pi[isp])) return True
def _update(self, clique_data, parent=None): for c in clique_data['cliques']: self.cliques[c['name']] = v = self.model.addVar( vtype=GRB.BINARY, name=c['name'] ) self.model.update() for img, cmd in product(c['images'], c['commands']): self.by_img_cmd[img, cmd].append(v) self._obj.append(c['time'] * v) if parent is not None: self.model.addConstr(v <= self.cliques[parent['name']]) for child in c['children']: self._update(child, c) for inter in clique_data['intersections']: self.model.addConstr(sum(self.cliques[i] for i in inter) <= 1)
[10, 6, 12], [ 8, 4, 15], [ 6, 12, 5] ] # x[i][j] = 1 if i is assigned to j x = [] for i in range(len(c)): x_i = [] for j in c[i]: x_i.append(model.addVar(vtype=GRB.BINARY)) x.append(x_i) # sum <j> x_ij <= 1 for all i for x_i in x: model.addLRConstr(sum(x_i) <= 1) # sum <i> a_ij * x_ij <= b[j] for all j for j in range(len(b)): model.addConstr(sum(a[i][j] * x[i][j] for i in range(len(x))) <= b[j]) # max sum <i,j> c_ij * x_ij model.setObjective( sum( sum(c_ij * x_ij for c_ij, x_ij in zip(c_i, x_i)) for c_i, x_i in zip(c, x) ) ) model.LRoptimize(debug=True)
def solve(self, problem, saver): # TODO: time limits # TODO: symmetry? # Construct master model. self.problem = problem self.model = model = Model() model.params.LazyConstraints = 1 self.theta = theta = model.addVar(lb=-GRB.INFINITY, name='theta') # x[i,s,c] = 1 if image i runs command c during stage s, 0 otherwise self.x = x = {} for i, cmds in problem.images.items(): for s, c in product(problem.stages[i], cmds): x[i,s,c] = model.addVar(vtype=GRB.BINARY, name='x[%s,%s,%s]' % (i,s,c)) model.update() # Need to reference vars by their indices later. self.xind = {xvar: isc for isc, xvar in x.items()} # Each image one command per stage, and each command once. for i in problem.images: for s in problem.stages[i]: model.addConstr(sum(x[i,s,c] for c in problem.images[i]) == 1) for c in problem.images[i]: model.addConstr(sum(x[i,s,c] for s in problem.stages[i]) == 1) model.setObjective(theta, GRB.MINIMIZE) # Optimize until we can longer add optimality cuts. iteration = 1 while True: if iteration == 1: # Use heuristic for initial feasible solution. soln = [] def save_initial(schedule): soln.append(schedule) MostCommonHeuristic().solve(problem, save_initial) init = soln.pop() # Inform the master model of this solution. for i,s,c in x: if init[i][s-1] == c: x[i,s,c].start = 1 def val_func(m, xvar): if xvar is theta: return -GRB.INFINITY i,s,c = self.xind[xvar] if init[i][s-1] == c: return 1 else: return 0 else: model.optimize(lambda *args: self._callback(saver, *args)) val_func = lambda m, xvar: xvar.x cut_func = lambda m, cons: m.addConstr(cons) saver(self._schedule(val_func)) if not self._cut(model, val_func, cut_func): break iteration += 1 saver(self._schedule(val_func))
def solve(self, problem, saver): # Construct model. self.problem = problem self.model = model = Model() if self.time is not None: model.params.TimeLimit = 60 * int(self.time) # x[i,s,c] = 1 if image i runs command c during stage s, 0 otherwise. self.x = x = {} for i, cmds in problem.images.items(): for s, c in product(problem.stages[i], cmds): x[i, s, c] = model.addVar(vtype=GRB.BINARY, name="x[%s,%s,%s]" % (i, s, c)) # y[ip,iq,s,c] = 1 if images ip & iq have a shared path through stage # s by running command c during s, 0 otherwise. y = {} for (ip, iq), cmds in problem.shared_cmds.items(): for s, c in product(problem.shared_stages[ip, iq], cmds): y[ip, iq, s, c] = model.addVar(vtype=GRB.BINARY, name="y[%s,%s,%s,%s]" % (ip, iq, s, c)) model.update() # TODO: need to remove presolved commands so the heuristic doesn't try them. # Add a heuristic initial solution. if self.heur is not None: self._heur() # Presolving if self.presol in ("all", "unshared"): self._presol_unshared() if self.presol in ("all", "shared"): self._presol_shared() # Each image one command per stage, and each command once. for i in problem.images: for s in problem.stages[i]: model.addConstr(sum(x[i, s, c] for c in problem.images[i]) == 1) for c in problem.images[i]: model.addConstr(sum(x[i, s, c] for s in problem.stages[i]) == 1) # Find shared paths among image pairs. for (ip, iq), cmds in problem.shared_cmds.items(): for s in problem.shared_stages[ip, iq]: for c in cmds: model.addConstr(y[ip, iq, s, c] <= x[ip, s, c]) model.addConstr(y[ip, iq, s, c] <= x[iq, s, c]) if s > 1: model.addConstr(sum(y[ip, iq, s, c] for c in cmds) <= sum(y[ip, iq, s - 1, c] for c in cmds)) model.setObjective(sum(problem.commands[c] * y[ip, iq, s, c] for ip, iq, s, c in y), GRB.MAXIMIZE) model.optimize(lambda *args: self._callback(saver, *args)) # Create optimal schedule. schedule = defaultdict(list) for i, stages in problem.stages.items(): for s in stages: for c in problem.images[i]: if x[i, s, c].x > 0.5: schedule[i].append(c) break saver(schedule)
def solve(self, problem, saver): # Construct model. self.problem = problem self.model = model = Model() if self.time is not None: model.params.TimeLimit = 60 * int(self.time) # x[i,s,c] = 1 if image i runs command c during stage s, 0 otherwise. self.x = x = {} for i, cmds in problem.images.items(): for s, c in product(problem.stages[i], cmds): x[i, s, c] = model.addVar(vtype=GRB.BINARY, name='x[%s,%s,%s]' % (i, s, c)) # y[ip,iq,s,c] = 1 if images ip & iq have a shared path through stage # s by running command c during s, 0 otherwise. y = {} for (ip, iq), cmds in problem.shared_cmds.items(): for s, c in product(problem.shared_stages[ip, iq], cmds): y[ip, iq, s, c] = model.addVar(vtype=GRB.BINARY, name='y[%s,%s,%s,%s]' % (ip, iq, s, c)) model.update() # TODO: need to remove presolved commands so the heuristic doesn't try them. # Add a heuristic initial solution. if self.heur is not None: self._heur() # Presolving if self.presol in ('all', 'unshared'): self._presol_unshared() if self.presol in ('all', 'shared'): self._presol_shared() # Each image one command per stage, and each command once. for i in problem.images: for s in problem.stages[i]: model.addConstr( sum(x[i, s, c] for c in problem.images[i]) == 1) for c in problem.images[i]: model.addConstr( sum(x[i, s, c] for s in problem.stages[i]) == 1) # Find shared paths among image pairs. for (ip, iq), cmds in problem.shared_cmds.items(): for s in problem.shared_stages[ip, iq]: for c in cmds: model.addConstr(y[ip, iq, s, c] <= x[ip, s, c]) model.addConstr(y[ip, iq, s, c] <= x[iq, s, c]) if s > 1: model.addConstr( sum(y[ip, iq, s, c] for c in cmds) <= sum(y[ip, iq, s - 1, c] for c in cmds)) model.setObjective( sum(problem.commands[c] * y[ip, iq, s, c] for ip, iq, s, c in y), GRB.MAXIMIZE) model.optimize(lambda *args: self._callback(saver, *args)) # Create optimal schedule. schedule = defaultdict(list) for i, stages in problem.stages.items(): for s in stages: for c in problem.images[i]: if x[i, s, c].x > 0.5: schedule[i].append(c) break saver(schedule)
def solve(self, problem, saver): # TODO: time limits # TODO: symmetry? # Construct master model. self.problem = problem self.model = model = Model() model.params.LazyConstraints = 1 self.theta = theta = model.addVar(lb=-GRB.INFINITY, name='theta') # x[i,s,c] = 1 if image i runs command c during stage s, 0 otherwise self.x = x = {} for i, cmds in problem.images.items(): for s, c in product(problem.stages[i], cmds): x[i, s, c] = model.addVar(vtype=GRB.BINARY, name='x[%s,%s,%s]' % (i, s, c)) model.update() # Need to reference vars by their indices later. self.xind = {xvar: isc for isc, xvar in x.items()} # Each image one command per stage, and each command once. for i in problem.images: for s in problem.stages[i]: model.addConstr( sum(x[i, s, c] for c in problem.images[i]) == 1) for c in problem.images[i]: model.addConstr( sum(x[i, s, c] for s in problem.stages[i]) == 1) model.setObjective(theta, GRB.MINIMIZE) # Optimize until we can longer add optimality cuts. iteration = 1 while True: if iteration == 1: # Use heuristic for initial feasible solution. soln = [] def save_initial(schedule): soln.append(schedule) MostCommonHeuristic().solve(problem, save_initial) init = soln.pop() # Inform the master model of this solution. for i, s, c in x: if init[i][s - 1] == c: x[i, s, c].start = 1 def val_func(m, xvar): if xvar is theta: return -GRB.INFINITY i, s, c = self.xind[xvar] if init[i][s - 1] == c: return 1 else: return 0 else: model.optimize(lambda *args: self._callback(saver, *args)) val_func = lambda m, xvar: xvar.x cut_func = lambda m, cons: m.addConstr(cons) saver(self._schedule(val_func)) if not self._cut(model, val_func, cut_func): break iteration += 1 saver(self._schedule(val_func))