Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: lekin examples with forward and backward scheduling #36

Merged
merged 11 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[license-url]: https://opensource.org/licenses/Apache-2.0
[pypi-image]: https://badge.fury.io/py/lekin.svg
[pypi-url]: https://pypi.python.org/pypi/lekin
[pepy-image]: https://pepy.tech/badge/lekin/month
[pepy-image]: https://pepy.tech/badge/lekin
[pepy-url]: https://pepy.tech/project/lekin
[build-image]: https://github.com/LongxingTan/python-lekin/actions/workflows/test.yml/badge.svg?branch=master
[build-url]: https://github.com/LongxingTan/python-lekin/actions/workflows/test.yml?query=branch%3Amaster
Expand All @@ -29,7 +29,8 @@

**[Documentation](https://python-lekin.readthedocs.io)** | **[Tutorials](https://python-lekin.readthedocs.io/en/latest/tutorials.html)** | **[Release Notes](https://python-lekin.readthedocs.io/en/latest/CHANGELOG.html)** | **[中文](https://github.com/LongxingTan/python-lekin/blob/master/README_zh_CN.md)**

**python-lekin** is a rapid-to-implement and easy-to-use Flexible Job Shop Scheduler Library, named after and inspired by [Lekin](https://web-static.stern.nyu.edu/om/software/lekin/). As a core function in **APS (advanced planning and scheduler)**, it helps manufacturers optimize the allocation of materials and production capacity optimally to balance demand and capacity.
**python-lekin** is a Flexible Job Shop Scheduler Library, named after [Lekin](https://web-static.stern.nyu.edu/om/software/lekin/).
As a core function in **APS (advanced planning and scheduler)**, it helps manufacturers optimize the allocation of materials and production capacity optimally to balance demand and capacity.

- Changeover Optimization
- Ready for demo, research and maybe production
Expand All @@ -50,6 +51,8 @@

## Tutorial

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1H3o6tqJKr1yTvPNI9t0yggbb7BzE_iPz?usp=sharing)

**Installation**

``` shell
Expand Down
5 changes: 3 additions & 2 deletions README_zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@

**[文档](https://python-lekin.readthedocs.io)** | **[教程](https://python-lekin.readthedocs.io/en/latest/tutorials.html)** | **[发布日志](https://python-lekin.readthedocs.io/en/latest/CHANGELOG.html)** | **[English](https://github.com/LongxingTan/python-lekin/blob/master/README.md)**

**python-lekin**是一个APS智能排产调度工具,名字来源于[Lekin](https://web-static.stern.nyu.edu/om/software/lekin/)。在考虑实际约束的前提下,实现动态调整计划排程,高效响应客户订单承诺。

**python-lekin**是一个APS智能排产调度工具。考虑实际约束的前提下,实现动态调整计划排程,高效响应客户订单承诺。

- 支持工艺路线约束
- 支持产能约束
Expand All @@ -41,6 +40,8 @@

## 快速入门

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1H3o6tqJKr1yTvPNI9t0yggbb7BzE_iPz?usp=sharing)

### 安装

``` shell
Expand Down
7 changes: 6 additions & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ coverage:
status:
project:
default:
threshold: 2%
threshold: 3%

patch:
default:
enabled: false
changes: no
26 changes: 24 additions & 2 deletions docs/source/application.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Application
===========
应用
============

基本概念
----------------
Expand Down Expand Up @@ -198,3 +198,25 @@ Material_op一开始,解析爬坡配置, 得到按小时或按数量的map,
计算最终详细排产结果时,根据一个op的初始时间和结束时间,划分落在每个班次的时长,和数量

[修正: 不能在结果生成时,才产出数量。结果时,每个op在资源那里拆成了按单班产能,结果生成时已经不知道具体的详细爬坡了?.在生成时就确定数量. 但最后一个的数量,可以在最后矫正]


车间排产
------------------

1. 准备环境

2. 定义领域模型
Entity
- Job
- Resource
- Timeslot

Planning entity
- JobAssignment

Solution
- Jobschedule

3. 约束

4. Solver
2 changes: 0 additions & 2 deletions docs/source/demand.rst

This file was deleted.

6 changes: 3 additions & 3 deletions docs/source/heuristics.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
heuristics
============
启发式排产
===============


禁忌搜索
Expand All @@ -9,7 +9,7 @@ heuristics
遗传算法
-------------

遗传算法应用在排产中的关键就是如何将排产结果进行编码、以及如何计算fitness。
遗传算法应用在排产中的关键就是如何将排产结果进行编码、以及如何计算fitness。简单理解就是: 把序列的permutation优化一下

每一个可行解被称为一个染色体,一个染色体由多个元素构成,这个元素称为基因。

Expand Down
1 change: 0 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ Finite Capacity Planning
rules
heuristics
application
demand
GitHub <https://github.com/LongxingTan/python-lekin>


Expand Down
13 changes: 12 additions & 1 deletion docs/source/rules.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Rules
规则排产
============

分为rule-based、event-based、resource-based两种思路。
Expand Down Expand Up @@ -72,3 +72,14 @@ SPT—EDD规则
.. code-block:: python

forward(operations, next_op_start_until, with_material_kitting_constraint, align_with_same_production_line, earliest_start_time, earliest_end_time)



终局
----------------------

规则启发在排产中的应用,进行足够的抽象后,灵活使用多种方法的结合。

一批次可开始的。剩余未开始的
- 那么,可开始的是否一定要比未开始的先开始呢?其实不是
- 那么通过工序图的依赖关系,其实给定了每个工序的最早开始时间约束。甚至不是具体的时间,而是一个变量之间的
18 changes: 18 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Examples

forward scheduling
```shell

```


backward scheduling
```shell

```


genetic
```shell

```
2 changes: 2 additions & 0 deletions examples/genetic_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def __init__(self):
if selection_rand[i] > qk[j] and selection_rand[i] <= qk[j + 1]:
population_list[i] = copy.deepcopy(total_chromosome[j + 1])
break

"""----------comparison----------"""
for i in range(population_size * 2):
if chrom_fit[i] < Tbest_now:
Expand All @@ -201,6 +202,7 @@ def __init__(self):
sequence_best = copy.deepcopy(sequence_now)

makespan_record.append(Tbest)

"""----------result----------"""
print("optimal sequence", sequence_best)
print("optimal value:%f" % Tbest)
Expand Down
2 changes: 0 additions & 2 deletions examples/pymoo_demo.py → examples/pymoo_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
每个任务有自己的需求日期

目标为延误日期最少,同时换型最少

做的事情就是把这个序列的permutation优化一下
"""

from typing import Union
Expand Down
19 changes: 14 additions & 5 deletions examples/rule_example.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import argparse
import json
import logging

Expand Down Expand Up @@ -25,7 +26,7 @@ def prepare_data(file_path="./data/k1.json"):
re_name = re["machineName"]
re_id = int(re_name.replace("M", ""))
resource = Resource(resource_id=re_id, resource_name=re_name)
resource.available_hours = list(range(1, 100))
resource.available_hours = list(range(1, 200))
resource_collector.add_resource_dict(resource)

print([i.resource_id for i in resource_collector.get_all_resources()])
Expand Down Expand Up @@ -73,17 +74,25 @@ def prepare_data(file_path="./data/k1.json"):
return job_collector, resource_collector, route_collector


def run_scheduling(job_collector, resource_collector, route_collector):
# scheduler = BackwardScheduler(job_collector, resource_collector, route_collector)
scheduler = ForwardScheduler(job_collector, resource_collector, route_collector)
def run_scheduling(job_collector, resource_collector, route_collector, use_model="forward"):
if use_model == "forward":
scheduler = ForwardScheduler(job_collector, resource_collector, route_collector)
elif use_model == "backward":
scheduler = BackwardScheduler(job_collector, resource_collector, route_collector)
else:
raise ValueError

scheduler.run()
return


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--use_model", type=str, default="backward")
args = parser.parse_args()

job_collector, resource_collector, route_collector = prepare_data(file_path="./data/k1.json")
run_scheduling(job_collector, resource_collector, route_collector)
run_scheduling(job_collector, resource_collector, route_collector, use_model=args.use_model)

scheduling_res = get_scheduling_res_from_all_jobs(job_collector)
print(scheduling_res)
Expand Down
6 changes: 1 addition & 5 deletions lekin/lekin_struct/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,9 @@ def __str__(self):

class JobCollector:
def __init__(self):
self.job_list = [] # List to store Job objects
self.job_list = []
self.color_dict = dict() # List to store colors for job
self.index = -1
# self.route_list = []
# self.operation_list = []
# self.resource_list = []
# self.time_slot_list = []

def __iter__(self):
return self
Expand Down
3 changes: 0 additions & 3 deletions lekin/lekin_struct/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ def __init__(
operation_id: str,
operation_name: str,
quantity: int,
# beat_time: Union[int, List[int], float, List[float]],
processing_time: Union[int, List[int], float, List[float]],
pre_time: float = 0, # setup times
post_time: float = 0,
Expand All @@ -31,13 +30,11 @@ def __init__(
self.operation_id = operation_id
self.operation_name = operation_name
self.quantity = quantity
# self.beat_time = beat_time
self.processing_time = processing_time
self.pre_time = pre_time
self.post_time = post_time
self.lead_time = lead_time
self.lag_time = lag_time
# self.demand_time = demand_time
self.route_constraint = route_constraint
self.available_resource = available_resource
self.available_resource_priority = available_resource_priority
Expand Down
2 changes: 1 addition & 1 deletion lekin/lekin_struct/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def find_first_index_larger(self, input_value, lists):
for j, value in enumerate(lists):
if value > input_value:
return j
return None # If no value is larger than the input
return None

def find_last_index_larger(self, input_value, lists):
lists = lists[::-1]
Expand Down
11 changes: 11 additions & 0 deletions lekin/solver/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class SolverConfig:
def __init__(self, entity_selector=None, move_selector=None, termination=None):
self.entity_selector = entity_selector
self.move_selector = move_selector
self.termination = termination


class TerminationConfig:
def __init__(self, seconds_spent_limit=None, max_iterations=None):
self.seconds_spent_limit = seconds_spent_limit
self.max_iterations = max_iterations
65 changes: 65 additions & 0 deletions lekin/solver/solver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import copy
import random
import time


def check_constraints(job_assignments):
for i, assignment1 in enumerate(job_assignments):
for j, assignment2 in enumerate(job_assignments):
if i < j:
# Check resource conflict
if assignment1.resource == assignment2.resource:
if not (
assignment1.timeslot.end_time <= assignment2.timeslot.start_time
or assignment2.timeslot.end_time <= assignment1.timeslot.start_time
):
return False
# Check timeslot conflict
if assignment1.timeslot is None or assignment2.timeslot is None:
return False
return True


class LekinSolver(object):
def __init__(self, config):
self.config = config
self.best_solution = None

def solve(self, schedule):
start_time = time.time()
current_solution = copy.deepcopy(schedule)
self.best_solution = current_solution
tabu_list = []
iterations = 0

while not self._is_termination_reached(start_time, iterations):
neighbors = self.config.move_selector.generate_neighbors(current_solution)
feasible_neighbors = [neighbor for neighbor in neighbors if check_constraints(neighbor.job_assignments)]

if not feasible_neighbors:
continue

feasible_neighbors.sort(key=self.config.entity_selector.evaluate)
current_solution = feasible_neighbors[0]
current_cost = self.config.entity_selector.evaluate(current_solution)
best_cost = self.config.entity_selector.evaluate(self.best_solution)

if current_cost < best_cost:
self.best_solution = current_solution

tabu_list.append(current_solution)
if len(tabu_list) > self.config.entity_selector.tabu_tenure:
tabu_list.pop(0)

iterations += 1

return self.best_solution

def _is_termination_reached(self, start_time, iterations):
if self.config.termination.seconds_spent_limit:
if time.time() - start_time > self.config.termination.seconds_spent_limit:
return True
if self.config.termination.max_iterations:
if iterations >= self.config.termination.max_iterations:
return True
return False
3 changes: 0 additions & 3 deletions lekin/solver/solver_auto.py

This file was deleted.

Loading