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

Update backward scheduling #30

Merged
merged 2 commits into from
Aug 19, 2023
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
Empty file added examples/genetic_example.py
Empty file.
5 changes: 3 additions & 2 deletions examples/rule_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
Route,
RouteCollector,
)
from lekin.solver.construction_heuristics import ForwardScheduler, LPSTScheduler, SPTScheduler
from lekin.solver.construction_heuristics import BackwardScheduler, ForwardScheduler, LPSTScheduler, SPTScheduler

logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.DEBUG)

Expand All @@ -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, 100))
resource.available_hours = list(range(1, 150))
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 Expand Up @@ -87,6 +87,7 @@ def prepare_data(file_path="./data/k1.json"):

def run_scheduling(job_collector, resource_collector, route_collector):
scheduler = ForwardScheduler(job_collector, resource_collector, route_collector)
# scheduler = BackwardScheduler(job_collector, resource_collector, route_collector)
scheduler.run()
return

Expand Down
106 changes: 53 additions & 53 deletions lekin/lekin_struct/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,59 @@
from typing import Any, Callable, Dict, List, Optional, Tuple, Union


class Operation:
def __init__(
self,
operation_id: str,
operation_name: str,
quantity: int,
processing_time: Union[int, List[int]],
pre_time=0, # setup times
post_time=0,
lead_time=0,
route_constraint=None,
available_resource=None,
available_resource_priority=None,
parent_job_id=None,
prev_operation_ids=None,
next_operation_ids=None,
**kwargs,
):
self.operation_id = operation_id
self.operation_name = operation_name
self.quantity = quantity
self.processing_time = processing_time
self.pre_time = pre_time
self.post_time = post_time
self.lead_time = lead_time
# self.demand_time = demand_time
self.route_constraint = route_constraint
self.available_resource = available_resource
self.available_resource_priority = available_resource_priority
self.parent_job_id = parent_job_id
self.prev_operation_ids = prev_operation_ids # predecessors
self.next_operation_ids = next_operation_ids # successors

self.earliest_start_time = None
self.latest_start_time = None
self.earliest_end_time = None
self.latest_end_time = None

self.assigned_resource = None # Track the assigned resource
self.assigned_time_slot = None # Track the assigned time slot

for key, value in kwargs.items():
setattr(self, key, value)

# Add a method to calculate granularity metric based on processing time and available time slot
def calculate_granularity_metric(self, available_time_slot):
# Calculate the granularity metric based on processing time and available time slot
pass

def __str__(self):
return f"{self.operation_id}-{self.operation_name}"


class OperationCollector:
def __init__(self):
self.operation_list = [] # List to store Operation objects
Expand Down Expand Up @@ -57,56 +110,3 @@ def get_operations_by_job_and_route(self, job_list, route_list):
# (op for op in self.operations if op.operation_id == next_operation_id), None
# )
# return job_operations


class Operation:
def __init__(
self,
operation_id: str,
operation_name: str,
quantity: int,
processing_time: Union[int, List[int]],
pre_time=0, # setup times
post_time=0,
lead_time=0,
route_constraint=None,
available_resource=None,
available_resource_priority=None,
parent_job_id=None,
prev_operation_ids=None,
next_operation_ids=None,
**kwargs,
):
self.operation_id = operation_id
self.operation_name = operation_name
self.quantity = quantity
self.processing_time = processing_time
self.pre_time = pre_time
self.post_time = post_time
self.lead_time = lead_time
# self.demand_time = demand_time
self.route_constraint = route_constraint
self.available_resource = available_resource
self.available_resource_priority = available_resource_priority
self.parent_job_id = parent_job_id
self.prev_operation_ids = prev_operation_ids # predecessors
self.next_operation_ids = next_operation_ids # successors

self.earliest_start_time = None
self.latest_start_time = None
self.earliest_end_time = None
self.latest_end_time = None

self.assigned_resource = None # Track the assigned resource
self.assigned_time_slot = None # Track the assigned time slot

for key, value in kwargs.items():
setattr(self, key, value)

# Add a method to calculate granularity metric based on processing time and available time slot
def calculate_granularity_metric(self, available_time_slot):
# Calculate the granularity metric based on processing time and available time slot
pass

def __str__(self):
return f"{self.operation_id}-{self.operation_name}"
11 changes: 6 additions & 5 deletions lekin/lekin_struct/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,20 @@ def get_available_timeslot_for_op(self, start=None, end=None, periods=None, freq
else:
return []

def get_earliest_available_time(self, duration=None):
def get_earliest_available_time(self, duration=None, start=None):
if len(self.available_hours) > len(self.assigned_hours):
return min(set(self.available_hours).difference(set(self.assigned_hours)))
else:
return None

def get_latest_available_time(self, duration=None):
return
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])

def update_continuous_empty_hours(self):
if len(self.available_hours) != len(self.available_timeslots):
if len(self.available_hours) != len(self._available_timeslots):
pass
if len(self.assigned_hours) != len(self.assigned_time_slot):
if len(self.assigned_hours) != len(self.assigned_time_slots):
pass

# for hours_list in self.available_hours:
Expand Down
32 changes: 18 additions & 14 deletions lekin/solver/construction_heuristics/backward.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ def scheduling_job(self, job, resource_collector, route_collector):

job.operations = route.operations_sequence

op_earliest_start = 0
for operation in job.operations[::-1]:
op_earliest_start = 0 # forward constraint
op_latest_end = 150 # backward constraint
for operation in job.operations[::-1]: # inverse
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_earliest_start
operation, op_latest_end, op_earliest_start
)

if chosen_resource and chosen_timeslot_hour:
Expand All @@ -57,24 +58,27 @@ def scheduling_job(self, job, resource_collector, route_collector):
chosen_resource.assigned_operations.append(operation)
chosen_resource.assigned_hours += chosen_timeslot_hour

op_earliest_start = chosen_timeslot_hour[-1] + 1
# op_earliest_start = chosen_timeslot_hour[-1] + 1
op_latest_end = chosen_timeslot_hour[0] - 1
return

def find_best_resource_and_timeslot_for_operation(self, operation, op_earliest_start, **kwargs):
def find_best_resource_and_timeslot_for_operation(
self, operation, op_latest_end=None, op_earliest_start=None, **kwargs
):
available_resource = operation.available_resource

earliest_index = 0
resource_earliest_time = float("inf")
latest_index = float("inf")
resource_latest_time = 0
for i, resource in enumerate(available_resource):
resource_time = resource.get_earliest_available_time(duration=operation.processing_time)
resource_time = resource.get_latest_available_time(duration=operation.processing_time, end=op_latest_end)

if resource_time < resource_earliest_time:
earliest_index = i
resource_earliest_time = resource_time
if resource_time > resource_latest_time:
latest_index = i
resource_latest_time = resource_time

chosen_resource = available_resource[earliest_index]
earliest_time = int(max(op_earliest_start, resource_earliest_time))
chosen_hours = list(range(earliest_time, earliest_time + math.ceil(operation.processing_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))
return chosen_resource, chosen_hours

def assign_operation(self, operation, start_time, end_time, resources):
Expand Down
Loading