From d7649bf570a86da31da9353eee6faf6b12e7e96a Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 22 Jan 2025 15:04:53 +0100 Subject: [PATCH 01/52] bumped to qtpy6 --- b_asic/scheduler_gui/main_window.py | 16 +- b_asic/scheduler_gui/ui_main_window.py | 796 ++++++++----------------- 2 files changed, 268 insertions(+), 544 deletions(-) diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index bd03b5f2..4de6e4cd 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -31,7 +31,7 @@ from qtpy.QtCore import ( Qt, Slot, ) -from qtpy.QtGui import QCloseEvent, QColor, QFont, QIcon, QIntValidator +from qtpy.QtGui import QCloseEvent, QColor, QFont, QIcon, QIntValidator, QPalette from qtpy.QtWidgets import ( QAbstractButton, QAction, @@ -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 @@ -1688,8 +1688,16 @@ def start_scheduler(schedule: Optional[Schedule] = None) -> Optional[Schedule]: The edited schedule. """ if not QApplication.instance(): - QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) 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() window = ScheduleMainWindow() diff --git a/b_asic/scheduler_gui/ui_main_window.py b/b_asic/scheduler_gui/ui_main_window.py index ec174369..7def5879 100644 --- a/b_asic/scheduler_gui/ui_main_window.py +++ b/b_asic/scheduler_gui/ui_main_window.py @@ -1,293 +1,220 @@ -################################################################################ -## Form generated from reading UI file 'main_window.ui' -## -## Created by: Qt User Interface Compiler version 5.15.8 -## -## WARNING! All changes made in this file will be lost when recompiling UI file! -################################################################################ +# 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. -from qtpy.QtCore import QSize, QCoreApplication, QRect, QMetaObject -from qtpy.QtGui import QIcon, QColor, QFont, QBrush, Qt, QPainter -from qtpy.QtWidgets import ( - QSizePolicy, - QAction, - QMenu, - QMenuBar, - QToolBar, - QHBoxLayout, - QWidget, - QGraphicsView, - QSplitter, - QTableWidgetItem, - QTableWidget, - QAbstractItemView, - QStatusBar, -) +from qtpy import QtCore, QtGui, QtWidgets -class Ui_MainWindow: + +class Ui_MainWindow(object): def setupUi(self, MainWindow): - if not MainWindow.objectName(): - MainWindow.setObjectName("MainWindow") + MainWindow.setObjectName("MainWindow") MainWindow.resize(800, 600) - sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) MainWindow.setSizePolicy(sizePolicy) - icon = QIcon() - icon.addFile(":/icons/basic/small_logo.png", QSize(), QIcon.Normal, QIcon.Off) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/icons/basic/small_logo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) MainWindow.setWindowIcon(icon) - self.menu_load_from_file = QAction(MainWindow) + 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.setObjectName("menu_load_from_file") - icon1 = QIcon() - iconThemeName = "document-open-folder" - if QIcon.hasThemeIcon(iconThemeName): - icon1 = QIcon.fromTheme(iconThemeName) - else: - icon1.addFile("../../../.designer/backup", QSize(), QIcon.Normal, QIcon.Off) - - self.menu_load_from_file.setIcon(icon1) - self.menu_save = QAction(MainWindow) - self.menu_save.setObjectName("menu_save") + self.menu_save = 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.Normal, QIcon.Off) - - self.menu_save.setIcon(icon2) - self.menu_node_info = QAction(MainWindow) - self.menu_node_info.setObjectName("menu_node_info") + icon = QtGui.QIcon.fromTheme("document-save") + self.menu_save.setIcon(icon) + self.menu_save.setObjectName("menu_save") + self.menu_node_info = QtGui.QAction(parent=MainWindow) self.menu_node_info.setCheckable(True) self.menu_node_info.setChecked(True) - icon3 = QIcon() - icon3.addFile(":/icons/misc/right_panel.svg", QSize(), QIcon.Normal, QIcon.Off) - icon3.addFile( - ":/icons/misc/right_filled_panel.svg", QSize(), QIcon.Normal, QIcon.On - ) - self.menu_node_info.setIcon(icon3) + 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) self.menu_node_info.setIconVisibleInMenu(False) - self.menu_quit = QAction(MainWindow) + 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.setObjectName("menu_quit") - icon4 = QIcon() - iconThemeName = "application-exit" - if QIcon.hasThemeIcon(iconThemeName): - icon4 = QIcon.fromTheme(iconThemeName) - else: - icon4.addFile("../../../.designer/backup", QSize(), QIcon.Normal, QIcon.Off) - - self.menu_quit.setIcon(icon4) - self.menu_save_as = QAction(MainWindow) - self.menu_save_as.setObjectName("menu_save_as") + self.menu_save_as = 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.Normal, QIcon.Off) - - self.menu_save_as.setIcon(icon5) - self.menu_exit_dialog = QAction(MainWindow) - self.menu_exit_dialog.setObjectName("menu_exit_dialog") + icon = QtGui.QIcon.fromTheme("document-save-as") + self.menu_save_as.setIcon(icon) + self.menu_save_as.setObjectName("menu_save_as") + self.menu_exit_dialog = QtGui.QAction(parent=MainWindow) self.menu_exit_dialog.setCheckable(True) self.menu_exit_dialog.setChecked(True) - icon6 = QIcon() - iconThemeName = "view-close" - if QIcon.hasThemeIcon(iconThemeName): - icon6 = QIcon.fromTheme(iconThemeName) - else: - icon6.addFile("../../../.designer/backup", QSize(), QIcon.Normal, QIcon.Off) - - self.menu_exit_dialog.setIcon(icon6) - self.menu_close_schedule = QAction(MainWindow) - self.menu_close_schedule.setObjectName("menu_close_schedule") + 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) - self.menu_close_schedule.setIcon(icon6) - self.actionAbout = QAction(MainWindow) + icon = QtGui.QIcon.fromTheme("view-close") + self.menu_close_schedule.setIcon(icon) + self.menu_close_schedule.setObjectName("menu_close_schedule") + self.actionAbout = QtGui.QAction(parent=MainWindow) self.actionAbout.setObjectName("actionAbout") - self.actionDocumentation = QAction(MainWindow) + self.actionDocumentation = QtGui.QAction(parent=MainWindow) self.actionDocumentation.setObjectName("actionDocumentation") - self.actionReorder = QAction(MainWindow) + self.actionReorder = QtGui.QAction(parent=MainWindow) self.actionReorder.setObjectName("actionReorder") - self.actionPlot_schedule = QAction(MainWindow) + self.actionPlot_schedule = QtGui.QAction(parent=MainWindow) self.actionPlot_schedule.setObjectName("actionPlot_schedule") - self.action_view_variables = QAction(MainWindow) - self.action_view_variables.setObjectName("action_view_variables") + self.action_view_variables = QtGui.QAction(parent=MainWindow) self.action_view_variables.setEnabled(False) - self.action_view_port_accesses = QAction(MainWindow) - self.action_view_port_accesses.setObjectName("action_view_port_accesses") + 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.actionUndo = QAction(MainWindow) - self.actionUndo.setObjectName("actionUndo") + self.action_view_port_accesses.setObjectName("action_view_port_accesses") + self.actionUndo = QtGui.QAction(parent=MainWindow) self.actionUndo.setEnabled(False) - self.actionRedo = QAction(MainWindow) - self.actionRedo.setObjectName("actionRedo") + self.actionUndo.setObjectName("actionUndo") + self.actionRedo = 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 = QAction(MainWindow) - self.actionDecrease_time_resolution.setObjectName( - "actionDecrease_time_resolution" - ) - self.actionZoom_to_fit = QAction(MainWindow) + self.actionRedo.setObjectName("actionRedo") + self.actionIncrease_time_resolution = QtGui.QAction(parent=MainWindow) + self.actionIncrease_time_resolution.setObjectName("actionIncrease_time_resolution") + self.actionDecrease_time_resolution = QtGui.QAction(parent=MainWindow) + self.actionDecrease_time_resolution.setObjectName("actionDecrease_time_resolution") + self.actionZoom_to_fit = QtGui.QAction(parent=MainWindow) self.actionZoom_to_fit.setObjectName("actionZoom_to_fit") - self.actionStatus_bar = QAction(MainWindow) - self.actionStatus_bar.setObjectName("actionStatus_bar") + self.actionStatus_bar = QtGui.QAction(parent=MainWindow) self.actionStatus_bar.setCheckable(True) self.actionStatus_bar.setChecked(True) - self.actionToolbar = QAction(MainWindow) - self.actionToolbar.setObjectName("actionToolbar") + self.actionStatus_bar.setObjectName("actionStatus_bar") + self.actionToolbar = QtGui.QAction(parent=MainWindow) self.actionToolbar.setCheckable(True) self.actionToolbar.setChecked(True) - self.action_show_port_numbers = QAction(MainWindow) - self.action_show_port_numbers.setObjectName("action_show_port_numbers") + self.actionToolbar.setObjectName("actionToolbar") + self.action_show_port_numbers = QtGui.QAction(parent=MainWindow) self.action_show_port_numbers.setCheckable(True) self.action_show_port_numbers.setChecked(False) self.action_show_port_numbers.setIconVisibleInMenu(False) - self.action_incorrect_execution_time = QAction(MainWindow) - self.action_incorrect_execution_time.setObjectName( - "action_incorrect_execution_time" - ) + 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.menu_open = 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.menu_open.setObjectName("menu_open") - 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.actionToggle_full_screen = QtGui.QAction(parent=MainWindow) self.actionToggle_full_screen.setCheckable(True) - self.actionPreferences = 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.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) - font.setWeight(50) - __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.setWeight(50) - 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.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) @@ -322,6 +249,11 @@ 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) @@ -335,300 +267,84 @@ class Ui_MainWindow: self.toolBar.addAction(self.actionReorder) self.retranslateUi(MainWindow) - - QMetaObject.connectSlotsByName(MainWindow) - - # setupUi + QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): - self.menu_load_from_file.setText( - QCoreApplication.translate( - "MainWindow", "&Import schedule from file...", None - ) - ) - # if QT_CONFIG(tooltip) - self.menu_load_from_file.setToolTip( - QCoreApplication.translate( - "MainWindow", "Import schedule from python script", None - ) - ) - # 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( - 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) - ) - # if QT_CONFIG(tooltip) - self.menu_save_as.setToolTip( - 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( - QCoreApplication.translate("MainWindow", "Open documentation", None) - ) - # endif // QT_CONFIG(tooltip) - self.actionReorder.setText( - QCoreApplication.translate("MainWindow", "Reorder", None) - ) - # if QT_CONFIG(tooltip) - self.actionReorder.setToolTip( - 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) - ) - # 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( - QCoreApplication.translate( - "MainWindow", "View execution times of variables", None - ) - ) - # if QT_CONFIG(tooltip) - self.action_view_variables.setToolTip( - QCoreApplication.translate("MainWindow", "View all variables", None) - ) - # endif // QT_CONFIG(tooltip) - self.action_view_port_accesses.setText( - QCoreApplication.translate( - "MainWindow", "View port access statistics", None - ) - ) - # if QT_CONFIG(tooltip) - self.action_view_port_accesses.setToolTip( - 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) - ) - # endif // QT_CONFIG(shortcut) - self.actionIncrease_time_resolution.setText( - QCoreApplication.translate( - "MainWindow", "Increase time resolution...", None - ) - ) - self.actionDecrease_time_resolution.setText( - QCoreApplication.translate( - "MainWindow", "Decrease time resolution...", None - ) - ) - self.actionZoom_to_fit.setText( - QCoreApplication.translate("MainWindow", "Zoom to &fit", None) - ) - self.actionStatus_bar.setText( - QCoreApplication.translate("MainWindow", "&Status bar", None) - ) - # if QT_CONFIG(tooltip) - self.actionStatus_bar.setToolTip( - 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) - ) - # endif // QT_CONFIG(tooltip) - self.action_show_port_numbers.setText( - QCoreApplication.translate("MainWindow", "S&how port numbers", None) - ) - # if QT_CONFIG(tooltip) - self.action_show_port_numbers.setToolTip( - QCoreApplication.translate( - "MainWindow", "Show port numbers of operation", None - ) - ) - # endif // QT_CONFIG(tooltip) - self.action_incorrect_execution_time.setText( - QCoreApplication.translate("MainWindow", "&Incorrect execution time", None) - ) - # if QT_CONFIG(tooltip) - self.action_incorrect_execution_time.setToolTip( - QCoreApplication.translate( - "MainWindow", - "Highlight processes with execution time longer than schedule time", - None, - ) - ) - # endif // QT_CONFIG(tooltip) - self.menu_open.setText( - QCoreApplication.translate("MainWindow", "&Open...", None) - ) - # if QT_CONFIG(tooltip) - self.menu_open.setToolTip( - 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) - ) - # endif // QT_CONFIG(shortcut) - self.actionToggle_full_screen.setText( - 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) - ) - + _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) - ___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) - ) + 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(QCoreApplication.translate("MainWindow", "&File", None)) - self.menu_Recent_Schedule.setTitle( - QCoreApplication.translate("MainWindow", "Open &recent", None) - ) - 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 + 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...")) + self.menu_load_from_file.setToolTip(_translate("MainWindow", "Import schedule from python script")) + 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")) + self.menu_node_info.setToolTip(_translate("MainWindow", "Show/hide node information")) + 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...")) + 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")) + self.actionDocumentation.setToolTip(_translate("MainWindow", "Open documentation")) + self.actionReorder.setText(_translate("MainWindow", "Reorder")) + self.actionReorder.setToolTip(_translate("MainWindow", "Reorder schedule based on start time")) + self.actionReorder.setShortcut(_translate("MainWindow", "Ctrl+R")) + self.actionPlot_schedule.setText(_translate("MainWindow", "&Plot schedule")) + self.actionPlot_schedule.setToolTip(_translate("MainWindow", "Plot schedule")) + self.action_view_variables.setText(_translate("MainWindow", "View execution times of variables")) + self.action_view_variables.setToolTip(_translate("MainWindow", "View all variables")) + self.action_view_port_accesses.setText(_translate("MainWindow", "View port access statistics")) + self.action_view_port_accesses.setToolTip(_translate("MainWindow", "View port access statistics for storage")) + 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")) + self.actionIncrease_time_resolution.setText(_translate("MainWindow", "Increase time resolution...")) + self.actionDecrease_time_resolution.setText(_translate("MainWindow", "Decrease time resolution...")) + self.actionZoom_to_fit.setText(_translate("MainWindow", "Zoom to &fit")) + self.actionStatus_bar.setText(_translate("MainWindow", "&Status bar")) + self.actionStatus_bar.setToolTip(_translate("MainWindow", "Show/hide status bar")) + self.actionToolbar.setText(_translate("MainWindow", "&Toolbar")) + self.actionToolbar.setToolTip(_translate("MainWindow", "Show/hide toolbar")) + self.action_show_port_numbers.setText(_translate("MainWindow", "S&how port numbers")) + self.action_show_port_numbers.setToolTip(_translate("MainWindow", "Show port numbers of operation")) + self.action_incorrect_execution_time.setText(_translate("MainWindow", "&Incorrect execution time")) + self.action_incorrect_execution_time.setToolTip(_translate("MainWindow", "Highlight processes with execution time longer than schedule time")) + self.menu_open.setText(_translate("MainWindow", "&Open...")) + self.menu_open.setToolTip(_translate("MainWindow", "Open previously saved schedule")) + self.menu_open.setShortcut(_translate("MainWindow", "Ctrl+O")) + self.actionToggle_full_screen.setText(_translate("MainWindow", "Toggle f&ull screen")) + 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")) -- GitLab From bf03b089c551e8177ccc78bc45e39cc8cd96f4ed Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 22 Jan 2025 15:53:13 +0100 Subject: [PATCH 02/52] fixes from last time, also ran post-commit on all files --- b_asic/scheduler_gui/main_window.py | 8 +- b_asic/scheduler_gui/ui_main_window.py | 180 +++++++++++++++++++------ 2 files changed, 144 insertions(+), 44 deletions(-) diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index 4de6e4cd..f4502f99 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 PySide6 + import PySide2 - log.debug(f"PySide version: {PySide6.__version__}") + log.debug(f"PySide version: {PySide2.__version__}") if QT_API.lower().startswith("pyqt"): from qtpy.QtCore import PYQT_VERSION_STR @@ -1694,8 +1694,8 @@ def start_scheduler(schedule: Optional[Schedule] = None) -> Optional[Schedule]: 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.Base, QtCore.Qt.white) + palette.setColor(QPalette.ColorRole.AlternateBase, QtCore.Qt.lightGray) palette.setColor(QPalette.ColorRole.Text, QtCore.Qt.black) app.setPalette(palette) else: diff --git a/b_asic/scheduler_gui/ui_main_window.py b/b_asic/scheduler_gui/ui_main_window.py index 7def5879..ad3fa898 100644 --- a/b_asic/scheduler_gui/ui_main_window.py +++ b/b_asic/scheduler_gui/ui_main_window.py @@ -9,23 +9,35 @@ from qtpy import QtCore, QtGui, QtWidgets -class Ui_MainWindow(object): +class Ui_MainWindow: def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(800, 600) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.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.addPixmap( + QtGui.QPixmap(":/icons/basic/small_logo.png"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) MainWindow.setWindowIcon(icon) self.centralwidget = QtWidgets.QWidget(parent=MainWindow) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.QSizePolicy.Policy.Preferred, + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + self.centralwidget.sizePolicy().hasHeightForWidth() + ) self.centralwidget.setSizePolicy(sizePolicy) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) @@ -37,15 +49,30 @@ class Ui_MainWindow(object): 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.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.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.setSelectionBehavior( + QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows + ) self.info_table.setRowCount(2) self.info_table.setColumnCount(2) self.info_table.setObjectName("info_table") @@ -54,14 +81,18 @@ class Ui_MainWindow(object): item = QtWidgets.QTableWidgetItem() self.info_table.setVerticalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() - item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignVCenter) + 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) + item.setTextAlignment( + QtCore.Qt.AlignmentFlag.AlignLeading | QtCore.Qt.AlignmentFlag.AlignVCenter + ) self.info_table.setHorizontalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() font = QtGui.QFont() @@ -75,7 +106,13 @@ class Ui_MainWindow(object): 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) + 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() @@ -88,7 +125,13 @@ class Ui_MainWindow(object): 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) + 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) @@ -135,8 +178,16 @@ class Ui_MainWindow(object): 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) + 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) self.menu_node_info.setIconVisibleInMenu(False) self.menu_node_info.setObjectName("menu_node_info") @@ -181,9 +232,13 @@ class Ui_MainWindow(object): self.actionRedo.setEnabled(False) self.actionRedo.setObjectName("actionRedo") self.actionIncrease_time_resolution = QtGui.QAction(parent=MainWindow) - self.actionIncrease_time_resolution.setObjectName("actionIncrease_time_resolution") + self.actionIncrease_time_resolution.setObjectName( + "actionIncrease_time_resolution" + ) self.actionDecrease_time_resolution = QtGui.QAction(parent=MainWindow) - self.actionDecrease_time_resolution.setObjectName("actionDecrease_time_resolution") + self.actionDecrease_time_resolution.setObjectName( + "actionDecrease_time_resolution" + ) self.actionZoom_to_fit = QtGui.QAction(parent=MainWindow) self.actionZoom_to_fit.setObjectName("actionZoom_to_fit") self.actionStatus_bar = QtGui.QAction(parent=MainWindow) @@ -203,7 +258,9 @@ class Ui_MainWindow(object): 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.setObjectName("action_incorrect_execution_time") + 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) @@ -289,24 +346,34 @@ class Ui_MainWindow(object): 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_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...")) - self.menu_load_from_file.setToolTip(_translate("MainWindow", "Import schedule from python script")) + self.menu_load_from_file.setText( + _translate("MainWindow", "&Import schedule from file...") + ) + self.menu_load_from_file.setToolTip( + _translate("MainWindow", "Import schedule from python script") + ) 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")) - self.menu_node_info.setToolTip(_translate("MainWindow", "Show/hide node information")) + self.menu_node_info.setToolTip( + _translate("MainWindow", "Show/hide node information") + ) 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...")) - self.menu_save_as.setToolTip(_translate("MainWindow", "Save schedule with new file name")) + 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")) @@ -315,35 +382,68 @@ class Ui_MainWindow(object): self.actionAbout.setText(_translate("MainWindow", "&About")) self.actionAbout.setToolTip(_translate("MainWindow", "Open about window")) self.actionDocumentation.setText(_translate("MainWindow", "&Documentation")) - self.actionDocumentation.setToolTip(_translate("MainWindow", "Open documentation")) + self.actionDocumentation.setToolTip( + _translate("MainWindow", "Open documentation") + ) self.actionReorder.setText(_translate("MainWindow", "Reorder")) - self.actionReorder.setToolTip(_translate("MainWindow", "Reorder schedule based on start time")) + self.actionReorder.setToolTip( + _translate("MainWindow", "Reorder schedule based on start time") + ) self.actionReorder.setShortcut(_translate("MainWindow", "Ctrl+R")) self.actionPlot_schedule.setText(_translate("MainWindow", "&Plot schedule")) self.actionPlot_schedule.setToolTip(_translate("MainWindow", "Plot schedule")) - self.action_view_variables.setText(_translate("MainWindow", "View execution times of variables")) - self.action_view_variables.setToolTip(_translate("MainWindow", "View all variables")) - self.action_view_port_accesses.setText(_translate("MainWindow", "View port access statistics")) - self.action_view_port_accesses.setToolTip(_translate("MainWindow", "View port access statistics for storage")) + self.action_view_variables.setText( + _translate("MainWindow", "View execution times of variables") + ) + self.action_view_variables.setToolTip( + _translate("MainWindow", "View all variables") + ) + self.action_view_port_accesses.setText( + _translate("MainWindow", "View port access statistics") + ) + self.action_view_port_accesses.setToolTip( + _translate("MainWindow", "View port access statistics for storage") + ) 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")) - self.actionIncrease_time_resolution.setText(_translate("MainWindow", "Increase time resolution...")) - self.actionDecrease_time_resolution.setText(_translate("MainWindow", "Decrease time resolution...")) + self.actionIncrease_time_resolution.setText( + _translate("MainWindow", "Increase time resolution...") + ) + self.actionDecrease_time_resolution.setText( + _translate("MainWindow", "Decrease time resolution...") + ) self.actionZoom_to_fit.setText(_translate("MainWindow", "Zoom to &fit")) self.actionStatus_bar.setText(_translate("MainWindow", "&Status bar")) - self.actionStatus_bar.setToolTip(_translate("MainWindow", "Show/hide status bar")) + self.actionStatus_bar.setToolTip( + _translate("MainWindow", "Show/hide status bar") + ) self.actionToolbar.setText(_translate("MainWindow", "&Toolbar")) self.actionToolbar.setToolTip(_translate("MainWindow", "Show/hide toolbar")) - self.action_show_port_numbers.setText(_translate("MainWindow", "S&how port numbers")) - self.action_show_port_numbers.setToolTip(_translate("MainWindow", "Show port numbers of operation")) - self.action_incorrect_execution_time.setText(_translate("MainWindow", "&Incorrect execution time")) - self.action_incorrect_execution_time.setToolTip(_translate("MainWindow", "Highlight processes with execution time longer than schedule time")) + self.action_show_port_numbers.setText( + _translate("MainWindow", "S&how port numbers") + ) + self.action_show_port_numbers.setToolTip( + _translate("MainWindow", "Show port numbers of operation") + ) + self.action_incorrect_execution_time.setText( + _translate("MainWindow", "&Incorrect execution time") + ) + self.action_incorrect_execution_time.setToolTip( + _translate( + "MainWindow", + "Highlight processes with execution time longer than schedule time", + ) + ) self.menu_open.setText(_translate("MainWindow", "&Open...")) - self.menu_open.setToolTip(_translate("MainWindow", "Open previously saved schedule")) + self.menu_open.setToolTip( + _translate("MainWindow", "Open previously saved schedule") + ) self.menu_open.setShortcut(_translate("MainWindow", "Ctrl+O")) - self.actionToggle_full_screen.setText(_translate("MainWindow", "Toggle f&ull screen")) + self.actionToggle_full_screen.setText( + _translate("MainWindow", "Toggle f&ull screen") + ) self.actionToggle_full_screen.setShortcut(_translate("MainWindow", "F11")) self.actionPreferences.setText(_translate("MainWindow", "Preferences")) self.actionPreferences.setToolTip(_translate("MainWindow", "Color and Fonts")) -- GitLab From 6fd1f5f987fc6c190e7ce40c6822d080c26d8e3f Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 22 Jan 2025 15:03:18 +0000 Subject: [PATCH 03/52] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 32946f44..2e59a32d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -91,6 +91,12 @@ run-test-3.12-pyqt5: image: python:3.12 extends: ".run-test" +run-test-3.12-pyqt6: + variables: + QT_API: pyqt6 + image: python:3.12 + extends: ".run-test" + # Seemingly works with Qt6, but tests stall on closing scheduler GUI due to modal dialog(?) #run-test-3.10-pyside6: # variables: -- GitLab From 97342c36a63c7917c270d18ef84e071fbedb4585 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Thu, 23 Jan 2025 16:11:05 +0100 Subject: [PATCH 04/52] moved alap and asap to seperate class and changed some of the code --- b_asic/schedule.py | 386 +++++++++++++++++++++++---------- b_asic/scheduling_algorithm.py | 8 + 2 files changed, 274 insertions(+), 120 deletions(-) create mode 100644 b_asic/scheduling_algorithm.py diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 4b34d580..3f8a0592 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -36,6 +36,7 @@ from b_asic.resources import ProcessCollection from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output from b_asic.types import TypeName +from b_asic.scheduling_algorithm import SchedAlg # Need RGB from 0 to 1 _EXECUTION_TIME_COLOR: Tuple[float, ...] = tuple( @@ -68,13 +69,8 @@ class Schedule: algorithm. cyclic : bool, default: False If the schedule is cyclic. - algorithm : {'ASAP', 'ALAP', 'provided'}, default: 'ASAP' - The scheduling algorithm to use. The following algorithm are available: - - * ``'ASAP'``: As-soon-as-possible scheduling. - * ``'ALAP'``: As-late-as-possible scheduling. - - If 'provided', use provided *start_times* and *laps* dictionaries. + 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'. @@ -100,7 +96,7 @@ class Schedule: sfg: SFG, schedule_time: Optional[int] = None, cyclic: bool = False, - algorithm: Literal["ASAP", "ALAP", "provided"] = "ASAP", + algorithm: SchedAlg = "ASAP", start_times: Optional[Dict[GraphID, int]] = None, laps: Optional[Dict[GraphID, int]] = None, max_resources: Optional[Dict[TypeName, int]] = None, @@ -115,10 +111,14 @@ class Schedule: self._cyclic = cyclic self._y_locations = defaultdict(_y_locations_default) self._schedule_time = schedule_time + + self.scheduler = Scheduler(self) if algorithm == "ASAP": - self._schedule_asap() + self.scheduler.schedule_asap() elif algorithm == "ALAP": - self._schedule_alap() + self.scheduler.schedule_alap() + elif algorithm == "earliest_deadline": + self.scheduler.schedule_earliest_deadline() elif algorithm == "provided": if start_times is None: raise ValueError("Must provide start_times when using 'provided'") @@ -797,116 +797,116 @@ class Schedule: new_sfg = new_sfg.insert_operation_before(op, Delay(), port) return new_sfg() - def _schedule_alap(self) -> None: - """Schedule the operations using as-late-as-possible scheduling.""" - precedence_list = self._sfg.get_precedence_list() - self._schedule_asap() - max_end_time = self.get_max_end_time() - - if self.schedule_time is None: - self._schedule_time = max_end_time - elif self.schedule_time < max_end_time: - raise ValueError(f"Too short schedule time. Minimum is {max_end_time}.") - - for output in self._sfg.find_by_type_name(Output.type_name()): - output = cast(Output, output) - self.move_operation_alap(output.graph_id) - for step in reversed(precedence_list): - graph_ids = { - outport.operation.graph_id - for outport in step - if not isinstance(outport.operation, Delay) - } - for graph_id in graph_ids: - self.move_operation_alap(graph_id) - - def _schedule_asap(self) -> None: - """Schedule the operations using as-soon-as-possible scheduling.""" - precedence_list = self._sfg.get_precedence_list() - - if len(precedence_list) < 2: - raise ValueError("Empty signal flow graph cannot be scheduled.") - - non_schedulable_ops = set() - for outport in precedence_list[0]: - operation = outport.operation - if operation.type_name() not in [Delay.type_name()]: - if operation.graph_id not in self._start_times: - # Set start time of all operations in the first iter to 0 - self._start_times[operation.graph_id] = 0 - else: - non_schedulable_ops.add(operation.graph_id) - - for outport in precedence_list[1]: - operation = outport.operation - if operation.graph_id not in self._start_times: - # Set start time of all operations in the first iter to 0 - self._start_times[operation.graph_id] = 0 - - for outports in precedence_list[2:]: - for outport in outports: - operation = outport.operation - if operation.graph_id not in self._start_times: - # Schedule the operation if it does not have a start time yet. - 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 = self._start_times[ - source_port.operation.graph_id - ] - - if source_port.latency_offset is None: - raise ValueError( - f"Output port {source_port.index} of" - " operation" - f" {source_port.operation.graph_id} has no" - " latency-offset." - ) - - source_end_time = ( - source_op_time + source_port.latency_offset - ) - - if current_input.latency_offset is None: - raise ValueError( - f"Input port {current_input.index} of operation" - f" {current_input.operation.graph_id} has no" - " latency-offset." - ) - op_start_time_from_in = ( - source_end_time - current_input.latency_offset - ) - op_start_time = max(op_start_time, op_start_time_from_in) - - self._start_times[operation.graph_id] = op_start_time - for output in self._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: - self._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." - ) - self._start_times[output.graph_id] = self._start_times[ - source_port.operation.graph_id - ] + cast(int, source_port.latency_offset) - self._remove_delays() + # def _schedule_alap(self) -> None: + # """Schedule the operations using as-late-as-possible scheduling.""" + # precedence_list = self._sfg.get_precedence_list() + # self._schedule_asap() + # max_end_time = self.get_max_end_time() + + # if self.schedule_time is None: + # self._schedule_time = max_end_time + # elif self.schedule_time < max_end_time: + # raise ValueError(f"Too short schedule time. Minimum is {max_end_time}.") + + # for output in self._sfg.find_by_type_name(Output.type_name()): + # output = cast(Output, output) + # self.move_operation_alap(output.graph_id) + # for step in reversed(precedence_list): + # graph_ids = { + # outport.operation.graph_id + # for outport in step + # if not isinstance(outport.operation, Delay) + # } + # for graph_id in graph_ids: + # self.move_operation_alap(graph_id) + + # def _schedule_asap(self) -> None: + # """Schedule the operations using as-soon-as-possible scheduling.""" + # precedence_list = self._sfg.get_precedence_list() + + # if len(precedence_list) < 2: + # raise ValueError("Empty signal flow graph cannot be scheduled.") + + # non_schedulable_ops = set() + # for outport in precedence_list[0]: + # operation = outport.operation + # if operation.type_name() not in [Delay.type_name()]: + # if operation.graph_id not in self._start_times: + # # Set start time of all operations in the first iter to 0 + # self._start_times[operation.graph_id] = 0 + # else: + # non_schedulable_ops.add(operation.graph_id) + + # for outport in precedence_list[1]: + # operation = outport.operation + # if operation.graph_id not in self._start_times: + # # Set start time of all operations in the first iter to 0 + # self._start_times[operation.graph_id] = 0 + + # for outports in precedence_list[2:]: + # for outport in outports: + # operation = outport.operation + # if operation.graph_id not in self._start_times: + # # Schedule the operation if it does not have a start time yet. + # 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 = self._start_times[ + # source_port.operation.graph_id + # ] + + # if source_port.latency_offset is None: + # raise ValueError( + # f"Output port {source_port.index} of" + # " operation" + # f" {source_port.operation.graph_id} has no" + # " latency-offset." + # ) + + # source_end_time = ( + # source_op_time + source_port.latency_offset + # ) + + # if current_input.latency_offset is None: + # raise ValueError( + # f"Input port {current_input.index} of operation" + # f" {current_input.operation.graph_id} has no" + # " latency-offset." + # ) + # op_start_time_from_in = ( + # source_end_time - current_input.latency_offset + # ) + # op_start_time = max(op_start_time, op_start_time_from_in) + + # self._start_times[operation.graph_id] = op_start_time + # for output in self._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: + # self._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." + # ) + # self._start_times[output.graph_id] = self._start_times[ + # source_port.operation.graph_id + # ] + cast(int, source_port.latency_offset) + # self._remove_delays() def _get_memory_variables_list(self) -> List[MemoryVariable]: ret: List[MemoryVariable] = [] @@ -1227,3 +1227,149 @@ class Schedule: # SVG is valid HTML. This is useful for e.g. sphinx-gallery _repr_html_ = _repr_svg_ + + +class Scheduler(): + def __init__(self, schedule: Schedule) -> None: + self.schedule = schedule + + def schedule_asap(self) -> None: + """Schedule the operations using as-soon-as-possible scheduling.""" + sched = self.schedule + prec_list = sched.sfg.get_precedence_list() + if len(prec_list) < 2: + raise ValueError("Empty signal flow graph cannot be scheduled.") + + # handle the first set in precedence graph (input and delays) + non_schedulable_ops = set() + for outport in prec_list[0]: + operation = outport.operation + if operation.type_name() == Delay.type_name(): + non_schedulable_ops.add(operation.graph_id) + elif operation.graph_id not in sched._start_times: + sched._start_times[operation.graph_id] = 0 + + # handle second set in precedence graph (first operations) + for outport in prec_list[1]: + operation = outport.operation + if operation.graph_id not in sched._start_times: + sched._start_times[operation.graph_id] = 0 + + # handle the remaining sets + for outports in prec_list[2:]: + for outport in outports: + operation = outport.operation + if operation.graph_id not in sched._start_times: + op_start_time = 0 + for current_input in operation.inputs: + if len(current_input.signals) != 1: + raise ValueError( + "Error in scheduling, dangling input port detected." + ) + if current_input.signals[0].source is None: + raise ValueError( + "Error in scheduling, signal with no source detected." + ) + source_port = current_input.signals[0].source + + if source_port.operation.graph_id in non_schedulable_ops: + source_end_time = 0 + else: + source_op_time = sched._start_times[ + source_port.operation.graph_id + ] + + if source_port.latency_offset is None: + raise ValueError( + f"Output port {source_port.index} of" + " operation" + f" {source_port.operation.graph_id} has no" + " latency-offset." + ) + + source_end_time = ( + source_op_time + source_port.latency_offset + ) + + if current_input.latency_offset is None: + raise ValueError( + f"Input port {current_input.index} of operation" + f" {current_input.operation.graph_id} has no" + " latency-offset." + ) + op_start_time_from_in = ( + source_end_time - current_input.latency_offset + ) + op_start_time = max(op_start_time, op_start_time_from_in) + + sched._start_times[operation.graph_id] = op_start_time + + # handle output and remove delays + for output in sched._sfg.find_by_type_name(Output.type_name()): + output = cast(Output, output) + source_port = cast(OutputPort, output.inputs[0].signals[0].source) + if source_port.operation.graph_id in non_schedulable_ops: + sched._start_times[output.graph_id] = 0 + else: + if source_port.latency_offset is None: + raise ValueError( + f"Output port {source_port.index} of operation" + f" {source_port.operation.graph_id} has no" + " latency-offset." + ) + sched._start_times[output.graph_id] = sched._start_times[ + source_port.operation.graph_id + ] + cast(int, source_port.latency_offset) + sched._remove_delays() + + def schedule_alap(self) -> None: + """Schedule the operations using as-late-as-possible scheduling.""" + self.schedule_asap() + sched = self.schedule + max_end_time = sched.get_max_end_time() + + if sched.schedule_time is None: + sched.set_schedule_time(max_end_time) + elif sched.schedule_time < max_end_time: + raise ValueError(f"Too short schedule time. Minimum is {max_end_time}.") + + # move all outputs ALAP before operations + for output in sched.sfg.find_by_type_name(Output.type_name()): + output = cast(Output, output) + sched.move_operation_alap(output.graph_id) + + # move all operations ALAP + for step in reversed(sched.sfg.get_precedence_list()): + for outport in step: + if not isinstance(outport.operation, Delay): + sched.move_operation_alap(outport.operation.graph_id) + + def schedule_earliest_deadline(self) -> None: + """Schedule the operations using earliest deadline scheduling.""" + pass + # sched = self.schedule + # prec_list = sched.sfg.get_precedence_list() + # if len(prec_list) < 2: + # raise ValueError("Empty signal flow graph cannot be scheduled.") + + # # handle the first set in precedence graph (input and delays) + # non_schedulable_ops = set() + # for outport in prec_list[0]: + # operation = outport.operation + # if operation.type_name() == Delay.type_name(): + # non_schedulable_ops.add(operation.graph_id) + # elif operation.graph_id not in sched._start_times: + # sched._start_times[operation.graph_id] = 0 + + # # handle second set in precedence graph (first operations) + # for outport in prec_list[1]: + # operation = outport.operation + # if operation.graph_id not in sched._start_times: + # sched._start_times[operation.graph_id] = 0 + + # # handle the remaining sets + # for outports in prec_list[2:]: + # for outport in outports: + # operation = outport.operation + # if operation.graph_id not in sched._start_times: + # pass \ No newline at end of file diff --git a/b_asic/scheduling_algorithm.py b/b_asic/scheduling_algorithm.py new file mode 100644 index 00000000..8a1dbcd3 --- /dev/null +++ b/b_asic/scheduling_algorithm.py @@ -0,0 +1,8 @@ +from enum import Enum + +class SchedAlg(Enum): + ASAP = "ASAP" + ALAP = "ALAP" + EARLIEST_DEADLINE = "earliest_deadline" + LEAST_SLACK = "least_slack" + PROVIDED = "provided" -- GitLab From eb4a5de84b262910184d0c9dcda83f119612667d Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Thu, 23 Jan 2025 16:26:43 +0100 Subject: [PATCH 05/52] linting fixes --- b_asic/schedule.py | 16 ++++++++-------- b_asic/scheduling_algorithm.py | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 3f8a0592..86eaba91 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -7,7 +7,7 @@ Contains the schedule class for scheduling operations in an SFG. import io import sys from collections import defaultdict -from typing import Dict, List, Literal, Optional, Sequence, Tuple, cast +from typing import Dict, List, Optional, Sequence, Tuple, cast import matplotlib.pyplot as plt import numpy as np @@ -33,10 +33,10 @@ from b_asic.operation import Operation from b_asic.port import InputPort, OutputPort from b_asic.process import MemoryVariable, OperatorProcess from b_asic.resources import ProcessCollection +from b_asic.scheduling_algorithm import SchedAlg from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output from b_asic.types import TypeName -from b_asic.scheduling_algorithm import SchedAlg # Need RGB from 0 to 1 _EXECUTION_TIME_COLOR: Tuple[float, ...] = tuple( @@ -1229,10 +1229,10 @@ class Schedule: _repr_html_ = _repr_svg_ -class Scheduler(): +class Scheduler: def __init__(self, schedule: Schedule) -> None: self.schedule = schedule - + def schedule_asap(self) -> None: """Schedule the operations using as-soon-as-possible scheduling.""" sched = self.schedule @@ -1303,7 +1303,7 @@ class Scheduler(): op_start_time = max(op_start_time, op_start_time_from_in) sched._start_times[operation.graph_id] = op_start_time - + # handle output and remove delays for output in sched._sfg.find_by_type_name(Output.type_name()): output = cast(Output, output) @@ -1337,7 +1337,7 @@ class Scheduler(): for output in sched.sfg.find_by_type_name(Output.type_name()): output = cast(Output, output) sched.move_operation_alap(output.graph_id) - + # move all operations ALAP for step in reversed(sched.sfg.get_precedence_list()): for outport in step: @@ -1366,10 +1366,10 @@ class Scheduler(): # operation = outport.operation # if operation.graph_id not in sched._start_times: # sched._start_times[operation.graph_id] = 0 - + # # handle the remaining sets # for outports in prec_list[2:]: # for outport in outports: # operation = outport.operation # if operation.graph_id not in sched._start_times: - # pass \ No newline at end of file + # pass diff --git a/b_asic/scheduling_algorithm.py b/b_asic/scheduling_algorithm.py index 8a1dbcd3..c6787a73 100644 --- a/b_asic/scheduling_algorithm.py +++ b/b_asic/scheduling_algorithm.py @@ -1,5 +1,6 @@ from enum import Enum + class SchedAlg(Enum): ASAP = "ASAP" ALAP = "ALAP" -- GitLab From c6c95908ada00ba951ceab486ce2d17cabfa69c8 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Fri, 24 Jan 2025 16:50:52 +0100 Subject: [PATCH 06/52] post commit --- b_asic/schedule.py | 126 +++++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 49 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 86eaba91..c0d998cc 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -118,7 +118,7 @@ class Schedule: elif algorithm == "ALAP": self.scheduler.schedule_alap() elif algorithm == "earliest_deadline": - self.scheduler.schedule_earliest_deadline() + self.scheduler.schedule_earliest_deadline([]) elif algorithm == "provided": if start_times is None: raise ValueError("Must provide start_times when using 'provided'") @@ -1246,14 +1246,15 @@ 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: + # elif operation.graph_id not in sched._start_times: + else: sched._start_times[operation.graph_id] = 0 # handle second set in precedence graph (first operations) for outport in prec_list[1]: operation = outport.operation - if operation.graph_id not in sched._start_times: - sched._start_times[operation.graph_id] = 0 + # if operation.graph_id not in sched._start_times: + sched._start_times[operation.graph_id] = 0 # handle the remaining sets for outports in prec_list[2:]: @@ -1304,23 +1305,7 @@ class Scheduler: sched._start_times[operation.graph_id] = op_start_time - # handle output and remove delays - for output in sched._sfg.find_by_type_name(Output.type_name()): - output = cast(Output, output) - source_port = cast(OutputPort, output.inputs[0].signals[0].source) - if source_port.operation.graph_id in non_schedulable_ops: - sched._start_times[output.graph_id] = 0 - else: - if source_port.latency_offset is None: - raise ValueError( - f"Output port {source_port.index} of operation" - f" {source_port.operation.graph_id} has no" - " latency-offset." - ) - sched._start_times[output.graph_id] = sched._start_times[ - source_port.operation.graph_id - ] + cast(int, source_port.latency_offset) - sched._remove_delays() + self._handle_outputs_and_delays(non_schedulable_ops) def schedule_alap(self) -> None: """Schedule the operations using as-late-as-possible scheduling.""" @@ -1344,32 +1329,75 @@ class Scheduler: if not isinstance(outport.operation, Delay): sched.move_operation_alap(outport.operation.graph_id) - def schedule_earliest_deadline(self) -> None: + def schedule_earliest_deadline(self, process_elements: list[Operation]) -> None: """Schedule the operations using earliest deadline scheduling.""" - pass - # sched = self.schedule - # prec_list = sched.sfg.get_precedence_list() - # if len(prec_list) < 2: - # raise ValueError("Empty signal flow graph cannot be scheduled.") - - # # handle the first set in precedence graph (input and delays) - # non_schedulable_ops = set() - # for outport in prec_list[0]: - # operation = outport.operation - # if operation.type_name() == Delay.type_name(): - # non_schedulable_ops.add(operation.graph_id) - # elif operation.graph_id not in sched._start_times: - # sched._start_times[operation.graph_id] = 0 - - # # handle second set in precedence graph (first operations) - # for outport in prec_list[1]: - # operation = outport.operation - # if operation.graph_id not in sched._start_times: - # sched._start_times[operation.graph_id] = 0 - - # # handle the remaining sets - # for outports in prec_list[2:]: - # for outport in outports: - # operation = outport.operation - # if operation.graph_id not in sched._start_times: - # pass + + # ACT BASED ON THE NUMBER OF PEs! + + sched = self.schedule + prec_list = sched.sfg.get_precedence_list() + if len(prec_list) < 2: + raise ValueError("Empty signal flow graph cannot be scheduled.") + + # handle the first set in precedence graph (input and delays) + non_schedulable_ops = set() + for outport in prec_list[0]: + operation = outport.operation + if operation.type_name() == Delay.type_name(): + non_schedulable_ops.add(operation.graph_id) + elif operation.graph_id not in sched._start_times: + sched._start_times[operation.graph_id] = 0 + + # latencies = [outport.operation.latency for outport in prec_list[1]] + current_time = 0 + sorted_outports = sorted( + prec_list[1], key=lambda outport: outport.operation.latency + ) + for outport in sorted_outports: + op = outport.operation + sched._start_times[op.graph_id] = current_time + current_time += 1 + + for outports in prec_list[2:]: + # try all remaining operations for one time step + candidates = [] + while len(candidates) == 0: + 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 + current_time += 1 + sorted_candidates = sorted( + candidates, key=lambda candidate: candidate.latency + ) + # schedule the best candidate to current time + sched._start_times[sorted_candidates[0].graph_id] = current_time + + self._handle_outputs_and_delays(non_schedulable_ops) + + def _handle_outputs_and_delays(self, non_schedulable_ops) -> None: + sched = self.schedule + for output in sched._sfg.find_by_type_name(Output.type_name()): + output = cast(Output, output) + source_port = cast(OutputPort, output.inputs[0].signals[0].source) + if source_port.operation.graph_id in non_schedulable_ops: + sched._start_times[output.graph_id] = 0 + else: + if source_port.latency_offset is None: + raise ValueError( + f"Output port {source_port.index} of operation" + f" {source_port.operation.graph_id} has no" + " latency-offset." + ) + sched._start_times[output.graph_id] = sched._start_times[ + source_port.operation.graph_id + ] + cast(int, source_port.latency_offset) + sched._remove_delays() -- GitLab From 980170f1f54558cfd7c27e71f51879d83ad7ea78 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Fri, 24 Jan 2025 17:03:53 +0100 Subject: [PATCH 07/52] earliest deadline, first version working, needs testing --- b_asic/schedule.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index c0d998cc..58fa572a 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -1361,7 +1361,9 @@ class Scheduler: for outports in prec_list[2:]: # try all remaining operations for one time step candidates = [] + current_time -= 1 while len(candidates) == 0: + current_time += 1 for outport in outports: remaining_op = outport.operation op_is_ready_to_be_scheduled = True @@ -1374,7 +1376,6 @@ class Scheduler: if op_is_ready_to_be_scheduled: candidates.append(remaining_op) # sched._start_times[remaining_op.graph_id] = current_time - current_time += 1 sorted_candidates = sorted( candidates, key=lambda candidate: candidate.latency ) -- GitLab From 831687a8776d95d32501a73fe255b9d881b58ce7 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 08:38:14 +0100 Subject: [PATCH 08/52] removed end-of-life py version 3.8 --- .gitlab-ci.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2e59a32d..96ca283e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,18 +35,6 @@ before_script: path: cov.xml coverage: /(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/ -run-test-3.8-pyside2: - variables: - QT_API: pyside2 - image: python:3.8 - extends: ".run-test" - -run-test-3.8-pyqt5: - variables: - QT_API: pyqt5 - image: python:3.8 - extends: ".run-test" - run-test-3.9-pyside2: variables: QT_API: pyside2 -- GitLab From 8c2e1053f977c33ef116fe346525690b7499cc18 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 08:40:42 +0100 Subject: [PATCH 09/52] removed py version 3.9 since it will reach end-of-life this year --- .gitlab-ci.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 96ca283e..873cea6c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,18 +35,6 @@ before_script: path: cov.xml coverage: /(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/ -run-test-3.9-pyside2: - variables: - QT_API: pyside2 - image: python:3.9 - extends: ".run-test" - -run-test-3.9-pyqt5: - variables: - QT_API: pyqt5 - image: python:3.9 - extends: ".run-test" - run-test-3.10-pyside2: variables: QT_API: pyside2 -- GitLab From 9f512f7cd59d72f9443f5926f9e5d182faab3569 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 08:41:11 +0100 Subject: [PATCH 10/52] updated toml file with new py version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c0d7de2f..ff1ea7b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ description = "Better ASIC Toolbox" readme = "README.md" maintainers = [{ name = "Oscar Gustafsson", email = "oscar.gustafsson@liu.se" }] license = { file = "LICENSE" } -requires-python = ">=3.8" +requires-python = ">=3.10" dependencies = [ "numpy", "qtpy", -- GitLab From 40eb2980980dad07d536e74c5b1e44682add922c Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 08:45:29 +0100 Subject: [PATCH 11/52] updated ci file with pyqt6 tests for python 3.10 and 3.11 --- .gitlab-ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 873cea6c..aeb235f0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -47,6 +47,12 @@ run-test-3.10-pyqt5: image: python:3.10 extends: ".run-test" +run-test-3.10-pyqt6: + variables: + QT_API: pyqt6 + image: python:3.10 + extends: ".run-test" + # PySide2 does not seem to have support for 3.11, "almost works" though #run-test-3.11-pyside2: # variables: @@ -61,6 +67,12 @@ run-test-3.11-pyqt5: image: python:3.11 extends: ".run-test" +run-test-3.11-pyqt6: + variables: + QT_API: pyqt6 + image: python:3.11 + extends: ".run-test" + run-test-3.12-pyqt5: variables: QT_API: pyqt5 -- GitLab From 7bc3424f7a7322d2b8f05cc9ed6556cfa2db0cd6 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 08:46:47 +0100 Subject: [PATCH 12/52] updated ci file with python 3.13 --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aeb235f0..1bb8d867 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -85,6 +85,12 @@ run-test-3.12-pyqt6: image: python:3.12 extends: ".run-test" +run-test-3.13-pyqt6: + variables: + QT_API: pyqt6 + image: python:3.13 + extends: ".run-test" + # Seemingly works with Qt6, but tests stall on closing scheduler GUI due to modal dialog(?) #run-test-3.10-pyside6: # variables: -- GitLab From c32e17ee379c60165c8f790d8c8270bb989f5d24 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 08:01:17 +0000 Subject: [PATCH 13/52] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1bb8d867..275368b0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,7 +49,7 @@ run-test-3.10-pyqt5: run-test-3.10-pyqt6: variables: - QT_API: pyqt6 + QT_API: PyQt6 image: python:3.10 extends: ".run-test" @@ -69,7 +69,7 @@ run-test-3.11-pyqt5: run-test-3.11-pyqt6: variables: - QT_API: pyqt6 + QT_API: PyQt6 image: python:3.11 extends: ".run-test" @@ -81,13 +81,13 @@ run-test-3.12-pyqt5: run-test-3.12-pyqt6: variables: - QT_API: pyqt6 + QT_API: PyQt6 image: python:3.12 extends: ".run-test" run-test-3.13-pyqt6: variables: - QT_API: pyqt6 + QT_API: PyQt6 image: python:3.13 extends: ".run-test" -- GitLab From 2e41421589db056bce9473775c87b4d102c1781c Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 08:13:02 +0000 Subject: [PATCH 14/52] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 275368b0..88a2d4be 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,6 +19,7 @@ before_script: # Install test dependencies - pip install -r requirements_test.txt - export PYTEST_QT_API=$QT_API + - export MPLBACKEND=qtagg .run-test: stage: test -- GitLab From 450b1e86d267471a8ec78c17d91b38af4aec156d Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 08:26:48 +0000 Subject: [PATCH 15/52] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 88a2d4be..1bb8d867 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,6 @@ before_script: # Install test dependencies - pip install -r requirements_test.txt - export PYTEST_QT_API=$QT_API - - export MPLBACKEND=qtagg .run-test: stage: test @@ -50,7 +49,7 @@ run-test-3.10-pyqt5: run-test-3.10-pyqt6: variables: - QT_API: PyQt6 + QT_API: pyqt6 image: python:3.10 extends: ".run-test" @@ -70,7 +69,7 @@ run-test-3.11-pyqt5: run-test-3.11-pyqt6: variables: - QT_API: PyQt6 + QT_API: pyqt6 image: python:3.11 extends: ".run-test" @@ -82,13 +81,13 @@ run-test-3.12-pyqt5: run-test-3.12-pyqt6: variables: - QT_API: PyQt6 + QT_API: pyqt6 image: python:3.12 extends: ".run-test" run-test-3.13-pyqt6: variables: - QT_API: PyQt6 + QT_API: pyqt6 image: python:3.13 extends: ".run-test" -- GitLab From 54d614eb4df4cd6a11c05c3d129ab7cf68a7c81f Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 09:15:30 +0000 Subject: [PATCH 16/52] debugging ci file --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1bb8d867..1fa2fb74 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -84,6 +84,7 @@ run-test-3.12-pyqt6: QT_API: pyqt6 image: python:3.12 extends: ".run-test" + allow_failure: true run-test-3.13-pyqt6: variables: -- GitLab From 7ef91215bd9f3aece99901e627dfab2980fe89d8 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 09:35:51 +0000 Subject: [PATCH 17/52] debugging (adding -s to pytest command) --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1fa2fb74..7001d19d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,7 +23,7 @@ before_script: .run-test: stage: test script: - - pytest --cov=b_asic --cov-report=xml:cov.xml --cov-report=term --cov-branch --color=yes test --timeout=20 --durations=10 + - pytest -s --cov=b_asic --cov-report=xml:cov.xml --cov-report=term --cov-branch --color=yes test --timeout=20 --durations=10 # - lcov --capture --directory . --output-file coverage.info # - lcov --output-file coverage.info --extract coverage.info $PWD/src/'*' $PWD/b_asic/'*' # - lcov --list coverage.info -- GitLab From 7167a5b2f6501039a8c5f8cb23b6b026282ae783 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 09:41:50 +0000 Subject: [PATCH 18/52] attempting fix by adding libxcb-cursor-dev --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7001d19d..f95d4f67 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,6 +6,7 @@ before_script: - apt-get update --yes # - apt-get install --yes build-essential cmake graphviz python3-pyqt5 xvfb xdg-utils lcov - apt-get install --yes graphviz python3-pyqt5 xvfb xdg-utils + - apt-get install -y libxcb-cursor-dev - python -m pip install --upgrade pip - python --version - pip install -r requirements.txt @@ -23,7 +24,7 @@ before_script: .run-test: stage: test script: - - pytest -s --cov=b_asic --cov-report=xml:cov.xml --cov-report=term --cov-branch --color=yes test --timeout=20 --durations=10 + - pytest --cov=b_asic --cov-report=xml:cov.xml --cov-report=term --cov-branch --color=yes test --timeout=20 --durations=10 # - lcov --capture --directory . --output-file coverage.info # - lcov --output-file coverage.info --extract coverage.info $PWD/src/'*' $PWD/b_asic/'*' # - lcov --list coverage.info -- GitLab From c23e613e89b4fb8639b3702c427ed0b6786446b3 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 11:01:17 +0100 Subject: [PATCH 19/52] CI fixes, hopefully working now --- .gitlab-ci.yml | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f95d4f67..5f4c6f59 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -54,14 +54,6 @@ run-test-3.10-pyqt6: image: python:3.10 extends: ".run-test" -# PySide2 does not seem to have support for 3.11, "almost works" though -#run-test-3.11-pyside2: -# variables: -# QT_API: pyside2 -# image: python:3.11 -# extends: ".run-test" -# allow_failure: true - run-test-3.11-pyqt5: variables: QT_API: pyqt5 @@ -85,28 +77,6 @@ run-test-3.12-pyqt6: QT_API: pyqt6 image: python:3.12 extends: ".run-test" - allow_failure: true - -run-test-3.13-pyqt6: - variables: - QT_API: pyqt6 - image: python:3.13 - extends: ".run-test" - -# Seemingly works with Qt6, but tests stall on closing scheduler GUI due to modal dialog(?) -#run-test-3.10-pyside6: -# variables: -# QT_API: pyside6 -# image: python:3.10 -# extends: ".run-test" -# allow_failure: true -# -#run-test-3.10-pyqt6: -# variables: -# QT_API: pyqt6 -# image: python:3.10 -# extends: ".run-test" -# allow_failure: true run-vhdl-tests: variables: -- GitLab From e3dc0015eac210792ba4ab365f9382474742106b Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 15:06:12 +0100 Subject: [PATCH 20/52] work in progress --- b_asic/sfg_generators.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/b_asic/sfg_generators.py b/b_asic/sfg_generators.py index cdfb93b4..df1bf401 100644 --- a/b_asic/sfg_generators.py +++ b/b_asic/sfg_generators.py @@ -255,3 +255,39 @@ def transposed_direct_form_fir( output <<= tmp_add return SFG([input_op], [output], name=Name(name)) + + +def direct_form_I_iir( + b: Sequence[complex], + a: Sequence[complex], + name: Optional[str] = None, + mult_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, + add_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, +) -> SFG: + # np_b = np.atleast_1d(np.squeeze(np.asarray(b))) + # np_a = np.atleast_1d(np.squeeze(np.asarray(a))) + + if mult_properties is None: + mult_properties = {} + if add_properties is None: + add_properties = {} + + input_op = Input() + output = Output() + + muls = [ConstantMultiplication(b[0], input_op, **mult_properties)] + delays = [] + prev_delay = input_op + for i, coeff in enumerate(b[1:]): + prev_delay = Delay(prev_delay) + delays.append(prev_delay) + if i < len(b) - 1: + muls.append(ConstantMultiplication(coeff, prev_delay, **mult_properties)) + + # mul_pairs = [(muls[i], muls[i+1]) for i in range(len(muls)-1)] + for i in range(len(muls)): + Addition(muls[i], delays[i], **add_properties) + + output <<= muls[0] + + return SFG([input_op], [output], name=Name(name)) -- GitLab From 2a7a7305c79f9eb587cf1e4e68e2b013f9bdc945 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 16:00:09 +0100 Subject: [PATCH 21/52] fixes from mr comments --- b_asic/schedule.py | 179 +----------------------------- b_asic/scheduler.py | 193 +++++++++++++++++++++++++++++++++ b_asic/scheduling_algorithm.py | 9 -- pyproject.toml | 7 +- 4 files changed, 199 insertions(+), 189 deletions(-) create mode 100644 b_asic/scheduler.py delete mode 100644 b_asic/scheduling_algorithm.py diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 58fa572a..a708fe1a 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.scheduling_algorithm import SchedAlg +from b_asic.scheduler import Scheduler, SchedulingAlgorithm from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output from b_asic.types import TypeName @@ -96,7 +96,7 @@ class Schedule: sfg: SFG, schedule_time: Optional[int] = None, cyclic: bool = False, - algorithm: SchedAlg = "ASAP", + algorithm: SchedulingAlgorithm = "ASAP", start_times: Optional[Dict[GraphID, int]] = None, laps: Optional[Dict[GraphID, int]] = None, max_resources: Optional[Dict[TypeName, int]] = None, @@ -1227,178 +1227,3 @@ class Schedule: # SVG is valid HTML. This is useful for e.g. sphinx-gallery _repr_html_ = _repr_svg_ - - -class Scheduler: - def __init__(self, schedule: Schedule) -> None: - self.schedule = schedule - - def schedule_asap(self) -> None: - """Schedule the operations using as-soon-as-possible scheduling.""" - sched = self.schedule - prec_list = sched.sfg.get_precedence_list() - if len(prec_list) < 2: - raise ValueError("Empty signal flow graph cannot be scheduled.") - - # handle the first set in precedence graph (input and delays) - non_schedulable_ops = set() - for outport in prec_list[0]: - operation = outport.operation - if operation.type_name() == Delay.type_name(): - non_schedulable_ops.add(operation.graph_id) - # elif operation.graph_id not in sched._start_times: - else: - sched._start_times[operation.graph_id] = 0 - - # handle second set in precedence graph (first operations) - for outport in prec_list[1]: - operation = outport.operation - # if operation.graph_id not in sched._start_times: - sched._start_times[operation.graph_id] = 0 - - # handle the remaining sets - for outports in prec_list[2:]: - for outport in outports: - operation = outport.operation - if operation.graph_id not in sched._start_times: - op_start_time = 0 - for current_input in operation.inputs: - if len(current_input.signals) != 1: - raise ValueError( - "Error in scheduling, dangling input port detected." - ) - if current_input.signals[0].source is None: - raise ValueError( - "Error in scheduling, signal with no source detected." - ) - source_port = current_input.signals[0].source - - if source_port.operation.graph_id in non_schedulable_ops: - source_end_time = 0 - else: - source_op_time = sched._start_times[ - source_port.operation.graph_id - ] - - if source_port.latency_offset is None: - raise ValueError( - f"Output port {source_port.index} of" - " operation" - f" {source_port.operation.graph_id} has no" - " latency-offset." - ) - - source_end_time = ( - source_op_time + source_port.latency_offset - ) - - if current_input.latency_offset is None: - raise ValueError( - f"Input port {current_input.index} of operation" - f" {current_input.operation.graph_id} has no" - " latency-offset." - ) - op_start_time_from_in = ( - source_end_time - current_input.latency_offset - ) - op_start_time = max(op_start_time, op_start_time_from_in) - - sched._start_times[operation.graph_id] = op_start_time - - self._handle_outputs_and_delays(non_schedulable_ops) - - def schedule_alap(self) -> None: - """Schedule the operations using as-late-as-possible scheduling.""" - self.schedule_asap() - sched = self.schedule - max_end_time = sched.get_max_end_time() - - if sched.schedule_time is None: - sched.set_schedule_time(max_end_time) - elif sched.schedule_time < max_end_time: - raise ValueError(f"Too short schedule time. Minimum is {max_end_time}.") - - # move all outputs ALAP before operations - for output in sched.sfg.find_by_type_name(Output.type_name()): - output = cast(Output, output) - sched.move_operation_alap(output.graph_id) - - # move all operations ALAP - for step in reversed(sched.sfg.get_precedence_list()): - for outport in step: - if not isinstance(outport.operation, Delay): - sched.move_operation_alap(outport.operation.graph_id) - - def schedule_earliest_deadline(self, process_elements: list[Operation]) -> None: - """Schedule the operations using earliest deadline scheduling.""" - - # ACT BASED ON THE NUMBER OF PEs! - - sched = self.schedule - prec_list = sched.sfg.get_precedence_list() - if len(prec_list) < 2: - raise ValueError("Empty signal flow graph cannot be scheduled.") - - # handle the first set in precedence graph (input and delays) - non_schedulable_ops = set() - for outport in prec_list[0]: - operation = outport.operation - if operation.type_name() == Delay.type_name(): - non_schedulable_ops.add(operation.graph_id) - elif operation.graph_id not in sched._start_times: - sched._start_times[operation.graph_id] = 0 - - # latencies = [outport.operation.latency for outport in prec_list[1]] - current_time = 0 - sorted_outports = sorted( - prec_list[1], key=lambda outport: outport.operation.latency - ) - for outport in sorted_outports: - op = outport.operation - sched._start_times[op.graph_id] = current_time - current_time += 1 - - for outports in prec_list[2:]: - # try all remaining operations for one time step - candidates = [] - current_time -= 1 - while len(candidates) == 0: - current_time += 1 - for outport in outports: - remaining_op = outport.operation - op_is_ready_to_be_scheduled = True - for op_input in remaining_op.inputs: - source_op = op_input.signals[0].source.operation - source_op_time = sched.start_times[source_op.graph_id] - source_end_time = source_op_time + source_op.latency - if source_end_time > current_time: - op_is_ready_to_be_scheduled = False - if op_is_ready_to_be_scheduled: - candidates.append(remaining_op) - # sched._start_times[remaining_op.graph_id] = current_time - sorted_candidates = sorted( - candidates, key=lambda candidate: candidate.latency - ) - # schedule the best candidate to current time - sched._start_times[sorted_candidates[0].graph_id] = current_time - - self._handle_outputs_and_delays(non_schedulable_ops) - - def _handle_outputs_and_delays(self, non_schedulable_ops) -> None: - sched = self.schedule - for output in sched._sfg.find_by_type_name(Output.type_name()): - output = cast(Output, output) - source_port = cast(OutputPort, output.inputs[0].signals[0].source) - if source_port.operation.graph_id in non_schedulable_ops: - sched._start_times[output.graph_id] = 0 - else: - if source_port.latency_offset is None: - raise ValueError( - f"Output port {source_port.index} of operation" - f" {source_port.operation.graph_id} has no" - " latency-offset." - ) - sched._start_times[output.graph_id] = sched._start_times[ - source_port.operation.graph_id - ] + cast(int, source_port.latency_offset) - sched._remove_delays() diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py new file mode 100644 index 00000000..845859c9 --- /dev/null +++ b/b_asic/scheduler.py @@ -0,0 +1,193 @@ +from enum import Enum +from typing import TYPE_CHECKING, cast + +from b_asic.operation import Operation +from b_asic.port import OutputPort +from b_asic.special_operations import Delay, Output + +if TYPE_CHECKING: + from b_asic.schedule import Schedule + + +class SchedulingAlgorithm(Enum): + ASAP = "ASAP" + ALAP = "ALAP" + EARLIEST_DEADLINE = "earliest_deadline" + # LEAST_SLACK = "least_slack" # to be implemented + PROVIDED = "provided" + + +class Scheduler: + def __init__(self, schedule: "Schedule") -> None: + self.schedule = schedule + + def schedule_asap(self) -> None: + """Schedule the operations using as-soon-as-possible scheduling.""" + sched = self.schedule + prec_list = sched.sfg.get_precedence_list() + if len(prec_list) < 2: + raise ValueError("Empty signal flow graph cannot be scheduled.") + + # handle the first set in precedence graph (input and delays) + non_schedulable_ops = set() + for outport in prec_list[0]: + operation = outport.operation + if operation.type_name() == Delay.type_name(): + non_schedulable_ops.add(operation.graph_id) + # elif operation.graph_id not in sched._start_times: + else: + sched._start_times[operation.graph_id] = 0 + + # handle second set in precedence graph (first operations) + for outport in prec_list[1]: + operation = outport.operation + # if operation.graph_id not in sched._start_times: + sched._start_times[operation.graph_id] = 0 + + # handle the remaining sets + for outports in prec_list[2:]: + for outport in outports: + operation = outport.operation + if operation.graph_id not in sched._start_times: + op_start_time = 0 + for current_input in operation.inputs: + if len(current_input.signals) != 1: + raise ValueError( + "Error in scheduling, dangling input port detected." + ) + if current_input.signals[0].source is None: + raise ValueError( + "Error in scheduling, signal with no source detected." + ) + source_port = current_input.signals[0].source + + if source_port.operation.graph_id in non_schedulable_ops: + source_end_time = 0 + else: + source_op_time = sched._start_times[ + source_port.operation.graph_id + ] + + if source_port.latency_offset is None: + raise ValueError( + f"Output port {source_port.index} of" + " operation" + f" {source_port.operation.graph_id} has no" + " latency-offset." + ) + + source_end_time = ( + source_op_time + source_port.latency_offset + ) + + if current_input.latency_offset is None: + raise ValueError( + f"Input port {current_input.index} of operation" + f" {current_input.operation.graph_id} has no" + " latency-offset." + ) + op_start_time_from_in = ( + source_end_time - current_input.latency_offset + ) + op_start_time = max(op_start_time, op_start_time_from_in) + + sched._start_times[operation.graph_id] = op_start_time + + self._handle_outputs_and_delays(non_schedulable_ops) + + def schedule_alap(self) -> None: + """Schedule the operations using as-late-as-possible scheduling.""" + self.schedule_asap() + sched = self.schedule + max_end_time = sched.get_max_end_time() + + if sched.schedule_time is None: + sched.set_schedule_time(max_end_time) + elif sched.schedule_time < max_end_time: + raise ValueError(f"Too short schedule time. Minimum is {max_end_time}.") + + # move all outputs ALAP before operations + for output in sched.sfg.find_by_type_name(Output.type_name()): + output = cast(Output, output) + sched.move_operation_alap(output.graph_id) + + # move all operations ALAP + for step in reversed(sched.sfg.get_precedence_list()): + for outport in step: + if not isinstance(outport.operation, Delay): + sched.move_operation_alap(outport.operation.graph_id) + + def schedule_earliest_deadline( + self, process_elements: dict[Operation, int] + ) -> None: + """Schedule the operations using earliest deadline scheduling.""" + + # ACT BASED ON THE NUMBER OF PEs! + + sched = self.schedule + prec_list = sched.sfg.get_precedence_list() + if len(prec_list) < 2: + raise ValueError("Empty signal flow graph cannot be scheduled.") + + # handle the first set in precedence graph (input and delays) + non_schedulable_ops = set() + for outport in prec_list[0]: + operation = outport.operation + if operation.type_name() == Delay.type_name(): + non_schedulable_ops.add(operation.graph_id) + elif operation.graph_id not in sched._start_times: + sched._start_times[operation.graph_id] = 0 + + current_time = 0 + sorted_outports = sorted( + prec_list[1], key=lambda outport: outport.operation.latency + ) + for outport in sorted_outports: + op = outport.operation + sched._start_times[op.graph_id] = current_time + current_time += 1 + + for outports in prec_list[2:]: + # try all remaining operations for one time step + candidates = [] + current_time -= 1 + while len(candidates) == 0: + current_time += 1 + for outport in outports: + remaining_op = outport.operation + op_is_ready_to_be_scheduled = True + for op_input in remaining_op.inputs: + source_op = op_input.signals[0].source.operation + source_op_time = sched.start_times[source_op.graph_id] + source_end_time = source_op_time + source_op.latency + if source_end_time > current_time: + op_is_ready_to_be_scheduled = False + if op_is_ready_to_be_scheduled: + candidates.append(remaining_op) + # sched._start_times[remaining_op.graph_id] = current_time + sorted_candidates = sorted( + candidates, key=lambda candidate: candidate.latency + ) + # schedule the best candidate to current time + sched._start_times[sorted_candidates[0].graph_id] = current_time + + self._handle_outputs_and_delays(non_schedulable_ops) + + def _handle_outputs_and_delays(self, non_schedulable_ops) -> None: + sched = self.schedule + for output in sched._sfg.find_by_type_name(Output.type_name()): + output = cast(Output, output) + source_port = cast(OutputPort, output.inputs[0].signals[0].source) + if source_port.operation.graph_id in non_schedulable_ops: + sched._start_times[output.graph_id] = 0 + else: + if source_port.latency_offset is None: + raise ValueError( + f"Output port {source_port.index} of operation" + f" {source_port.operation.graph_id} has no" + " latency-offset." + ) + sched._start_times[output.graph_id] = sched._start_times[ + source_port.operation.graph_id + ] + cast(int, source_port.latency_offset) + sched._remove_delays() diff --git a/b_asic/scheduling_algorithm.py b/b_asic/scheduling_algorithm.py deleted file mode 100644 index c6787a73..00000000 --- a/b_asic/scheduling_algorithm.py +++ /dev/null @@ -1,9 +0,0 @@ -from enum import Enum - - -class SchedAlg(Enum): - ASAP = "ASAP" - ALAP = "ALAP" - EARLIEST_DEADLINE = "earliest_deadline" - LEAST_SLACK = "least_slack" - PROVIDED = "provided" diff --git a/pyproject.toml b/pyproject.toml index ff1ea7b2..21ece32f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,16 +12,17 @@ dependencies = [ "matplotlib>=3.7", "setuptools_scm[toml]>=6.2", "networkx>=3", - "qtawesome" + "qtawesome", + "pyqt6", ] classifiers = [ "Intended Audience :: Education", "Intended Audience :: Science/Research", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: C++", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", -- GitLab From 720336ddda37470237b57937d0ee95912b7001f7 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 16:01:21 +0100 Subject: [PATCH 22/52] fixes from mr comments --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 21ece32f..a9074708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ dependencies = [ "setuptools_scm[toml]>=6.2", "networkx>=3", "qtawesome", - "pyqt6", ] classifiers = [ "Intended Audience :: Education", -- GitLab From e8faeb947d0e1fd4ab471a3c7aec9e9f34a463ef Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 27 Jan 2025 16:57:23 +0100 Subject: [PATCH 23/52] work in progress, seems like feed-forward part of direct-form I is working --- b_asic/sfg_generators.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/b_asic/sfg_generators.py b/b_asic/sfg_generators.py index df1bf401..96fcdfdc 100644 --- a/b_asic/sfg_generators.py +++ b/b_asic/sfg_generators.py @@ -275,6 +275,7 @@ def direct_form_I_iir( input_op = Input() output = Output() + # construct the feed-forward part muls = [ConstantMultiplication(b[0], input_op, **mult_properties)] delays = [] prev_delay = input_op @@ -284,10 +285,27 @@ def direct_form_I_iir( if i < len(b) - 1: muls.append(ConstantMultiplication(coeff, prev_delay, **mult_properties)) - # mul_pairs = [(muls[i], muls[i+1]) for i in range(len(muls)-1)] - for i in range(len(muls)): - Addition(muls[i], delays[i], **add_properties) + op_a = muls[-1] + for i in range(len(muls) - 1): + op_a = Addition(op_a, muls[-i - 2], **add_properties) + # construct the feedback part + tmp_add = Addition(op_a, None, **add_properties) + muls = [ConstantMultiplication(a[0], tmp_add, **mult_properties)] output <<= muls[0] + delays = [] + prev_delay = muls[0] + for i, coeff in enumerate(a[1:]): + prev_delay = Delay(prev_delay) + delays.append(prev_delay) + if i < len(a) - 1: + muls.append(ConstantMultiplication(coeff, prev_delay, **mult_properties)) + + op_a = muls[-1] + for i in range(len(muls) - 1): + op_a = Addition(op_a, muls[-i - 2], **add_properties) + + tmp_add.input(1).connect(op_a) + return SFG([input_op], [output], name=Name(name)) -- GitLab From 22e5526cb4f07a897048e0149ff4d3d1f9fc7ed1 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Tue, 28 Jan 2025 11:48:59 +0100 Subject: [PATCH 24/52] generators for direct form iir 1 and 2 working, needs automatic tests --- b_asic/gui_utils/plot_window.py | 6 +- b_asic/sfg_generators.py | 97 ++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 11 deletions(-) diff --git a/b_asic/gui_utils/plot_window.py b/b_asic/gui_utils/plot_window.py index 5322debf..96c3bec5 100644 --- a/b_asic/gui_utils/plot_window.py +++ b/b_asic/gui_utils/plot_window.py @@ -6,8 +6,8 @@ from typing import Dict, List, Mapping, Optional, Sequence # , Union # from numpy import (array, real, imag, real_if_close, absolute, angle) import numpy as np -from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas -from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar +from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar from matplotlib.figure import Figure from matplotlib.ticker import MaxNLocator from qtpy.QtCore import Qt @@ -251,7 +251,7 @@ def start_simulation_dialog( The name of the SFG. """ if not QApplication.instance(): - QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) + # QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) app = QApplication(sys.argv) else: app = QApplication.instance() diff --git a/b_asic/sfg_generators.py b/b_asic/sfg_generators.py index 96fcdfdc..97cf1ced 100644 --- a/b_asic/sfg_generators.py +++ b/b_asic/sfg_generators.py @@ -257,16 +257,15 @@ def transposed_direct_form_fir( return SFG([input_op], [output], name=Name(name)) -def direct_form_I_iir( +def direct_form_1_iir( b: Sequence[complex], a: Sequence[complex], name: Optional[str] = None, mult_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, add_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, ) -> SFG: - # np_b = np.atleast_1d(np.squeeze(np.asarray(b))) - # np_a = np.atleast_1d(np.squeeze(np.asarray(a))) - + if name is None: + name = "Direct-form I IIR filter" if mult_properties is None: mult_properties = {} if add_properties is None: @@ -291,16 +290,17 @@ def direct_form_I_iir( # construct the feedback part tmp_add = Addition(op_a, None, **add_properties) - muls = [ConstantMultiplication(a[0], tmp_add, **mult_properties)] - output <<= muls[0] + # muls = [ConstantMultiplication(a[0], tmp_add, **mult_properties)] + muls = [] + output <<= tmp_add delays = [] - prev_delay = muls[0] + prev_delay = tmp_add for i, coeff in enumerate(a[1:]): prev_delay = Delay(prev_delay) delays.append(prev_delay) if i < len(a) - 1: - muls.append(ConstantMultiplication(coeff, prev_delay, **mult_properties)) + muls.append(ConstantMultiplication(-coeff, prev_delay, **mult_properties)) op_a = muls[-1] for i in range(len(muls) - 1): @@ -309,3 +309,84 @@ def direct_form_I_iir( tmp_add.input(1).connect(op_a) return SFG([input_op], [output], name=Name(name)) + + +def direct_form_2_iir( + b: Sequence[complex], + a: Sequence[complex], + name: Optional[str] = None, + mult_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, + add_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, +) -> SFG: + if name is None: + name = "Direct-form I IIR filter" + if mult_properties is None: + mult_properties = {} + if add_properties is None: + add_properties = {} + + input_op = Input() + + left_adds = [] + right_adds = [] + left_muls = [] + right_muls = [] + delays = [Delay()] + + if len(a) != len(b): + raise ValueError("size of coefficient lists a and b are not the same") + + # all except the final + op_a_left = None + op_a_right = None + for i in range(len(a) - 1): + a_coeff = a[-i - 1] + b_coeff = b[-i - 1] + if len(left_muls) != 0: # not first iteration + new_delay = Delay() + delays[-1] <<= new_delay + delays.append(new_delay) + left_muls.append( + ConstantMultiplication(-a_coeff, delays[-1], **mult_properties) + ) + right_muls.append( + ConstantMultiplication(b_coeff, delays[-1], **mult_properties) + ) + if len(left_muls) > 1: # not first iteration + left_adds.append(Addition(op_a_left, left_muls[-1], **add_properties)) + right_adds.append(Addition(op_a_right, right_muls[-1], **add_properties)) + op_a_left = left_adds[-1] + op_a_right = right_adds[-1] + else: + op_a_left = left_muls[-1] + op_a_right = right_muls[-1] + + # finalize + if left_adds: + left_adds.append(Addition(input_op, left_adds[-1], **add_properties)) + else: + left_adds.append(Addition(input_op, left_muls[-1], **add_properties)) + delays[-1] <<= left_adds[-1] + mul = ConstantMultiplication(b[0], left_adds[-1], **mult_properties) + add = Addition(mul, right_adds[-1], **add_properties) + + # for i, coeff in enumerate(list(reversed(a[1:]))): + # if len(left_muls) != 0: # not first iteration + # new_delay = Delay() + # delays[-1] <<= new_delay + # delays.append(new_delay) + # left_muls.append(ConstantMultiplication(-coeff, delays[-1], **mult_properties)) + # if len(left_muls) > 1: # not first iteration + # left_adds.append(Addition(op_a, left_muls[-1], **add_properties)) + # op_a = left_adds[-1] + # else: + # op_a = left_muls[-1] + # if left_adds: + # left_adds.append(Addition(input_op, left_adds[-1], **add_properties)) + # else: + # left_adds.append(Addition(input_op, left_muls[-1], **add_properties)) + # delays[-1] <<= left_adds[-1] + + output = Output() + output <<= add + return SFG([input_op], [output], name=Name(name)) -- GitLab From 96c159b8bb279750a29f3ba44c4e9431bf0143a7 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Tue, 28 Jan 2025 11:49:43 +0100 Subject: [PATCH 25/52] removed commented code --- b_asic/sfg_generators.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/b_asic/sfg_generators.py b/b_asic/sfg_generators.py index 97cf1ced..910b3fd6 100644 --- a/b_asic/sfg_generators.py +++ b/b_asic/sfg_generators.py @@ -370,23 +370,6 @@ def direct_form_2_iir( mul = ConstantMultiplication(b[0], left_adds[-1], **mult_properties) add = Addition(mul, right_adds[-1], **add_properties) - # for i, coeff in enumerate(list(reversed(a[1:]))): - # if len(left_muls) != 0: # not first iteration - # new_delay = Delay() - # delays[-1] <<= new_delay - # delays.append(new_delay) - # left_muls.append(ConstantMultiplication(-coeff, delays[-1], **mult_properties)) - # if len(left_muls) > 1: # not first iteration - # left_adds.append(Addition(op_a, left_muls[-1], **add_properties)) - # op_a = left_adds[-1] - # else: - # op_a = left_muls[-1] - # if left_adds: - # left_adds.append(Addition(input_op, left_adds[-1], **add_properties)) - # else: - # left_adds.append(Addition(input_op, left_muls[-1], **add_properties)) - # delays[-1] <<= left_adds[-1] - output = Output() output <<= add return SFG([input_op], [output], name=Name(name)) -- GitLab From 6698c9a9429b2e722ae4f4b88e249d0a5be523ce Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Tue, 28 Jan 2025 11:59:46 +0100 Subject: [PATCH 26/52] improved some of the styling --- b_asic/sfg_generators.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/b_asic/sfg_generators.py b/b_asic/sfg_generators.py index 910b3fd6..789cd161 100644 --- a/b_asic/sfg_generators.py +++ b/b_asic/sfg_generators.py @@ -264,6 +264,8 @@ def direct_form_1_iir( mult_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, add_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, ) -> SFG: + if len(a) != len(b): + raise ValueError("size of coefficient lists a and b are not the same") if name is None: name = "Direct-form I IIR filter" if mult_properties is None: @@ -271,10 +273,8 @@ def direct_form_1_iir( if add_properties is None: add_properties = {} - input_op = Input() - output = Output() - # construct the feed-forward part + input_op = Input() muls = [ConstantMultiplication(b[0], input_op, **mult_properties)] delays = [] prev_delay = input_op @@ -290,8 +290,8 @@ def direct_form_1_iir( # construct the feedback part tmp_add = Addition(op_a, None, **add_properties) - # muls = [ConstantMultiplication(a[0], tmp_add, **mult_properties)] muls = [] + output = Output() output <<= tmp_add delays = [] @@ -318,6 +318,8 @@ def direct_form_2_iir( mult_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, add_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, ) -> SFG: + if len(a) != len(b): + raise ValueError("size of coefficient lists a and b are not the same") if name is None: name = "Direct-form I IIR filter" if mult_properties is None: @@ -325,18 +327,12 @@ def direct_form_2_iir( if add_properties is None: add_properties = {} - input_op = Input() - + # construct the repeated part of the SFG left_adds = [] right_adds = [] left_muls = [] right_muls = [] delays = [Delay()] - - if len(a) != len(b): - raise ValueError("size of coefficient lists a and b are not the same") - - # all except the final op_a_left = None op_a_right = None for i in range(len(a) - 1): @@ -361,7 +357,8 @@ def direct_form_2_iir( op_a_left = left_muls[-1] op_a_right = right_muls[-1] - # finalize + # finalize the SFG + input_op = Input() if left_adds: left_adds.append(Addition(input_op, left_adds[-1], **add_properties)) else: @@ -369,7 +366,6 @@ def direct_form_2_iir( delays[-1] <<= left_adds[-1] mul = ConstantMultiplication(b[0], left_adds[-1], **mult_properties) add = Addition(mul, right_adds[-1], **add_properties) - output = Output() output <<= add return SFG([input_op], [output], name=Name(name)) -- GitLab From 11fc50e9f68b8cee32c3c27cf079172c7b6a3d10 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Tue, 28 Jan 2025 12:40:25 +0100 Subject: [PATCH 27/52] added docstrings --- b_asic/sfg_generators.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/b_asic/sfg_generators.py b/b_asic/sfg_generators.py index 789cd161..998c20a8 100644 --- a/b_asic/sfg_generators.py +++ b/b_asic/sfg_generators.py @@ -264,6 +264,7 @@ def direct_form_1_iir( mult_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, add_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, ) -> SFG: + """Generates a direct-form IIR filter of type I with coefficients a and b.""" if len(a) != len(b): raise ValueError("size of coefficient lists a and b are not the same") if name is None: @@ -318,6 +319,7 @@ def direct_form_2_iir( mult_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, add_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, ) -> SFG: + """Generates a direct-form IIR filter of type II with coefficients a and b.""" if len(a) != len(b): raise ValueError("size of coefficient lists a and b are not the same") if name is None: -- GitLab From f505a53c32c906f95d8b53581615db9aea0d969d Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Tue, 28 Jan 2025 16:24:56 +0100 Subject: [PATCH 28/52] redid schedule/scheduler interface to new standard --- b_asic/schedule.py | 28 ++++---- b_asic/scheduler.py | 159 ++++++++++++++++++++++---------------------- 2 files changed, 91 insertions(+), 96 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index a708fe1a..d1c63041 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 @@ -94,12 +94,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 +111,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 + 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 +122,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 +391,12 @@ 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[GraphID, 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 +768,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 845859c9..30089db2 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,30 +1,48 @@ -from enum import Enum +from abc import ABC, abstractmethod from typing import TYPE_CHECKING, cast -from b_asic.operation import Operation from b_asic.port import OutputPort from b_asic.special_operations import Delay, Output +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 SchedulingAlgorithm(Enum): +# ASAP = "ASAP" +# ALAP = "ALAP" +# EARLIEST_DEADLINE = "earliest_deadline" +# # LEAST_SLACK = "least_slack" # to be implemented +# PROVIDED = "provided" -class Scheduler: - def __init__(self, schedule: "Schedule") -> None: - self.schedule = schedule +class Scheduler(ABC): + @abstractmethod + def apply_scheduling(self, schedule: "Schedule") -> None: + pass - def schedule_asap(self) -> None: - """Schedule the operations using as-soon-as-possible scheduling.""" - sched = self.schedule - prec_list = sched.sfg.get_precedence_list() + def _handle_outputs(self, schedule, non_schedulable_ops) -> 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): + def apply_scheduling(self, schedule: "Schedule") -> None: + prec_list = schedule.sfg.get_precedence_list() if len(prec_list) < 2: raise ValueError("Empty signal flow graph cannot be scheduled.") @@ -34,21 +52,19 @@ 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: @@ -64,7 +80,7 @@ class Scheduler: 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,41 +107,42 @@ 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(schedule, non_schedulable_ops) + schedule.remove_delays() - self._handle_outputs_and_delays(non_schedulable_ops) - def schedule_alap(self) -> None: - """Schedule the operations using as-late-as-possible scheduling.""" - self.schedule_asap() - sched = self.schedule - max_end_time = sched.get_max_end_time() +class ALAPScheduler(Scheduler): + def apply_scheduling(self, schedule: "Schedule") -> None: + # self.schedule_asap() + ASAPScheduler().apply_scheduling(schedule) + max_end_time = schedule.get_max_end_time() - if sched.schedule_time is None: - sched.set_schedule_time(max_end_time) - elif sched.schedule_time < 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): + def __init__(self, max_resources: dict[TypeName, int]) -> None: + self._max_resources = max_resources - sched = self.schedule - prec_list = sched.sfg.get_precedence_list() + def apply_scheduling(self, schedule: "Schedule") -> None: + # ACT BASED ON THE NUMBER OF PEs! + prec_list = schedule.sfg.get_precedence_list() if len(prec_list) < 2: raise ValueError("Empty signal flow graph cannot be scheduled.") @@ -135,8 +152,8 @@ 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: - sched._start_times[operation.graph_id] = 0 + elif operation.graph_id not in schedule.start_times: + schedule.start_times[operation.graph_id] = 0 current_time = 0 sorted_outports = sorted( @@ -144,50 +161,30 @@ class Scheduler: ) for outport in sorted_outports: op = outport.operation - sched._start_times[op.graph_id] = current_time + schedule.start_times[op.graph_id] = current_time current_time += 1 for outports in prec_list[2:]: - # try all remaining operations for one time step - candidates = [] current_time -= 1 - while len(candidates) == 0: - current_time += 1 - for outport in outports: - remaining_op = outport.operation + for outport in outports: + op = outport.operation + candidates = [] + while not candidates: + current_time += 1 op_is_ready_to_be_scheduled = True - for op_input in remaining_op.inputs: + for op_input in op.inputs: source_op = op_input.signals[0].source.operation - source_op_time = sched.start_times[source_op.graph_id] + source_op_time = schedule.start_times[source_op.graph_id] source_end_time = source_op_time + source_op.latency if source_end_time > current_time: op_is_ready_to_be_scheduled = False if op_is_ready_to_be_scheduled: - candidates.append(remaining_op) - # sched._start_times[remaining_op.graph_id] = current_time - sorted_candidates = sorted( - candidates, key=lambda candidate: candidate.latency - ) - # schedule the best candidate to current time - sched._start_times[sorted_candidates[0].graph_id] = current_time - - self._handle_outputs_and_delays(non_schedulable_ops) - - def _handle_outputs_and_delays(self, non_schedulable_ops) -> None: - sched = self.schedule - for output in sched._sfg.find_by_type_name(Output.type_name()): - output = cast(Output, output) - source_port = cast(OutputPort, output.inputs[0].signals[0].source) - if source_port.operation.graph_id in non_schedulable_ops: - sched._start_times[output.graph_id] = 0 - else: - if source_port.latency_offset is None: - raise ValueError( - f"Output port {source_port.index} of operation" - f" {source_port.operation.graph_id} has no" - " latency-offset." - ) - sched._start_times[output.graph_id] = sched._start_times[ - source_port.operation.graph_id - ] + cast(int, source_port.latency_offset) - sched._remove_delays() + candidates.append(op) + sorted_candidates = sorted( + candidates, key=lambda candidate: candidate.latency + ) + # schedule the best candidate to current time + schedule.start_times[sorted_candidates[0].graph_id] = current_time + + self._handle_outputs(schedule, non_schedulable_ops) + schedule.remove_delays() -- GitLab From 3b2d67c2082088c990f43a5adbaf0c657cfd8a92 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 29 Jan 2025 09:18:50 +0100 Subject: [PATCH 29/52] tests now working --- b_asic/schedule.py | 11 ++--- b_asic/signal_flow_graph.py | 3 +- test/fixtures/schedule.py | 9 ++-- test/test_architecture.py | 3 +- test/test_schedule.py | 83 +++++++++++++++++++------------------ test/test_scheduler_gui.py | 3 +- 6 files changed, 58 insertions(+), 54 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index d1c63041..acb6572b 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -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 @@ -113,7 +108,7 @@ class Schedule: if scheduler: self._scheduler = scheduler - scheduler.apply_scheduling(self) + self._scheduler.apply_scheduling(self) else: if start_times is None: raise ValueError("Must provide start_times when using 'provided'") diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index d3600a68..c1ff7ee6 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/test/fixtures/schedule.py b/test/fixtures/schedule.py index e11b95c9..39b375ed 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/test_architecture.py b/test/test_architecture.py index 2dac82ff..2ab9c539 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 c307798a..bbd98c36 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_gui.py b/test/test_scheduler_gui.py index d6ca2517..02e9d36e 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" -- GitLab From 11845eb03acf61a2213f6f133dfa7e4772c29939 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 29 Jan 2025 09:31:46 +0100 Subject: [PATCH 30/52] updated examples to not fail --- examples/fivepointwinograddft.py | 3 ++- examples/folding_example_with_architecture.py | 3 ++- examples/lwdfallpass.py | 3 ++- examples/secondorderdirectformiir.py | 3 ++- examples/secondorderdirectformiir_architecture.py | 3 ++- examples/thirdorderblwdf.py | 3 ++- examples/threepointwinograddft.py | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/fivepointwinograddft.py b/examples/fivepointwinograddft.py index f9afd0e6..d2156151 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 9baf92e0..43bdc798 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 281856fe..6bbde629 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 b4eee825..aa84c26b 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 1a234a36..a7d72fb3 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 7496b078..d29fd215 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 79bb7fb9..7e7a58ac 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() # %% -- GitLab From 823bb689a55cde659f2fb082d217f81636eee3d3 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 29 Jan 2025 09:50:23 +0100 Subject: [PATCH 31/52] added docstrings for scheduler --- b_asic/scheduler.py | 57 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index 30089db2..554ce97e 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, Optional, cast from b_asic.port import OutputPort from b_asic.special_operations import Delay, Output @@ -9,17 +9,16 @@ 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. + """ pass def _handle_outputs(self, schedule, non_schedulable_ops) -> None: @@ -41,7 +40,16 @@ class Scheduler(ABC): 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. + + 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.") @@ -114,8 +122,16 @@ class ASAPScheduler(Scheduler): class ALAPScheduler(Scheduler): + """Scheduler that implements the as-late-as-possible (ALAP) algorithm.""" + def apply_scheduling(self, schedule: "Schedule") -> None: - # self.schedule_asap() + """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() @@ -137,10 +153,29 @@ class ALAPScheduler(Scheduler): class EarliestDeadlineScheduler(Scheduler): - def __init__(self, max_resources: dict[TypeName, int]) -> None: + """ + Scheduler that implements the earliest-deadline-first algorithm. + + 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. + """ + + def __init__(self, max_resources: Optional[dict[TypeName, int]]) -> None: self._max_resources = 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. + """ # ACT BASED ON THE NUMBER OF PEs! prec_list = schedule.sfg.get_precedence_list() if len(prec_list) < 2: -- GitLab From bc0326a1bf38e5b31502c79c028ec734ccbc0923 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 29 Jan 2025 15:38:11 +0100 Subject: [PATCH 32/52] seems like its working for execution time 1 --- b_asic/scheduler.py | 122 +++++++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 46 deletions(-) diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index 554ce97e..a7d1b85a 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,8 +1,9 @@ from abc import ABC, abstractmethod +from collections import defaultdict from typing import TYPE_CHECKING, Optional, cast 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: @@ -21,7 +22,7 @@ class Scheduler(ABC): """ pass - def _handle_outputs(self, schedule, non_schedulable_ops) -> None: + 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) @@ -165,8 +166,11 @@ class EarliestDeadlineScheduler(Scheduler): resource is used. """ - def __init__(self, max_resources: Optional[dict[TypeName, int]]) -> None: - self._max_resources = max_resources + 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. @@ -176,50 +180,76 @@ class EarliestDeadlineScheduler(Scheduler): schedule : Schedule Schedule to apply the scheduling algorithm on. """ - # ACT BASED ON THE NUMBER OF PEs! - prec_list = schedule.sfg.get_precedence_list() - if len(prec_list) < 2: - raise ValueError("Empty signal flow graph cannot be scheduled.") - # handle the first set in precedence graph (input and delays) - non_schedulable_ops = set() - for outport in prec_list[0]: - operation = outport.operation - if operation.type_name() == Delay.type_name(): - non_schedulable_ops.add(operation.graph_id) - elif operation.graph_id not in schedule.start_times: - schedule.start_times[operation.graph_id] = 0 + # TODO: Take the execution time into consideration! + 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) + + remaining_ops = set(schedule.start_times.keys()) + remaining_ops = {elem for elem in remaining_ops if not elem.startswith("in")} + + 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 - schedule.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 + ) + + if not best_candidate: + current_time += 1 + remaining_resources = self._max_resources.copy() + continue + + # update remaining resources + if best_candidate.type_name() in remaining_resources: + remaining_resources[best_candidate.type_name()] -= 1 + + 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 + ): + # precompute source end times for faster checks + sfg = schedule.sfg + source_end_times = defaultdict(float) + for op_id in remaining_ops: + operation = sfg.find_by_id(op_id) + 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, + ) - for outports in prec_list[2:]: - current_time -= 1 - for outport in outports: - op = outport.operation - candidates = [] - while not candidates: - current_time += 1 - op_is_ready_to_be_scheduled = True - for op_input in op.inputs: - source_op = op_input.signals[0].source.operation - source_op_time = schedule.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(op) - sorted_candidates = sorted( - candidates, key=lambda candidate: candidate.latency - ) - # schedule the best candidate to current time - schedule.start_times[sorted_candidates[0].graph_id] = current_time + best_candidate = None + best_deadline = float('inf') + for op_id in remaining_ops: + operation = sfg.find_by_id(op_id) - self._handle_outputs(schedule, non_schedulable_ops) - schedule.remove_delays() + # 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 -- GitLab From 081823ce1dd6272e474d9968fc5197b01c81b0f7 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Thu, 30 Jan 2025 11:00:26 +0100 Subject: [PATCH 33/52] there is a bug that can occur when an op is scheduled before predecessors --- b_asic/scheduler.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index a7d1b85a..b17b4842 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -181,7 +181,7 @@ class EarliestDeadlineScheduler(Scheduler): Schedule to apply the scheduling algorithm on. """ - # TODO: Take the execution time into consideration! + # TODO: Solve bug where operation (ADD) is scheduled before proceeding (MUL) -> pipelining ALAPScheduler().apply_scheduling(schedule) @@ -190,9 +190,15 @@ class EarliestDeadlineScheduler(Scheduler): input_op = cast(Input, input_op) schedule.move_operation_asap(input_op.graph_id) + # construct the set of remaining operations, excluding inputs remaining_ops = set(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 while remaining_ops: @@ -202,13 +208,23 @@ class EarliestDeadlineScheduler(Scheduler): if not best_candidate: current_time += 1 - remaining_resources = self._max_resources.copy() + + # update available operators + for operation, ready_time in used_resources_ready_times.items(): + print(ready_time, current_time) + if ready_time == current_time: + remaining_resources[operation.type_name()] += 1 + # remaining_resources = self._max_resources.copy() continue - # update remaining resources + # if the resource is constrained, update remaining resources if best_candidate.type_name() in remaining_resources: remaining_resources[best_candidate.type_name()] -= 1 + used_resources_ready_times[best_candidate] = ( + current_time + best_candidate.execution_time + ) + # schedule the best candidate to the current time remaining_ops.remove(best_candidate.graph_id) schedule.start_times[best_candidate.graph_id] = current_time @@ -222,11 +238,16 @@ class EarliestDeadlineScheduler(Scheduler): def _find_best_candidate( schedule, remaining_ops, remaining_resources, current_time ): - # precompute source end times for faster checks 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): @@ -235,11 +256,6 @@ class EarliestDeadlineScheduler(Scheduler): schedule.start_times[source_op.graph_id] + source_op.latency, ) - best_candidate = None - best_deadline = float('inf') - for op_id in remaining_ops: - operation = sfg.find_by_id(op_id) - # check resource constraints if operation.type_name() in remaining_resources: if remaining_resources[operation.type_name()] == 0: -- GitLab From d1cde303a460672b1228cfc26cbe93daaeab1348 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Thu, 30 Jan 2025 14:14:56 +0100 Subject: [PATCH 34/52] solved previous bug, now possible to schedule for different execution times, next step will be to write automatic tests --- b_asic/scheduler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index b17b4842..cc4086d9 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,3 +1,4 @@ +import sys from abc import ABC, abstractmethod from collections import defaultdict from typing import TYPE_CHECKING, Optional, cast @@ -181,8 +182,6 @@ class EarliestDeadlineScheduler(Scheduler): Schedule to apply the scheduling algorithm on. """ - # TODO: Solve bug where operation (ADD) is scheduled before proceeding (MUL) -> pipelining - ALAPScheduler().apply_scheduling(schedule) # move all inputs ASAP to ensure correct operation @@ -211,7 +210,6 @@ class EarliestDeadlineScheduler(Scheduler): # update available operators for operation, ready_time in used_resources_ready_times.items(): - print(ready_time, current_time) if ready_time == current_time: remaining_resources[operation.type_name()] += 1 # remaining_resources = self._max_resources.copy() @@ -255,6 +253,9 @@ class EarliestDeadlineScheduler(Scheduler): 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: -- GitLab From b8f2960ebf184e70294f4afbda32817006e91b66 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Fri, 31 Jan 2025 14:16:36 +0000 Subject: [PATCH 35/52] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: Oscar Gustafsson <oscar.gustafsson@liu.se> --- b_asic/sfg_generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b_asic/sfg_generators.py b/b_asic/sfg_generators.py index 998c20a8..2b693af3 100644 --- a/b_asic/sfg_generators.py +++ b/b_asic/sfg_generators.py @@ -323,7 +323,7 @@ def direct_form_2_iir( if len(a) != len(b): raise ValueError("size of coefficient lists a and b are not the same") if name is None: - name = "Direct-form I IIR filter" + name = "Direct-form II IIR filter" if mult_properties is None: mult_properties = {} if add_properties is None: -- GitLab From 012aaf3137919bdfbce8344ddde771fb6eedacfb Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Fri, 31 Jan 2025 16:43:15 +0100 Subject: [PATCH 36/52] fixed start_times setter --- b_asic/schedule.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index acb6572b..4c7f01b6 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -388,8 +388,11 @@ class Schedule: @start_times.setter def start_times(self, start_times: dict[GraphID, int]) -> None: - if not isinstance(start_times, dict[GraphID, int]): - raise TypeError("start_times must be a dict[GraphID, int]") + 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 -- GitLab From 23f06d335c5fcf79a17182405a9cfb07bd971966 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Fri, 31 Jan 2025 20:04:00 +0100 Subject: [PATCH 37/52] added tests, did some bug fixes and added .coveragerc file for setting up excludes for code coverage --- .coveragerc | 4 + .gitignore | 1 + README.md | 6 +- b_asic/__init__.py | 1 + b_asic/scheduler.py | 25 ++- pyproject.toml | 4 + test/fixtures/signal_flow_graph.py | 8 + test/test_scheduler.py | 259 +++++++++++++++++++++++++++++ 8 files changed, 290 insertions(+), 18 deletions(-) create mode 100644 .coveragerc create mode 100644 test/test_scheduler.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..a765f497 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[report] +exclude_lines = + .*if TYPE_CHECKING:.* + raise NotImplementedError diff --git a/.gitignore b/.gitignore index 8a416e12..4530b5b1 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/README.md b/README.md index 074f2828..8f6a2984 100644 --- a/README.md +++ b/README.md @@ -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/__init__.py b/b_asic/__init__.py index fae7aec4..423e4676 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/scheduler.py b/b_asic/scheduler.py index cc4086d9..e109d01d 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -21,7 +21,7 @@ class Scheduler(ABC): schedule : Schedule Schedule to apply the scheduling algorithm on. """ - pass + raise NotImplementedError def _handle_outputs(self, schedule, non_schedulable_ops=set()) -> None: for output in schedule.sfg.find_by_type_name(Output.type_name()): @@ -77,14 +77,6 @@ class ASAPScheduler(Scheduler): 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: @@ -190,8 +182,8 @@ class EarliestDeadlineScheduler(Scheduler): schedule.move_operation_asap(input_op.graph_id) # construct the set of remaining operations, excluding inputs - remaining_ops = set(schedule.start_times.keys()) - remaining_ops = {elem for elem in remaining_ops if not elem.startswith("in")} + 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 = {} @@ -218,9 +210,14 @@ class EarliestDeadlineScheduler(Scheduler): # if the resource is constrained, update remaining resources if best_candidate.type_name() in remaining_resources: remaining_resources[best_candidate.type_name()] -= 1 - used_resources_ready_times[best_candidate] = ( - current_time + best_candidate.execution_time - ) + if best_candidate.execution_time: + used_resources_ready_times[best_candidate] = ( + current_time + best_candidate.execution_time + ) + 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) diff --git a/pyproject.toml b/pyproject.toml index a9074708..bffec3c7 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/signal_flow_graph.py b/test/fixtures/signal_flow_graph.py index 21c07d0f..7eaccbe7 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_scheduler.py b/test/test_scheduler.py new file mode 100644 index 00000000..0959b2cb --- /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 -- GitLab From 87bf24d65e50b918754ccdfd4898adb46978ccd9 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Tue, 28 Jan 2025 16:24:56 +0100 Subject: [PATCH 38/52] redid schedule/scheduler interface to new standard --- b_asic/schedule.py | 28 ++++---- b_asic/scheduler.py | 159 ++++++++++++++++++++++---------------------- 2 files changed, 91 insertions(+), 96 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index a708fe1a..d1c63041 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 @@ -94,12 +94,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 +111,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 + 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 +122,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 +391,12 @@ 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[GraphID, 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 +768,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 845859c9..30089db2 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,30 +1,48 @@ -from enum import Enum +from abc import ABC, abstractmethod from typing import TYPE_CHECKING, cast -from b_asic.operation import Operation from b_asic.port import OutputPort from b_asic.special_operations import Delay, Output +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 SchedulingAlgorithm(Enum): +# ASAP = "ASAP" +# ALAP = "ALAP" +# EARLIEST_DEADLINE = "earliest_deadline" +# # LEAST_SLACK = "least_slack" # to be implemented +# PROVIDED = "provided" -class Scheduler: - def __init__(self, schedule: "Schedule") -> None: - self.schedule = schedule +class Scheduler(ABC): + @abstractmethod + def apply_scheduling(self, schedule: "Schedule") -> None: + pass - def schedule_asap(self) -> None: - """Schedule the operations using as-soon-as-possible scheduling.""" - sched = self.schedule - prec_list = sched.sfg.get_precedence_list() + def _handle_outputs(self, schedule, non_schedulable_ops) -> 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): + def apply_scheduling(self, schedule: "Schedule") -> None: + prec_list = schedule.sfg.get_precedence_list() if len(prec_list) < 2: raise ValueError("Empty signal flow graph cannot be scheduled.") @@ -34,21 +52,19 @@ 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: @@ -64,7 +80,7 @@ class Scheduler: 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,41 +107,42 @@ 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(schedule, non_schedulable_ops) + schedule.remove_delays() - self._handle_outputs_and_delays(non_schedulable_ops) - def schedule_alap(self) -> None: - """Schedule the operations using as-late-as-possible scheduling.""" - self.schedule_asap() - sched = self.schedule - max_end_time = sched.get_max_end_time() +class ALAPScheduler(Scheduler): + def apply_scheduling(self, schedule: "Schedule") -> None: + # self.schedule_asap() + ASAPScheduler().apply_scheduling(schedule) + max_end_time = schedule.get_max_end_time() - if sched.schedule_time is None: - sched.set_schedule_time(max_end_time) - elif sched.schedule_time < 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): + def __init__(self, max_resources: dict[TypeName, int]) -> None: + self._max_resources = max_resources - sched = self.schedule - prec_list = sched.sfg.get_precedence_list() + def apply_scheduling(self, schedule: "Schedule") -> None: + # ACT BASED ON THE NUMBER OF PEs! + prec_list = schedule.sfg.get_precedence_list() if len(prec_list) < 2: raise ValueError("Empty signal flow graph cannot be scheduled.") @@ -135,8 +152,8 @@ 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: - sched._start_times[operation.graph_id] = 0 + elif operation.graph_id not in schedule.start_times: + schedule.start_times[operation.graph_id] = 0 current_time = 0 sorted_outports = sorted( @@ -144,50 +161,30 @@ class Scheduler: ) for outport in sorted_outports: op = outport.operation - sched._start_times[op.graph_id] = current_time + schedule.start_times[op.graph_id] = current_time current_time += 1 for outports in prec_list[2:]: - # try all remaining operations for one time step - candidates = [] current_time -= 1 - while len(candidates) == 0: - current_time += 1 - for outport in outports: - remaining_op = outport.operation + for outport in outports: + op = outport.operation + candidates = [] + while not candidates: + current_time += 1 op_is_ready_to_be_scheduled = True - for op_input in remaining_op.inputs: + for op_input in op.inputs: source_op = op_input.signals[0].source.operation - source_op_time = sched.start_times[source_op.graph_id] + source_op_time = schedule.start_times[source_op.graph_id] source_end_time = source_op_time + source_op.latency if source_end_time > current_time: op_is_ready_to_be_scheduled = False if op_is_ready_to_be_scheduled: - candidates.append(remaining_op) - # sched._start_times[remaining_op.graph_id] = current_time - sorted_candidates = sorted( - candidates, key=lambda candidate: candidate.latency - ) - # schedule the best candidate to current time - sched._start_times[sorted_candidates[0].graph_id] = current_time - - self._handle_outputs_and_delays(non_schedulable_ops) - - def _handle_outputs_and_delays(self, non_schedulable_ops) -> None: - sched = self.schedule - for output in sched._sfg.find_by_type_name(Output.type_name()): - output = cast(Output, output) - source_port = cast(OutputPort, output.inputs[0].signals[0].source) - if source_port.operation.graph_id in non_schedulable_ops: - sched._start_times[output.graph_id] = 0 - else: - if source_port.latency_offset is None: - raise ValueError( - f"Output port {source_port.index} of operation" - f" {source_port.operation.graph_id} has no" - " latency-offset." - ) - sched._start_times[output.graph_id] = sched._start_times[ - source_port.operation.graph_id - ] + cast(int, source_port.latency_offset) - sched._remove_delays() + candidates.append(op) + sorted_candidates = sorted( + candidates, key=lambda candidate: candidate.latency + ) + # schedule the best candidate to current time + schedule.start_times[sorted_candidates[0].graph_id] = current_time + + self._handle_outputs(schedule, non_schedulable_ops) + schedule.remove_delays() -- GitLab From c9c9e1bc6d05903c1aafd6da773d13def2b6474d Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 29 Jan 2025 09:18:50 +0100 Subject: [PATCH 39/52] tests now working --- b_asic/schedule.py | 11 ++--- b_asic/signal_flow_graph.py | 3 +- test/fixtures/schedule.py | 9 ++-- test/test_architecture.py | 3 +- test/test_schedule.py | 83 +++++++++++++++++++------------------ test/test_scheduler_gui.py | 3 +- 6 files changed, 58 insertions(+), 54 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index d1c63041..acb6572b 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -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 @@ -113,7 +108,7 @@ class Schedule: if scheduler: self._scheduler = scheduler - scheduler.apply_scheduling(self) + self._scheduler.apply_scheduling(self) else: if start_times is None: raise ValueError("Must provide start_times when using 'provided'") diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index d3600a68..c1ff7ee6 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/test/fixtures/schedule.py b/test/fixtures/schedule.py index e11b95c9..39b375ed 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/test_architecture.py b/test/test_architecture.py index 2dac82ff..2ab9c539 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 c307798a..bbd98c36 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_gui.py b/test/test_scheduler_gui.py index d6ca2517..02e9d36e 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" -- GitLab From 4303787cfb23f6a008f91eb51898cfcfd61f5406 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 29 Jan 2025 09:31:46 +0100 Subject: [PATCH 40/52] updated examples to not fail --- examples/fivepointwinograddft.py | 3 ++- examples/folding_example_with_architecture.py | 3 ++- examples/lwdfallpass.py | 3 ++- examples/secondorderdirectformiir.py | 3 ++- examples/secondorderdirectformiir_architecture.py | 3 ++- examples/thirdorderblwdf.py | 3 ++- examples/threepointwinograddft.py | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/fivepointwinograddft.py b/examples/fivepointwinograddft.py index f9afd0e6..d2156151 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 9baf92e0..43bdc798 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 281856fe..6bbde629 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 b4eee825..aa84c26b 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 1a234a36..a7d72fb3 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 7496b078..d29fd215 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 79bb7fb9..7e7a58ac 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() # %% -- GitLab From d5935e9d614be17586c743feaf3b57833539cc11 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 29 Jan 2025 09:50:23 +0100 Subject: [PATCH 41/52] added docstrings for scheduler --- b_asic/scheduler.py | 57 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index 30089db2..554ce97e 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, Optional, cast from b_asic.port import OutputPort from b_asic.special_operations import Delay, Output @@ -9,17 +9,16 @@ 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. + """ pass def _handle_outputs(self, schedule, non_schedulable_ops) -> None: @@ -41,7 +40,16 @@ class Scheduler(ABC): 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. + + 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.") @@ -114,8 +122,16 @@ class ASAPScheduler(Scheduler): class ALAPScheduler(Scheduler): + """Scheduler that implements the as-late-as-possible (ALAP) algorithm.""" + def apply_scheduling(self, schedule: "Schedule") -> None: - # self.schedule_asap() + """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() @@ -137,10 +153,29 @@ class ALAPScheduler(Scheduler): class EarliestDeadlineScheduler(Scheduler): - def __init__(self, max_resources: dict[TypeName, int]) -> None: + """ + Scheduler that implements the earliest-deadline-first algorithm. + + 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. + """ + + def __init__(self, max_resources: Optional[dict[TypeName, int]]) -> None: self._max_resources = 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. + """ # ACT BASED ON THE NUMBER OF PEs! prec_list = schedule.sfg.get_precedence_list() if len(prec_list) < 2: -- GitLab From 87268b645b76b1a7aa7af3d67444987853eac04e Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 29 Jan 2025 15:38:11 +0100 Subject: [PATCH 42/52] seems like its working for execution time 1 --- b_asic/scheduler.py | 122 +++++++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 46 deletions(-) diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index 554ce97e..a7d1b85a 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,8 +1,9 @@ from abc import ABC, abstractmethod +from collections import defaultdict from typing import TYPE_CHECKING, Optional, cast 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: @@ -21,7 +22,7 @@ class Scheduler(ABC): """ pass - def _handle_outputs(self, schedule, non_schedulable_ops) -> None: + 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) @@ -165,8 +166,11 @@ class EarliestDeadlineScheduler(Scheduler): resource is used. """ - def __init__(self, max_resources: Optional[dict[TypeName, int]]) -> None: - self._max_resources = max_resources + 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. @@ -176,50 +180,76 @@ class EarliestDeadlineScheduler(Scheduler): schedule : Schedule Schedule to apply the scheduling algorithm on. """ - # ACT BASED ON THE NUMBER OF PEs! - prec_list = schedule.sfg.get_precedence_list() - if len(prec_list) < 2: - raise ValueError("Empty signal flow graph cannot be scheduled.") - # handle the first set in precedence graph (input and delays) - non_schedulable_ops = set() - for outport in prec_list[0]: - operation = outport.operation - if operation.type_name() == Delay.type_name(): - non_schedulable_ops.add(operation.graph_id) - elif operation.graph_id not in schedule.start_times: - schedule.start_times[operation.graph_id] = 0 + # TODO: Take the execution time into consideration! + 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) + + remaining_ops = set(schedule.start_times.keys()) + remaining_ops = {elem for elem in remaining_ops if not elem.startswith("in")} + + 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 - schedule.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 + ) + + if not best_candidate: + current_time += 1 + remaining_resources = self._max_resources.copy() + continue + + # update remaining resources + if best_candidate.type_name() in remaining_resources: + remaining_resources[best_candidate.type_name()] -= 1 + + 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 + ): + # precompute source end times for faster checks + sfg = schedule.sfg + source_end_times = defaultdict(float) + for op_id in remaining_ops: + operation = sfg.find_by_id(op_id) + 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, + ) - for outports in prec_list[2:]: - current_time -= 1 - for outport in outports: - op = outport.operation - candidates = [] - while not candidates: - current_time += 1 - op_is_ready_to_be_scheduled = True - for op_input in op.inputs: - source_op = op_input.signals[0].source.operation - source_op_time = schedule.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(op) - sorted_candidates = sorted( - candidates, key=lambda candidate: candidate.latency - ) - # schedule the best candidate to current time - schedule.start_times[sorted_candidates[0].graph_id] = current_time + best_candidate = None + best_deadline = float('inf') + for op_id in remaining_ops: + operation = sfg.find_by_id(op_id) - self._handle_outputs(schedule, non_schedulable_ops) - schedule.remove_delays() + # 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 -- GitLab From f36057d7edbe4e43f1768aed8aae719264667bc8 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Thu, 30 Jan 2025 11:00:26 +0100 Subject: [PATCH 43/52] there is a bug that can occur when an op is scheduled before predecessors --- b_asic/scheduler.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index a7d1b85a..b17b4842 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -181,7 +181,7 @@ class EarliestDeadlineScheduler(Scheduler): Schedule to apply the scheduling algorithm on. """ - # TODO: Take the execution time into consideration! + # TODO: Solve bug where operation (ADD) is scheduled before proceeding (MUL) -> pipelining ALAPScheduler().apply_scheduling(schedule) @@ -190,9 +190,15 @@ class EarliestDeadlineScheduler(Scheduler): input_op = cast(Input, input_op) schedule.move_operation_asap(input_op.graph_id) + # construct the set of remaining operations, excluding inputs remaining_ops = set(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 while remaining_ops: @@ -202,13 +208,23 @@ class EarliestDeadlineScheduler(Scheduler): if not best_candidate: current_time += 1 - remaining_resources = self._max_resources.copy() + + # update available operators + for operation, ready_time in used_resources_ready_times.items(): + print(ready_time, current_time) + if ready_time == current_time: + remaining_resources[operation.type_name()] += 1 + # remaining_resources = self._max_resources.copy() continue - # update remaining resources + # if the resource is constrained, update remaining resources if best_candidate.type_name() in remaining_resources: remaining_resources[best_candidate.type_name()] -= 1 + used_resources_ready_times[best_candidate] = ( + current_time + best_candidate.execution_time + ) + # schedule the best candidate to the current time remaining_ops.remove(best_candidate.graph_id) schedule.start_times[best_candidate.graph_id] = current_time @@ -222,11 +238,16 @@ class EarliestDeadlineScheduler(Scheduler): def _find_best_candidate( schedule, remaining_ops, remaining_resources, current_time ): - # precompute source end times for faster checks 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): @@ -235,11 +256,6 @@ class EarliestDeadlineScheduler(Scheduler): schedule.start_times[source_op.graph_id] + source_op.latency, ) - best_candidate = None - best_deadline = float('inf') - for op_id in remaining_ops: - operation = sfg.find_by_id(op_id) - # check resource constraints if operation.type_name() in remaining_resources: if remaining_resources[operation.type_name()] == 0: -- GitLab From 5ec071d9323de130956d0d124154bd4db3a285fd Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Thu, 30 Jan 2025 14:14:56 +0100 Subject: [PATCH 44/52] solved previous bug, now possible to schedule for different execution times, next step will be to write automatic tests --- b_asic/scheduler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index b17b4842..cc4086d9 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,3 +1,4 @@ +import sys from abc import ABC, abstractmethod from collections import defaultdict from typing import TYPE_CHECKING, Optional, cast @@ -181,8 +182,6 @@ class EarliestDeadlineScheduler(Scheduler): Schedule to apply the scheduling algorithm on. """ - # TODO: Solve bug where operation (ADD) is scheduled before proceeding (MUL) -> pipelining - ALAPScheduler().apply_scheduling(schedule) # move all inputs ASAP to ensure correct operation @@ -211,7 +210,6 @@ class EarliestDeadlineScheduler(Scheduler): # update available operators for operation, ready_time in used_resources_ready_times.items(): - print(ready_time, current_time) if ready_time == current_time: remaining_resources[operation.type_name()] += 1 # remaining_resources = self._max_resources.copy() @@ -255,6 +253,9 @@ class EarliestDeadlineScheduler(Scheduler): 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: -- GitLab From 12402efb9df9f6047cd27ea47d7e4e0f07709384 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Fri, 31 Jan 2025 16:43:15 +0100 Subject: [PATCH 45/52] fixed start_times setter --- b_asic/schedule.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index acb6572b..4c7f01b6 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -388,8 +388,11 @@ class Schedule: @start_times.setter def start_times(self, start_times: dict[GraphID, int]) -> None: - if not isinstance(start_times, dict[GraphID, int]): - raise TypeError("start_times must be a dict[GraphID, int]") + 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 -- GitLab From fd3ffdf63b95eba117dcb3129664351104488e11 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Fri, 31 Jan 2025 20:04:00 +0100 Subject: [PATCH 46/52] added tests, did some bug fixes and added .coveragerc file for setting up excludes for code coverage --- .coveragerc | 4 + .gitignore | 1 + README.md | 6 +- b_asic/__init__.py | 1 + b_asic/scheduler.py | 25 ++- pyproject.toml | 4 + test/fixtures/signal_flow_graph.py | 8 + test/test_scheduler.py | 259 +++++++++++++++++++++++++++++ 8 files changed, 290 insertions(+), 18 deletions(-) create mode 100644 .coveragerc create mode 100644 test/test_scheduler.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..a765f497 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[report] +exclude_lines = + .*if TYPE_CHECKING:.* + raise NotImplementedError diff --git a/.gitignore b/.gitignore index 8a416e12..4530b5b1 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/README.md b/README.md index 074f2828..8f6a2984 100644 --- a/README.md +++ b/README.md @@ -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/__init__.py b/b_asic/__init__.py index fae7aec4..423e4676 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/scheduler.py b/b_asic/scheduler.py index cc4086d9..e109d01d 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -21,7 +21,7 @@ class Scheduler(ABC): schedule : Schedule Schedule to apply the scheduling algorithm on. """ - pass + raise NotImplementedError def _handle_outputs(self, schedule, non_schedulable_ops=set()) -> None: for output in schedule.sfg.find_by_type_name(Output.type_name()): @@ -77,14 +77,6 @@ class ASAPScheduler(Scheduler): 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: @@ -190,8 +182,8 @@ class EarliestDeadlineScheduler(Scheduler): schedule.move_operation_asap(input_op.graph_id) # construct the set of remaining operations, excluding inputs - remaining_ops = set(schedule.start_times.keys()) - remaining_ops = {elem for elem in remaining_ops if not elem.startswith("in")} + 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 = {} @@ -218,9 +210,14 @@ class EarliestDeadlineScheduler(Scheduler): # if the resource is constrained, update remaining resources if best_candidate.type_name() in remaining_resources: remaining_resources[best_candidate.type_name()] -= 1 - used_resources_ready_times[best_candidate] = ( - current_time + best_candidate.execution_time - ) + if best_candidate.execution_time: + used_resources_ready_times[best_candidate] = ( + current_time + best_candidate.execution_time + ) + 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) diff --git a/pyproject.toml b/pyproject.toml index a9074708..bffec3c7 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/signal_flow_graph.py b/test/fixtures/signal_flow_graph.py index 21c07d0f..7eaccbe7 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_scheduler.py b/test/test_scheduler.py new file mode 100644 index 00000000..0959b2cb --- /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 -- GitLab From ab69992ba104052a7405fe7a98848b3495ab7b59 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 3 Feb 2025 14:54:15 +0000 Subject: [PATCH 47/52] Edit README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f6a2984..d216f6dc 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/) -- GitLab From 57685b4603cb24fdb5ae629386fb1b9c8b7fe8b5 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 3 Feb 2025 16:50:03 +0100 Subject: [PATCH 48/52] removed qt5 tests from CI-file --- .gitlab-ci.yml | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5f4c6f59..4b33da65 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: -- GitLab From 7d6742ebaf1c42b82430cd8bb0c5d5b5c4c84777 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 3 Feb 2025 19:02:26 +0100 Subject: [PATCH 49/52] fixed bug where pyside2 was imported even though pyside6 used --- b_asic/scheduler_gui/main_window.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index f4502f99..c15be267 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) -- GitLab From 8a38ddc02910969470fe3548664b30f45e2084e4 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 3 Feb 2025 19:26:41 +0100 Subject: [PATCH 50/52] updated compile.py to hopefully work with pyside6 and pyqt6, also recompiled --- b_asic/scheduler_gui/compile.py | 47 +++----------------------- b_asic/scheduler_gui/ui_main_window.py | 2 +- 2 files changed, 5 insertions(+), 44 deletions(-) diff --git a/b_asic/scheduler_gui/compile.py b/b_asic/scheduler_gui/compile.py index 2d9be528..046544d3 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,7 @@ 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: - from qtpy.uic import compileUi - - with open(outfile, "w") as ofile: - compileUi(filename, ofile) - elif uic.PYQT6: + if uic.PYSIDE6 or uic.PYQT6: uic_ = shutil.which("pyside6-uic") arguments = f"-g python -o {outfile} {filename}" @@ -218,9 +180,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/ui_main_window.py b/b_asic/scheduler_gui/ui_main_window.py index ad3fa898..dfe6a056 100644 --- a/b_asic/scheduler_gui/ui_main_window.py +++ b/b_asic/scheduler_gui/ui_main_window.py @@ -1,4 +1,4 @@ -# Form implementation generated from reading ui file '.\B-ASIC\b_asic\scheduler_gui\main_window.ui' +# Form implementation generated from reading ui file '.\b_asic\scheduler_gui\main_window.ui' # # Created by: PyQt6 UI code generator 6.8.0 # -- GitLab From 127a5687fc91a2f7b5d2f0ad49927548f7160fce Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 3 Feb 2025 20:03:50 +0100 Subject: [PATCH 51/52] recompiled with pyside6, will hopefully work with pyqt6 also --- b_asic/scheduler_gui/compile.py | 8 +- b_asic/scheduler_gui/ui_main_window.py | 848 ++++++++++++++++--------- 2 files changed, 545 insertions(+), 311 deletions(-) diff --git a/b_asic/scheduler_gui/compile.py b/b_asic/scheduler_gui/compile.py index 046544d3..2dc7b719 100644 --- a/b_asic/scheduler_gui/compile.py +++ b/b_asic/scheduler_gui/compile.py @@ -160,7 +160,13 @@ def compile_ui(*filenames: str) -> None: directory = directory if directory else "." outfile = f"{directory}/ui_{file}.py" - if uic.PYSIDE6 or uic.PYQT6: + if uic.PYQT6: + from qtpy.uic import compileUi + + with open(outfile, "w") as ofile: + compileUi(filename, ofile) + + elif uic.PYSIDE6: uic_ = shutil.which("pyside6-uic") arguments = f"-g python -o {outfile} {filename}" diff --git a/b_asic/scheduler_gui/ui_main_window.py b/b_asic/scheduler_gui/ui_main_window.py index dfe6a056..829aeb12 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\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 -- GitLab From 8b68ff9e0781420a9d24aeb43d76d8f2b4f44176 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Tue, 4 Feb 2025 10:29:52 +0100 Subject: [PATCH 52/52] fixed error from mr --- b_asic/GUI/main_window.py | 1 - 1 file changed, 1 deletion(-) diff --git a/b_asic/GUI/main_window.py b/b_asic/GUI/main_window.py index b3ab5a76..90146b87 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() -- GitLab