diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000000000000000000000000000000000000..a765f497b5e293ca87d49861608420f9d7939332 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[report] +exclude_lines = + .*if TYPE_CHECKING:.* + raise NotImplementedError diff --git a/.gitignore b/.gitignore index 8a416e12c9c75b449f4f84a1bc2519e620935800..4530b5b1ee950d5342ab55d9d551681bb3258bfb 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,4 @@ b_asic/_version.py docs_sphinx/_build/ docs_sphinx/examples result_images/ +.coverage diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5f4c6f59ac55d9f6701cdb3a282395abc8dac315..4b33da657a56f302a939c62e3341155f5f7f4fb5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,51 +36,45 @@ before_script: path: cov.xml coverage: /(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/ -run-test-3.10-pyside2: +run-test-3.10-pyqt6: variables: - QT_API: pyside2 + QT_API: pyqt6 image: python:3.10 extends: ".run-test" -run-test-3.10-pyqt5: +run-test-3.10-pyside6: variables: - QT_API: pyqt5 + QT_API: pyside6 image: python:3.10 extends: ".run-test" -run-test-3.10-pyqt6: +run-test-3.11-pyqt6: variables: QT_API: pyqt6 - image: python:3.10 - extends: ".run-test" - -run-test-3.11-pyqt5: - variables: - QT_API: pyqt5 image: python:3.11 extends: ".run-test" -run-test-3.11-pyqt6: +run-test-3.11-pyside6: variables: - QT_API: pyqt6 + QT_API: pyside6 image: python:3.11 extends: ".run-test" -run-test-3.12-pyqt5: +run-test-3.12-pyqt6: variables: - QT_API: pyqt5 + QT_API: pyqt6 image: python:3.12 extends: ".run-test" -run-test-3.12-pyqt6: +run-test-3.12-pyside6: variables: - QT_API: pyqt6 + QT_API: pyside6 image: python:3.12 extends: ".run-test" run-vhdl-tests: variables: - QT_API: pyqt5 + QT_API: pyqt6 image: python:3.10 stage: test script: @@ -92,7 +86,7 @@ run-vhdl-tests: run-doc-test: variables: - QT_API: pyside2 + QT_API: pyside6 image: python:3.10 stage: test script: @@ -111,7 +105,7 @@ run-doc-test: pages: variables: - QT_API: pyqt5 + QT_API: pyqt6 stage: deploy image: python:3.10 script: diff --git a/README.md b/README.md index 074f282851d96a46099695dfefd13571167eb755..d216f6dcbfeb0ac97162f02be3bcb2837d1bbbcc 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ How to build and debug the library during development. The following packages are required in order to build the library: -- [Python](https://python.org/) 3.8+ +- [Python](https://python.org/) 3.10+ - Python dependencies (install with `pip install -r requirements.txt` or they will be installed as part of the installation process): - [Graphviz](https://graphviz.org/) @@ -24,11 +24,9 @@ The following packages are required in order to build the library: - [setuptools_scm](https://github.com/pypa/setuptools_scm/) - [NetworkX](https://networkx.org/) - [QtAwesome](https://github.com/spyder-ide/qtawesome/) -- Qt 5 or 6, with Python bindings, one of: - - pyside2 - - pyqt5 - - pyside6 +- Qt 6, with Python bindings, one of: - pyqt6 + - pyside6 To build a binary distribution, the following additional packages are required: diff --git a/b_asic/GUI/main_window.py b/b_asic/GUI/main_window.py index b3ab5a7683694fde4f53d999ec3493b756ca242a..90146b871a9289c01aa2de8b3e13d22025d52c78 100644 --- a/b_asic/GUI/main_window.py +++ b/b_asic/GUI/main_window.py @@ -978,7 +978,6 @@ def start_editor(sfg: Optional[SFG] = None) -> Dict[str, SFG]: All SFGs currently in the editor. """ if not QApplication.instance(): - QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) app = QApplication(sys.argv) else: app = QApplication.instance() diff --git a/b_asic/__init__.py b/b_asic/__init__.py index fae7aec4b6efe6d3a1c1223d6b35f25aa4aceb9e..423e4676d9eda02aed6ec6accff2900743a0a04f 100644 --- a/b_asic/__init__.py +++ b/b_asic/__init__.py @@ -9,6 +9,7 @@ from b_asic.operation import * from b_asic.port import * from b_asic.save_load_structure import * from b_asic.schedule import * +from b_asic.scheduler import * from b_asic.signal import * from b_asic.signal_flow_graph import * from b_asic.simulation import * diff --git a/b_asic/schedule.py b/b_asic/schedule.py index a708fe1ad0ed4048143317b00d37827a96ff9259..4c7f01b6dea0be070b775e84605c0b0b84363ab8 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -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.scheduler import Scheduler, SchedulingAlgorithm +from b_asic.scheduler import Scheduler from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output from b_asic.types import TypeName @@ -64,24 +64,19 @@ class Schedule: ---------- sfg : :class:`~b_asic.signal_flow_graph.SFG` The signal flow graph to schedule. + scheduler : Scheduler, default: None + The automatic scheduler to be used. schedule_time : int, optional The schedule time. If not provided, it will be determined by the scheduling algorithm. cyclic : bool, default: False If the schedule is cyclic. - algorithm : SchedulingAlgorithm, default: 'ASAP' - The scheduling algorithm to use. start_times : dict, optional Dictionary with GraphIDs as keys and start times as values. Used when *algorithm* is 'provided'. laps : dict, optional Dictionary with GraphIDs as keys and laps as values. Used when *algorithm* is 'provided'. - max_resources : dict, optional - Dictionary like ``{Addition.type_name(): 2}`` denoting the maximum number of - resources for a given operation type if the scheduling algorithm considers - that. If not provided, or an operation type is not provided, at most one - resource is used. """ _sfg: SFG @@ -94,12 +89,11 @@ class Schedule: def __init__( self, sfg: SFG, + scheduler: Optional[Scheduler] = None, schedule_time: Optional[int] = None, cyclic: bool = False, - algorithm: SchedulingAlgorithm = "ASAP", start_times: Optional[Dict[GraphID, int]] = None, laps: Optional[Dict[GraphID, int]] = None, - max_resources: Optional[Dict[TypeName, int]] = None, ): """Construct a Schedule from an SFG.""" if not isinstance(sfg, SFG): @@ -112,14 +106,10 @@ class Schedule: self._y_locations = defaultdict(_y_locations_default) self._schedule_time = schedule_time - self.scheduler = Scheduler(self) - if algorithm == "ASAP": - self.scheduler.schedule_asap() - elif algorithm == "ALAP": - self.scheduler.schedule_alap() - elif algorithm == "earliest_deadline": - self.scheduler.schedule_earliest_deadline([]) - elif algorithm == "provided": + if scheduler: + self._scheduler = scheduler + self._scheduler.apply_scheduling(self) + else: if start_times is None: raise ValueError("Must provide start_times when using 'provided'") if laps is None: @@ -127,11 +117,8 @@ class Schedule: self._start_times = start_times self._laps.update(laps) self._remove_delays_no_laps() - else: - raise NotImplementedError(f"No algorithm with name: {algorithm} defined.") max_end_time = self.get_max_end_time() - if schedule_time is None: self._schedule_time = max_end_time elif schedule_time < max_end_time: @@ -399,6 +386,15 @@ class Schedule: """The start times of the operations in the schedule.""" return self._start_times + @start_times.setter + def start_times(self, start_times: dict[GraphID, int]) -> None: + if not isinstance(start_times, dict): + raise TypeError("start_times must be a dict") + for key, value in start_times.items(): + if not isinstance(key, str) or not isinstance(value, int): + raise TypeError("start_times must be a dict[GraphID, int]") + self._start_times = start_times + @property def laps(self) -> Dict[GraphID, int]: """ @@ -770,7 +766,7 @@ class Schedule: self._sfg = cast(SFG, self._sfg.remove_operation(delay_op.graph_id)) delay_list = self._sfg.find_by_type_name(Delay.type_name()) - def _remove_delays(self) -> None: + def remove_delays(self) -> None: """Remove delay elements and update laps. Used after scheduling algorithm.""" delay_list = self._sfg.find_by_type_name(Delay.type_name()) while delay_list: diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index 845859c971d98cc50f50a2f3c314a841880d0fcd..e109d01d9de77ed74b7f685ead2982d085def749 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,30 +1,58 @@ -from enum import Enum -from typing import TYPE_CHECKING, cast +import sys +from abc import ABC, abstractmethod +from collections import defaultdict +from typing import TYPE_CHECKING, Optional, cast -from b_asic.operation import Operation from b_asic.port import OutputPort -from b_asic.special_operations import Delay, Output +from b_asic.special_operations import Delay, Input, Output +from b_asic.types import TypeName 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(ABC): + @abstractmethod + def apply_scheduling(self, schedule: "Schedule") -> None: + """Applies the scheduling algorithm on the given Schedule. + Parameters + ---------- + schedule : Schedule + Schedule to apply the scheduling algorithm on. + """ + raise NotImplementedError -class Scheduler: - def __init__(self, schedule: "Schedule") -> None: - self.schedule = schedule + def _handle_outputs(self, schedule, non_schedulable_ops=set()) -> None: + for output in schedule.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: + schedule.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." + ) + schedule.start_times[output.graph_id] = schedule.start_times[ + source_port.operation.graph_id + ] + cast(int, source_port.latency_offset) + + +class ASAPScheduler(Scheduler): + """Scheduler that implements the as-soon-as-possible (ASAP) algorithm.""" + + def apply_scheduling(self, schedule: "Schedule") -> None: + """Applies the scheduling algorithm on the given 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() + Parameters + ---------- + schedule : Schedule + Schedule to apply the scheduling algorithm on. + """ + prec_list = schedule.sfg.get_precedence_list() if len(prec_list) < 2: raise ValueError("Empty signal flow graph cannot be scheduled.") @@ -34,37 +62,27 @@ class Scheduler: 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 + schedule.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 + schedule.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: + if operation.graph_id not in schedule.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_op_time = schedule.start_times[ source_port.operation.graph_id ] @@ -91,103 +109,161 @@ class Scheduler: ) op_start_time = max(op_start_time, op_start_time_from_in) - sched._start_times[operation.graph_id] = op_start_time + schedule.start_times[operation.graph_id] = op_start_time - self._handle_outputs_and_delays(non_schedulable_ops) + self._handle_outputs(schedule, non_schedulable_ops) + schedule.remove_delays() - 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: +class ALAPScheduler(Scheduler): + """Scheduler that implements the as-late-as-possible (ALAP) algorithm.""" + + def apply_scheduling(self, schedule: "Schedule") -> None: + """Applies the scheduling algorithm on the given Schedule. + + Parameters + ---------- + schedule : Schedule + Schedule to apply the scheduling algorithm on. + """ + ASAPScheduler().apply_scheduling(schedule) + max_end_time = schedule.get_max_end_time() + + if schedule.schedule_time is None: + schedule.set_schedule_time(max_end_time) + elif schedule.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()): + for output in schedule.sfg.find_by_type_name(Output.type_name()): output = cast(Output, output) - sched.move_operation_alap(output.graph_id) + schedule.move_operation_alap(output.graph_id) # move all operations ALAP - for step in reversed(sched.sfg.get_precedence_list()): + for step in reversed(schedule.sfg.get_precedence_list()): for outport in step: if not isinstance(outport.operation, Delay): - sched.move_operation_alap(outport.operation.graph_id) + schedule.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! +class EarliestDeadlineScheduler(Scheduler): + """ + Scheduler that implements the earliest-deadline-first algorithm. - sched = self.schedule - prec_list = sched.sfg.get_precedence_list() - if len(prec_list) < 2: - raise ValueError("Empty signal flow graph cannot be scheduled.") + Parameters + ---------- + max_resources : dict, optional + Dictionary like ``{Addition.type_name(): 2}`` denoting the maximum number of + resources for a given operation type if the scheduling algorithm considers + that. If not provided, or an operation type is not provided, at most one + resource is used. + """ - # 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 + def __init__(self, max_resources: Optional[dict[TypeName, int]] = None) -> None: + if max_resources: + self._max_resources = max_resources + else: + self._max_resources = {} + def apply_scheduling(self, schedule: "Schedule") -> None: + """Applies the scheduling algorithm on the given Schedule. + + Parameters + ---------- + schedule : Schedule + Schedule to apply the scheduling algorithm on. + """ + + ALAPScheduler().apply_scheduling(schedule) + + # move all inputs ASAP to ensure correct operation + for input_op in schedule.sfg.find_by_type_name(Input.type_name()): + input_op = cast(Input, input_op) + schedule.move_operation_asap(input_op.graph_id) + + # construct the set of remaining operations, excluding inputs + remaining_ops = list(schedule.start_times.keys()) + remaining_ops = [elem for elem in remaining_ops if not elem.startswith("in")] + + # construct a dictionarry for storing how many times until a resource is available again + used_resources_ready_times = {} + + # iterate through all remaining operations and schedule them + # while not exceeding the available resources + remaining_resources = self._max_resources.copy() 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 + while remaining_ops: + best_candidate = self._find_best_candidate( + schedule, remaining_ops, remaining_resources, current_time + ) - for outports in prec_list[2:]: - # try all remaining operations for one time step - candidates = [] - current_time -= 1 - while len(candidates) == 0: + if not best_candidate: 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) + # update available operators + for operation, ready_time in used_resources_ready_times.items(): + if ready_time == current_time: + remaining_resources[operation.type_name()] += 1 + # remaining_resources = self._max_resources.copy() + continue - 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." + # if the resource is constrained, update remaining resources + if best_candidate.type_name() in remaining_resources: + remaining_resources[best_candidate.type_name()] -= 1 + if best_candidate.execution_time: + used_resources_ready_times[best_candidate] = ( + current_time + best_candidate.execution_time ) - sched._start_times[output.graph_id] = sched._start_times[ - source_port.operation.graph_id - ] + cast(int, source_port.latency_offset) - sched._remove_delays() + else: + used_resources_ready_times[best_candidate] = ( + current_time + best_candidate.latency + ) + + # schedule the best candidate to the current time + remaining_ops.remove(best_candidate.graph_id) + schedule.start_times[best_candidate.graph_id] = current_time + + # move all inputs and outputs ALAP now that operations have moved + for input_op in schedule.sfg.find_by_type_name(Input.type_name()): + input_op = cast(Input, input_op) + schedule.move_operation_alap(input_op.graph_id) + self._handle_outputs(schedule) + + @staticmethod + def _find_best_candidate( + schedule, remaining_ops, remaining_resources, current_time + ): + sfg = schedule.sfg + source_end_times = defaultdict(float) + + # find the best candidate + best_candidate = None + best_deadline = float('inf') + for op_id in remaining_ops: + operation = sfg.find_by_id(op_id) + + # compute maximum end times of preceding operations + for op_input in operation.inputs: + source_op = op_input.signals[0].source.operation + if not isinstance(source_op, Delay): + source_end_times[op_id] = max( + source_end_times[op_id], + schedule.start_times[source_op.graph_id] + source_op.latency, + ) + # ensure that the source is already scheduled + if source_op.graph_id in remaining_ops: + source_end_times[op_id] = sys.maxsize + + # check resource constraints + if operation.type_name() in remaining_resources: + if remaining_resources[operation.type_name()] == 0: + continue + + # check if all inputs are available + if source_end_times[op_id] <= current_time: + operation_deadline = schedule.start_times[op_id] + operation.latency + if operation_deadline < best_deadline: + best_candidate = operation + best_deadline = operation_deadline + + return best_candidate diff --git a/b_asic/scheduler_gui/compile.py b/b_asic/scheduler_gui/compile.py index 2d9be5282811344e6a95548244e85a9195a1dfde..2dc7b719d7fdff9fa8b5f7fd0530814eb6f21b75 100644 --- a/b_asic/scheduler_gui/compile.py +++ b/b_asic/scheduler_gui/compile.py @@ -2,7 +2,7 @@ """ B-ASIC Scheduler-gui Resource and Form Compiler Module. -Compile Qt5 resource and form files. Requires PySide2 or PyQt5 to be installed. +Compile Qt6 resource and form files. Requires PySide6 or PyQt6 to be installed. If no arguments is given, the compiler search for and compiles all form (.ui) files. """ @@ -160,45 +160,13 @@ def compile_ui(*filenames: str) -> None: directory = directory if directory else "." outfile = f"{directory}/ui_{file}.py" - if uic.PYSIDE2: - uic_ = shutil.which("pyside2-uic") - arguments = f"-g python -o {outfile} {filename}" - - if uic_ is None: - uic_ = shutil.which("uic") - if uic_ is None: - uic_ = shutil.which("pyuic5") - arguments = f"-o {outfile} {filename}" - assert uic_, ( - "Qt User Interface Compiler failed, cannot find pyside2-uic," - " uic, or pyuic5" - ) - - os_ = sys.platform - if os_.startswith("linux"): # Linux - cmd = f"{uic_} {arguments}" - subprocess.call(cmd.split()) - - elif os_.startswith("win32"): # Windows - # TODO: implement - log.error("Windows UI compiler not implemented") - raise NotImplementedError - - elif os_.startswith("darwin"): # macOS - # TODO: implement - log.error("macOS UI compiler not implemented") - raise NotImplementedError - - else: # other OS - log.error(f"{os_} UI compiler not supported") - raise NotImplementedError - - elif uic.PYQT5 or uic.PYQT6: + if uic.PYQT6: from qtpy.uic import compileUi with open(outfile, "w") as ofile: compileUi(filename, ofile) - elif uic.PYQT6: + + elif uic.PYSIDE6: uic_ = shutil.which("pyside6-uic") arguments = f"-g python -o {outfile} {filename}" @@ -218,9 +186,8 @@ def compile_ui(*filenames: str) -> None: subprocess.call(cmd.split()) elif os_.startswith("win32"): # Windows - # TODO: implement - log.error("Windows UI compiler not implemented") - raise NotImplementedError + cmd = f"{uic_} {arguments}" + subprocess.call(cmd.split()) elif os_.startswith("darwin"): # macOS # TODO: implement diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index f4502f9908905c8579608297c366bdcb2a16c013..c15be2679acd4a0e725638dc8849d2ae4ba83000 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -98,9 +98,9 @@ if __debug__: log.debug(f"Qt version (compile time): {QtCore.__version__}") log.debug(f"QT_API: {QT_API}") if QT_API.lower().startswith("pyside"): - import PySide2 + import PySide6 - log.debug(f"PySide version: {PySide2.__version__}") + log.debug(f"PySide version: {PySide6.__version__}") if QT_API.lower().startswith("pyqt"): from qtpy.QtCore import PYQT_VERSION_STR @@ -1689,17 +1689,19 @@ def start_scheduler(schedule: Optional[Schedule] = None) -> Optional[Schedule]: """ if not QApplication.instance(): app = QApplication(sys.argv) - # Enforce a light palette regardless of laptop theme - palette = QPalette() - palette.setColor(QPalette.ColorRole.Window, QtCore.Qt.white) - palette.setColor(QPalette.ColorRole.WindowText, QtCore.Qt.black) - palette.setColor(QPalette.ColorRole.ButtonText, QtCore.Qt.black) - palette.setColor(QPalette.ColorRole.Base, QtCore.Qt.white) - palette.setColor(QPalette.ColorRole.AlternateBase, QtCore.Qt.lightGray) - palette.setColor(QPalette.ColorRole.Text, QtCore.Qt.black) - app.setPalette(palette) else: app = QApplication.instance() + + # Enforce a light palette regardless of laptop theme + palette = QPalette() + palette.setColor(QPalette.ColorRole.Window, QtCore.Qt.white) + palette.setColor(QPalette.ColorRole.WindowText, QtCore.Qt.black) + palette.setColor(QPalette.ColorRole.ButtonText, QtCore.Qt.black) + palette.setColor(QPalette.ColorRole.Base, QtCore.Qt.white) + palette.setColor(QPalette.ColorRole.AlternateBase, QtCore.Qt.lightGray) + palette.setColor(QPalette.ColorRole.Text, QtCore.Qt.black) + app.setPalette(palette) + window = ScheduleMainWindow() if schedule: window.open(schedule) diff --git a/b_asic/scheduler_gui/ui_main_window.py b/b_asic/scheduler_gui/ui_main_window.py index ad3fa898840bd8511fc62700272e4018ca22e926..829aeb12278eceef1e46cb9a991901d03e834fe0 100644 --- a/b_asic/scheduler_gui/ui_main_window.py +++ b/b_asic/scheduler_gui/ui_main_window.py @@ -1,277 +1,337 @@ -# Form implementation generated from reading ui file '.\B-ASIC\b_asic\scheduler_gui\main_window.ui' -# -# Created by: PyQt6 UI code generator 6.8.0 -# -# WARNING: Any manual changes made to this file will be lost when pyuic6 is -# run again. Do not edit this file unless you know what you are doing. +################################################################################ +## Form generated from reading UI file 'main_window.ui' +## +## Created by: Qt User Interface Compiler version 6.8.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ - -from qtpy import QtCore, QtGui, QtWidgets +from qtpy.QtCore import ( + QCoreApplication, + QMetaObject, + QRect, + QSize, + Qt, +) +from qtpy.QtGui import ( + QAction, + QBrush, + QColor, + QFont, + QIcon, + QPainter, +) +from qtpy.QtWidgets import ( + QAbstractItemView, + QGraphicsView, + QHBoxLayout, + QMenu, + QMenuBar, + QSizePolicy, + QSplitter, + QStatusBar, + QTableWidget, + QTableWidgetItem, + QToolBar, + QWidget, +) class Ui_MainWindow: def setupUi(self, MainWindow): - MainWindow.setObjectName("MainWindow") + if not MainWindow.objectName(): + MainWindow.setObjectName("MainWindow") MainWindow.resize(800, 600) - sizePolicy = QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.Policy.Preferred, - QtWidgets.QSizePolicy.Policy.Preferred, + sizePolicy = QSizePolicy( + QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) MainWindow.setSizePolicy(sizePolicy) - icon = QtGui.QIcon() - icon.addPixmap( - QtGui.QPixmap(":/icons/basic/small_logo.png"), - QtGui.QIcon.Mode.Normal, - QtGui.QIcon.State.Off, + icon = QIcon() + icon.addFile( + ":/icons/basic/small_logo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off ) MainWindow.setWindowIcon(icon) - self.centralwidget = QtWidgets.QWidget(parent=MainWindow) - sizePolicy = QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.Policy.Preferred, - QtWidgets.QSizePolicy.Policy.Preferred, - ) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( - self.centralwidget.sizePolicy().hasHeightForWidth() - ) - self.centralwidget.setSizePolicy(sizePolicy) - self.centralwidget.setObjectName("centralwidget") - self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) - self.horizontalLayout.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout.setSpacing(0) - self.horizontalLayout.setObjectName("horizontalLayout") - self.splitter = QtWidgets.QSplitter(parent=self.centralwidget) - self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal) - self.splitter.setHandleWidth(0) - self.splitter.setObjectName("splitter") - self.view = QtWidgets.QGraphicsView(parent=self.splitter) - self.view.setAlignment( - QtCore.Qt.AlignmentFlag.AlignLeading - | QtCore.Qt.AlignmentFlag.AlignLeft - | QtCore.Qt.AlignmentFlag.AlignTop - ) - self.view.setRenderHints( - QtGui.QPainter.RenderHint.Antialiasing - | QtGui.QPainter.RenderHint.TextAntialiasing - ) - self.view.setViewportUpdateMode( - QtWidgets.QGraphicsView.ViewportUpdateMode.FullViewportUpdate - ) - self.view.setObjectName("view") - self.info_table = QtWidgets.QTableWidget(parent=self.splitter) - self.info_table.setStyleSheet( - "alternate-background-color: #fadefb;background-color: #ebebeb;" - ) - self.info_table.setEditTriggers( - QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers - ) - self.info_table.setAlternatingRowColors(True) - self.info_table.setSelectionBehavior( - QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows - ) - self.info_table.setRowCount(2) - self.info_table.setColumnCount(2) - self.info_table.setObjectName("info_table") - item = QtWidgets.QTableWidgetItem() - self.info_table.setVerticalHeaderItem(0, item) - item = QtWidgets.QTableWidgetItem() - self.info_table.setVerticalHeaderItem(1, item) - item = QtWidgets.QTableWidgetItem() - item.setTextAlignment( - QtCore.Qt.AlignmentFlag.AlignLeading | QtCore.Qt.AlignmentFlag.AlignVCenter - ) - font = QtGui.QFont() - font.setBold(False) - font.setWeight(50) - item.setFont(font) - self.info_table.setHorizontalHeaderItem(0, item) - item = QtWidgets.QTableWidgetItem() - item.setTextAlignment( - QtCore.Qt.AlignmentFlag.AlignLeading | QtCore.Qt.AlignmentFlag.AlignVCenter - ) - self.info_table.setHorizontalHeaderItem(1, item) - item = QtWidgets.QTableWidgetItem() - font = QtGui.QFont() - font.setBold(False) - font.setWeight(50) - font.setKerning(True) - item.setFont(font) - brush = QtGui.QBrush(QtGui.QColor(160, 160, 164)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - item.setBackground(brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - item.setForeground(brush) - item.setFlags( - QtCore.Qt.ItemFlag.ItemIsSelectable - | QtCore.Qt.ItemFlag.ItemIsEditable - | QtCore.Qt.ItemFlag.ItemIsDragEnabled - | QtCore.Qt.ItemFlag.ItemIsDropEnabled - | QtCore.Qt.ItemFlag.ItemIsUserCheckable - ) - self.info_table.setItem(0, 0, item) - item = QtWidgets.QTableWidgetItem() - font = QtGui.QFont() - font.setBold(False) - font.setWeight(50) - item.setFont(font) - brush = QtGui.QBrush(QtGui.QColor(160, 160, 164)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - item.setBackground(brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - item.setForeground(brush) - item.setFlags( - QtCore.Qt.ItemFlag.ItemIsSelectable - | QtCore.Qt.ItemFlag.ItemIsEditable - | QtCore.Qt.ItemFlag.ItemIsDragEnabled - | QtCore.Qt.ItemFlag.ItemIsDropEnabled - | QtCore.Qt.ItemFlag.ItemIsUserCheckable - ) - self.info_table.setItem(1, 0, item) - self.info_table.horizontalHeader().setHighlightSections(False) - self.info_table.horizontalHeader().setStretchLastSection(True) - self.info_table.verticalHeader().setVisible(False) - self.info_table.verticalHeader().setDefaultSectionSize(24) - self.horizontalLayout.addWidget(self.splitter) - MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(parent=MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 20)) - self.menubar.setObjectName("menubar") - self.menuFile = QtWidgets.QMenu(parent=self.menubar) - self.menuFile.setObjectName("menuFile") - self.menu_Recent_Schedule = QtWidgets.QMenu(parent=self.menuFile) - self.menu_Recent_Schedule.setObjectName("menu_Recent_Schedule") - self.menuView = QtWidgets.QMenu(parent=self.menubar) - self.menuView.setObjectName("menuView") - self.menu_view_execution_times = QtWidgets.QMenu(parent=self.menuView) - self.menu_view_execution_times.setEnabled(False) - self.menu_view_execution_times.setObjectName("menu_view_execution_times") - self.menu_Edit = QtWidgets.QMenu(parent=self.menubar) - self.menu_Edit.setObjectName("menu_Edit") - self.menuWindow = QtWidgets.QMenu(parent=self.menubar) - self.menuWindow.setObjectName("menuWindow") - self.menuHelp = QtWidgets.QMenu(parent=self.menubar) - self.menuHelp.setObjectName("menuHelp") - MainWindow.setMenuBar(self.menubar) - self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) - self.statusbar.setObjectName("statusbar") - MainWindow.setStatusBar(self.statusbar) - self.toolBar = QtWidgets.QToolBar(parent=MainWindow) - self.toolBar.setObjectName("toolBar") - MainWindow.addToolBar(QtCore.Qt.ToolBarArea.TopToolBarArea, self.toolBar) - self.menu_load_from_file = QtGui.QAction(parent=MainWindow) - icon = QtGui.QIcon.fromTheme("document-open-folder") - self.menu_load_from_file.setIcon(icon) - self.menu_load_from_file.setStatusTip("") + self.menu_load_from_file = QAction(MainWindow) self.menu_load_from_file.setObjectName("menu_load_from_file") - self.menu_save = QtGui.QAction(parent=MainWindow) - self.menu_save.setEnabled(False) - icon = QtGui.QIcon.fromTheme("document-save") - self.menu_save.setIcon(icon) + icon1 = QIcon() + iconThemeName = "document-open-folder" + if QIcon.hasThemeIcon(iconThemeName): + icon1 = QIcon.fromTheme(iconThemeName) + else: + icon1.addFile( + "../../../.designer/backup", + QSize(), + QIcon.Mode.Normal, + QIcon.State.Off, + ) + + self.menu_load_from_file.setIcon(icon1) + self.menu_save = QAction(MainWindow) self.menu_save.setObjectName("menu_save") - self.menu_node_info = QtGui.QAction(parent=MainWindow) + self.menu_save.setEnabled(False) + icon2 = QIcon() + iconThemeName = "document-save" + if QIcon.hasThemeIcon(iconThemeName): + icon2 = QIcon.fromTheme(iconThemeName) + else: + icon2.addFile( + "../../../.designer/backup", + QSize(), + QIcon.Mode.Normal, + QIcon.State.Off, + ) + + self.menu_save.setIcon(icon2) + self.menu_node_info = QAction(MainWindow) + self.menu_node_info.setObjectName("menu_node_info") self.menu_node_info.setCheckable(True) self.menu_node_info.setChecked(True) - icon1 = QtGui.QIcon() - icon1.addPixmap( - QtGui.QPixmap(":/icons/misc/right_panel.svg"), - QtGui.QIcon.Mode.Normal, - QtGui.QIcon.State.Off, - ) - icon1.addPixmap( - QtGui.QPixmap(":/icons/misc/right_filled_panel.svg"), - QtGui.QIcon.Mode.Normal, - QtGui.QIcon.State.On, - ) - self.menu_node_info.setIcon(icon1) + icon3 = QIcon() + icon3.addFile( + ":/icons/misc/right_panel.svg", QSize(), QIcon.Mode.Normal, QIcon.State.Off + ) + icon3.addFile( + ":/icons/misc/right_filled_panel.svg", + QSize(), + QIcon.Mode.Normal, + QIcon.State.On, + ) + self.menu_node_info.setIcon(icon3) self.menu_node_info.setIconVisibleInMenu(False) - self.menu_node_info.setObjectName("menu_node_info") - self.menu_quit = QtGui.QAction(parent=MainWindow) - icon = QtGui.QIcon.fromTheme("application-exit") - self.menu_quit.setIcon(icon) + self.menu_quit = QAction(MainWindow) self.menu_quit.setObjectName("menu_quit") - self.menu_save_as = QtGui.QAction(parent=MainWindow) - self.menu_save_as.setEnabled(False) - icon = QtGui.QIcon.fromTheme("document-save-as") - self.menu_save_as.setIcon(icon) + icon4 = QIcon() + iconThemeName = "application-exit" + if QIcon.hasThemeIcon(iconThemeName): + icon4 = QIcon.fromTheme(iconThemeName) + else: + icon4.addFile( + "../../../.designer/backup", + QSize(), + QIcon.Mode.Normal, + QIcon.State.Off, + ) + + self.menu_quit.setIcon(icon4) + self.menu_save_as = QAction(MainWindow) self.menu_save_as.setObjectName("menu_save_as") - self.menu_exit_dialog = QtGui.QAction(parent=MainWindow) + self.menu_save_as.setEnabled(False) + icon5 = QIcon() + iconThemeName = "document-save-as" + if QIcon.hasThemeIcon(iconThemeName): + icon5 = QIcon.fromTheme(iconThemeName) + else: + icon5.addFile( + "../../../.designer/backup", + QSize(), + QIcon.Mode.Normal, + QIcon.State.Off, + ) + + self.menu_save_as.setIcon(icon5) + self.menu_exit_dialog = QAction(MainWindow) + self.menu_exit_dialog.setObjectName("menu_exit_dialog") self.menu_exit_dialog.setCheckable(True) self.menu_exit_dialog.setChecked(True) - icon = QtGui.QIcon.fromTheme("view-close") - self.menu_exit_dialog.setIcon(icon) - self.menu_exit_dialog.setObjectName("menu_exit_dialog") - self.menu_close_schedule = QtGui.QAction(parent=MainWindow) - self.menu_close_schedule.setEnabled(False) - icon = QtGui.QIcon.fromTheme("view-close") - self.menu_close_schedule.setIcon(icon) + icon6 = QIcon() + iconThemeName = "view-close" + if QIcon.hasThemeIcon(iconThemeName): + icon6 = QIcon.fromTheme(iconThemeName) + else: + icon6.addFile( + "../../../.designer/backup", + QSize(), + QIcon.Mode.Normal, + QIcon.State.Off, + ) + + self.menu_exit_dialog.setIcon(icon6) + self.menu_close_schedule = QAction(MainWindow) self.menu_close_schedule.setObjectName("menu_close_schedule") - self.actionAbout = QtGui.QAction(parent=MainWindow) + self.menu_close_schedule.setEnabled(False) + self.menu_close_schedule.setIcon(icon6) + self.actionAbout = QAction(MainWindow) self.actionAbout.setObjectName("actionAbout") - self.actionDocumentation = QtGui.QAction(parent=MainWindow) + self.actionDocumentation = QAction(MainWindow) self.actionDocumentation.setObjectName("actionDocumentation") - self.actionReorder = QtGui.QAction(parent=MainWindow) + self.actionReorder = QAction(MainWindow) self.actionReorder.setObjectName("actionReorder") - self.actionPlot_schedule = QtGui.QAction(parent=MainWindow) + self.actionPlot_schedule = QAction(MainWindow) self.actionPlot_schedule.setObjectName("actionPlot_schedule") - self.action_view_variables = QtGui.QAction(parent=MainWindow) - self.action_view_variables.setEnabled(False) + self.action_view_variables = QAction(MainWindow) self.action_view_variables.setObjectName("action_view_variables") - self.action_view_port_accesses = QtGui.QAction(parent=MainWindow) - self.action_view_port_accesses.setEnabled(False) + self.action_view_variables.setEnabled(False) + self.action_view_port_accesses = QAction(MainWindow) self.action_view_port_accesses.setObjectName("action_view_port_accesses") - self.actionUndo = QtGui.QAction(parent=MainWindow) - self.actionUndo.setEnabled(False) + self.action_view_port_accesses.setEnabled(False) + self.actionUndo = QAction(MainWindow) self.actionUndo.setObjectName("actionUndo") - self.actionRedo = QtGui.QAction(parent=MainWindow) - self.actionRedo.setEnabled(False) + self.actionUndo.setEnabled(False) + self.actionRedo = QAction(MainWindow) self.actionRedo.setObjectName("actionRedo") - self.actionIncrease_time_resolution = QtGui.QAction(parent=MainWindow) + self.actionRedo.setEnabled(False) + self.actionIncrease_time_resolution = QAction(MainWindow) self.actionIncrease_time_resolution.setObjectName( "actionIncrease_time_resolution" ) - self.actionDecrease_time_resolution = QtGui.QAction(parent=MainWindow) + self.actionDecrease_time_resolution = QAction(MainWindow) self.actionDecrease_time_resolution.setObjectName( "actionDecrease_time_resolution" ) - self.actionZoom_to_fit = QtGui.QAction(parent=MainWindow) + self.actionZoom_to_fit = QAction(MainWindow) self.actionZoom_to_fit.setObjectName("actionZoom_to_fit") - self.actionStatus_bar = QtGui.QAction(parent=MainWindow) + self.actionStatus_bar = QAction(MainWindow) + self.actionStatus_bar.setObjectName("actionStatus_bar") self.actionStatus_bar.setCheckable(True) self.actionStatus_bar.setChecked(True) - self.actionStatus_bar.setObjectName("actionStatus_bar") - self.actionToolbar = QtGui.QAction(parent=MainWindow) + self.actionToolbar = QAction(MainWindow) + self.actionToolbar.setObjectName("actionToolbar") self.actionToolbar.setCheckable(True) self.actionToolbar.setChecked(True) - self.actionToolbar.setObjectName("actionToolbar") - self.action_show_port_numbers = QtGui.QAction(parent=MainWindow) + self.action_show_port_numbers = QAction(MainWindow) + self.action_show_port_numbers.setObjectName("action_show_port_numbers") self.action_show_port_numbers.setCheckable(True) self.action_show_port_numbers.setChecked(False) self.action_show_port_numbers.setIconVisibleInMenu(False) - self.action_show_port_numbers.setObjectName("action_show_port_numbers") - self.action_incorrect_execution_time = QtGui.QAction(parent=MainWindow) - self.action_incorrect_execution_time.setCheckable(True) - self.action_incorrect_execution_time.setChecked(True) - self.action_incorrect_execution_time.setIconVisibleInMenu(False) + self.action_incorrect_execution_time = QAction(MainWindow) self.action_incorrect_execution_time.setObjectName( "action_incorrect_execution_time" ) - self.menu_open = QtGui.QAction(parent=MainWindow) - icon = QtGui.QIcon.fromTheme("personal") - self.menu_open.setIcon(icon) + self.action_incorrect_execution_time.setCheckable(True) + self.action_incorrect_execution_time.setChecked(True) + self.action_incorrect_execution_time.setIconVisibleInMenu(False) + self.menu_open = QAction(MainWindow) self.menu_open.setObjectName("menu_open") - self.actionToggle_full_screen = QtGui.QAction(parent=MainWindow) - self.actionToggle_full_screen.setCheckable(True) + icon7 = QIcon(QIcon.fromTheme("personal")) + self.menu_open.setIcon(icon7) + self.actionToggle_full_screen = QAction(MainWindow) self.actionToggle_full_screen.setObjectName("actionToggle_full_screen") - self.actionPreferences = QtGui.QAction(parent=MainWindow) - icon = QtGui.QIcon.fromTheme("preferences-desktop-personal") - self.actionPreferences.setIcon(icon) + self.actionToggle_full_screen.setCheckable(True) + self.actionPreferences = QAction(MainWindow) self.actionPreferences.setObjectName("actionPreferences") + icon8 = QIcon(QIcon.fromTheme("preferences-desktop-personal")) + self.actionPreferences.setIcon(icon8) + self.centralwidget = QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + sizePolicy.setHeightForWidth( + self.centralwidget.sizePolicy().hasHeightForWidth() + ) + self.centralwidget.setSizePolicy(sizePolicy) + self.horizontalLayout = QHBoxLayout(self.centralwidget) + self.horizontalLayout.setSpacing(0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.splitter = QSplitter(self.centralwidget) + self.splitter.setObjectName("splitter") + self.splitter.setOrientation(Qt.Horizontal) + self.splitter.setHandleWidth(0) + self.view = QGraphicsView(self.splitter) + self.view.setObjectName("view") + self.view.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignTop) + self.view.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing) + self.view.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) + self.splitter.addWidget(self.view) + self.info_table = QTableWidget(self.splitter) + if self.info_table.columnCount() < 2: + self.info_table.setColumnCount(2) + font = QFont() + font.setBold(False) + __qtablewidgetitem = QTableWidgetItem() + __qtablewidgetitem.setTextAlignment(Qt.AlignLeading | Qt.AlignVCenter) + __qtablewidgetitem.setFont(font) + self.info_table.setHorizontalHeaderItem(0, __qtablewidgetitem) + __qtablewidgetitem1 = QTableWidgetItem() + __qtablewidgetitem1.setTextAlignment(Qt.AlignLeading | Qt.AlignVCenter) + self.info_table.setHorizontalHeaderItem(1, __qtablewidgetitem1) + if self.info_table.rowCount() < 2: + self.info_table.setRowCount(2) + __qtablewidgetitem2 = QTableWidgetItem() + self.info_table.setVerticalHeaderItem(0, __qtablewidgetitem2) + __qtablewidgetitem3 = QTableWidgetItem() + self.info_table.setVerticalHeaderItem(1, __qtablewidgetitem3) + brush = QBrush(QColor(255, 255, 255, 255)) + brush.setStyle(Qt.SolidPattern) + brush1 = QBrush(QColor(160, 160, 164, 255)) + brush1.setStyle(Qt.SolidPattern) + font1 = QFont() + font1.setBold(False) + font1.setKerning(True) + __qtablewidgetitem4 = QTableWidgetItem() + __qtablewidgetitem4.setFont(font1) + __qtablewidgetitem4.setBackground(brush1) + __qtablewidgetitem4.setForeground(brush) + __qtablewidgetitem4.setFlags( + Qt.ItemIsSelectable + | Qt.ItemIsEditable + | Qt.ItemIsDragEnabled + | Qt.ItemIsDropEnabled + | Qt.ItemIsUserCheckable + ) + self.info_table.setItem(0, 0, __qtablewidgetitem4) + __qtablewidgetitem5 = QTableWidgetItem() + __qtablewidgetitem5.setFont(font) + __qtablewidgetitem5.setBackground(brush1) + __qtablewidgetitem5.setForeground(brush) + __qtablewidgetitem5.setFlags( + Qt.ItemIsSelectable + | Qt.ItemIsEditable + | Qt.ItemIsDragEnabled + | Qt.ItemIsDropEnabled + | Qt.ItemIsUserCheckable + ) + self.info_table.setItem(1, 0, __qtablewidgetitem5) + self.info_table.setObjectName("info_table") + self.info_table.setStyleSheet( + "alternate-background-color: #fadefb;background-color: #ebebeb;" + ) + self.info_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.info_table.setAlternatingRowColors(True) + self.info_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.info_table.setRowCount(2) + self.info_table.setColumnCount(2) + self.splitter.addWidget(self.info_table) + self.info_table.horizontalHeader().setHighlightSections(False) + self.info_table.horizontalHeader().setStretchLastSection(True) + self.info_table.verticalHeader().setVisible(False) + self.info_table.verticalHeader().setDefaultSectionSize(24) + + self.horizontalLayout.addWidget(self.splitter) + + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QMenuBar(MainWindow) + self.menubar.setObjectName("menubar") + self.menubar.setGeometry(QRect(0, 0, 800, 20)) + self.menuFile = QMenu(self.menubar) + self.menuFile.setObjectName("menuFile") + self.menu_Recent_Schedule = QMenu(self.menuFile) + self.menu_Recent_Schedule.setObjectName("menu_Recent_Schedule") + self.menuView = QMenu(self.menubar) + self.menuView.setObjectName("menuView") + self.menu_view_execution_times = QMenu(self.menuView) + self.menu_view_execution_times.setObjectName("menu_view_execution_times") + self.menu_view_execution_times.setEnabled(False) + self.menu_Edit = QMenu(self.menubar) + self.menu_Edit.setObjectName("menu_Edit") + self.menuWindow = QMenu(self.menubar) + self.menuWindow.setObjectName("menuWindow") + self.menuHelp = QMenu(self.menubar) + self.menuHelp.setObjectName("menuHelp") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QStatusBar(MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.toolBar = QToolBar(MainWindow) + self.toolBar.setObjectName("toolBar") + MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.toolBar) + + self.menubar.addAction(self.menuFile.menuAction()) + self.menubar.addAction(self.menu_Edit.menuAction()) + self.menubar.addAction(self.menuView.menuAction()) + self.menubar.addAction(self.menuWindow.menuAction()) + self.menubar.addAction(self.menuHelp.menuAction()) self.menuFile.addAction(self.menu_open) self.menuFile.addAction(self.menu_Recent_Schedule.menuAction()) self.menuFile.addAction(self.menu_load_from_file) @@ -306,11 +366,6 @@ class Ui_MainWindow: self.menuHelp.addAction(self.actionDocumentation) self.menuHelp.addSeparator() self.menuHelp.addAction(self.actionAbout) - self.menubar.addAction(self.menuFile.menuAction()) - self.menubar.addAction(self.menu_Edit.menuAction()) - self.menubar.addAction(self.menuView.menuAction()) - self.menubar.addAction(self.menuWindow.menuAction()) - self.menubar.addAction(self.menuHelp.menuAction()) self.toolBar.addAction(self.menu_open) self.toolBar.addAction(self.menu_save) self.toolBar.addAction(self.menu_save_as) @@ -324,127 +379,300 @@ class Ui_MainWindow: self.toolBar.addAction(self.actionReorder) self.retranslateUi(MainWindow) - QtCore.QMetaObject.connectSlotsByName(MainWindow) + + QMetaObject.connectSlotsByName(MainWindow) + + # setupUi def retranslateUi(self, MainWindow): - _translate = QtCore.QCoreApplication.translate - item = self.info_table.verticalHeaderItem(0) - item.setText(_translate("MainWindow", "1")) - item = self.info_table.verticalHeaderItem(1) - item.setText(_translate("MainWindow", "2")) - item = self.info_table.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "Property")) - item = self.info_table.horizontalHeaderItem(1) - item.setText(_translate("MainWindow", "Value")) - __sortingEnabled = self.info_table.isSortingEnabled() - self.info_table.setSortingEnabled(False) - item = self.info_table.item(0, 0) - item.setText(_translate("MainWindow", "Schedule")) - item = self.info_table.item(1, 0) - item.setText(_translate("MainWindow", "Operator")) - self.info_table.setSortingEnabled(__sortingEnabled) - self.menuFile.setTitle(_translate("MainWindow", "&File")) - self.menu_Recent_Schedule.setTitle(_translate("MainWindow", "Open &recent")) - self.menuView.setTitle(_translate("MainWindow", "&View")) - self.menu_view_execution_times.setTitle( - _translate("MainWindow", "View execution times of type") - ) - self.menu_Edit.setTitle(_translate("MainWindow", "&Edit")) - self.menuWindow.setTitle(_translate("MainWindow", "&Window")) - self.menuHelp.setTitle(_translate("MainWindow", "&Help")) - self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar")) self.menu_load_from_file.setText( - _translate("MainWindow", "&Import schedule from file...") + QCoreApplication.translate( + "MainWindow", "&Import schedule from file...", None + ) ) + # if QT_CONFIG(tooltip) self.menu_load_from_file.setToolTip( - _translate("MainWindow", "Import schedule from python script") + QCoreApplication.translate( + "MainWindow", "Import schedule from python script", None + ) ) - self.menu_load_from_file.setShortcut(_translate("MainWindow", "Ctrl+I")) - self.menu_save.setText(_translate("MainWindow", "&Save")) - self.menu_save.setToolTip(_translate("MainWindow", "Save schedule")) - self.menu_save.setShortcut(_translate("MainWindow", "Ctrl+S")) - self.menu_node_info.setText(_translate("MainWindow", "&Node info")) + # endif // QT_CONFIG(tooltip) + # if QT_CONFIG(statustip) + self.menu_load_from_file.setStatusTip("") + # endif // QT_CONFIG(statustip) + # if QT_CONFIG(shortcut) + self.menu_load_from_file.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+I", None) + ) + # endif // QT_CONFIG(shortcut) + self.menu_save.setText(QCoreApplication.translate("MainWindow", "&Save", None)) + # if QT_CONFIG(tooltip) + self.menu_save.setToolTip( + QCoreApplication.translate("MainWindow", "Save schedule", None) + ) + # endif // QT_CONFIG(tooltip) + # if QT_CONFIG(shortcut) + self.menu_save.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+S", None) + ) + # endif // QT_CONFIG(shortcut) + self.menu_node_info.setText( + QCoreApplication.translate("MainWindow", "&Node info", None) + ) + # if QT_CONFIG(tooltip) self.menu_node_info.setToolTip( - _translate("MainWindow", "Show/hide node information") + QCoreApplication.translate("MainWindow", "Show/hide node information", None) + ) + # endif // QT_CONFIG(tooltip) + # if QT_CONFIG(shortcut) + self.menu_node_info.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+N", None) + ) + # endif // QT_CONFIG(shortcut) + self.menu_quit.setText(QCoreApplication.translate("MainWindow", "&Quit", None)) + # if QT_CONFIG(shortcut) + self.menu_quit.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+Q", None) + ) + # endif // QT_CONFIG(shortcut) + self.menu_save_as.setText( + QCoreApplication.translate("MainWindow", "Save &as...", None) ) - self.menu_node_info.setShortcut(_translate("MainWindow", "Ctrl+N")) - self.menu_quit.setText(_translate("MainWindow", "&Quit")) - self.menu_quit.setShortcut(_translate("MainWindow", "Ctrl+Q")) - self.menu_save_as.setText(_translate("MainWindow", "Save &as...")) + # if QT_CONFIG(tooltip) self.menu_save_as.setToolTip( - _translate("MainWindow", "Save schedule with new file name") - ) - self.menu_save_as.setShortcut(_translate("MainWindow", "Ctrl+Shift+S")) - self.menu_exit_dialog.setText(_translate("MainWindow", "&Hide exit dialog")) - self.menu_exit_dialog.setToolTip(_translate("MainWindow", "Hide exit dialog")) - self.menu_close_schedule.setText(_translate("MainWindow", "&Close schedule")) - self.menu_close_schedule.setShortcut(_translate("MainWindow", "Ctrl+W")) - self.actionAbout.setText(_translate("MainWindow", "&About")) - self.actionAbout.setToolTip(_translate("MainWindow", "Open about window")) - self.actionDocumentation.setText(_translate("MainWindow", "&Documentation")) + QCoreApplication.translate( + "MainWindow", "Save schedule with new file name", None + ) + ) + # endif // QT_CONFIG(tooltip) + # if QT_CONFIG(shortcut) + self.menu_save_as.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+Shift+S", None) + ) + # endif // QT_CONFIG(shortcut) + self.menu_exit_dialog.setText( + QCoreApplication.translate("MainWindow", "&Hide exit dialog", None) + ) + # if QT_CONFIG(tooltip) + self.menu_exit_dialog.setToolTip( + QCoreApplication.translate("MainWindow", "Hide exit dialog", None) + ) + # endif // QT_CONFIG(tooltip) + self.menu_close_schedule.setText( + QCoreApplication.translate("MainWindow", "&Close schedule", None) + ) + # if QT_CONFIG(shortcut) + self.menu_close_schedule.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+W", None) + ) + # endif // QT_CONFIG(shortcut) + self.actionAbout.setText( + QCoreApplication.translate("MainWindow", "&About", None) + ) + # if QT_CONFIG(tooltip) + self.actionAbout.setToolTip( + QCoreApplication.translate("MainWindow", "Open about window", None) + ) + # endif // QT_CONFIG(tooltip) + self.actionDocumentation.setText( + QCoreApplication.translate("MainWindow", "&Documentation", None) + ) + # if QT_CONFIG(tooltip) self.actionDocumentation.setToolTip( - _translate("MainWindow", "Open documentation") + QCoreApplication.translate("MainWindow", "Open documentation", None) ) - self.actionReorder.setText(_translate("MainWindow", "Reorder")) + # endif // QT_CONFIG(tooltip) + self.actionReorder.setText( + QCoreApplication.translate("MainWindow", "Reorder", None) + ) + # if QT_CONFIG(tooltip) self.actionReorder.setToolTip( - _translate("MainWindow", "Reorder schedule based on start time") + QCoreApplication.translate( + "MainWindow", "Reorder schedule based on start time", None + ) + ) + # endif // QT_CONFIG(tooltip) + # if QT_CONFIG(shortcut) + self.actionReorder.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+R", None) ) - self.actionReorder.setShortcut(_translate("MainWindow", "Ctrl+R")) - self.actionPlot_schedule.setText(_translate("MainWindow", "&Plot schedule")) - self.actionPlot_schedule.setToolTip(_translate("MainWindow", "Plot schedule")) + # endif // QT_CONFIG(shortcut) + self.actionPlot_schedule.setText( + QCoreApplication.translate("MainWindow", "&Plot schedule", None) + ) + # if QT_CONFIG(tooltip) + self.actionPlot_schedule.setToolTip( + QCoreApplication.translate("MainWindow", "Plot schedule", None) + ) + # endif // QT_CONFIG(tooltip) self.action_view_variables.setText( - _translate("MainWindow", "View execution times of variables") + QCoreApplication.translate( + "MainWindow", "View execution times of variables", None + ) ) + # if QT_CONFIG(tooltip) self.action_view_variables.setToolTip( - _translate("MainWindow", "View all variables") + QCoreApplication.translate("MainWindow", "View all variables", None) ) + # endif // QT_CONFIG(tooltip) self.action_view_port_accesses.setText( - _translate("MainWindow", "View port access statistics") + QCoreApplication.translate( + "MainWindow", "View port access statistics", None + ) ) + # if QT_CONFIG(tooltip) self.action_view_port_accesses.setToolTip( - _translate("MainWindow", "View port access statistics for storage") + QCoreApplication.translate( + "MainWindow", "View port access statistics for storage", None + ) + ) + # endif // QT_CONFIG(tooltip) + self.actionUndo.setText(QCoreApplication.translate("MainWindow", "Undo", None)) + # if QT_CONFIG(shortcut) + self.actionUndo.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+Z", None) + ) + # endif // QT_CONFIG(shortcut) + self.actionRedo.setText(QCoreApplication.translate("MainWindow", "Redo", None)) + # if QT_CONFIG(shortcut) + self.actionRedo.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+Y, Ctrl+Shift+Z", None) ) - self.actionUndo.setText(_translate("MainWindow", "Undo")) - self.actionUndo.setShortcut(_translate("MainWindow", "Ctrl+Z")) - self.actionRedo.setText(_translate("MainWindow", "Redo")) - self.actionRedo.setShortcut(_translate("MainWindow", "Ctrl+Y, Ctrl+Shift+Z")) + # endif // QT_CONFIG(shortcut) self.actionIncrease_time_resolution.setText( - _translate("MainWindow", "Increase time resolution...") + QCoreApplication.translate( + "MainWindow", "Increase time resolution...", None + ) ) self.actionDecrease_time_resolution.setText( - _translate("MainWindow", "Decrease time resolution...") + QCoreApplication.translate( + "MainWindow", "Decrease time resolution...", None + ) + ) + self.actionZoom_to_fit.setText( + QCoreApplication.translate("MainWindow", "Zoom to &fit", None) ) - self.actionZoom_to_fit.setText(_translate("MainWindow", "Zoom to &fit")) - self.actionStatus_bar.setText(_translate("MainWindow", "&Status bar")) + self.actionStatus_bar.setText( + QCoreApplication.translate("MainWindow", "&Status bar", None) + ) + # if QT_CONFIG(tooltip) self.actionStatus_bar.setToolTip( - _translate("MainWindow", "Show/hide status bar") + QCoreApplication.translate("MainWindow", "Show/hide status bar", None) + ) + # endif // QT_CONFIG(tooltip) + self.actionToolbar.setText( + QCoreApplication.translate("MainWindow", "&Toolbar", None) + ) + # if QT_CONFIG(tooltip) + self.actionToolbar.setToolTip( + QCoreApplication.translate("MainWindow", "Show/hide toolbar", None) ) - self.actionToolbar.setText(_translate("MainWindow", "&Toolbar")) - self.actionToolbar.setToolTip(_translate("MainWindow", "Show/hide toolbar")) + # endif // QT_CONFIG(tooltip) self.action_show_port_numbers.setText( - _translate("MainWindow", "S&how port numbers") + QCoreApplication.translate("MainWindow", "S&how port numbers", None) ) + # if QT_CONFIG(tooltip) self.action_show_port_numbers.setToolTip( - _translate("MainWindow", "Show port numbers of operation") + QCoreApplication.translate( + "MainWindow", "Show port numbers of operation", None + ) ) + # endif // QT_CONFIG(tooltip) self.action_incorrect_execution_time.setText( - _translate("MainWindow", "&Incorrect execution time") + QCoreApplication.translate("MainWindow", "&Incorrect execution time", None) ) + # if QT_CONFIG(tooltip) self.action_incorrect_execution_time.setToolTip( - _translate( + QCoreApplication.translate( "MainWindow", "Highlight processes with execution time longer than schedule time", + None, ) ) - self.menu_open.setText(_translate("MainWindow", "&Open...")) + # endif // QT_CONFIG(tooltip) + self.menu_open.setText( + QCoreApplication.translate("MainWindow", "&Open...", None) + ) + # if QT_CONFIG(tooltip) self.menu_open.setToolTip( - _translate("MainWindow", "Open previously saved schedule") + QCoreApplication.translate( + "MainWindow", "Open previously saved schedule", None + ) + ) + # endif // QT_CONFIG(tooltip) + # if QT_CONFIG(shortcut) + self.menu_open.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+O", None) ) - self.menu_open.setShortcut(_translate("MainWindow", "Ctrl+O")) + # endif // QT_CONFIG(shortcut) self.actionToggle_full_screen.setText( - _translate("MainWindow", "Toggle f&ull screen") + QCoreApplication.translate("MainWindow", "Toggle f&ull screen", None) + ) + # if QT_CONFIG(shortcut) + self.actionToggle_full_screen.setShortcut( + QCoreApplication.translate("MainWindow", "F11", None) + ) + # endif // QT_CONFIG(shortcut) + self.actionPreferences.setText( + QCoreApplication.translate("MainWindow", "Preferences", None) + ) + # if QT_CONFIG(tooltip) + self.actionPreferences.setToolTip( + QCoreApplication.translate("MainWindow", "Color and Fonts", None) + ) + # endif // QT_CONFIG(tooltip) + # if QT_CONFIG(shortcut) + self.actionPreferences.setShortcut( + QCoreApplication.translate("MainWindow", "Ctrl+M", None) + ) + # endif // QT_CONFIG(shortcut) + ___qtablewidgetitem = self.info_table.horizontalHeaderItem(0) + ___qtablewidgetitem.setText( + QCoreApplication.translate("MainWindow", "Property", None) + ) + ___qtablewidgetitem1 = self.info_table.horizontalHeaderItem(1) + ___qtablewidgetitem1.setText( + QCoreApplication.translate("MainWindow", "Value", None) + ) + ___qtablewidgetitem2 = self.info_table.verticalHeaderItem(0) + ___qtablewidgetitem2.setText( + QCoreApplication.translate("MainWindow", "1", None) + ) + ___qtablewidgetitem3 = self.info_table.verticalHeaderItem(1) + ___qtablewidgetitem3.setText( + QCoreApplication.translate("MainWindow", "2", None) + ) + + __sortingEnabled = self.info_table.isSortingEnabled() + self.info_table.setSortingEnabled(False) + ___qtablewidgetitem4 = self.info_table.item(0, 0) + ___qtablewidgetitem4.setText( + QCoreApplication.translate("MainWindow", "Schedule", None) + ) + ___qtablewidgetitem5 = self.info_table.item(1, 0) + ___qtablewidgetitem5.setText( + QCoreApplication.translate("MainWindow", "Operator", None) + ) + self.info_table.setSortingEnabled(__sortingEnabled) + + self.menuFile.setTitle(QCoreApplication.translate("MainWindow", "&File", None)) + self.menu_Recent_Schedule.setTitle( + QCoreApplication.translate("MainWindow", "Open &recent", None) ) - self.actionToggle_full_screen.setShortcut(_translate("MainWindow", "F11")) - self.actionPreferences.setText(_translate("MainWindow", "Preferences")) - self.actionPreferences.setToolTip(_translate("MainWindow", "Color and Fonts")) - self.actionPreferences.setShortcut(_translate("MainWindow", "Ctrl+M")) + self.menuView.setTitle(QCoreApplication.translate("MainWindow", "&View", None)) + self.menu_view_execution_times.setTitle( + QCoreApplication.translate( + "MainWindow", "View execution times of type", None + ) + ) + self.menu_Edit.setTitle(QCoreApplication.translate("MainWindow", "&Edit", None)) + self.menuWindow.setTitle( + QCoreApplication.translate("MainWindow", "&Window", None) + ) + self.menuHelp.setTitle(QCoreApplication.translate("MainWindow", "&Help", None)) + self.toolBar.setWindowTitle( + QCoreApplication.translate("MainWindow", "toolBar", None) + ) + pass + + # retranslateUi diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index d3600a68d418db28de3b28aafe634a6f4a99fef4..c1ff7ee675067a1e9ad6d3cca957413ea775c52f 100644 --- a/b_asic/signal_flow_graph.py +++ b/b_asic/signal_flow_graph.py @@ -38,6 +38,7 @@ from b_asic.operation import ( ResultKey, ) from b_asic.port import InputPort, OutputPort, SignalSourceProvider +from b_asic.scheduler import ASAPScheduler from b_asic.signal import Signal from b_asic.special_operations import Delay, Input, Output from b_asic.types import GraphID, GraphIDNumber, Name, Num, TypeName @@ -1709,7 +1710,7 @@ class SFG(AbstractOperation): # Import here needed to avoid circular imports from b_asic.schedule import Schedule - return Schedule(self, algorithm="ASAP").schedule_time + return Schedule(self, ASAPScheduler()).schedule_time def _dfs(self, graph, start, end): """ diff --git a/examples/fivepointwinograddft.py b/examples/fivepointwinograddft.py index f9afd0e6acf5f1a6feeb78449a231832c96e06ab..d21561511d5fafa2c9249adf4ffac5e939e91cd4 100644 --- a/examples/fivepointwinograddft.py +++ b/examples/fivepointwinograddft.py @@ -14,6 +14,7 @@ import networkx as nx from b_asic.architecture import Architecture, Memory, ProcessingElement from b_asic.core_operations import AddSub, Butterfly, ConstantMultiplication from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Input, Output @@ -75,7 +76,7 @@ sfg.set_execution_time_of_type(Butterfly.type_name(), 1) # %% # Generate schedule -schedule = Schedule(sfg, cyclic=True) +schedule = Schedule(sfg, scheduler=ASAPScheduler(), cyclic=True) schedule.show() # %% diff --git a/examples/folding_example_with_architecture.py b/examples/folding_example_with_architecture.py index 9baf92e0162d91167012b10314815f5e9312125b..43bdc798e0a24fcd0ce8a10e66cc27c412b0acea 100644 --- a/examples/folding_example_with_architecture.py +++ b/examples/folding_example_with_architecture.py @@ -17,6 +17,7 @@ shorter than the scheduling period. from b_asic.architecture import Architecture, Memory, ProcessingElement from b_asic.core_operations import Addition, ConstantMultiplication from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output @@ -48,7 +49,7 @@ sfg.set_execution_time_of_type(Addition.type_name(), 1) # %% # Create schedule -schedule = Schedule(sfg, cyclic=True) +schedule = Schedule(sfg, scheduler=ASAPScheduler(), cyclic=True) schedule.show(title='Original schedule') # %% diff --git a/examples/lwdfallpass.py b/examples/lwdfallpass.py index 281856fe347c4ec0307d2d96ca78303db1ba7e0b..6bbde6291eca6404efb3a5df66e28633b3ea0a91 100644 --- a/examples/lwdfallpass.py +++ b/examples/lwdfallpass.py @@ -9,6 +9,7 @@ This has different latency offsets for the different inputs/outputs. from b_asic.core_operations import SymmetricTwoportAdaptor from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output @@ -22,5 +23,5 @@ d0 <<= adaptor0.output(1) out0 = Output(adaptor0.output(0)) adaptor0.execution_time = 2 sfg = SFG([in0], [out0]) -schedule = Schedule(sfg) +schedule = Schedule(sfg, scheduler=ASAPScheduler()) schedule.show() diff --git a/examples/secondorderdirectformiir.py b/examples/secondorderdirectformiir.py index b4eee8255a65c8ad0b5ff8e268e1fa186863c4f3..aa84c26b532b0304fd012805bb88b1719723e9a7 100644 --- a/examples/secondorderdirectformiir.py +++ b/examples/secondorderdirectformiir.py @@ -7,6 +7,7 @@ Second-order IIR Filter with Schedule from b_asic.core_operations import Addition, ConstantMultiplication from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output @@ -43,5 +44,5 @@ sfg.set_execution_time_of_type(Addition.type_name(), 1) # %% # Create schedule -schedule = Schedule(sfg, cyclic=True) +schedule = Schedule(sfg, scheduler=ASAPScheduler(), cyclic=True) schedule.show() diff --git a/examples/secondorderdirectformiir_architecture.py b/examples/secondorderdirectformiir_architecture.py index 1a234a36b5f1b5bbe12deeaebc7c7eb3b3c065e5..a7d72fb36fa631c7a39395597351a9b4c1674930 100644 --- a/examples/secondorderdirectformiir_architecture.py +++ b/examples/secondorderdirectformiir_architecture.py @@ -8,6 +8,7 @@ Second-order IIR Filter with Architecture from b_asic.architecture import Architecture, Memory, ProcessingElement from b_asic.core_operations import Addition, ConstantMultiplication from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output @@ -42,7 +43,7 @@ sfg.set_execution_time_of_type(Addition.type_name(), 1) # %% # Create schedule. -schedule = Schedule(sfg, cyclic=True) +schedule = Schedule(sfg, scheduler=ASAPScheduler(), cyclic=True) schedule.show(title='Original schedule') # %% diff --git a/examples/thirdorderblwdf.py b/examples/thirdorderblwdf.py index 7496b07803e6018ae252f7af14dcda9796b84de0..d29fd21566982464ffae9107ba64e79eccf79fce 100644 --- a/examples/thirdorderblwdf.py +++ b/examples/thirdorderblwdf.py @@ -11,6 +11,7 @@ from mplsignal.freq_plots import freqz_fir from b_asic.core_operations import Addition, SymmetricTwoportAdaptor from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler from b_asic.signal_flow_graph import SFG from b_asic.signal_generator import Impulse from b_asic.simulation import Simulation @@ -49,5 +50,5 @@ freqz_fir(np.array(sim.results['0']) / 2) # %% # Create and display schedule -schedule = Schedule(sfg, cyclic=True) +schedule = Schedule(sfg, scheduler=ASAPScheduler(), cyclic=True) schedule.show() diff --git a/examples/threepointwinograddft.py b/examples/threepointwinograddft.py index 79bb7fb9037f737a42a6d26ce69672301392c560..7e7a58acd5465969142aa85f95883998b0a74689 100644 --- a/examples/threepointwinograddft.py +++ b/examples/threepointwinograddft.py @@ -12,6 +12,7 @@ import networkx as nx from b_asic.architecture import Architecture, Memory, ProcessingElement from b_asic.core_operations import AddSub, ConstantMultiplication from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Input, Output @@ -54,7 +55,7 @@ sfg.set_execution_time_of_type(AddSub.type_name(), 1) # %% # Generate schedule -schedule = Schedule(sfg, cyclic=True) +schedule = Schedule(sfg, scheduler=ASAPScheduler(), cyclic=True) schedule.show() # %% diff --git a/pyproject.toml b/pyproject.toml index a907470817301b3809ac152ec394770a0338e282..bffec3c704e29ea49dca272c30a53871d7e4d777 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,10 @@ classifiers = [ ] dynamic = ["version", "authors"] +[project.optional-dependencies] +pyqt6 = ["pyqt6"] +pyside6 = ["pyside6"] + [tool.setuptools] zip-safe = false diff --git a/test/fixtures/schedule.py b/test/fixtures/schedule.py index e11b95c9e2d8bdfcd57fdb7d93f6f82aed45b779..39b375ed12eaae2a350ad9f38d1d8dd0273093d3 100644 --- a/test/fixtures/schedule.py +++ b/test/fixtures/schedule.py @@ -2,6 +2,7 @@ import pytest from b_asic.core_operations import Addition, ConstantMultiplication from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler from b_asic.signal_flow_graph import SFG @@ -10,7 +11,7 @@ def secondorder_iir_schedule(precedence_sfg_delays): precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) return schedule @@ -23,7 +24,7 @@ def secondorder_iir_schedule_with_execution_times(precedence_sfg_delays): ConstantMultiplication.type_name(), 1 ) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) return schedule @@ -37,7 +38,9 @@ def schedule_direct_form_iir_lp_filter(sfg_direct_form_iir_lp_filter: SFG): sfg_direct_form_iir_lp_filter.set_execution_time_of_type( ConstantMultiplication.type_name(), 1 ) - schedule = Schedule(sfg_direct_form_iir_lp_filter, algorithm="ASAP", cyclic=True) + schedule = Schedule( + sfg_direct_form_iir_lp_filter, scheduler=ASAPScheduler(), cyclic=True + ) schedule.move_operation('cmul3', -1) schedule.move_operation('cmul2', -1) schedule.move_operation('cmul3', -10) diff --git a/test/fixtures/signal_flow_graph.py b/test/fixtures/signal_flow_graph.py index 21c07d0f77e495a2c647839b3d9718352953838c..7eaccbe7c01809ffe7b8842d6e57d1db502948e3 100644 --- a/test/fixtures/signal_flow_graph.py +++ b/test/fixtures/signal_flow_graph.py @@ -332,3 +332,11 @@ def sfg_direct_form_iir_lp_filter(): d1.input(0).connect(d0) y <<= a1 * d0 + a2 * d1 + a0 * top_node return SFG(inputs=[x], outputs=[y], name='Direct Form 2 IIR Lowpass filter') + + +@pytest.fixture +def sfg_empty(): + """Empty SFG consisting of an Input followed by an Output.""" + in0 = Input() + out0 = Output(in0) + return SFG(inputs=[in0], outputs=[out0]) diff --git a/test/test_architecture.py b/test/test_architecture.py index 2dac82ff7bdd04569a9771abafdce498f283826f..2ab9c539c6e3985e50829a0a4068eb89cfeec459 100644 --- a/test/test_architecture.py +++ b/test/test_architecture.py @@ -9,6 +9,7 @@ from b_asic.core_operations import Addition, ConstantMultiplication from b_asic.process import PlainMemoryVariable from b_asic.resources import ProcessCollection from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler from b_asic.special_operations import Input, Output @@ -253,7 +254,7 @@ def test_resource_errors(precedence_sfg_delays): ConstantMultiplication.type_name(), 1 ) - schedule = Schedule(precedence_sfg_delays) + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) operations = schedule.get_operations() additions = operations.get_by_type_name(Addition.type_name()) with pytest.raises( diff --git a/test/test_schedule.py b/test/test_schedule.py index c307798a283c2dd7ee43e037415d109b1020167c..bbd98c3622e99526a59663dd08394997de2d57d1 100644 --- a/test/test_schedule.py +++ b/test/test_schedule.py @@ -10,6 +10,7 @@ import pytest from b_asic.core_operations import Addition, Butterfly, ConstantMultiplication from b_asic.process import OperatorProcess from b_asic.schedule import Schedule +from b_asic.scheduler import ALAPScheduler, ASAPScheduler from b_asic.sfg_generators import direct_form_fir from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output @@ -20,7 +21,7 @@ class TestInit: sfg_simple_filter.set_latency_of_type(Addition.type_name(), 5) sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 4) - schedule = Schedule(sfg_simple_filter) + schedule = Schedule(sfg_simple_filter, scheduler=ASAPScheduler()) assert schedule._start_times == { "in0": 0, @@ -39,7 +40,7 @@ class TestInit: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) start_times_names = {} for op_id, start_time in schedule._start_times.items(): @@ -69,7 +70,7 @@ class TestInit: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ALAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ALAPScheduler()) start_times_names = {} for op_id in schedule.start_times: @@ -99,7 +100,9 @@ class TestInit: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, schedule_time=25, algorithm="ALAP") + schedule = Schedule( + precedence_sfg_delays, schedule_time=25, scheduler=ALAPScheduler() + ) start_times_names = {} for op_id in schedule.start_times: @@ -129,7 +132,7 @@ class TestInit: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) with pytest.raises(ValueError, match="Too short schedule time. Minimum is 21."): - Schedule(precedence_sfg_delays, schedule_time=19, algorithm="ALAP") + Schedule(precedence_sfg_delays, schedule_time=19, scheduler=ALAPScheduler()) def test_complicated_single_outputs_normal_latency_from_fixture( self, secondorder_iir_schedule @@ -193,7 +196,7 @@ class TestInit: {"in0": 6, "in1": 7, "out0": 9} ) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) start_times_names = {} for op_id, start_time in schedule._start_times.items(): @@ -221,7 +224,7 @@ class TestInit: def test_independent_sfg(self, sfg_two_inputs_two_outputs_independent_with_cmul): schedule = Schedule( sfg_two_inputs_two_outputs_independent_with_cmul, - algorithm="ASAP", + scheduler=ASAPScheduler(), ) start_times_names = {} @@ -250,7 +253,7 @@ class TestSlacks: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) assert ( schedule.forward_slack( precedence_sfg_delays.find_by_name("ADD3")[0].graph_id @@ -279,7 +282,7 @@ class TestSlacks: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) assert schedule.slacks( precedence_sfg_delays.find_by_name("ADD3")[0].graph_id ) == (0, 7) @@ -291,7 +294,7 @@ class TestSlacks: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) schedule.print_slacks() captured = capsys.readouterr() assert ( @@ -319,7 +322,7 @@ out0 | 0 | oo precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) schedule.print_slacks(1) captured = capsys.readouterr() assert ( @@ -347,7 +350,7 @@ in0 | oo | 0 precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) with pytest.raises( ValueError, match="No operation with graph_id 'foo' in schedule" ): @@ -367,7 +370,7 @@ class TestRescheduling: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) schedule.move_operation( precedence_sfg_delays.find_by_name("ADD3")[0].graph_id, 4 @@ -404,7 +407,7 @@ class TestRescheduling: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) add3_id = precedence_sfg_delays.find_by_name("ADD3")[0].graph_id schedule.move_operation(add3_id, 4) assert schedule.forward_slack(add3_id) == 3 @@ -426,7 +429,7 @@ class TestRescheduling: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) with pytest.raises( ValueError, match="Operation 'add3' got incorrect move: -4. Must be between 0 and 7.", @@ -439,7 +442,7 @@ class TestRescheduling: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) with pytest.raises( ValueError, match="Operation 'add3' got incorrect move: 10. Must be between 0 and 7.", @@ -452,7 +455,7 @@ class TestRescheduling: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) assert schedule.backward_slack('cmul5') == 16 assert schedule.forward_slack('cmul5') == 0 schedule.move_operation_asap('cmul5') @@ -465,7 +468,7 @@ class TestRescheduling: precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) old_laps = schedule.laps['in0'] schedule.move_operation_asap('in0') assert schedule.start_time_of_operation('in0') == 0 @@ -479,7 +482,7 @@ class TestRescheduling: d <<= a sfg = SFG([in0], [out0]) sfg.set_latency_of_type(Addition.type_name(), 1) - schedule = Schedule(sfg, cyclic=True) + schedule = Schedule(sfg, scheduler=ASAPScheduler(), cyclic=True) # Check initial conditions assert schedule.laps[sfg.find_by_id("add0").input(0).signals[0].graph_id] == 1 @@ -538,11 +541,11 @@ class TestRescheduling: ConstantMultiplication.type_name(), 3 ) - schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler()) sfg = schedule.sfg assert precedence_sfg_delays.evaluate(5) == sfg.evaluate(5) - schedule = Schedule(sfg_direct_form_iir_lp_filter, algorithm="ASAP") + schedule = Schedule(sfg_direct_form_iir_lp_filter, scheduler=ASAPScheduler()) sfg = schedule.sfg assert sfg_direct_form_iir_lp_filter.evaluate(5) == sfg.evaluate(5) @@ -551,7 +554,7 @@ class TestRescheduling: mult_properties={'latency': 2, 'execution_time': 1}, add_properties={'latency': 2, 'execution_time': 1}, ) - schedule = Schedule(fir_sfg, algorithm="ASAP") + schedule = Schedule(fir_sfg, scheduler=ASAPScheduler()) sfg = schedule.sfg assert fir_sfg.evaluate(5) == sfg.evaluate(5) @@ -562,7 +565,7 @@ class TestTimeResolution: ): schedule = Schedule( sfg_two_inputs_two_outputs_independent_with_cmul, - algorithm="ASAP", + scheduler=ASAPScheduler(), ) old_schedule_time = schedule.schedule_time assert schedule.get_possible_time_resolution_decrements() == [1] @@ -596,7 +599,7 @@ class TestTimeResolution: ): schedule = Schedule( sfg_two_inputs_two_outputs_independent_with_cmul, - algorithm="ASAP", + scheduler=ASAPScheduler(), ) old_schedule_time = schedule.schedule_time @@ -633,7 +636,7 @@ class TestTimeResolution: ): schedule = Schedule( sfg_two_inputs_two_outputs_independent_with_cmul, - algorithm="ASAP", + scheduler=ASAPScheduler(), ) old_schedule_time = schedule.schedule_time assert schedule.get_possible_time_resolution_decrements() == [1] @@ -713,7 +716,7 @@ class TestErrors: ValueError, match="Input port 0 of operation add0 has no latency-offset.", ): - Schedule(sfg_simple_filter) + Schedule(sfg_simple_filter, scheduler=ASAPScheduler()) def test_no_output_latency(self): in1 = Input() @@ -726,7 +729,7 @@ class TestErrors: ValueError, match="Output port 1 of operation bfly0 has no latency-offset.", ): - Schedule(sfg) + Schedule(sfg, scheduler=ASAPScheduler()) in1 = Input() in2 = Input() bfly1 = Butterfly(in1, in2, latency_offsets={"in0": 4, "in1": 2, "out1": 10}) @@ -742,28 +745,28 @@ class TestErrors: ValueError, match="Output port 0 of operation bfly0 has no latency-offset.", ): - Schedule(sfg) + Schedule(sfg, scheduler=ASAPScheduler()) def test_too_short_schedule_time(self, sfg_simple_filter): sfg_simple_filter.set_latency_of_type(Addition.type_name(), 5) sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 4) with pytest.raises(ValueError, match="Too short schedule time. Minimum is 9."): - Schedule(sfg_simple_filter, schedule_time=3) + Schedule(sfg_simple_filter, scheduler=ASAPScheduler(), schedule_time=3) - schedule = Schedule(sfg_simple_filter) + schedule = Schedule(sfg_simple_filter, scheduler=ASAPScheduler()) with pytest.raises( ValueError, match=re.escape("New schedule time (3) too short, minimum: 9."), ): schedule.set_schedule_time(3) - def test_incorrect_scheduling_algorithm(self, sfg_simple_filter): - sfg_simple_filter.set_latency_of_type(Addition.type_name(), 1) - sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 2) - with pytest.raises( - NotImplementedError, match="No algorithm with name: foo defined." - ): - Schedule(sfg_simple_filter, algorithm="foo") + # def test_incorrect_scheduling_algorithm(self, sfg_simple_filter): + # sfg_simple_filter.set_latency_of_type(Addition.type_name(), 1) + # sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 2) + # with pytest.raises( + # NotImplementedError, match="No algorithm with name: foo defined." + # ): + # Schedule(sfg_simple_filter, algorithm="foo") def test_no_sfg(self): with pytest.raises(TypeError, match="An SFG must be provided"): @@ -775,13 +778,13 @@ class TestErrors: with pytest.raises( ValueError, match="Must provide start_times when using 'provided'" ): - Schedule(sfg_simple_filter, algorithm="provided") + Schedule(sfg_simple_filter) def test_provided_no_laps(self, sfg_simple_filter): sfg_simple_filter.set_latency_of_type(Addition.type_name(), 1) sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 2) with pytest.raises(ValueError, match="Must provide laps when using 'provided'"): - Schedule(sfg_simple_filter, algorithm="provided", start_times={'in0': 0}) + Schedule(sfg_simple_filter, start_times={'in0': 0}) class TestGetUsedTypeNames: @@ -798,7 +801,7 @@ class TestYLocations: def test_provided_no_laps(self, sfg_simple_filter): sfg_simple_filter.set_latency_of_type(Addition.type_name(), 1) sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 2) - schedule = Schedule(sfg_simple_filter) + schedule = Schedule(sfg_simple_filter, ASAPScheduler()) # Assign locations schedule.show() print(schedule._y_locations) diff --git a/test/test_scheduler.py b/test/test_scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..0959b2cb3fd81422d0de97531c3c004828e6f66e --- /dev/null +++ b/test/test_scheduler.py @@ -0,0 +1,259 @@ +import pytest + +from b_asic.core_operations import Addition, ConstantMultiplication +from b_asic.schedule import Schedule +from b_asic.scheduler import ALAPScheduler, ASAPScheduler, EarliestDeadlineScheduler + + +class TestASAPScheduler: + def test_empty_sfg(self, sfg_empty): + with pytest.raises( + ValueError, match="Empty signal flow graph cannot be scheduled." + ): + Schedule(sfg_empty, scheduler=ASAPScheduler()) + + def test_direct_form_2_iir(self, sfg_direct_form_iir_lp_filter): + sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 5) + sfg_direct_form_iir_lp_filter.set_latency_of_type( + ConstantMultiplication.type_name(), 4 + ) + + schedule = Schedule(sfg_direct_form_iir_lp_filter, scheduler=ASAPScheduler()) + + assert schedule._start_times == { + "in0": 0, + "cmul1": 0, + "cmul4": 0, + "cmul2": 0, + "cmul3": 0, + "add3": 4, + "add1": 4, + "add0": 9, + "cmul0": 14, + "add2": 18, + "out0": 23, + } + assert schedule.schedule_time == 23 + + def test_direct_form_2_iir_with_scheduling_time( + self, sfg_direct_form_iir_lp_filter + ): + sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 5) + sfg_direct_form_iir_lp_filter.set_latency_of_type( + ConstantMultiplication.type_name(), 4 + ) + + schedule = Schedule( + sfg_direct_form_iir_lp_filter, scheduler=ASAPScheduler(), schedule_time=30 + ) + + assert schedule._start_times == { + "in0": 0, + "cmul1": 0, + "cmul4": 0, + "cmul2": 0, + "cmul3": 0, + "add3": 4, + "add1": 4, + "add0": 9, + "cmul0": 14, + "add2": 18, + "out0": 23, + } + assert schedule.schedule_time == 30 + + +class TestALAPScheduler: + def test_empty_sfg(self, sfg_empty): + with pytest.raises( + ValueError, match="Empty signal flow graph cannot be scheduled." + ): + Schedule(sfg_empty, scheduler=ALAPScheduler()) + + def test_direct_form_2_iir(self, sfg_direct_form_iir_lp_filter): + sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 5) + sfg_direct_form_iir_lp_filter.set_latency_of_type( + ConstantMultiplication.type_name(), 4 + ) + + schedule = Schedule(sfg_direct_form_iir_lp_filter, scheduler=ALAPScheduler()) + + assert schedule._start_times == { + "cmul3": 0, + "cmul4": 0, + "add1": 4, + "in0": 9, + "cmul2": 9, + "cmul1": 9, + "add0": 9, + "add3": 13, + "cmul0": 14, + "add2": 18, + "out0": 23, + } + assert schedule.schedule_time == 23 + + def test_direct_form_2_iir_with_scheduling_time( + self, sfg_direct_form_iir_lp_filter + ): + sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 5) + sfg_direct_form_iir_lp_filter.set_latency_of_type( + ConstantMultiplication.type_name(), 4 + ) + + schedule = Schedule( + sfg_direct_form_iir_lp_filter, scheduler=ALAPScheduler(), schedule_time=30 + ) + + assert schedule._start_times == { + "cmul3": 7, + "cmul4": 7, + "add1": 11, + "in0": 16, + "cmul2": 16, + "cmul1": 16, + "add0": 16, + "add3": 20, + "cmul0": 21, + "add2": 25, + "out0": 30, + } + assert schedule.schedule_time == 30 + + +class TestEarliestDeadlineScheduler: + def test_empty_sfg(self, sfg_empty): + with pytest.raises( + ValueError, match="Empty signal flow graph cannot be scheduled." + ): + Schedule(sfg_empty, scheduler=EarliestDeadlineScheduler()) + + def test_direct_form_2_iir_inf_resources_no_exec_time( + self, sfg_direct_form_iir_lp_filter + ): + sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 5) + sfg_direct_form_iir_lp_filter.set_latency_of_type( + ConstantMultiplication.type_name(), 4 + ) + + schedule = Schedule( + sfg_direct_form_iir_lp_filter, scheduler=EarliestDeadlineScheduler() + ) + + # should be the same as for ASAP due to infinite resources, except for input + assert schedule._start_times == { + "in0": 9, + "cmul1": 0, + "cmul4": 0, + "cmul2": 0, + "cmul3": 0, + "add3": 4, + "add1": 4, + "add0": 9, + "cmul0": 14, + "add2": 18, + "out0": 23, + } + assert schedule.schedule_time == 23 + + def test_direct_form_2_iir_1_add_1_mul_no_exec_time( + self, sfg_direct_form_iir_lp_filter + ): + sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 5) + sfg_direct_form_iir_lp_filter.set_latency_of_type( + ConstantMultiplication.type_name(), 4 + ) + + max_resources = {ConstantMultiplication.type_name(): 1, Addition.type_name(): 1} + + schedule = Schedule( + sfg_direct_form_iir_lp_filter, + scheduler=EarliestDeadlineScheduler(max_resources), + ) + assert schedule._start_times == { + "cmul4": 0, + "cmul3": 4, + "cmul1": 8, + "add1": 8, + "cmul2": 12, + "in0": 13, + "add0": 13, + "add3": 18, + "cmul0": 18, + "add2": 23, + "out0": 28, + } + + assert schedule.schedule_time == 28 + + def test_direct_form_2_iir_1_add_1_mul_exec_time_1( + self, sfg_direct_form_iir_lp_filter + ): + sfg_direct_form_iir_lp_filter.set_latency_of_type( + ConstantMultiplication.type_name(), 3 + ) + sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 2) + sfg_direct_form_iir_lp_filter.set_execution_time_of_type( + ConstantMultiplication.type_name(), 1 + ) + sfg_direct_form_iir_lp_filter.set_execution_time_of_type( + Addition.type_name(), 1 + ) + + max_resources = {ConstantMultiplication.type_name(): 1, Addition.type_name(): 1} + + schedule = Schedule( + sfg_direct_form_iir_lp_filter, + scheduler=EarliestDeadlineScheduler(max_resources), + ) + assert schedule._start_times == { + "cmul4": 0, + "cmul3": 1, + "cmul1": 2, + "cmul2": 3, + "add1": 4, + "in0": 6, + "add0": 6, + "add3": 7, + "cmul0": 8, + "add2": 11, + "out0": 13, + } + + assert schedule.schedule_time == 13 + + def test_direct_form_2_iir_2_add_3_mul_exec_time_1( + self, sfg_direct_form_iir_lp_filter + ): + sfg_direct_form_iir_lp_filter.set_latency_of_type( + ConstantMultiplication.type_name(), 3 + ) + sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 2) + sfg_direct_form_iir_lp_filter.set_execution_time_of_type( + ConstantMultiplication.type_name(), 1 + ) + sfg_direct_form_iir_lp_filter.set_execution_time_of_type( + Addition.type_name(), 1 + ) + + max_resources = {ConstantMultiplication.type_name(): 3, Addition.type_name(): 2} + + schedule = Schedule( + sfg_direct_form_iir_lp_filter, + scheduler=EarliestDeadlineScheduler(max_resources), + ) + assert schedule._start_times == { + "cmul1": 0, + "cmul4": 0, + "cmul3": 0, + "cmul2": 1, + "add1": 3, + "add3": 4, + "in0": 5, + "add0": 5, + "cmul0": 7, + "add2": 10, + "out0": 12, + } + + assert schedule.schedule_time == 12 diff --git a/test/test_scheduler_gui.py b/test/test_scheduler_gui.py index d6ca2517aed686edd846f5be7ae40ece18b36415..02e9d36e475d32c2db5d8a37a22c9e828c2b8ddf 100644 --- a/test/test_scheduler_gui.py +++ b/test/test_scheduler_gui.py @@ -2,6 +2,7 @@ import pytest from b_asic.core_operations import Addition, ConstantMultiplication from b_asic.schedule import Schedule +from b_asic.scheduler import ASAPScheduler try: from b_asic.scheduler_gui.main_window import ScheduleMainWindow @@ -22,6 +23,6 @@ def test_load_schedule(qtbot, sfg_simple_filter): widget = ScheduleMainWindow() qtbot.addWidget(widget) - schedule = Schedule(sfg_simple_filter) + schedule = Schedule(sfg_simple_filter, ASAPScheduler()) widget.open(schedule) assert widget.statusbar.currentMessage() == "Schedule loaded successfully"