当前位置: 首页 > article >正文

Python搭建运筹模型的代码框架

前言

刚踏入职场用python写运筹模型时,基本是一个个函数堆砌,中间会出现非常多的重复数据操作,只能怪自己当初代码能力和结构思维都太弱(捶胸口)。

好在身边还是有能行的同事的,某一天看了工程出身的算法同事的模型代码,有一种“相见恨晚”的感觉,后面我基本都是参照他这个框架,在搭建运筹模型啦!

Python代码框架

class MathModel:
	def __init__(self,
				model_input
				):
		"""这里定义模型需要的一些输入"""
		self.model_input = model_input
		return
		
	def solve(self):
		"""模型求解的全过程"""
        self._set_iterables()
        self._set_variables()
        if self.problem.type == 'fix_local_hub':
            self._init_var()
        self._set_constraints()
        self._set_object()
        self._set_parameter()
        self.model.update()
        self.model.write('facility_location.lp')
        self.model.optimize()
        # 具体status code说明见
        # https://www.gurobi.com/documentation/8.1/refman/optimization_status_codes.html
        # 以下代码只处理了常见的不可行/unbounded情况。
        if self.model.status in range(3, 6):
            self._calculate_iis()
            return
        else:
            return self._post_process()
            
	def _set_iterables(self):
        self.arcs: List[Tuple[str, str]] = list()
        self.demand_coverage: Dict[str, Set[str]] = dict()
        self.hub_coverage: Dict[str, Set[str]] = dict()
        self.arc_coverage: Dict[Tuple[str, str], Set[str]] = dict()  # 构建最近覆盖约束使用
        tmp = []
        for customer in self.problem.demand_nodes:
            for hub in self.problem.candidate_nodes:
                if (customer, hub) in self.problem.possible_arcs.keys() and self.problem.distance_matrix[
                    (customer, hub)] <= self.problem.dist_ub:
                    self.arcs.append((customer, hub))
                    self.demand_coverage.setdefault(customer, set()).add(hub)
                    self.hub_coverage.setdefault(hub, set()).add(customer)

        if self.problem.type == 'fix_local_hub':  # 若需优化对应关系,则构建相关集合
            local_hubs = set()
            for i in self.problem.demand_nodes:
                if i == self.problem.demand_nodes[i].local_hub.node_name:
                    local_hubs.add(i)

            for i in self.problem.demand_nodes:
                for j in local_hubs:
                    if j in self.demand_coverage[i]:
                        for k in local_hubs:
                            if k in self.demand_coverage[i] and self.problem.distance_matrix[(i, j)] >= \
                                    self.problem.distance_matrix[(i, k)] + 0.5 and i != k:
                                self.arc_coverage.setdefault((i, j), set()).add(k)

	def _set_variables(self):
        self.x = self.model.addVars(self.arcs, vtype=GRB.BINARY, name='x') 
        self.y = self.model.addVars(self.problem.candidate_nodes, vtype=GRB.BINARY, name='y') 
        return
	
	def _init_var(self):
        # 固定上一步选出的点必备选择
        for i in self.problem.demand_nodes:
            if i == self.problem.demand_nodes[i].local_hub.node_name:
                self.model.addConstr(self.y[i], GRB.EQUAL, 1, 'init_var')
            else:
                self.model.addConstr(self.y[i], GRB.EQUAL, 0, 'init_var')
        return

	def _set_constraints(self):
        self._set_coverage_constr()
        self._set_relation_constr()
        self._set_capacity_constr()
        self._set_hub_num_constr()
        self._set_nearest_cover_constr()
        return

    def _set_coverage_constr(self):
        self.model.addConstrs(
            (quicksum(self.x[i, j] for j in self.demand_coverage[i]) == 1 for i in self.problem.demand_nodes),
            name='cover')
        return

    def _set_relation_constr(self):
        self.model.addConstrs(
            (self.x[i, j] <= self.y[j] for i in self.problem.demand_nodes for j in self.demand_coverage[i]),
            name='relation')
        self.model.addConstrs(self.x[j, j] == self.y[j] for j in self.problem.candidate_nodes)  # 若点被选中,必被自身使用
        return

	def _set_capacity_constr(self):
		return
		
    def _set_hub_num_constr(self):
    	return
    	
    def _set_nearest_cover_constr(self):
    	return

	def _set_object(self):
        var_cost = quicksum(self.problem.demand_nodes[i].demand[t] * self.x[i, j] * self.problem.distance_matrix[
            (i, j)] * self.problem.variable_cost for (i, j) in self.arcs for t in self.problem.prod_type)  
        if self.problem.type in ['local_hub', 'fix_local_hub']:
            vehicle_cost = quicksum(
                self.z_1[j, t] * self.problem.fix_cost[j, t][0] + self.z_2[j, t] * self.problem.fix_cost[j, t][1]
                for j in self.problem.candidate_nodes for t in self.problem.prod_type) 
            overload_cost = quicksum(
                self.s[j, t] * self.problem.overload_cost[j] for j in self.problem.candidate_nodes for t in
                self.problem.prod_type)
            rent = quicksum(
                self.y[j] * self.problem.config['cost']['hub_fix_cost']['local_hub'] for j in
                self.problem.candidate_nodes) 
        else:
            vehicle_cost = quicksum(self.z_1[j] * self.problem.fix_cost[j] for j in self.problem.candidate_nodes)
            overload_cost = quicksum(
                self.s[j] * self.problem.overload_cost[j] for j in self.problem.candidate_nodes)

        if self.problem.type == 'access_hub':
            rent = quicksum((self.problem.demand_nodes[j].cost - self.problem.demand_nodes[j].is_current_hub *
                             self.problem.config['cost']['hub_refund']) * self.q_2[j] + self.k_2[j] *
                            self.problem.config['cost']['hub_fix_cost']['access_hub'] for j in
                            self.problem.candidate_nodes if self.problem.demand_nodes[j].cost > 0)  
            obj = var_cost + vehicle_cost + overload_cost + rent 
        elif self.problem.type in ['local_hub', 'fix_local_hub']:
            obj = var_cost + vehicle_cost + overload_cost + rent  
        else:
            obj = var_cost + vehicle_cost + overload_cost  
        self.model.setObjective(obj, sense=GRB.MINIMIZE)
        
        return
        
	def _set_parameter(self):
        self.model.Params.mip_gap = self.problem.config['grb']['mip_gap']
        self.model.Params.time_limit = self.problem.config['grb']['time_limit']

    def _calculate_iis(self):
        # Do IIS
        logging.warning('The model is infeasible; computing IIS')
        self.model.computeIIS()
        logging.warning('\nThe following constraint(s) cannot be satisfied:')
        for c in self.model.getConstrs():
            if c.IISConstr:
                logging.warning('%s' % c.constrName)
        return

    def _post_process(self):
        '''
        结果处理
        :return:
        '''
        selected_nodes = set()
        for (i, j) in self.arcs:
            if self.y[j].x >= 0.9 and self.x[i, j].x >= 0.9:
                selected_nodes.add(j)
                self.problem.demand_nodes[i].parent = self.problem.demand_nodes[j]
                self.problem.demand_nodes[j].children.update({i: self.problem.demand_nodes[i]})
                if self.problem.type in ['access_hub', 'pre_process']:
                    self.problem.demand_nodes[i].access_hub = self.problem.demand_nodes[i].parent
                    self.problem.demand_nodes[j].mini_truck_num = round(self.z_1[j].x)
                elif self.problem.type in ['local_hub', 'fix_local_hub']:
                    temp = [[round(self.z_1[j, t].x), round(self.z_2[j, t].x)] for t in self.problem.prod_type]
                    self.problem.demand_nodes[j].truck_num = list(zip(*temp))
                    self.problem.demand_nodes[i].local_hub = self.problem.demand_nodes[i].parent

        for k, v in self.problem.demand_nodes.items():
            if v.local_hub is not None and v.local_hub == v:
                v.mini_truck_num = 0
                
        return selected_nodes

