OR Tools SCIP求解器约束不生效问题排查求助
作物分配线性规划模型约束失效排查
我用OR Tools的SCIP求解器开发了作物分配线性规划模型,小数据集测试时结果符合所有约束,但加载JSON大规模数据后,分配给各作物的总面积超出了预设的crop_limit值,明明已经添加了面积约束,求解器却没遵守,需要排查原因。
小数据集测试代码
from ortools.linear_solver import pywraplp import pandas as pd import json def main(): # Data priority = [ [30, 20, 10, 10], [10, 20, 30, 40], [40, 30, 20, 10], [30, 20, 10, 10], [30, 20, 10, 10], [10, 20, 30, 40], [20, 20, 10, 10], [40, 20, 30, 40], [10, 30, 20, 20], [10, 40, 20, 10], [30, 20, 10, 99], [10, 15, 30, 40], ] field_area = [30,50,100,20,40,30,130,150,110,29,410,320] crop_limit = [110,1121,120,140] num_crops = len(priority) num_fields = len(priority[0]) num_area = len(field_area) num_limit = len(crop_limit) print ('Fields', num_crops) print ('Crops' , num_fields) print ('Area', num_crops) print ('Constraints', num_limit) solver = pywraplp.Solver.CreateSolver('SCIP') # Variables x = {} for i in range(num_crops): for j in range(num_fields): x[i, j] = solver.IntVar(0, 1, '') # Constraints # One field - one crop for i in range(num_crops): solver.Add(solver.Sum([x[i, j] for j in range(num_fields)]) == 1) # Area constraints for j in range(num_fields): for i in range(num_crops): solver.Add(solver.Sum([x[i, j]] * field_area[i]) <= crop_limit[j]) # Objective objective_terms = [] for i in range(num_crops): for j in range(num_fields): objective_terms.append(priority[i][j] * x[i, j]) solver.Minimize(solver.Sum(objective_terms)) # Solve status = solver.Solve() # Print solution. if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE: print(f'Total optimum = {solver.Objective().Value()}\n') for i in range(num_crops): for j in range(num_fields): if x[i, j].solution_value() > 0.5: print(f'Field {i} Crop {j}.' + f' Priority: {priority[i][j]}' + f' Area: {field_area[i]}' + f' From limit: {crop_limit[j]}' ) else: print('Task doesent have decision.') if __name__ == '__main__': main()
大规模数据加载代码
from ortools.linear_solver import pywraplp import pandas as pd import json with open('..........SPP_DATA_v4.json') as json_file: data = json.load(json_file) field_area = data['fld_area'] crop_limit = data['crop_goal'] priority = data['spp_lines'] num_fields = len(priority) num_crops = len(priority[0]) num_area = len(field_area) num_limit = len(crop_limit) print ('Field', num_fields ) print ('Crops' ,num_crops ) print ('Constraints', num_limit) solver = pywraplp.Solver.CreateSolver('SCIP') # Variables x = {} for i in range(num_fields): for j in range(num_crops): x[i, j] = solver.IntVar(0, 1, '') # Constraints # one field - one crop for i in range(num_fields): solver.Add(solver.Sum([x[i, j] for j in range(num_crops)]) == 1) # Maximum area constr for j in range(num_crops): for i in range(num_fields): solver.Add(solver.Sum([x[i, j]] * field_area[i]) <= crop_limit[j]) # Objective objective_terms = [] for i in range(num_fields): for j in range(num_crops) : objective_terms.append(priority[i][j] * x[i, j]) solver.Minimize(solver.Sum(objective_terms)) # Solve status = solver.Solve() # Print solution. print(status) if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE: print(f'Total optimum = {solver.Objective().Value()}\n') result_arr = [] jsondata = {} for i in range(num_fields): for j in range(num_crops): if x[i, j].solution_value() > 0.5: result_arr.append({'f':i, 'c':j, 'p':priority[i][j],'a':field_area[i]}) else: print('Task doedent have decision.') # Save the file jsondata['result'] = result_arr jsonString = json.dumps(jsondata, indent=4) with open('/content/drive/MyDrive/Colab Notebooks/response.json', 'w') as outfile: outfile.write(jsonString) print('File saved.')
核心问题分析与修正方案
面积约束逻辑完全错误
你当前的面积约束写法是给每个(字段i,作物j)的组合单独加约束:x[i,j] * field_area[i] <= crop_limit[j],这只限制了单个字段分配给某作物的面积,完全没实现“某作物分配的总面积不超过限制”的需求。正确的约束逻辑应该是对每个作物j,求和所有分配给它的字段面积,确保总和不超过
crop_limit[j]:# 修正后的面积约束(大规模数据版本) for j in range(num_crops): total_crop_area = solver.Sum([x[i, j] * field_area[i] for i in range(num_fields)]) solver.Add(total_crop_area <= crop_limit[j])小数据集测试代码的面积约束同样犯了这个错误,只是小数据规模的巧合让结果看起来符合预期。
目标函数重复设置冗余
两个版本的代码都把solver.Minimize放在了循环内部,导致每次循环都重新设置目标函数。虽然最终结果可能不受影响,但属于冗余错误,应移到循环外:# 修正后的目标函数(大规模数据版本) objective_terms = [] for i in range(num_fields): for j in range(num_crops): objective_terms.append(priority[i][j] * x[i, j]) solver.Minimize(solver.Sum(objective_terms))小数据集的索引命名混淆
小数据代码中把num_crops设为len(priority)(实际是字段数量),num_fields设为len(priority[0])(实际是作物数量),索引完全搞反,这种命名混淆会增加后续维护出错概率,建议统一命名规则。
内容的提问来源于stack exchange,提问作者Grigo Port
相关产品推荐
相关产品推荐

