Gurobi Pool Search Mode重复输出相同线路规划解问题咨询
公共交通线路规划线性规划问题:Gurobi Pool Search Mode重复输出相同解
我目前研究公共交通领域的线路规划问题(线性规划),参考《Models for Line Planning in Public Transport》构建模型。在Python中实现了以直接乘客量与成本加权和为目标的模型,通过Gurobi求解。为获取所有可行解,启用了Gurobi的Pool Search Mode,但代码仅能输出最优解,却重复输出相同的解,无法获取所有不同的可行解及其对应的combined_obj值。对比输出的可行解参数未发现差异,推测是约束未对直接乘客分配做特定限制导致。现寻求Gurobi的解决方案,实现仅输出参数相同的结果一次。
代码实现
if __name__ == '__main__': if len(sys.argv) < 2: raise ConfigNoFileNameGivenException() logger.info("Start reading configuration") config = ConfigReader.read(sys.argv[1]) #parameters = SolverParameters(config, "lc_") parameters = DirectParameters(config) logger.info("Finished reading configuration") logger.info("Begin reading input data") ptn = PTNReader.read(read_loads=True) od = DictOD() #od = ODReader.read(od) ods = ODReader.readMultiple(od) logger.info(f"Read in {len(ods)} OD matrices") logger.info("Finished reading input data") for index_of_od_matrix,od in enumerate(ods): logger.info(f"Begin execution of robust multicrieteria line planning model for OD-Matrix {index_of_od_matrix}") line_pool = LineReader.read(ptn, read_frequencies=False) solver = Solver.createSolver(parameters.getSolverType()) model = solver.createModel() logger.debug("Add variables") # ToDo: add variables frequencies = {} for line in line_pool.getLines(): frequency = model.addVariable(0, float('inf'), VariableType.INTEGER, name=f"f_{line.getId()}") frequencies[line] = frequency acceptableLineIds = compute_acceptable_line_ids(line_pool, compute_shortest_paths(ptn, od, parameters), ptn) d = {} for origin in ptn.getNodes(): d[origin.getId()] = {} for destination in ptn.getNodes(): d[origin.getId()][destination.getId()] = {} sumOfAllVariablesPerODPair = None if origin == destination or od.getValue(origin.getId(), destination.getId()) == 0: continue for lineId in acceptableLineIds[(origin.getId(), destination.getId())]: d[origin.getId()][destination.getId()][lineId] = model.addVariable(0, od.getValue(origin.getId(), destination.getId()), VariableType.INTEGER, name=f"d{origin.getId()}{destination.getId()}{lineId}") if sumOfAllVariablesPerODPair is None: sumOfAllVariablesPerODPair = d[origin.getId()][destination.getId()][lineId] else: sumOfAllVariablesPerODPair += d[origin.getId()][destination.getId()][lineId] if not sumOfAllVariablesPerODPair: sumOfAllVariablesPerODPair = 0 model.addConstraint(sumOfAllVariablesPerODPair, ConstraintSense.LESS_EQUAL, od.getValue(origin.getId(), destination.getId()), name=f"od_constraint{origin.getId()}_{destination.getId()}") logger.debug("Adding more constraints") # ToDo: add constraints for link in ptn.getEdges(): sum_freq_per_line = model.createExpression() for line in line_pool.getLines(): if link in line.getLinePath().getEdges(): sum_freq_per_line.add(frequencies[line]) model.addConstraint(sum_freq_per_line, ConstraintSense.LESS_EQUAL, link.getUpperFrequencyBound(), f"u_{link.getId()}") model.addConstraint(sum_freq_per_line, ConstraintSense.GREATER_EQUAL, link.getLowerFrequencyBound(), f"l_{link.getId()}") logger.debug("Add capacity constraints") # Constraint 3.7 -> Ensure that the capacity of each line is not exceeded capacity = parameters.get_capacity() for link in ptn.getEdges(): direct_travellers_on_line_and_edge = {} for line in line_pool.getLines(): if link not in line.getLinePath().getEdges(): continue for origin in ptn.getNodes(): for destination in ptn.getNodes(): if origin == destination or od.getValue(origin.getId(), destination.getId()) == 0: continue od_pair = (origin.getId(), destination.getId()) if line.getId() not in acceptableLineIds.get(od_pair): continue if link not in acceptableLineIds.get(od_pair).get(line.getId()).getEdges(): continue if line.getId() not in direct_travellers_on_line_and_edge: direct_travellers_on_line_and_edge[line.getId()] = model.createExpression() direct_travellers_on_line_and_edge[line.getId()].multiAdd(1, d.get(origin.getId()).get( destination.getId()).get( line.getId())) capacity_of_line = model.createExpression() capacity_of_line.multiAdd(capacity, frequencies.get(line)) # There may be a line that is not acceptable for anybody direct_travellers_on_line = direct_travellers_on_line_and_edge.get(line.getId()) if direct_travellers_on_line is not None: model.addConstraint(direct_travellers_on_line, ConstraintSense.LESS_EQUAL, capacity_of_line, "capacity_constraint_" + str(link.getId()) + "_" + str(line.getId())) logger.debug("Add parameters") parameters.setSolverParameters(model) # model.setSense(OptimizationSense.MINIMIZE) #objectives = list() model.getObjective().clear() obj = None for line, frequency in frequencies.items(): if obj is None: obj = line.getCost() * frequency else: obj += line.getCost() * frequency #objectives.append(obj) obj2 = None for origin, destinations in d.items(): for destination, lines in destinations.items(): for line, variable in lines.items(): if obj2 is None: obj2 = variable else: obj2 += variable #objectives.append(obj2) if obj2: combined_obj = obj - obj2 else: combined_obj = obj
解决方案
针对Gurobi重复输出相同解的问题,可通过以下配置和优化解决:
1. 配置解池参数,过滤重复解
Gurobi默认允许解池中存在等价解(变量值不同但目标函数值相同,或对关键决策变量无影响的冗余变量差异)。需设置以下参数:
PoolSearchMode:设为2,启用严格的解搜索模式,尽可能找到所有不同的解PoolGap:设置为0.0,仅保留目标值与最优值完全一致的解PoolSolutions:设置最大保留的解数量(按需调整)NonConvex:若模型涉及非凸整数规划,设为2以启用相关求解逻辑
添加参数设置代码:
# 配置解池参数 model.setParam('PoolSearchMode', 2) # 严格搜索所有不同解 model.setParam('PoolGap', 0.0) # 仅保留目标值等于最优值的解 model.setParam('PoolSolutions', 100) # 最多保留100个解(可按需修改) model.setParam('NonConvex', 2) # 处理非凸整数规划场景(如果适用)
2. 自定义解的唯一性判断
模型中d变量(直接乘客分配)存在冗余:约束仅限制OD对的总分配量,未指定具体分配方式,导致不同d变量组合对应相同的线路频率方案(核心决策变量)。若仅关心线路频率的唯一性,可在获取解时过滤重复的核心变量组合:
# 求解模型 model.optimize() # 存储已出现的线路频率方案 seen_frequency_schemes = set() # 遍历解池中的所有解 for sol_idx in range(model.SolCount): model.setParam('SolutionNumber', sol_idx) # 获取核心变量frequencies的值,转为可哈希的元组 freq_tuple = tuple((line.getId(), round(model.getVarByName(f"f_{line.getId()}").Xn, 4)) for line in line_pool.getLines()) # 检查是否已出现过该方案 if freq_tuple not in seen_frequency_schemes: seen_frequency_schemes.add(freq_tuple) # 输出该解的信息 print(f"唯一解 {len(seen_frequency_schemes)}:") print(f"目标函数值: {model.PoolObjVal}") for line_id, freq in freq_tuple: print(f"线路 {line_id} 频率: {freq}") # 如需输出d变量值可在此添加逻辑
3. 优化模型约束减少冗余
若不需要d变量的所有可能分配,可添加约束限制其唯一性,例如:
- 对每个OD对,指定优先分配到某条线路
- 或删除
d变量,将直接乘客量计算整合到目标函数中(若业务逻辑允许)
从根源上减少等价解的产生,让解池中的解均为真正不同的决策方案。
内容的提问来源于stack exchange,提问作者2XCL
相关产品推荐
相关产品推荐

