Skip to content

Commit

Permalink
Update forward and backward
Browse files Browse the repository at this point in the history
  • Loading branch information
LongxingTan committed Aug 21, 2023
1 parent bf503fd commit 75081ac
Show file tree
Hide file tree
Showing 21 changed files with 110 additions and 46 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,45 @@ jobs:
flags: cpu, unittest
name: CPU-coverage
fail_ci_if_error: false

docs:
name: Test docs build
runs-on: ubuntu-latest

steps:
- name: Check out Git repository
uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: 3.8

- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('docs/requirements_docs.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
sudo apt-get update && sudo apt-get install -y pandoc
python -m pip install --upgrade pip
pip install -r docs/requirements_docs.txt
shell: bash

- name: Build sphinx documentation
run: |
cd docs
make clean
make html --debug --jobs 2 SPHINXOPTS="-W"
- name: Upload built docs
uses: actions/upload-artifact@v2
with:
name: docs-results-${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.requires }}
path: docs/build/html/
# Use always() to always run this step to publish test results when there are test failures
if: success()
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
- spt
- fifo
- edd
- heuristics
- forward scheduling
- backward scheduling
- meta heuristics
- local search
- shifting_bottle_neck
- genetic

### Contributor
Expand Down
2 changes: 1 addition & 1 deletion docs/source/application.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Application
----------------

MRP: Material Requirements Planning
- 每个零件的库存


BOM: Bill Of Materials

Expand Down
1 change: 1 addition & 0 deletions docs/source/rules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ SPT—EDD规则

顺排和倒排,和其他规则启发式算法一样,一个工序集一个工序集的排。每排一个工序,工序job完成后,更新机器、job状态、后续job状态。

在顺排中,排的比较紧密的资源往往就是瓶颈资源。

倒排
---------------
Expand Down
2 changes: 1 addition & 1 deletion examples/rule_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,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, 150))
resource.available_hours = list(range(1, 151))
resource_collector.add_resource_dict({re_id: resource})
# print([i.resource_id for i in resource_collector.get_all_resources()])
# print(resource_collector.get_all_resources()[0].available_hours)
Expand Down
3 changes: 2 additions & 1 deletion lekin/lekin_struct/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import math

import numpy as np
import pandas as pd

from lekin.lekin_struct.timeslot import TimeSlot
Expand Down Expand Up @@ -67,7 +68,7 @@ def get_earliest_available_time(self, duration=None, start=None):

def get_latest_available_time(self, duration=None, end=None):
self.update_continuous_empty_hours()
return max([i for i in self.continuous_empty_hours[:end] if i >= duration])
return max([i + 1 for (i, v) in enumerate(self.continuous_empty_hours[:end]) if v >= duration])

def update_continuous_empty_hours(self):
if len(self.available_hours) != len(self._available_timeslots):
Expand Down
4 changes: 2 additions & 2 deletions lekin/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@


class Scheduler(object):
def __init__(self, objective, solver, max_operations):
def __init__(self, objective, solver, max_operations, **kwargs):
self.objective = objective
self.solver = solver
self.max_operations = max_operations

def solve(self, jobs, machines):
def run(self, jobs, machines):
self.solver.solve(jobs, machines)

def evaluate(self):
Expand Down
15 changes: 11 additions & 4 deletions lekin/solver/construction_heuristics/backward.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
import logging
import math

from lekin.lekin_struct.timeslot import TimeSlot
from lekin.lekin_struct import JobCollector, ResourceCollector, RouteCollector, TimeSlot
from lekin.solver.construction_heuristics.base import BaseScheduler


class BackwardScheduler(object):
def __init__(self, job_collector, resource_collector, route_collector=None, **kwargs):
def __init__(
self,
job_collector: JobCollector,
resource_collector: ResourceCollector,
route_collector: RouteCollector = None,
**kwargs,
):
self.job_collector = job_collector
self.resource_collector = resource_collector
self.route_collector = route_collector
Expand Down Expand Up @@ -39,7 +45,7 @@ def scheduling_job(self, job, resource_collector, route_collector):