http://www.kler.cn/a/149400.html

相关文章:

  • Gartner发布安全平台创新洞察:安全平台需具备的11项常见服务
  • 【VIM】vim 常用命令
  • 微服务各组件整合
  • 2024年11月12日Github流行趋势
  • 解决Anaconda出现CondaHTTPError: HTTP 000 CONNECTION FAILED for url
  • uni-app移动端与PC端兼容预览PDF文件
  • Android 13.0 系统settings系统属性控制一级菜单显示隐藏
  • layui下拉框jQuery动态修改选中并展示
  • Vue实现可拖拽边界布局
  • UE5、CesiumForUnreal实现加载GeoJson绘制多面(MultiPolygon)功能(支持点选高亮)
  • python循环调用http示例(一定时间duration内,每隔时间interval去调用一次)call_http()
  • osgFX扩展库-异性光照、贴图、卡通特效(1)
  • Docker+Anaconda+CUDA+cuDNN
  • C++ 17实现无锁队列
  • Servlet-Vue-JSON交互
  • JSP迭代标签之 forEach循环标签 基本使用讲解
  • 使用Wireshark提取流量中图片方法
  • JSP forEach 标签遍历map集合
  • 【nlp】4.5 迁移学习实践项目(相关概念、中文分类、填空、句子关系、模型微调)
  • Less 安装教程
  • Java第二十章
  • Android 12.0 禁用adb reboot recovery命令实现正常重启功能
  • Android控件全解手册 - 任意View缩放平移工具-源码
  • Oracle整体架构
  • IP-Adapter:文本兼容图像提示适配器,用于文本到图像扩散模型
  • 振南技术干货集:znFAT 硬刚日本的 FATFS 历险记(9)