Skip to content
Snippets Groups Projects
Commit 2a7a7305 authored by Simon Bjurek's avatar Simon Bjurek
Browse files

fixes from mr comments

parent c23e613e
No related branches found
No related tags found
2 merge requests!461Finalize earliest deadline scheduler,!459Qt6 fixes and scheduler class started
......@@ -33,7 +33,7 @@ from b_asic.operation import Operation
from b_asic.port import InputPort, OutputPort
from b_asic.process import MemoryVariable, OperatorProcess
from b_asic.resources import ProcessCollection
from b_asic.scheduling_algorithm import SchedAlg
from b_asic.scheduler import Scheduler, SchedulingAlgorithm
from b_asic.signal_flow_graph import SFG
from b_asic.special_operations import Delay, Input, Output
from b_asic.types import TypeName
......@@ -96,7 +96,7 @@ class Schedule:
sfg: SFG,
schedule_time: Optional[int] = None,
cyclic: bool = False,
algorithm: SchedAlg = "ASAP",
algorithm: SchedulingAlgorithm = "ASAP",
start_times: Optional[Dict[GraphID, int]] = None,
laps: Optional[Dict[GraphID, int]] = None,
max_resources: Optional[Dict[TypeName, int]] = None,
......@@ -1227,178 +1227,3 @@ class Schedule:
# SVG is valid HTML. This is useful for e.g. sphinx-gallery
_repr_html_ = _repr_svg_
class Scheduler:
def __init__(self, schedule: Schedule) -> None:
self.schedule = schedule
def schedule_asap(self) -> None:
"""Schedule the operations using as-soon-as-possible scheduling."""
sched = self.schedule
prec_list = sched.sfg.get_precedence_list()
if len(prec_list) < 2:
raise ValueError("Empty signal flow graph cannot be scheduled.")
# handle the first set in precedence graph (input and delays)
non_schedulable_ops = set()
for outport in prec_list[0]:
operation = outport.operation
if operation.type_name() == Delay.type_name():
non_schedulable_ops.add(operation.graph_id)
# elif operation.graph_id not in sched._start_times:
else:
sched._start_times[operation.graph_id] = 0
# handle second set in precedence graph (first operations)
for outport in prec_list[1]:
operation = outport.operation
# if operation.graph_id not in sched._start_times:
sched._start_times[operation.graph_id] = 0
# handle the remaining sets
for outports in prec_list[2:]:
for outport in outports:
operation = outport.operation
if operation.graph_id not in sched._start_times:
op_start_time = 0
for current_input in operation.inputs:
if len(current_input.signals) != 1:
raise ValueError(
"Error in scheduling, dangling input port detected."
)
if current_input.signals[0].source is None:
raise ValueError(
"Error in scheduling, signal with no source detected."
)
source_port = current_input.signals[0].source
if source_port.operation.graph_id in non_schedulable_ops:
source_end_time = 0
else:
source_op_time = sched._start_times[
source_port.operation.graph_id
]
if source_port.latency_offset is None:
raise ValueError(
f"Output port {source_port.index} of"
" operation"
f" {source_port.operation.graph_id} has no"
" latency-offset."
)
source_end_time = (
source_op_time + source_port.latency_offset
)
if current_input.latency_offset is None:
raise ValueError(
f"Input port {current_input.index} of operation"
f" {current_input.operation.graph_id} has no"
" latency-offset."
)
op_start_time_from_in = (
source_end_time - current_input.latency_offset
)
op_start_time = max(op_start_time, op_start_time_from_in)
sched._start_times[operation.graph_id] = op_start_time
self._handle_outputs_and_delays(non_schedulable_ops)
def schedule_alap(self) -> None:
"""Schedule the operations using as-late-as-possible scheduling."""
self.schedule_asap()
sched = self.schedule
max_end_time = sched.get_max_end_time()
if sched.schedule_time is None:
sched.set_schedule_time(max_end_time)
elif sched.schedule_time < max_end_time:
raise ValueError(f"Too short schedule time. Minimum is {max_end_time}.")
# move all outputs ALAP before operations
for output in sched.sfg.find_by_type_name(Output.type_name()):
output = cast(Output, output)
sched.move_operation_alap(output.graph_id)
# move all operations ALAP
for step in reversed(sched.sfg.get_precedence_list()):
for outport in step:
if not isinstance(outport.operation, Delay):
sched.move_operation_alap(outport.operation.graph_id)
def schedule_earliest_deadline(self, process_elements: list[Operation]) -> None:
"""Schedule the operations using earliest deadline scheduling."""
# ACT BASED ON THE NUMBER OF PEs!
sched = self.schedule
prec_list = sched.sfg.get_precedence_list()
if len(prec_list) < 2:
raise ValueError("Empty signal flow graph cannot be scheduled.")
# handle the first set in precedence graph (input and delays)
non_schedulable_ops = set()
for outport in prec_list[0]:
operation = outport.operation
if operation.type_name() == Delay.type_name():
non_schedulable_ops.add(operation.graph_id)
elif operation.graph_id not in sched._start_times:
sched._start_times[operation.graph_id] = 0
# latencies = [outport.operation.latency for outport in prec_list[1]]
current_time = 0
sorted_outports = sorted(
prec_list[1], key=lambda outport: outport.operation.latency
)
for outport in sorted_outports:
op = outport.operation
sched._start_times[op.graph_id] = current_time
current_time += 1
for outports in prec_list[2:]:
# try all remaining operations for one time step
candidates = []
current_time -= 1
while len(candidates) == 0:
current_time += 1
for outport in outports:
remaining_op = outport.operation
op_is_ready_to_be_scheduled = True
for op_input in remaining_op.inputs:
source_op = op_input.signals[0].source.operation
source_op_time = sched.start_times[source_op.graph_id]
source_end_time = source_op_time + source_op.latency
if source_end_time > current_time:
op_is_ready_to_be_scheduled = False
if op_is_ready_to_be_scheduled:
candidates.append(remaining_op)
# sched._start_times[remaining_op.graph_id] = current_time
sorted_candidates = sorted(
candidates, key=lambda candidate: candidate.latency
)
# schedule the best candidate to current time
sched._start_times[sorted_candidates[0].graph_id] = current_time
self._handle_outputs_and_delays(non_schedulable_ops)
def _handle_outputs_and_delays(self, non_schedulable_ops) -> None:
sched = self.schedule
for output in sched._sfg.find_by_type_name(Output.type_name()):
output = cast(Output, output)
source_port = cast(OutputPort, output.inputs[0].signals[0].source)
if source_port.operation.graph_id in non_schedulable_ops:
sched._start_times[output.graph_id] = 0
else:
if source_port.latency_offset is None:
raise ValueError(
f"Output port {source_port.index} of operation"
f" {source_port.operation.graph_id} has no"
" latency-offset."
)
sched._start_times[output.graph_id] = sched._start_times[
source_port.operation.graph_id
] + cast(int, source_port.latency_offset)
sched._remove_delays()
from enum import Enum
from typing import TYPE_CHECKING, cast
from b_asic.operation import Operation
from b_asic.port import OutputPort
from b_asic.special_operations import Delay, Output
if TYPE_CHECKING:
from b_asic.schedule import Schedule
class SchedulingAlgorithm(Enum):
ASAP = "ASAP"
ALAP = "ALAP"
EARLIEST_DEADLINE = "earliest_deadline"
# LEAST_SLACK = "least_slack" # to be implemented
PROVIDED = "provided"
class Scheduler:
def __init__(self, schedule: "Schedule") -> None:
self.schedule = schedule
def schedule_asap(self) -> None:
"""Schedule the operations using as-soon-as-possible scheduling."""
sched = self.schedule
prec_list = sched.sfg.get_precedence_list()
if len(prec_list) < 2:
raise ValueError("Empty signal flow graph cannot be scheduled.")
# handle the first set in precedence graph (input and delays)
non_schedulable_ops = set()
for outport in prec_list[0]:
operation = outport.operation
if operation.type_name() == Delay.type_name():
non_schedulable_ops.add(operation.graph_id)
# elif operation.graph_id not in sched._start_times:
else:
sched._start_times[operation.graph_id] = 0
# handle second set in precedence graph (first operations)
for outport in prec_list[1]:
operation = outport.operation
# if operation.graph_id not in sched._start_times:
sched._start_times[operation.graph_id] = 0
# handle the remaining sets
for outports in prec_list[2:]:
for outport in outports:
operation = outport.operation
if operation.graph_id not in sched._start_times:
op_start_time = 0
for current_input in operation.inputs:
if len(current_input.signals) != 1:
raise ValueError(
"Error in scheduling, dangling input port detected."
)
if current_input.signals[0].source is None:
raise ValueError(
"Error in scheduling, signal with no source detected."
)
source_port = current_input.signals[0].source
if source_port.operation.graph_id in non_schedulable_ops:
source_end_time = 0
else:
source_op_time = sched._start_times[
source_port.operation.graph_id
]
if source_port.latency_offset is None:
raise ValueError(
f"Output port {source_port.index} of"
" operation"
f" {source_port.operation.graph_id} has no"
" latency-offset."
)
source_end_time = (
source_op_time + source_port.latency_offset
)
if current_input.latency_offset is None:
raise ValueError(
f"Input port {current_input.index} of operation"
f" {current_input.operation.graph_id} has no"
" latency-offset."
)
op_start_time_from_in = (
source_end_time - current_input.latency_offset
)
op_start_time = max(op_start_time, op_start_time_from_in)
sched._start_times[operation.graph_id] = op_start_time
self._handle_outputs_and_delays(non_schedulable_ops)
def schedule_alap(self) -> None:
"""Schedule the operations using as-late-as-possible scheduling."""
self.schedule_asap()
sched = self.schedule
max_end_time = sched.get_max_end_time()
if sched.schedule_time is None:
sched.set_schedule_time(max_end_time)
elif sched.schedule_time < max_end_time:
raise ValueError(f"Too short schedule time. Minimum is {max_end_time}.")
# move all outputs ALAP before operations
for output in sched.sfg.find_by_type_name(Output.type_name()):
output = cast(Output, output)
sched.move_operation_alap(output.graph_id)
# move all operations ALAP
for step in reversed(sched.sfg.get_precedence_list()):
for outport in step:
if not isinstance(outport.operation, Delay):
sched.move_operation_alap(outport.operation.graph_id)
def schedule_earliest_deadline(
self, process_elements: dict[Operation, int]
) -> None:
"""Schedule the operations using earliest deadline scheduling."""
# ACT BASED ON THE NUMBER OF PEs!
sched = self.schedule
prec_list = sched.sfg.get_precedence_list()
if len(prec_list) < 2:
raise ValueError("Empty signal flow graph cannot be scheduled.")
# handle the first set in precedence graph (input and delays)
non_schedulable_ops = set()
for outport in prec_list[0]:
operation = outport.operation
if operation.type_name() == Delay.type_name():
non_schedulable_ops.add(operation.graph_id)
elif operation.graph_id not in sched._start_times:
sched._start_times[operation.graph_id] = 0
current_time = 0
sorted_outports = sorted(
prec_list[1], key=lambda outport: outport.operation.latency
)
for outport in sorted_outports:
op = outport.operation
sched._start_times[op.graph_id] = current_time
current_time += 1
for outports in prec_list[2:]:
# try all remaining operations for one time step
candidates = []
current_time -= 1
while len(candidates) == 0:
current_time += 1
for outport in outports:
remaining_op = outport.operation
op_is_ready_to_be_scheduled = True
for op_input in remaining_op.inputs:
source_op = op_input.signals[0].source.operation
source_op_time = sched.start_times[source_op.graph_id]
source_end_time = source_op_time + source_op.latency
if source_end_time > current_time:
op_is_ready_to_be_scheduled = False
if op_is_ready_to_be_scheduled:
candidates.append(remaining_op)
# sched._start_times[remaining_op.graph_id] = current_time
sorted_candidates = sorted(
candidates, key=lambda candidate: candidate.latency
)
# schedule the best candidate to current time
sched._start_times[sorted_candidates[0].graph_id] = current_time
self._handle_outputs_and_delays(non_schedulable_ops)
def _handle_outputs_and_delays(self, non_schedulable_ops) -> None:
sched = self.schedule
for output in sched._sfg.find_by_type_name(Output.type_name()):
output = cast(Output, output)
source_port = cast(OutputPort, output.inputs[0].signals[0].source)
if source_port.operation.graph_id in non_schedulable_ops:
sched._start_times[output.graph_id] = 0
else:
if source_port.latency_offset is None:
raise ValueError(
f"Output port {source_port.index} of operation"
f" {source_port.operation.graph_id} has no"
" latency-offset."
)
sched._start_times[output.graph_id] = sched._start_times[
source_port.operation.graph_id
] + cast(int, source_port.latency_offset)
sched._remove_delays()
from enum import Enum
class SchedAlg(Enum):
ASAP = "ASAP"
ALAP = "ALAP"
EARLIEST_DEADLINE = "earliest_deadline"
LEAST_SLACK = "least_slack"
PROVIDED = "provided"
......@@ -12,16 +12,17 @@ dependencies = [
"matplotlib>=3.7",
"setuptools_scm[toml]>=6.2",
"networkx>=3",
"qtawesome"
"qtawesome",
"pyqt6",
]
classifiers = [
"Intended Audience :: Education",
"Intended Audience :: Science/Research",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: C++",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment