def _init_edge_deps(self): self.delays = {node: self.model.addVar(name="delay_{}".format(node.node_id)) for node in self.nodes} for edge in self.forward_edges: src_node = self.id_to_node_lookup[edge.src] dst_node = self.id_to_node_lookup[edge.dst] # if they're not in the same partition, then guarantee inequality. dst_candidates = self._candidate_partitions(dst_node) enforce_inequality = self.model.addVar(name="enforce_inequality_{}".format(edge.out_id), vtype=GRB.BINARY) for partition_type in self._candidate_partitions(src_node): src_row = self._get_row(partition_type, src_node) if partition_type not in dst_candidates: self.model.addConstr((enforce_inequality == 0) >> (gpy.quicksum(src_row) == 0), name="edge_dep_pt_constraint_{}_{}".format(edge.out_id, partition_type.typename)) continue dst_row = self._get_row(partition_type, dst_node) for i, diff in enumerate(src_row - dst_row): self.model.addConstr((enforce_inequality == 0) >> (diff == 0), name="edge_dep_pt_constraint_{}_{}_{}".format(edge.out_id, partition_type.typename, i)) self.model.addConstr(self.delays[src_node] + enforce_inequality <= self.delays[dst_node], name="edge_dep_{}".format(edge.out_id)) # for each node, compute its partition delay. Then constrain that the partition delay and actual delay are # close. self.partition_delays = { partition_type: self._create_matrix("partition_delays_{}".format(partition_type.typename), (count,)) for partition_type, count in self.partition_counts.items()} max_delay = self.num_nodes for pt_type, delay_vec in self.partition_delays.items(): for i, delay in enumerate(delay_vec.flatten()): self.model.addConstr(delay <= max_delay, name="max_delay_{}_{}".format(pt_type.typename, i)) for node in self.nodes: target_delay_min = [] target_delay_max = [] for partition_type, node_to_loc in self.node_to_loc_map.items(): if node not in node_to_loc: continue # constrain that if node in partition, then delay[node] == delay[partition] loc = node_to_loc[node] row = self.partition_matrices[partition_type][loc, :] # we really just want row @ partition_delays[partition_type] but this is a quadratic constraint # instead, formulate as two sided constraint. activation = row * -max_delay + max_delay target_delay_min.extend(self.partition_delays[partition_type] + activation) activation = row * max_delay - max_delay target_delay_max.extend(self.partition_delays[partition_type] + activation) target_min = self._convert_to_var(gpy.min_(*self._convert_to_var(target_delay_min)), name="target_delay_min_{}".format(node.node_id)) # target_max = self._convert_to_var(gpy.max_(*self._convert_to_var(target_delay_max)), # name="target_delay_max_{}".format(node.node_id)) self.model.addConstr(self.delays[node] == target_min, name="delay_target_min_{}".format(node.node_id))
# 方法2:使用Gurobi的接口 import gurobipy as grb m = grb.Model() x = m.addVar(name='x') y = m.addVar(name='y') z = m.addVar(name='z') m.addConstr(x == 4, name='c4') m.addConstr(y == 5, name='c5') m.addConstr(z == grb.min_(x, y, 3)) m.setObjective(z) m.optimize() print("最小值是:z=", z.X) # 输出 z= 3.0
def MIP(probType, K, numEdges, numNodes, maxTime, graph=None, Edges=None, prob=None, seed=None, improvements=None): if improvements == None: improvements = { "start_at_leaf_constraint": False, "start_at_leaf_BP": False, "start_at_leaf_hint": False, "dont_visit_searched_leaves": False, "travel_towards_unsearched": False, "barrier_log": False, "Y_cts": False, "early_X_BP": False, "Y_BP": False, "high_prob_edges_BP": False } # sets if seed is None: seed = random.seed() if graph is None: graph, prob, Edges, _ = generateNetwork(numEdges, numNodes, probType, seed) if prob is None: prob, Edges = generateProbabilities(graph, probType) if Edges is None: Edges = genEdges(graph) # gen set of arcs Arcs = genArcs(graph) # gen set of nodes Nodes = genNodes(graph) # set of leaf arcs leafArcs = genLeaf(graph) # dict containing all the arcs that start at the ending node of each arc arcCon = genSuccessors(graph, Arcs) T = range(maxTime + 1) S = {} # whether arc a begins at node n E = {} # whether arc a ends at node n O = {} # whether arc a is on edge e # define values for functions S, E and O and store in dict. for a in Arcs: for e in Edges: if a == e or (a[1], a[0]) == e: O[a, e] = 1 else: O[a, e] = 0 for n in Nodes: if a[0] == n: S[a, n] = 1 E[a, n] = 0 elif a[1] == n: E[a, n] = 1 S[a, n] = 0 else: S[a, n] = 0 E[a, n] = 0 # model mip = Model("Model searchers searching for a randomly distributed immobile" \ "target on a unit network") # variables # X[t, a] = 1 if we traverse arc a at time t, 0 otherwise X = {(t, a): mip.addVar(vtype=GRB.BINARY) for t in T[1:] for a in Arcs} if improvements['start_at_leaf_BP']: for (t, a) in X: if a in leafArcs and t == 1: for x in arcCon[a]: # if a shares a node with another leaf arc if x in leafArcs: # higher branch priority than many other variables yet lower # than the variables for leaf arcs that aren't next to # another leaf X[t, a].BranchPriority = 15 else: # higher branch priority because these leaf arcs will have # bigger impact on mip gap X[t, a].BranchPriority = 25 if improvements['early_X_BP']: for (t, a) in X: # for earlier times, these have highest branch priority, but as time # goes on their contribution gets less significant X[t, a].BranchPriority = max(1, 2 * maxTime - 5 * t) # constraints if improvements['Y_cts']: """ This improvement simplifies the MIP by removing the alpha variables and allowing the Y variables to be continuous """ # Y is a continuous variable indicating whether an edge has been searched at # time t or not- it will take maximal value of 1 when edge has been searched Y = {(t, e): mip.addVar(ub=1) for t in T for e in Edges} # objective mip.setObjective( maxTime + 1 / 2 - quicksum(Y[t, e] * prob[e] for e in Edges for t in T[1:]), GRB.MINIMIZE) else: # Y[t, e] = 1 if we have searched edge e by time t, 0 otherwise Y = {(t, e): mip.addVar(vtype=GRB.BINARY) for t in T for e in Edges} #alpha[t] is the probability that the target is not found by time t alpha = {t: mip.addVar() for t in T} # objective mip.setObjective( quicksum((alpha[t - 1] + alpha[t]) / 2 for t in T[1:]), GRB.MINIMIZE) # define alpha as in paper defAlpha = { t: mip.addConstr(alpha[t] == 1 - quicksum(prob[e] * Y[t, e] for e in Edges)) for t in T } if improvements['Y_BP']: # YT[t] is the number of new edges we explore at time t- only defined for # early time points because these are the decisions with the most impact # when branched upon YT = {t: mip.addVar() for t in range(1, 4)} defYT = { t: mip.addConstr(quicksum(Y[t, e] for e in Edges) == YT[t]) for t in YT } for t in YT: # branch priority starts at 10 (higher than many other variables but # lower than others we are setting branch priorities for) and decreases # with t YT[t].BranchPriority = 10 - 2 * t # num arcs searched can't exceed num searchers- implicitly defines capacity searcherLim = { t: mip.addConstr(quicksum(X[t, a] for a in Arcs) <= K) for t in T[1:] } # update search info after every time step updateSearch = { (t, e): mip.addConstr(Y[t, e] <= Y[t - 1, e] + quicksum(O[a, e] * X[t, a] for a in Arcs)) for t in T[1:] for e in Edges } # conserve arc flow on nodes consFlow = {(t, n): mip.addConstr( quicksum(E[a, n] * X[t, a] for a in Arcs) == quicksum(S[a, n] * X[t + 1, a] for a in Arcs)) for t in T[1:-1] for n in Nodes} # initially, no edges have been searched initY = {e: mip.addConstr(Y[0, e] == 0) for e in Edges} # limit y so that every edge is searched by T everyEdgeSearched = {e: mip.addConstr(Y[maxTime, e] == 1) for e in Edges} if improvements['start_at_leaf_constraint']: # must have at least 1 searcher beginning at a leaf if probType == UNIFORM and len(leafArcs) != 0: mip.addConstr(quicksum(X[1, l] for l in leafArcs) >= 1) if improvements['start_at_leaf_hint'] and probType == UNIFORM: # hint to gurobi to start searchers at leaf nodes for a in leafArcs: X[1, a].VarHintVal = 1 if improvements['high_prob_edges_BP']: #if non-uniform, we preferably want to start by searching edges with high #probs first- branch on this if probType == NON_UNIFORM: maxEdges = getMaxEdges(Edges, prob) XE = mip.addVar(vtype=GRB.INTEGER) defXe = mip.addConstr(XE == quicksum(X[1, a] for a in Arcs if getEdge(a, Edges, O))) XE.BranchPriority = 15 if improvements['dont_visit_searched_leaves']: # don't search a leaf edge if it has already been searched leafConstraints = { (t, a): mip.addConstr(X[t, a] <= 1 - quicksum(O[a, e] * Y[t - 1, e] for e in Edges)) for t in T[1:] for a in Arcs if a[::-1] in leafArcs } if improvements['travel_towards_unsearched']: # add a constraint that only permits an arc to be searched if it either # a) puts us closer to an unsearched edge # b) all edges have already been searched distances = shortest_paths(graph) closer = {} further = {} for a in Arcs: closer[a] = [] further[a] = [] for e in Edges: if distance(a[1], e, distances) < distance(a[0], e, distances): closer[a].append(e) elif a == e or a == e[::-1]: closer[a].append(e) else: further[a].append(e) numEdgesUnsearched = {t: mip.addVar(vtype=GRB.INTEGER) for t in T[1:]} someEdgesUnsearched = {t: mip.addVar(vtype=GRB.BINARY) for t in T[1:]} for t in T[1:]: mip.addConstr(numEdgesUnsearched[t] == quicksum(1 - Y[t, e] for e in Edges)) mip.addConstr( someEdgesUnsearched[t] == min_(1, numEdgesUnsearched[t])) moveTowardsUnsearched = { (t, a): mip.addConstr(X[t, a] <= quicksum(1 - Y[t - 1, e] for e in closer[a]) + 1 - someEdgesUnsearched[t - 1]) for t in T[2:] for a in Arcs } #Parameter Adjustments #Set the maximum time to 900 seconds mip.setParam('TimeLimit', 900.0) if improvements['barrier_log']: # Run barrier algorithm for mip root node mip.setParam("Method", 2) #set optimality gap to 0 mip.setParam('MipGap', 0) mip.optimize() #Display Search Path state = { "X": X, "Y": Y, "T": T, "E": Edges, "L": leafArcs, "A": Arcs, "N": Nodes, "maxTime": maxTime, "seed": seed } # if you want to visualise the strategy, uncomment the below line #visualiseStrategy(state, graph) return mip, graph, state
# Link Xi and notXi model.addConstrs((Lit[i] + NotLit[i] == 1.0 for i in range(NLITERALS)), name="CNSTR_X") # Link clauses and literals for i, c in enumerate(Clauses): clause = [] for l in c: if l >= n: clause.append(NotLit[l - n]) else: clause.append(Lit[l]) model.addConstr(Cla[i] == gp.or_(clause), "CNSTR_Clause" + str(i)) # Link objs with clauses model.addConstr(Obj0 == gp.min_(Cla), name="CNSTR_Obj0") model.addConstr((Obj1 == 1) >> (Cla.sum() >= 4.0), name="CNSTR_Obj1") # Set optimization objective model.setObjective(Obj0 + Obj1, GRB.MAXIMIZE) # Save problem model.write("genconstr.mps") model.write("genconstr.lp") # Optimize model.optimize() # Status checking status = model.getAttr(GRB.Attr.Status)