Skip to content

Commit

Permalink
minor update
Browse files Browse the repository at this point in the history
  • Loading branch information
dothinking committed Aug 24, 2021
1 parent 1d20cbb commit 6bb6f4f
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ tags: [job shop schedule]
其中,所有对象按用途可以归为三类:


### (a) 物理对象
### (1)物理对象

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


### (b) 求解变量
### (2)求解变量

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

Expand All @@ -43,7 +43,7 @@ tags: [job shop schedule]
- 继承自 `MachineStep``pre_machine_op``next_machine_op`,分别表示当前工序在分配机器上的加工顺序:前一道工序和下一道工序;注意这个顺序即为需要求解的变量。


### (c) 求解流程
### (3)求解流程

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

Expand Down Expand Up @@ -111,15 +111,22 @@ class UserSolver(JSSolver):

## 测试算法

框架已经内置了作业车间调度问题的标准考题数据,便于根据名称直接初始化问题。以下示例调用上一节自定义的求解器 `UserSolver` 求解 `ft10` 问题
框架已经内置了作业车间调度问题的标准问题数据,便于根据名称直接初始化问题。更多基本问题数据 [参考](https://github.com/dothinking/job_shop_schedule/blob/master/benchmark/instances.json)

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

- 求解过程以甘特图形式动态展示解的变化
### (1)单个算法单个问题

单个算法求解单个问题主要用于算法调试阶段,`solve()`方法中的两个参数:

- `interval` 以一定的频率检测和动态显示当前解的甘特图,如果设为`None`则不显示甘特图

- `callback` 在每次获得更好的解后执行自定义的动作,例如打印这个解

以下示例调用上一节自定义的求解器 `UserSolver` 求解 `ft10` 问题。


```python
# test.py
# run.py
from jsp_fwk import JSProblem
from path/to/UserSolver import UserSolver

Expand All @@ -128,7 +135,61 @@ problem = JSProblem(benchmark='ft10')

# solve problem with user defined solver
s = UserSolver()
s.solve(problem=problem)
fun = lambda solution: print(f'makespan: {solution.makespan}')
s.solve(problem=problem, interval=2000, callback=fun)
```

### (2)多个算法多个问题

当算法调试稳定后,我们需要测试它在不同规模问题上的表现,或者对比不同算法对相同问题的求解效率。针对此类场景,本框架内置了 `Benchmark` 类:排列组合输入的求解器和问题,然后进行多线程异步求解,最后对比结果。显然,`Benchmark` 类也适用于单个算法单个问题的场景。

下面例子演示了两类求解器,Google OR-Tools整数规划和规则指派算法,求解不同规模的5个问题的对比。

```python
# benchmark.py
import logging
from jsp_fwk import (JSProblem, BenchMark)
from jsp_fwk.solver import (GoogleORCPSolver, PriorityDispatchSolver)

# ----------------------------------------
# create problem from benchmark
# ----------------------------------------
names = ['ft06', 'la01', 'ft10', 'swv01', 'la38']
problems = [JSProblem(benchmark=name) for name in names]

# ----------------------------------------
# test built-in solver
# ----------------------------------------
# googl or-tools
s1 = GoogleORCPSolver(max_time=300, name='or-tools')

# priority dispatching
s2 = PriorityDispatchSolver(rule='t', name='pd-t-rule')

solvers = [s1, s2]

# ----------------------------------------
# solve and result
# ----------------------------------------
benchmark = BenchMark(problems=problems, solvers=solvers, num_threads=4)
benchmark.run(show_info=True)
```

基本介绍到此结束,接下来将陆续实施一些求解算法。
结果示例:

```
+---------+-----------+-------+---------+----------+---------+-------+
| Problem | Solver | Scale | Optimum | Solution | Error % | Time |
+---------+-----------+-------+---------+----------+---------+-------+
| ft06 | pd-t-rule | 6x6 | 55 | 60.0 | 9.1 | 0.0 |
| la01 | pd-t-rule | 10x5 | 666 | 666.0 | 0.0 | 0.0 |
| ft10 | pd-t-rule | 10x10 | 930 | 1082.0 | 16.3 | 0.1 |
| ft06 | or-tools | 6x6 | 55 | 55 | 0.0 | 0.1 |
| swv01 | pd-t-rule | 20x10 | 1407 | 1839.0 | 30.7 | 0.2 |
| la01 | or-tools | 10x5 | 666 | 666 | 0.0 | 0.4 |
| la38 | pd-t-rule | 15x15 | 1196 | 1387.0 | 16.0 | 0.6 |
| ft10 | or-tools | 10x10 | 930 | 930 | 0.0 | 4.2 |
| la38 | or-tools | 15x15 | 1196 | 1196 | 0.0 | 141.1 |
| swv01 | or-tools | 20x10 | 1407 | 1414 | 0.5 | 300.1 |
+---------+-----------+-------+---------+----------+---------+-------+
```
Original file line number Diff line number Diff line change
Expand Up @@ -144,54 +144,20 @@ def NewIntervalVar(self, start, size, end, name): pass

```python
# benchmark.py
import threading
from queue import Queue
from jsp_fwk import (JSProblem, JSSolution)
from jsp_fwk import (JSProblem, BenchMark)
from jsp_fwk.solver import GoogleORCPSolver

# solving process in a child thread
def solve(names_queue:Queue, res:list, thread_name:str):
while True:
name = names_queue.get()
p = JSProblem(benchmark=name)
s = GoogleORCPSolver(max_time=300) # set upper limit of solving time
s.solve(problem=p, interval=None)
s.wait() # wait for termination

# collect results
items = [name, len(p.jobs), len(p.machines), p.optimum, \
p.solution.makespan, s.user_time]
res.append(','.join(map(str, items)))

names_queue.task_done()
print(f'{thread_name} processed {name}.')


if __name__=='__main__':

# benchmarks
names = ['ft06', 'la01', 'ft10', 'swv01', 'la38', \
'ta31', 'swv12', 'ta42', 'ta54', 'ta70']
names_queue = Queue(maxsize=20)
for name in names: names_queue.put(name)

# result
res = []
res.append('name,num_job,num_machine,optimum,solution,time')

# start child threads
for i in range(4):
thread = threading.Thread(target=solve, \
args=(names_queue, res, f'Child-{i}'))
thread.setDaemon(True) # terminate when main thread terminated
thread.start()

# wait for termination
names_queue.join()

# final results
print()
for line in res: print(line)
# problems
names = ['ft06', 'la01', 'ft10', 'swv01', 'la38', \
'ta31', 'swv12', 'ta42', 'ta54', 'ta70']
problems = [JSProblem(benchmark=name) for name in names]

# solver
solvers = [GoogleORCPSolver(max_time=300)]

# solve
benchmark = BenchMark(problems=problems, solvers=solvers, num_threads=5)
benchmark.run(show_info=True)
```

结果如下表所示:
Expand All @@ -200,18 +166,22 @@ if __name__=='__main__':

- 后5行变量总数在$[450, 1000]$量级,除了例9有些反常外,其余都没能在300秒内求得最优解,并且精度随着问题复杂度(例如变量总数、工序数等)增加而降低。

| |name|num of <br> job|num of <br> machine|optimum|solution|err / %|time|
|---|---|---|---|---|---|---|---|
|1|ft06|6|6|55|55|0.0|0.0
|2|la01|10|5|666|666|0.0|0.2
|3|ft10|10|10|930|930|0.0|3.8
|4|swv01|20|10|1407|1414|0.5|300.0
|5|la38|15|15|1196|1196|0|147.7
|6|ta31|30|15|1764|1814|2.8|300.0
|7|swv12|50|10|(2972, 3003)|3339|11.8|300.1
|8|ta42|30|20|(1867, 1956)|2096|9.6|300.1
|9|ta54|50|15|2839|2863|0.8|300.1
|10|ta70|50|20|2995|3307|10.4|300.1
```
+----+---------+------------------+-------+--------------+----------+---------+-------+
| ID | Problem | Solver | Scale | Optimum | Solution | Error % | Time |
+----+---------+------------------+-------+--------------+----------+---------+-------+
| 1 | ft06 | GoogleORCPSolver | 6x6 | 55 | 55 | 0.0 | 0.1 |
| 2 | la01 | GoogleORCPSolver | 10x5 | 666 | 666 | 0.0 | 0.2 |
| 3 | ft10 | GoogleORCPSolver | 10x10 | 930 | 930 | 0.0 | 3.4 |
| 4 | swv01 | GoogleORCPSolver | 20x10 | 1407 | 1414 | 0.5 | 300.1 |
| 5 | la38 | GoogleORCPSolver | 15x15 | 1196 | 1196 | 0.0 | 168.7 |
| 6 | ta31 | GoogleORCPSolver | 30x15 | 1764 | 1814 | 2.8 | 300.2 |
| 7 | swv12 | GoogleORCPSolver | 50x10 | (2972, 3003) | 3339 | 11.8 | 300.3 |
| 8 | ta42 | GoogleORCPSolver | 30x20 | (1867, 1956) | 2130 | 11.4 | 303.5 |
| 9 | ta54 | GoogleORCPSolver | 50x15 | 2839 | 2862 | 0.8 | 468.8 |
| 10 | ta70 | GoogleORCPSolver | 50x20 | 2995 | 3283 | 9.6 | 600.3 |
+----+---------+------------------+-------+--------------+----------+---------+-------+
```


问题规模较小时,能高效求出最优解;但随着问题规模的增大,求解效率急剧下降。例如,上例中即便将计算时间上限增加到600秒,后5个例子的结果也没有显著改善。这是整数规划等精确求解方法应用于实际大规模调度问题的限制所在。
Expand Down

0 comments on commit 6bb6f4f

Please sign in to comment.