Skip to content

Commit

Permalink
Update backward scheduling (#30)
Browse files Browse the repository at this point in the history
* Update operation

* Update backward

---------

Co-authored-by: LongxingTan <[email protected]>
  • Loading branch information
yuetan1988 and LongxingTan committed Aug 19, 2023
1 parent cfdc4a8 commit ad63841
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 74 deletions.
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

0 comments on commit ad63841

Please sign in to comment.