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()
            # result.status.setdefault('run_error', []).append(traceback.format_exc())
            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

你可能感兴趣的:(Python,运筹学,python,笔记)