Skip to content

Commit

Permalink
Job-shop problem solving framework
Browse files Browse the repository at this point in the history
  • Loading branch information
dothinking committed Aug 18, 2021
1 parent 8663f8c commit 6178d16
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 1 deletion.
30 changes: 30 additions & 0 deletions docs/2021-08-08-作业车间调度问题求解框架.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
categories: [optimization, mathematics]
tags: [job shop schedule]
---

# 作业车间调度问题求解框架

---

组合优化问题广泛出现于各行各业,例如生产制造排程,机场航班调度、项目管理计划等,求解这类问题有助于解决实际应用中的调度难题、提高生产效率;考虑到实际的业务逻辑因行业而异且可能相当复杂,本系列文章针对调度类问题的基本模型——作业车间调度(Job Shop Schedule),基于Python开发了一个通用的求解框架:

- 参考文献实现了一些基本解法例如基于规则指派、局部搜索、群体方法等

- 便于快速实施和测试新算法(参考 [Python建模](2021-08-14-作业车间调度问题求解框架:Python建模.md)


## 项目仓库

https://github.com/dothinking/job_shop_schedule

## 建模

- [问题描述](2021-08-08-作业车间调度问题求解框架:问题描述.md)

- [Python建模](2021-08-14-作业车间调度问题求解框架:Python建模.md)

- [基于matplotlib的动态甘特图](2021-08-15-基于matplotlib的动态甘特图.md)


## 求解
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ categories: [optimization, mathematics]
tags: [job shop schedule]
---

# 作业车间调度问题:问题描述
# 作业车间调度问题求解框架:问题描述

---

Expand Down
134 changes: 134 additions & 0 deletions docs/2021-08-14-作业车间调度问题求解框架:Python建模.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
---
categories: [optimization, python/vba/cpp]
tags: [job shop schedule]
---

# 作业车间调度问题求解框架:Python建模

---

基于前文对作业车间调度问题的定义和描述,本文进行Python建模,开发了一个通用的作业车间调度问题求解框架,把问题分解、抽象为可重用的部分,从而专注求解算法的开发、实施和验证。

> [dothinking/job_shop_schedule](https://github.com/dothinking/job_shop_schedule)

## 框架结构

整个求解框架基于Python面向对象编程实现,主要结构参考下图。

![class-diagram](images/2021-08-14-01.png)


其中,所有对象按用途可以归为三类:


### (a) 物理对象

- `Job` 作业实体
- `Machine` 机器实体
- `Operation` 工序实体,包含所属作业、分配的机器、加工时长等属性


### (b) 求解变量

`OperationStep`是工序实体`Operation` 的封装,同时加上待求解的参数 `start_time`。根据前文关于作业车间问题的两种不同的描述方式,相应有两种不同的求解思路:

- 对于以`start_time`为变量描述的数学模型,直接求解`start_time`即可
- 对于以 **析取图** 描述的模型,需要先求解工序的顺序,然后递推出`start_time`

因此,对于析取图描述的模型,还提供了以下中间属性:

- 继承自 `JobStep``pre_job_op``next_job_op`,分别表示当前工序在所属作业实体上的顺序:前一道工序和下一道工序;并且,它们是已知的。

- 继承自 `MachineStep``pre_machine_op``next_machine_op`,分别表示当前工序在分配机器上的加工顺序:前一道工序和下一道工序;注意这个顺序即为需要求解的变量。


### (c) 求解流程

`JSProblem` 是所有工序实体 `Operation` 的封装:

- 它的解为一个`JSSolution`实例
- 每当获得一个更好的解,**需要使用`update_solution()`方法显式更新**

`JSSolution` 是所有变量 `OperationStep` 的封装:

- 对于析取图求解模型,需要显式地调用 `evaluate()` 来基于求解的顺序计算最终变量 `start_time`;基于数学模型则无需这一步。

- `is_feasible()` 判断一个解是否满足所有约束

- 如果是一个可行解,`makespan`属性得到最大加工周期长度


`JSSolver` 是作业车间调度问题求解器的基类,便于继承此基类后实施新算法。


## 实施新算法

以上的设计可以避免重复工作,从而专注于算法本身的实现和测试。基于此框架,实施新算法的只需创建自定义求解器类,然后继承 `JSSolver` 并实现 `do_solver()` 方法。`do_solver()` 方法内部主要分为三大步骤:

- 基于问题创建初始状态的解(注意并非可行的 **初始解**

```python
solution = JSSolution(problem)
```

- 实施算法,计算或者优化这个解

- 对于以`start_time`为变量描述的数学模型,直接求解每个工序的`start_time`即可
- **对于以析取图描述的模型,需要先求解工序的顺序,然后显式地调用 `solution.evaluate()` 递推出`start_time`**


- 每次迭代得到更好的解后,显式更新问题的解

```python
problem.update_solution(solution)
```

关键代码参考:

```python
from model.solver import JSSolver
from model.problem import JSProblem
from model.solution import JSSolution

class UserSolver(JSSolver):

def do_solve(self, problem:JSProblem):
'''User defined solving process.'''

# (1) Initialize an empty solution from problem
solution = JSSolution(problem)

# (2) Solve or optimize the solution,
# i.e. determine the start_time of OperationStep instances.
# Note to evaluate solution explicitly if disjunctive graph model.
...
# solution.evaluate()

# (3) Update the solution for problem iteratively
problem.update_solution(solution)
```


## 测试算法

框架已经内置了作业车间调度问题的标准考题数据,便于根据名称直接初始化问题。以下示例调用自定义求解器 `SampleSolver` 求解 `ft10` 问题。

- 更多基本问题数据 [参考](https://github.com/dothinking/job_shop_schedule/blob/master/benchmark/instances.json)

- 求解过程以甘特图形式动态展示解的变化


```python
from model.problem import JSProblem
from solver.SampleSolver import SampleSolver

# load benchmark problem
problem = JSProblem(benchmark='ft10')

# solve problem with user defined solver
s = SampleSolver(name='sample')
s.solve(problem=problem)
```

基本介绍到此结束,接下来将陆续实施一些求解算法。
Binary file added docs/images/2021-08-14-01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6178d16

Please sign in to comment.