op_earliest_start = 0 # forward constraint
op_latest_end = 150 # backward constraint
for operation in job.operations[::-1]: # inverse
for operation in job.operations[::-1]: # inverse for backward
logging.info(f"\tAssign Operation {operation.operation_id} of Job {job.job_id}")
chosen_resource, chosen_timeslot_hour = self.find_best_resource_and_timeslot_for_operation(
operation, op_latest_end, op_earliest_start
Expand Down Expand Up @@ -76,9 +82,10 @@ def find_best_resource_and_timeslot_for_operation(
latest_index = i
resource_latest_time = resource_time

# print(operation.operation_id, operation.processing_time, op_latest_end, resource_latest_time)
chosen_resource = available_resource[latest_index]
latest_time = int(min(op_latest_end, resource_latest_time))
chosen_hours = list(range(latest_time - math.ceil(operation.processing_time), latest_time + 0))
chosen_hours = list(range(latest_time - math.ceil(operation.processing_time), latest_time + 1))
return chosen_resource, chosen_hours

def assign_operation(self, operation, start_time, end_time, resources):
Expand Down
9 changes: 8 additions & 1 deletion lekin/solver/construction_heuristics/forward.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@
import logging
import math

from lekin.lekin_struct import JobCollector, ResourceCollector, RouteCollector
from lekin.solver.construction_heuristics.base import BaseScheduler


class ForwardScheduler(BaseScheduler):
def __init__(self, job_collector, resource_collector, route_collector=None, **kwargs):
def __init__(
self,
job_collector: JobCollector,
resource_collector: ResourceCollector,
route_collector: RouteCollector = None,
**kwargs,
):
super().__init__(job_collector, resource_collector, **kwargs)
self.job_collector = job_collector
self.resource_collector = resource_collector
Expand Down
73 changes: 39 additions & 34 deletions lekin/solver/meta_heuristics/genetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,61 @@
import copy
import random

from lekin.lekin_struct import JobCollector, ResourceCollector, RouteCollector


class GeneticScheduler:
def __init__(
self,
job_collector=None,
job_collector: JobCollector,
resource_collector: ResourceCollector,
route_collector: RouteCollector = None,
initial_schedule=None,
population_size=50,
generations=1000,
crossover_rate=0.8,
mutation_rate=0.2,
**kwargs,
):
self.job_collector = job_collector
self.initial_schedule = initial_schedule # 倒排顺排后的初始结果
self.initial_schedule = initial_schedule
self.population_size = population_size
self.generations = generations
self.crossover_rate = crossover_rate
self.mutation_rate = mutation_rate

def run(self):
population = self.initialize_population()

for generation in range(self.generations):
selected_individuals = self.selection(population)
new_population = []

while len(new_population) < self.population_size:
parent1 = random.choice(selected_individuals)
parent2 = random.choice(selected_individuals)

if random.random() < self.crossover_rate:
offspring1, offspring2 = self.crossover(parent1, parent2)
else:
offspring1, offspring2 = parent1, parent2

if random.random() < self.mutation_rate:
offspring1 = self.mutation(offspring1)
if random.random() < self.mutation_rate:
offspring2 = self.mutation(offspring2)

new_population.append(offspring1)
new_population.append(offspring2)

population = new_population

# Find the best solution in the final population
best_solution = min(population, key=lambda chromosome: self.fitness(chromosome)[0])

# Return the best schedule
return self.job_collector.create_schedule_from_operations(best_solution)

def initialize_population(self):
population = []
for _ in range(self.population_size):
Expand Down Expand Up @@ -59,35 +96,3 @@ def mutation(self, chromosome):
# Return the mutated chromosome
mutated_chromosome = 0
return mutated_chromosome

def evolve(self):
population = self.initialize_population()

for generation in range(self.generations):
selected_individuals = self.selection(population)
new_population = []

while len(new_population) < self.population_size:
parent1 = random.choice(selected_individuals)
parent2 = random.choice(selected_individuals)

if random.random() < self.crossover_rate:
offspring1, offspring2 = self.crossover(parent1, parent2)
else:
offspring1, offspring2 = parent1, parent2

if random.random() < self.mutation_rate:
offspring1 = self.mutation(offspring1)
if random.random() < self.mutation_rate:
offspring2 = self.mutation(offspring2)

new_population.append(offspring1)
new_population.append(offspring2)

population = new_population

# Find the best solution in the final population
best_solution = min(population, key=lambda chromosome: self.fitness(chromosome)[0])

# Return the best schedule
return self.job_collector.create_schedule_from_operations(best_solution)
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.

0 comments on commit 75081ac

Please sign in to comment.