diff --git a/CMakeLists.txt b/CMakeLists.txt
index e0d087c07656292751f53b55abd2bdea0feeaeaa..485cd69235641fdd10b8aebacdebcee91bc65a26 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,7 @@ project(
 )
 
 # Find dependencies.
-find_package(fmt 5.2.1 REQUIRED)
+find_package(fmt REQUIRED)
 find_package(pybind11 CONFIG REQUIRED)
 
 set(LIBRARY_NAME "b_asic") # Name of the python library directory.
diff --git a/GUI/main_window.py b/GUI/main_window.py
deleted file mode 100644
index 53dacce61b5acbfe9de9607a2499c57e0a386eae..0000000000000000000000000000000000000000
--- a/GUI/main_window.py
+++ /dev/null
@@ -1,200 +0,0 @@
-"""@package docstring
-B-ASIC GUI Module.
-This python file is an example of how a GUI can be implemented
-using buttons and textboxes.
-"""
-
-import sys
-
-from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QLabel, QAction,\
-QStatusBar, QMenuBar, QLineEdit, QPushButton
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QIcon, QFont, QPainter, QPen
-
-
-class DragButton(QPushButton):
-    """How to create a dragbutton"""
-    def mousePressEvent(self, event):
-        self._mouse_press_pos = None
-        self._mouse_move_pos = None
-        if event.button() == Qt.LeftButton:
-            self._mouse_press_pos = event.globalPos()
-            self._mouse_move_pos = event.globalPos()
-
-        super(DragButton, self).mousePressEvent(event)
-
-    def mouseMoveEvent(self, event):
-        if event.buttons() == Qt.LeftButton:
-            cur_pos = self.mapToGlobal(self.pos())
-            global_pos = event.globalPos()
-            diff = global_pos - self._mouse_move_pos
-            new_pos = self.mapFromGlobal(cur_pos + diff)
-            self.move(new_pos)
-
-            self._mouse_move_pos = global_pos
-
-        super(DragButton, self).mouseMoveEvent(event)
-
-    def mouseReleaseEvent(self, event):
-        if self._mouse_press_pos is not None:
-            moved = event.globalPos() - self._mouse_press_pos
-            if moved.manhattanLength() > 3:
-                event.ignore()
-                return
-
-        super(DragButton, self).mouseReleaseEvent(event)
-
-class SubWindow(QWidget):
-    """Creates a sub window """
-    def create_window(self, window_width, window_height):
-        """Creates a window
-        """
-        parent = None
-        super(SubWindow, self).__init__(parent)
-        self.setWindowFlags(Qt.WindowStaysOnTopHint)
-        self.resize(window_width, window_height)
-
-class MainWindow(QMainWindow):
-    """Main window for the program"""
-    def __init__(self, *args, **kwargs):
-        super(MainWindow, self).__init__(*args, **kwargs)
-
-        self.setWindowTitle(" ")
-        self.setWindowIcon(QIcon('small_logo.png'))
-
-        # Menu buttons
-        test_button = QAction("Test", self)
-
-        exit_button = QAction("Exit", self)
-        exit_button.setShortcut("Ctrl+Q")
-        exit_button.triggered.connect(self.exit_app)
-
-        edit_button = QAction("Edit", self)
-        edit_button.setStatusTip("Open edit menu")
-        edit_button.triggered.connect(self.on_edit_button_click)
-
-        view_button = QAction("View", self)
-        view_button.setStatusTip("Open view menu")
-        view_button.triggered.connect(self.on_view_button_click)
-
-        menu_bar = QMenuBar()
-        menu_bar.setStyleSheet("background-color:rgb(222, 222, 222)")
-        self.setMenuBar(menu_bar)
-
-        file_menu = menu_bar.addMenu("&File")
-        file_menu.addAction(exit_button)
-        file_menu.addSeparator()
-        file_menu.addAction(test_button)
-
-        edit_menu = menu_bar.addMenu("&Edit")
-        edit_menu.addAction(edit_button)
-
-        edit_menu.addSeparator()
-
-        view_menu = menu_bar.addMenu("&View")
-        view_menu.addAction(view_button)
-
-        self.setStatusBar(QStatusBar(self))
-
-    def on_file_button_click(self):
-        print("File")
-
-    def on_edit_button_click(self):
-        print("Edit")
-
-    def on_view_button_click(self):
-        print("View")
-
-    def exit_app(self, checked):
-        QApplication.quit()
-
-    def clicked(self):
-        print("Drag button clicked")
-
-    def add_drag_buttons(self):
-        """Adds draggable buttons"""
-        addition_button = DragButton("Addition", self)
-        addition_button.move(10, 130)
-        addition_button.setFixedSize(70, 20)
-        addition_button.clicked.connect(self.create_sub_window)
-
-        addition_button2 = DragButton("Addition", self)
-        addition_button2.move(10, 130)
-        addition_button2.setFixedSize(70, 20)
-        addition_button2.clicked.connect(self.create_sub_window)
-
-        subtraction_button = DragButton("Subtraction", self)
-        subtraction_button.move(10, 170)
-        subtraction_button.setFixedSize(70, 20)
-        subtraction_button.clicked.connect(self.create_sub_window)
-
-        subtraction_button2 = DragButton("Subtraction", self)
-        subtraction_button2.move(10, 170)
-        subtraction_button2.setFixedSize(70, 20)
-        subtraction_button2.clicked.connect(self.create_sub_window)
-
-        multiplication_button = DragButton("Multiplication", self)
-        multiplication_button.move(10, 210)
-        multiplication_button.setFixedSize(70, 20)
-        multiplication_button.clicked.connect(self.create_sub_window)
-
-        multiplication_button2 = DragButton("Multiplication", self)
-        multiplication_button2.move(10, 210)
-        multiplication_button2.setFixedSize(70, 20)
-        multiplication_button2.clicked.connect(self.create_sub_window)
-
-    def paintEvent(self, e):
-        # Temporary black box for operations
-        painter = QPainter(self)
-        painter.setPen(QPen(Qt.black, 5, Qt.SolidLine))
-        painter.drawRect(0, 110, 100, 400)
-
-        # Temporary arrow resembling a signal
-        painter.setRenderHint(QPainter.Antialiasing)
-        painter.setPen(Qt.black)
-        painter.setBrush(Qt.white)
-        painter.drawLine(300, 200, 400, 200)
-        painter.drawLine(400, 200, 395, 195)
-        painter.drawLine(400, 200, 395, 205)
-
-    def create_sub_window(self):
-        """ Example of how to create a sub window
-        """
-        self.sub_window = SubWindow()
-        self.sub_window.create_window(400, 300)
-        self.sub_window.setWindowTitle("Properties")
-
-        self.sub_window.properties_label = QLabel(self.sub_window)
-        self.sub_window.properties_label.setText('Properties')
-        self.sub_window.properties_label.setFixedWidth(400)
-        self.sub_window.properties_label.setFont(QFont('SansSerif', 14, QFont.Bold))
-        self.sub_window.properties_label.setAlignment(Qt.AlignCenter)
-
-        self.sub_window.name_label = QLabel(self.sub_window)
-        self.sub_window.name_label.setText('Name:')
-        self.sub_window.name_label.move(20, 40)
-
-        self.sub_window.name_line = QLineEdit(self.sub_window)
-        self.sub_window.name_line.setPlaceholderText("Write a name here")
-        self.sub_window.name_line.move(70, 40)
-        self.sub_window.name_line.resize(100, 20)
-
-        self.sub_window.id_label = QLabel(self.sub_window)
-        self.sub_window.id_label.setText('Id:')
-        self.sub_window.id_label.move(20, 70)
-
-        self.sub_window.id_line = QLineEdit(self.sub_window)
-        self.sub_window.id_line.setPlaceholderText("Write an id here")
-        self.sub_window.id_line.move(70, 70)
-        self.sub_window.id_line.resize(100, 20)
-
-        self.sub_window.show()
-
-
-if __name__ == "__main__":
-    app = QApplication(sys.argv)
-    window = MainWindow()
-    window.add_drag_buttons()
-    window.resize(960, 720)
-    window.show()
-    app.exec_()
diff --git a/README.md b/README.md
index 20f28bee3cfd71fc1b03100b9f9f3632f8a8c284..b2972f30828f19038e5efe612831f989b8f5ffd5 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ The following packages are required in order to build the library:
   * setuptools
   * pybind11
   * numpy
-  * pyside2/pyqt5
+  * pyside2
 
 To build a binary distribution, the following additional packages are required:
 * Python:
diff --git a/b_asic/GUI/__init__.py b/b_asic/GUI/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..16dd0253b1690cef57efa5a25f5adf28f53646f7
--- /dev/null
+++ b/b_asic/GUI/__init__.py
@@ -0,0 +1,4 @@
+"""TODO"""
+from drag_button import *
+from improved_main_window import *
+from gui_interface import *
diff --git a/b_asic/GUI/about_window.py b/b_asic/GUI/about_window.py
new file mode 100644
index 0000000000000000000000000000000000000000..0cb5fa7e3ee73eac015995592db86f4f8c3972c4
--- /dev/null
+++ b/b_asic/GUI/about_window.py
@@ -0,0 +1,131 @@
+from PySide2.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QDialog, QLabel, QFrame, QScrollArea
+from PySide2.QtCore import Qt
+
+
+QUESTIONS = {
+    "Adding operations": "Select an operation under 'Special operations' or 'Core operations' to add it to the workspace.",
+    "Moving operations": "To drag an operation, select the operation on the workspace and drag it around.",
+    "Selecting operations": "To select one operation just press it once, it will then turn grey.",
+    "Selecting multiple operations using dragging": "To select multiple operations using your mouse, \ndrag the mouse while pressing left mouse button, any operation under the selection box will then be selected.",
+    "Selecting multiple operations using without dragging": "To select mutliple operations using without dragging, \npress 'Ctrl+LMouseButton' on any operation. Alternatively press 'Ctrl+A' to select all operations.",
+    "Remove operations": "To remove an operation, select the operation to be deleted, \nfinally press RMouseButton to bring up the context menu, then press 'Delete'.",
+    "Remove multiple operations": "To remove multiple operations, \nselect all operations to be deleted and press 'Delete' on your keyboard.",
+    "Connecting operations": "To connect operations, select the ports on the operation to connect from, \nthen select the next port by pressing 'Ctrl+LMouseButton' on the destination port. Tip: You can chain connection by selecting the ports in the order they should be connected.",
+    "Creating a signal-flow-graph": "To create a signal-flow-graph (SFG), \ncouple together the operations you wish to create a sfg from, then select all operations you wish to include in the sfg, \nfinally press 'Create SFG' in the upper left corner and enter the name of the sfg.",
+    "Simulating a signal-flow-graph": "To simulate a signal-flow-graph (SFG), press the run button in the toolbar, \nthen press 'Simulate SFG' and enter the properties of the simulation.",
+    "Properties of simulation": "The properties of the simulation are, 'Iteration Count': The number of iterations to run the simulation for, \n'Plot Results': Open a plot over the output in matplotlib, \n'Get All Results': Print the detailed output from simulating the sfg in the terminal, \n'Input Values': The input values to the SFG by index of the port."
+}
+
+
+class KeybindsWindow(QDialog):
+    def __init__(self, window):
+        super(KeybindsWindow, self).__init__()
+        self._window = window
+        self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
+        self.setWindowTitle("B-ASIC Keybinds")
+
+        self.dialog_layout = QVBoxLayout()
+        self.setLayout(self.dialog_layout)
+
+        self.add_information_to_layout()
+
+    def add_information_to_layout(self):
+        information_layout = QVBoxLayout()
+
+        title_label = QLabel("B-ASIC / Better ASIC Toolbox")
+        subtitle_label = QLabel("Keybinds in the GUI.")
+
+        frame = QFrame()
+        frame.setFrameShape(QFrame.HLine)
+        frame.setFrameShadow(QFrame.Sunken)
+        self.dialog_layout.addWidget(frame)
+
+        keybinds_label = QLabel(
+            "'Ctrl+A' - Select all operations on the workspace.\n"
+            "'Ctrl+R' - Reload the operation list to add any new operations created.\n"
+            "'Ctrl+Q' - Quit the application.\n"
+            "'Ctrl+LMouseButton' - On a operation will select the operation, without deselecting the other operations.\n"
+            "'Ctrl+S' (Plot) - Save the plot if a plot is visible.\n"
+            "'Ctrl+?' - Open the FAQ section."
+        )
+
+        information_layout.addWidget(title_label)
+        information_layout.addWidget(subtitle_label)
+
+        self.dialog_layout.addLayout(information_layout)
+        self.dialog_layout.addWidget(frame)
+
+        self.dialog_layout.addWidget(keybinds_label)
+
+
+class AboutWindow(QDialog):
+    def __init__(self, window):
+        super(AboutWindow, self).__init__()
+        self._window = window
+        self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
+        self.setWindowTitle("About B-ASIC")
+
+        self.dialog_layout = QVBoxLayout()
+        self.setLayout(self.dialog_layout)
+
+        self.add_information_to_layout()
+
+    def add_information_to_layout(self):
+        information_layout = QVBoxLayout()
+
+        title_label = QLabel("B-ASIC / Better ASIC Toolbox")
+        subtitle_label = QLabel("Construct, simulate and analyze components of an ASIC.")
+
+        frame = QFrame()
+        frame.setFrameShape(QFrame.HLine)
+        frame.setFrameShadow(QFrame.Sunken)
+        self.dialog_layout.addWidget(frame)
+
+        about_label = QLabel(
+            "B-ASIC is a open source tool using the B-ASIC library to construct, simulate and analyze ASICs.\n"
+            "B-ASIC is developed under the MIT-license and any extension to the program should follow that same license.\n"
+            "To read more about how the GUI works please refer to the FAQ under 'Help'."
+        )
+
+        information_layout.addWidget(title_label)
+        information_layout.addWidget(subtitle_label)
+
+        self.dialog_layout.addLayout(information_layout)
+        self.dialog_layout.addWidget(frame)
+
+        self.dialog_layout.addWidget(about_label)
+
+
+class FaqWindow(QDialog):
+    def __init__(self, window):
+        super(FaqWindow, self).__init__()
+        self._window = window
+        self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
+        self.setWindowTitle("Frequently Asked Questions")
+
+        self.dialog_layout = QVBoxLayout()
+        self.scroll_area = QScrollArea()
+        self.setLayout(self.dialog_layout)
+        for question, answer in QUESTIONS.items():
+            self.add_question_to_layout(question, answer)
+
+        self.scroll_area.setWidget(self)
+        self.scroll_area.setWidgetResizable(True)
+
+    def add_question_to_layout(self, question, answer):
+        question_layout = QVBoxLayout()
+        answer_layout = QHBoxLayout()
+
+        question_label = QLabel(question)
+        question_layout.addWidget(question_label)
+
+        answer_label = QLabel(answer)
+        answer_layout.addWidget(answer_label)
+
+        frame = QFrame()
+        frame.setFrameShape(QFrame.HLine)
+        frame.setFrameShadow(QFrame.Sunken)
+        self.dialog_layout.addWidget(frame)
+
+        question_layout.addLayout(answer_layout)
+        self.dialog_layout.addLayout(question_layout)
diff --git a/b_asic/GUI/arrow.py b/b_asic/GUI/arrow.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c931150f3af95f6f2f2d17552c91072e41c4008
--- /dev/null
+++ b/b_asic/GUI/arrow.py
@@ -0,0 +1,38 @@
+from PySide2.QtWidgets import QApplication, QWidget, QMainWindow, QLabel, QAction,\
+QStatusBar, QMenuBar, QLineEdit, QPushButton, QSlider, QScrollArea, QVBoxLayout,\
+QHBoxLayout, QDockWidget, QToolBar, QMenu, QLayout, QSizePolicy, QListWidget, QListWidgetItem,\
+QGraphicsLineItem, QGraphicsWidget
+from PySide2.QtCore import Qt, QSize, QLineF, QPoint, QRectF
+from PySide2.QtGui import QIcon, QFont, QPainter, QPen
+
+from b_asic import Signal
+
+class Arrow(QGraphicsLineItem):
+
+    def __init__(self, source, destination, window, parent=None):
+        super(Arrow, self).__init__(parent)
+        self.source = source
+        self.signal = Signal(source.port, destination.port)
+        self.destination = destination
+        self._window = window
+        self.moveLine()
+        self.source.moved.connect(self.moveLine)
+        self.destination.moved.connect(self.moveLine)
+
+    def contextMenuEvent(self, event):
+        menu = QMenu()
+        menu.addAction("Delete", self.remove)
+        menu.exec_(self.cursor().pos())
+
+    def remove(self):
+        self.signal.remove_destination()
+        self.signal.remove_source()
+        self._window.scene.removeItem(self)
+        self._window.signalList.remove(self)
+
+    def moveLine(self):
+        self.setPen(QPen(Qt.black, 3))
+        self.setLine(QLineF(self.source.operation.x()+self.source.x()+14,\
+             self.source.operation.y()+self.source.y()+7.5,\
+             self.destination.operation.x()+self.destination.x(),\
+             self.destination.operation.y()+self.destination.y()+7.5))
diff --git a/b_asic/GUI/drag_button.py b/b_asic/GUI/drag_button.py
new file mode 100644
index 0000000000000000000000000000000000000000..cd54115cfe0dd4b3c9365b21aaeb45124fa45944
--- /dev/null
+++ b/b_asic/GUI/drag_button.py
@@ -0,0 +1,139 @@
+"""@package docstring
+Drag button class.
+This class creates a dragbutton which can be clicked, dragged and dropped.
+"""
+
+import os.path
+
+from properties_window import PropertiesWindow
+
+from PySide2.QtWidgets import QPushButton, QMenu, QAction
+from PySide2.QtCore import Qt, QSize, Signal
+from PySide2.QtGui import QIcon
+
+from utils import decorate_class, handle_error
+
+
+@decorate_class(handle_error)
+class DragButton(QPushButton):
+    connectionRequested = Signal(QPushButton)
+    moved = Signal()
+    def __init__(self, name, operation, operation_path_name, is_show_name, window, parent = None):
+        self.name = name
+        self.ports = []
+        self.is_show_name = is_show_name
+        self._window = window
+        self.operation = operation
+        self.operation_path_name = operation_path_name
+        self.clicked = 0
+        self.pressed = False
+        self._m_press = False
+        self._m_drag = False
+        self._mouse_press_pos = None
+        self._mouse_move_pos = None
+        super(DragButton, self).__init__(parent)
+
+    def contextMenuEvent(self, event):
+        menu = QMenu()
+        properties = QAction("Properties")
+        menu.addAction(properties)
+        properties.triggered.connect(self.show_properties_window)
+
+        delete = QAction("Delete")
+        menu.addAction(delete)
+        delete.triggered.connect(self.remove)
+        menu.exec_(self.cursor().pos())
+
+    def show_properties_window(self):
+        self.properties_window = PropertiesWindow(self, self._window)
+        self.properties_window.show()
+
+    def add_label(self, label):
+        self.label = label
+
+    def mousePressEvent(self, event):
+        if event.button() == Qt.LeftButton:
+            self._m_press = True
+            self._mouse_press_pos = event.pos()
+            self._mouse_move_pos = event.pos()
+
+        super(DragButton, self).mousePressEvent(event)
+
+    def mouseMoveEvent(self, event):
+        if event.buttons() == Qt.LeftButton and self._m_press:
+            self._m_drag = True
+            self.move(self.mapToParent(event.pos() - self._mouse_press_pos))
+            if self in self._window.pressed_operations:
+                for button in self._window.pressed_operations:
+                    if button is self:
+                        continue
+
+                    button.move(button.mapToParent(event.pos() - self._mouse_press_pos))
+
+        self._window.update()
+        super(DragButton, self).mouseMoveEvent(event)
+
+    def mouseReleaseEvent(self, event):
+        self._m_press = False
+        if self._m_drag:
+            if self._mouse_press_pos is not None:
+                moved = event.pos() - self._mouse_press_pos
+                if moved.manhattanLength() > 3:
+                    event.ignore()
+
+            self._m_drag = False
+
+        else:
+            self.select_button(event.modifiers())
+
+        super(DragButton, self).mouseReleaseEvent(event)
+
+    def _toggle_button(self, pressed=False):
+        self.pressed = not pressed
+        self.setStyleSheet(f"background-color: {'white' if not self.pressed else 'grey'}; border-style: solid;\
+        border-color: black; border-width: 2px")
+        path_to_image = os.path.join('operation_icons', f"{self.operation_path_name}{'_grey.png' if self.pressed else '.png'}")
+        self.setIcon(QIcon(path_to_image))
+        self.setIconSize(QSize(55, 55))
+
+    def select_button(self, modifiers=None):
+        if modifiers != Qt.ControlModifier:
+            for button in self._window.pressed_operations:
+                button._toggle_button(button.pressed)
+
+            self._toggle_button(self.pressed)
+            self._window.pressed_operations = [self]
+
+        else:
+            self._toggle_button(self.pressed)
+            if self in self._window.pressed_operations:
+                self._window.pressed_operations.remove(self)
+            else:
+                self._window.pressed_operations.append(self)
+
+        for signal in self._window.signalList:
+            signal.update()
+
+    def remove(self):
+        self._window.logger.info(f"Removing operation with name {self.operation.name}.")
+        self._window.scene.removeItem(self._window.operationDict[self])
+
+        _signals = []
+        for signal, ports in self._window.signalPortDict.items():
+            if any([port in self._window.portDict[self] for port in ports]):
+                self._window.logger.info(f"Removed signal with name: {signal.signal.name} to/from operation: {self.operation.name}.")
+                signal.remove()
+                _signals.append(signal)
+
+        for signal in _signals:
+            del self._window.signalPortDict[signal]
+
+        for port in self._window.portDict[self]:
+            if port in self._window.pressed_ports:
+                self._window.pressed_ports.remove(port)
+
+        if self in self._window.pressed_operations:
+            self._window.pressed_operations.remove(self)
+
+        if self in self._window.operationDict.keys():
+            del self._window.operationDict[self]
\ No newline at end of file
diff --git a/b_asic/GUI/gui_interface.py b/b_asic/GUI/gui_interface.py
new file mode 100644
index 0000000000000000000000000000000000000000..50c6c1dd419744b9366057a357af8e81f4a4c64a
--- /dev/null
+++ b/b_asic/GUI/gui_interface.py
@@ -0,0 +1,315 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'gui_interface.ui'
+#
+# Created by: PyQt5 UI code generator 5.14.2
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PySide2 import QtCore, QtGui, QtWidgets
+
+
+class Ui_main_window(object):
+    def setupUi(self, main_window):
+        main_window.setObjectName("main_window")
+        main_window.setEnabled(True)
+        main_window.resize(897, 633)
+        self.centralwidget = QtWidgets.QWidget(main_window)
+        self.centralwidget.setObjectName("centralwidget")
+        self.operation_box = QtWidgets.QGroupBox(self.centralwidget)
+        self.operation_box.setGeometry(QtCore.QRect(10, 10, 201, 531))
+        self.operation_box.setLayoutDirection(QtCore.Qt.LeftToRight)
+        self.operation_box.setAutoFillBackground(False)
+        self.operation_box.setStyleSheet("QGroupBox { \n"
+"     border: 2px solid gray; \n"
+"     border-radius: 3px;\n"
+"     margin-top: 0.5em; \n"
+" } \n"
+"\n"
+"QGroupBox::title {\n"
+"    subcontrol-origin: margin;\n"
+"    left: 10px;\n"
+"    padding: 0 3px 0 3px;\n"
+"}")
+        self.operation_box.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
+        self.operation_box.setFlat(False)
+        self.operation_box.setCheckable(False)
+        self.operation_box.setObjectName("operation_box")
+        self.operation_list = QtWidgets.QToolBox(self.operation_box)
+        self.operation_list.setGeometry(QtCore.QRect(10, 20, 171, 271))
+        self.operation_list.setAutoFillBackground(False)
+        self.operation_list.setObjectName("operation_list")
+        self.core_operations_page = QtWidgets.QWidget()
+        self.core_operations_page.setGeometry(QtCore.QRect(0, 0, 171, 217))
+        self.core_operations_page.setObjectName("core_operations_page")
+        self.core_operations_list = QtWidgets.QListWidget(self.core_operations_page)
+        self.core_operations_list.setGeometry(QtCore.QRect(10, 0, 141, 211))
+        self.core_operations_list.setMinimumSize(QtCore.QSize(141, 0))
+        self.core_operations_list.setEditTriggers(QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed)
+        self.core_operations_list.setDragEnabled(False)
+        self.core_operations_list.setDragDropMode(QtWidgets.QAbstractItemView.NoDragDrop)
+        self.core_operations_list.setMovement(QtWidgets.QListView.Static)
+        self.core_operations_list.setFlow(QtWidgets.QListView.TopToBottom)
+        self.core_operations_list.setProperty("isWrapping", False)
+        self.core_operations_list.setResizeMode(QtWidgets.QListView.Adjust)
+        self.core_operations_list.setLayoutMode(QtWidgets.QListView.SinglePass)
+        self.core_operations_list.setViewMode(QtWidgets.QListView.ListMode)
+        self.core_operations_list.setUniformItemSizes(False)
+        self.core_operations_list.setWordWrap(False)
+        self.core_operations_list.setSelectionRectVisible(False)
+        self.core_operations_list.setObjectName("core_operations_list")
+        self.operation_list.addItem(self.core_operations_page, "")
+        self.special_operations_page = QtWidgets.QWidget()
+        self.special_operations_page.setGeometry(QtCore.QRect(0, 0, 171, 217))
+        self.special_operations_page.setObjectName("special_operations_page")
+        self.special_operations_list = QtWidgets.QListWidget(self.special_operations_page)
+        self.special_operations_list.setGeometry(QtCore.QRect(10, 0, 141, 81))
+        self.special_operations_list.setObjectName("special_operations_list")
+        self.operation_list.addItem(self.special_operations_page, "")
+        main_window.setCentralWidget(self.centralwidget)
+        self.menu_bar = QtWidgets.QMenuBar(main_window)
+        self.menu_bar.setGeometry(QtCore.QRect(0, 0, 897, 21))
+        palette = QtGui.QPalette()
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 255, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Light, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Midlight, brush)
+        brush = QtGui.QBrush(QtGui.QColor(127, 127, 127))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Dark, brush)
+        brush = QtGui.QBrush(QtGui.QColor(170, 170, 170))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Mid, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.BrightText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Shadow, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 255, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Light, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, brush)
+        brush = QtGui.QBrush(QtGui.QColor(127, 127, 127))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, brush)
+        brush = QtGui.QBrush(QtGui.QColor(170, 170, 170))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(127, 127, 127))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 255, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Light, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, brush)
+        brush = QtGui.QBrush(QtGui.QColor(127, 127, 127))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, brush)
+        brush = QtGui.QBrush(QtGui.QColor(170, 170, 170))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, brush)
+        brush = QtGui.QBrush(QtGui.QColor(127, 127, 127))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(127, 127, 127))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, brush)
+        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush)
+        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
+        brush.setStyle(QtCore.Qt.SolidPattern)
+        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
+        self.menu_bar.setPalette(palette)
+        self.menu_bar.setObjectName("menu_bar")
+        self.file_menu = QtWidgets.QMenu(self.menu_bar)
+        self.file_menu.setObjectName("file_menu")
+        self.edit_menu = QtWidgets.QMenu(self.menu_bar)
+        self.edit_menu.setObjectName("edit_menu")
+        self.view_menu = QtWidgets.QMenu(self.menu_bar)
+        self.view_menu.setObjectName("view_menu")
+        self.run_menu = QtWidgets.QMenu(self.menu_bar)
+        self.run_menu.setObjectName("run_menu")
+        self.help_menu = QtWidgets.QMenu(self.menu_bar)
+        self.help_menu.setObjectName("help_menu")
+        main_window.setMenuBar(self.menu_bar)
+        self.status_bar = QtWidgets.QStatusBar(main_window)
+        self.status_bar.setObjectName("status_bar")
+        main_window.setStatusBar(self.status_bar)
+        self.save_menu = QtWidgets.QAction(main_window)
+        self.save_menu.setObjectName("save_menu")
+        self.exit_menu = QtWidgets.QAction(main_window)
+        self.exit_menu.setObjectName("exit_menu")
+        self.actionUndo = QtWidgets.QAction(main_window)
+        self.actionUndo.setObjectName("actionUndo")
+        self.actionRedo = QtWidgets.QAction(main_window)
+        self.actionRedo.setObjectName("actionRedo")
+        self.actionSimulateSFG = QtWidgets.QAction(main_window)
+        self.actionSimulateSFG.setObjectName("actionSimulateSFG")
+        self.actionShowPC = QtWidgets.QAction(main_window)
+        self.actionShowPC.setObjectName("actionShowPC")
+        self.aboutBASIC = QtWidgets.QAction(main_window)
+        self.aboutBASIC.setObjectName("aboutBASIC")
+        self.faqBASIC = QtWidgets.QAction(main_window)
+        self.faqBASIC.setObjectName("faqBASIC")
+        self.keybindsBASIC = QtWidgets.QAction(main_window)
+        self.keybindsBASIC.setObjectName("keybindsBASIC")
+        self.actionToolbar = QtWidgets.QAction(main_window)
+        self.actionToolbar.setCheckable(True)
+        self.actionToolbar.setObjectName("actionToolbar")
+        self.file_menu.addAction(self.save_menu)
+        self.file_menu.addSeparator()
+        self.file_menu.addAction(self.exit_menu)
+        self.edit_menu.addAction(self.actionUndo)
+        self.edit_menu.addAction(self.actionRedo)
+        self.view_menu.addAction(self.actionToolbar)
+        self.run_menu.addAction(self.actionShowPC)
+        self.run_menu.addAction(self.actionSimulateSFG)
+        self.help_menu.addAction(self.aboutBASIC)
+        self.help_menu.addAction(self.faqBASIC)
+        self.help_menu.addAction(self.keybindsBASIC)
+        self.menu_bar.addAction(self.file_menu.menuAction())
+        self.menu_bar.addAction(self.edit_menu.menuAction())
+        self.menu_bar.addAction(self.view_menu.menuAction())
+        self.menu_bar.addAction(self.run_menu.menuAction())
+        self.menu_bar.addAction(self.help_menu.menuAction())
+
+        self.retranslateUi(main_window)
+        self.operation_list.setCurrentIndex(1)
+        self.core_operations_list.setCurrentRow(-1)
+        QtCore.QMetaObject.connectSlotsByName(main_window)
+
+    def retranslateUi(self, main_window):
+        _translate = QtCore.QCoreApplication.translate
+        main_window.setWindowTitle(_translate("main_window", "B-ASIC"))
+        self.operation_box.setTitle(_translate("main_window", "Operations"))
+        self.core_operations_list.setSortingEnabled(False)
+        __sortingEnabled = self.core_operations_list.isSortingEnabled()
+        self.core_operations_list.setSortingEnabled(False)
+        self.core_operations_list.setSortingEnabled(__sortingEnabled)
+        self.operation_list.setItemText(self.operation_list.indexOf(self.core_operations_page), _translate("main_window", "Core operations"))
+        __sortingEnabled = self.special_operations_list.isSortingEnabled()
+        self.special_operations_list.setSortingEnabled(False)
+        self.special_operations_list.setSortingEnabled(__sortingEnabled)
+        self.operation_list.setItemText(self.operation_list.indexOf(self.special_operations_page), _translate("main_window", "Special operations"))
+        self.file_menu.setTitle(_translate("main_window", "File"))
+        self.edit_menu.setTitle(_translate("main_window", "Edit"))
+        self.view_menu.setTitle(_translate("main_window", "View"))
+        self.run_menu.setTitle(_translate("main_window", "Run"))
+        self.actionShowPC.setText(_translate("main_window", "Show PC"))
+        self.help_menu.setTitle(_translate("main_window", "Help"))
+        self.actionSimulateSFG.setText(_translate("main_window", "Simulate SFG"))
+        self.aboutBASIC.setText(_translate("main_window", "About B-ASIC"))
+        self.faqBASIC.setText(_translate("main_window", "FAQ"))
+        self.keybindsBASIC.setText(_translate("main_window", "Keybinds"))
+        self.save_menu.setText(_translate("main_window", "Save"))
+        self.exit_menu.setText(_translate("main_window", "Exit"))
+        self.exit_menu.setShortcut(_translate("main_window", "Ctrl+Q"))
+        self.actionUndo.setText(_translate("main_window", "Undo"))
+        self.actionRedo.setText(_translate("main_window", "Redo"))
+        self.actionToolbar.setText(_translate("main_window", "Toolbar"))
+
+
+if __name__ == "__main__":
+    import sys
+    app = QtWidgets.QApplication(sys.argv)
+    main_window = QtWidgets.QMainWindow()
+    ui = Ui_main_window()
+    ui.setupUi(main_window)
+    main_window.show()
+    sys.exit(app.exec_())
diff --git a/b_asic/GUI/gui_interface.ui b/b_asic/GUI/gui_interface.ui
new file mode 100644
index 0000000000000000000000000000000000000000..382747818a3286373b825e4115c9b283799156bc
--- /dev/null
+++ b/b_asic/GUI/gui_interface.ui
@@ -0,0 +1,763 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>main_window</class>
+ <widget class="QMainWindow" name="main_window">
+  <property name="enabled">
+   <bool>true</bool>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>897</width>
+    <height>633</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <widget class="QGroupBox" name="operation_box">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>10</y>
+      <width>201</width>
+      <height>531</height>
+     </rect>
+    </property>
+    <property name="layoutDirection">
+     <enum>Qt::LeftToRight</enum>
+    </property>
+    <property name="autoFillBackground">
+     <bool>false</bool>
+    </property>
+    <property name="styleSheet">
+     <string notr="true">QGroupBox { 
+     border: 2px solid gray; 
+     border-radius: 3px;
+	 margin-top: 0.5em; 
+ } 
+
+QGroupBox::title {
+    subcontrol-origin: margin;
+    left: 10px;
+    padding: 0 3px 0 3px;
+}</string>
+    </property>
+    <property name="title">
+     <string>Operations</string>
+    </property>
+    <property name="alignment">
+     <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+    </property>
+    <property name="flat">
+     <bool>false</bool>
+    </property>
+    <property name="checkable">
+     <bool>false</bool>
+    </property>
+    <widget class="QToolBox" name="operation_list">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>20</y>
+       <width>171</width>
+       <height>271</height>
+      </rect>
+     </property>
+     <property name="autoFillBackground">
+      <bool>false</bool>
+     </property>
+     <property name="currentIndex">
+      <number>1</number>
+     </property>
+     <widget class="QWidget" name="core_operations_page">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>171</width>
+        <height>217</height>
+       </rect>
+      </property>
+      <attribute name="label">
+       <string>Core operations</string>
+      </attribute>
+      <widget class="QListWidget" name="core_operations_list">
+       <property name="geometry">
+        <rect>
+         <x>10</x>
+         <y>0</y>
+         <width>141</width>
+         <height>211</height>
+        </rect>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>141</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="editTriggers">
+        <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
+       </property>
+       <property name="dragEnabled">
+        <bool>false</bool>
+       </property>
+       <property name="dragDropMode">
+        <enum>QAbstractItemView::NoDragDrop</enum>
+       </property>
+       <property name="movement">
+        <enum>QListView::Static</enum>
+       </property>
+       <property name="flow">
+        <enum>QListView::TopToBottom</enum>
+       </property>
+       <property name="isWrapping" stdset="0">
+        <bool>false</bool>
+       </property>
+       <property name="resizeMode">
+        <enum>QListView::Adjust</enum>
+       </property>
+       <property name="layoutMode">
+        <enum>QListView::SinglePass</enum>
+       </property>
+       <property name="viewMode">
+        <enum>QListView::ListMode</enum>
+       </property>
+       <property name="uniformItemSizes">
+        <bool>false</bool>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+       <property name="selectionRectVisible">
+        <bool>false</bool>
+       </property>
+       <property name="currentRow">
+        <number>-1</number>
+       </property>
+       <property name="sortingEnabled">
+        <bool>false</bool>
+       </property>
+       <item>
+        <property name="text">
+         <string>Addition</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Subtraction</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Multiplication</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Division</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Constant</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Constant multiplication</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Square root</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Complex conjugate</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Absolute</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Max</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Min</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Butterfly</string>
+        </property>
+       </item>
+      </widget>
+     </widget>
+     <widget class="QWidget" name="special_operations_page">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>171</width>
+        <height>217</height>
+       </rect>
+      </property>
+      <attribute name="label">
+       <string>Special operations</string>
+      </attribute>
+      <widget class="QListWidget" name="special_operations_list">
+       <property name="geometry">
+        <rect>
+         <x>10</x>
+         <y>0</y>
+         <width>141</width>
+         <height>81</height>
+        </rect>
+       </property>
+       <item>
+        <property name="text">
+         <string>Input</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Output</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Delay</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Custom</string>
+        </property>
+       </item>
+      </widget>
+     </widget>
+    </widget>
+   </widget>
+  </widget>
+  <widget class="QMenuBar" name="menu_bar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>897</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="palette">
+    <palette>
+     <active>
+      <colorrole role="WindowText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Button">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>255</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Light">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Midlight">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Dark">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>127</red>
+         <green>127</green>
+         <blue>127</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Mid">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>170</red>
+         <green>170</green>
+         <blue>170</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Text">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="BrightText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="ButtonText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Base">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Window">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Shadow">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="AlternateBase">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="ToolTipBase">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>220</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="ToolTipText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="PlaceholderText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="128">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+     </active>
+     <inactive>
+      <colorrole role="WindowText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Button">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>255</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Light">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Midlight">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Dark">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>127</red>
+         <green>127</green>
+         <blue>127</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Mid">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>170</red>
+         <green>170</green>
+         <blue>170</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Text">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="BrightText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="ButtonText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Base">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Window">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Shadow">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="AlternateBase">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="ToolTipBase">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>220</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="ToolTipText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="PlaceholderText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="128">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+     </inactive>
+     <disabled>
+      <colorrole role="WindowText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>127</red>
+         <green>127</green>
+         <blue>127</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Button">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>255</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Light">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Midlight">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Dark">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>127</red>
+         <green>127</green>
+         <blue>127</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Mid">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>170</red>
+         <green>170</green>
+         <blue>170</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Text">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>127</red>
+         <green>127</green>
+         <blue>127</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="BrightText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="ButtonText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>127</red>
+         <green>127</green>
+         <blue>127</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Base">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Window">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="Shadow">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="AlternateBase">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>255</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="ToolTipBase">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>255</red>
+         <green>255</green>
+         <blue>220</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="ToolTipText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="255">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+      <colorrole role="PlaceholderText">
+       <brush brushstyle="SolidPattern">
+        <color alpha="128">
+         <red>0</red>
+         <green>0</green>
+         <blue>0</blue>
+        </color>
+       </brush>
+      </colorrole>
+     </disabled>
+    </palette>
+   </property>
+   <widget class="QMenu" name="file_menu">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="save_menu"/>
+    <addaction name="separator"/>
+    <addaction name="exit_menu"/>
+   </widget>
+   <widget class="QMenu" name="edit_menu">
+    <property name="title">
+     <string>Edit</string>
+    </property>
+    <addaction name="actionUndo"/>
+    <addaction name="actionRedo"/>
+   </widget>
+   <widget class="QMenu" name="view_menu">
+    <property name="title">
+     <string>View</string>
+    </property>
+    <addaction name="actionToolbar"/>
+   </widget>
+   <addaction name="file_menu"/>
+   <addaction name="edit_menu"/>
+   <addaction name="view_menu"/>
+  </widget>
+  <widget class="QStatusBar" name="status_bar"/>
+  <action name="save_menu">
+   <property name="text">
+    <string>Save</string>
+   </property>
+  </action>
+  <action name="exit_menu">
+   <property name="text">
+    <string>Exit</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+Q</string>
+   </property>
+  </action>
+  <action name="actionUndo">
+   <property name="text">
+    <string>Undo</string>
+   </property>
+  </action>
+  <action name="actionRedo">
+   <property name="text">
+    <string>Redo</string>
+   </property>
+  </action>
+  <action name="actionToolbar">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Toolbar</string>
+   </property>
+  </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/b_asic/GUI/main_window.py b/b_asic/GUI/main_window.py
new file mode 100644
index 0000000000000000000000000000000000000000..178eeefaf7053ebfffc83f18ac7acda509977788
--- /dev/null
+++ b/b_asic/GUI/main_window.py
@@ -0,0 +1,341 @@
+"""@package docstring
+B-ASIC GUI Module.
+This python file is the main window of the GUI for B-ASIC.
+"""
+
+from pprint import pprint
+from os import getcwd, path
+import logging
+logging.basicConfig(level=logging.INFO)
+import sys
+
+from about_window import AboutWindow, FaqWindow, KeybindsWindow
+from drag_button import DragButton
+from gui_interface import Ui_main_window
+from arrow import Arrow
+from port_button import PortButton
+from show_pc_window import ShowPCWindow
+
+from b_asic import Operation, SFG, InputPort, OutputPort
+from b_asic.simulation import Simulation
+import b_asic.core_operations as c_oper
+import b_asic.special_operations as s_oper
+from utils import decorate_class, handle_error
+from simulate_sfg_window import SimulateSFGWindow, Plot
+
+from numpy import linspace
+
+from PySide2.QtWidgets import QApplication, QWidget, QMainWindow, QLabel, QAction,\
+QStatusBar, QMenuBar, QLineEdit, QPushButton, QSlider, QScrollArea, QVBoxLayout,\
+QHBoxLayout, QDockWidget, QToolBar, QMenu, QLayout, QSizePolicy, QListWidget,\
+QListWidgetItem, QGraphicsView, QGraphicsScene, QShortcut, QGraphicsTextItem,\
+QGraphicsProxyWidget, QInputDialog, QTextEdit
+from PySide2.QtCore import Qt, QSize
+from PySide2.QtGui import QIcon, QFont, QPainter, QPen, QBrush, QKeySequence
+
+MIN_WIDTH_SCENE = 600
+MIN_HEIGHT_SCENE = 520
+
+@decorate_class(handle_error)
+class MainWindow(QMainWindow):
+    def __init__(self):
+        super(MainWindow, self).__init__()
+        self.ui = Ui_main_window()
+        self.ui.setupUi(self)
+        self.setWindowIcon(QIcon('small_logo.png'))
+        self.scene = None
+        self._operations_from_name = dict()
+        self.zoom = 1
+        self.sfg_name_i = 0
+        self.operationDict = dict()
+        self.operationItemSceneList = []
+        self.signalList = []
+        self.pressed_operations = []
+        self.portDict = dict()
+        self.signalPortDict = dict()
+        self.pressed_ports = []
+        self.sfg_list = []
+        self._window = self
+        self.logger = logging.getLogger(__name__)
+        self.init_ui()
+        self.add_operations_from_namespace(c_oper, self.ui.core_operations_list)
+        self.add_operations_from_namespace(s_oper, self.ui.special_operations_list)
+
+        self.shortcut_core = QShortcut(QKeySequence("Ctrl+R"), self.ui.operation_box)
+        self.shortcut_core.activated.connect(self._refresh_operations_list_from_namespace)
+        self.shortcut_select_all = QShortcut(QKeySequence("Ctrl+A"), self.graphic_view)
+        self.shortcut_select_all.activated.connect(self._select_all_operations)
+        self.scene.selectionChanged.connect(self._select_operations)
+
+        self.move_button_index = 0
+        self.is_show_names = True
+
+        self.check_show_names = QAction("Show operation names")
+        self.check_show_names.triggered.connect(self.view_operation_names)
+        self.check_show_names.setCheckable(True)
+        self.check_show_names.setChecked(1)
+        self.ui.view_menu.addAction(self.check_show_names)
+
+        self.ui.actionShowPC.triggered.connect(self.show_precedence_chart)
+        self.ui.actionSimulateSFG.triggered.connect(self.simulate_sfg)
+        self.ui.faqBASIC.triggered.connect(self.display_faq_page)
+        self.ui.aboutBASIC.triggered.connect(self.display_about_page)
+        self.ui.keybindsBASIC.triggered.connect(self.display_keybinds_page)
+        self.shortcut_help = QShortcut(QKeySequence("Ctrl+?"), self)
+        self.shortcut_help.activated.connect(self.display_faq_page)
+
+        self.logger.info("Finished setting up GUI")
+        self.logger.info("For questions please refer to 'Ctrl+?', or visit the 'Help' section on the toolbar.")
+
+    def init_ui(self):
+        self.ui.core_operations_list.itemClicked.connect(self.on_list_widget_item_clicked)
+        self.ui.special_operations_list.itemClicked.connect(self.on_list_widget_item_clicked)
+        self.ui.exit_menu.triggered.connect(self.exit_app)
+        self.create_toolbar_view()
+        self.create_graphics_view()
+
+    def create_graphics_view(self):
+        self.scene = QGraphicsScene(self)
+        self.graphic_view = QGraphicsView(self.scene, self)
+        self.graphic_view.setRenderHint(QPainter.Antialiasing)
+        self.graphic_view.setGeometry(self.ui.operation_box.width(), 20, self.width(), self.height())
+        self.graphic_view.setDragMode(QGraphicsView.RubberBandDrag)
+
+    def create_toolbar_view(self):
+        self.toolbar = self.addToolBar("Toolbar")
+        self.toolbar.addAction("Create SFG", self.create_SFG_from_toolbar)
+
+    def resizeEvent(self, event):
+        self.ui.operation_box.setGeometry(10, 10, self.ui.operation_box.width(), self.height())
+        self.graphic_view.setGeometry(self.ui.operation_box.width() + 20, 30, self.width() - self.ui.operation_box.width() - 20, self.height()-30)
+        super(MainWindow, self).resizeEvent(event)
+
+    def wheelEvent(self, event):
+        if event.modifiers() == Qt.ControlModifier:
+            old_zoom = self.zoom
+            self.zoom += event.angleDelta().y()/2500
+            self.graphic_view.scale(self.zoom, self.zoom)
+            self.zoom = old_zoom
+
+    def view_operation_names(self):
+        if self.check_show_names.isChecked():
+            self.is_show_names = True
+        else:
+            self.is_show_names = False
+
+        for operation in self.operationDict.keys():
+            operation.label.setOpacity(self.is_show_names)
+            operation.is_show_name = self.is_show_names
+
+    def exit_app(self):
+        self.logger.info("Exiting the application.")
+        QApplication.quit()
+
+    def create_SFG_from_toolbar(self):
+        inputs = []
+        outputs = []
+        for op in self.pressed_operations:
+            if isinstance(op.operation, s_oper.Input):
+                inputs.append(op.operation)
+            elif isinstance(op.operation, s_oper.Output):
+                outputs.append(op.operation)
+
+        name = QInputDialog.getText(self, "Create SFG", "Name: ", QLineEdit.Normal)
+        self.logger.info(f"Creating SFG with name: {name[0]} from selected operations.")
+
+        sfg = SFG(inputs=inputs, outputs=outputs, name=name[0])
+        self.logger.info(f"Created SFG with name: {name[0]} from selected operations.")
+
+        for op in self.pressed_operations:
+            op.setToolTip(sfg.name)
+        self.sfg_list.append(sfg)
+
+    def show_precedence_chart(self):
+        self.dialog = ShowPCWindow(self)
+        self.dialog.add_sfg_to_dialog()
+        self.dialog.show()
+
+    def _determine_port_distance(self, length, ports):
+        """Determine the distance between each port on the side of an operation.
+        The method returns the distance that each port should have from 0.
+        """
+        return [length / 2] if ports == 1 else linspace(0, length, ports)
+
+    def add_ports(self, operation):
+        _output_ports_dist = self._determine_port_distance(55 - 17, operation.operation.output_count)
+        _input_ports_dist = self._determine_port_distance(55 - 17, operation.operation.input_count)
+        self.portDict[operation] = list()
+
+        for i, dist in enumerate(_input_ports_dist):
+            port = PortButton(">", operation, operation.operation.input(i), self)
+            self.portDict[operation].append(port)
+            operation.ports.append(port)
+            port.move(0, dist)
+            port.show()
+
+        for i, dist in enumerate(_output_ports_dist):
+            port = PortButton(">", operation, operation.operation.output(i), self)
+            self.portDict[operation].append(port)
+            operation.ports.append(port)
+            port.move(55 - 12, dist)
+            port.show()
+
+    def get_operations_from_namespace(self, namespace):
+        self.logger.info(f"Fetching operations from namespace: {namespace.__name__}.")
+        return [comp for comp in dir(namespace) if hasattr(getattr(namespace, comp), "type_name")]
+
+    def add_operations_from_namespace(self, namespace, _list):
+        for attr_name in self.get_operations_from_namespace(namespace):
+            attr = getattr(namespace, attr_name)
+            try:
+                attr.type_name()
+                item = QListWidgetItem(attr_name)
+                _list.addItem(item)
+                self._operations_from_name[attr_name] = attr
+            except NotImplementedError:
+                pass
+
+        self.logger.info(f"Added operations from namespace: {namespace.__name__}.")
+
+    def _create_operation(self, item):
+        self.logger.info(f"Creating operation of type: {item.text()}.")
+        try:
+            attr_oper = self._operations_from_name[item.text()]()
+            attr_button = DragButton(attr_oper.graph_id, attr_oper, attr_oper.type_name().lower(), True, self)
+            attr_button.move(250, 100)
+            attr_button.setFixedSize(55, 55)
+            attr_button.setStyleSheet("background-color: white; border-style: solid;\
+            border-color: black; border-width: 2px")
+            self.add_ports(attr_button)
+
+            icon_path = path.join("operation_icons", f"{attr_oper.type_name().lower()}.png")
+            if not path.exists(icon_path):
+                icon_path = path.join("operation_icons", f"custom_operation.png")
+            attr_button.setIcon(QIcon(icon_path))
+            attr_button.setIconSize(QSize(55, 55))
+            attr_button.setToolTip("No sfg")
+            attr_button.setStyleSheet(""" QToolTip { background-color: white;
+            color: black }""")
+            attr_button.setParent(None)
+            attr_button_scene = self.scene.addWidget(attr_button)
+            attr_button_scene.moveBy(self.move_button_index * 100, 0)
+            attr_button_scene.setFlag(attr_button_scene.ItemIsSelectable, True)
+            self.move_button_index += 1
+            operation_label = QGraphicsTextItem(attr_oper.type_name(), attr_button_scene)
+            if not self.is_show_names:
+                operation_label.setOpacity(0)
+            operation_label.setTransformOriginPoint(operation_label.boundingRect().center())
+            operation_label.moveBy(10, -20)
+            attr_button.add_label(operation_label)
+            self.operationDict[attr_button] = attr_button_scene
+        except Exception as e:
+            self.logger.error(f"Unexpected error occured while creating operation: {e}.")
+
+    def _refresh_operations_list_from_namespace(self):
+        self.logger.info("Refreshing operation list.")
+        self.ui.core_operations_list.clear()
+        self.ui.special_operations_list.clear()
+
+        self.add_operations_from_namespace(c_oper, self.ui.core_operations_list)
+        self.add_operations_from_namespace(s_oper, self.ui.special_operations_list)
+        self.logger.info("Finished refreshing operation list.")
+
+    def on_list_widget_item_clicked(self, item):
+        self._create_operation(item)
+
+    def keyPressEvent(self, event):
+        if event.key() == Qt.Key_Delete:
+            for pressed_op in self.pressed_operations:
+                pressed_op.remove()
+                self.move_button_index -= 1
+            self.pressed_operations.clear()
+        super().keyPressEvent(event)
+
+    def connectButton(self, button):
+        if len(self.pressed_ports) < 2:
+            self.logger.warn("Can't connect less than two ports. Please select more.")
+            return
+
+        for i in range(len(self.pressed_ports) - 1):
+            if isinstance(self.pressed_ports[i].port, OutputPort) and \
+            isinstance(self.pressed_ports[i+1].port, InputPort):
+                self.logger.info(f"Connecting: {self.pressed_ports[i].operation.operation.type_name()} -> {self.pressed_ports[i + 1].operation.operation.type_name()}")
+                line = Arrow(self.pressed_ports[i], self.pressed_ports[i + 1], self)
+                self.signalPortDict[line] = [self.pressed_ports[i], self.pressed_ports[i + 1]]
+
+                self.scene.addItem(line)
+                self.signalList.append(line)
+
+        for port in self.pressed_ports:
+            port.select_port()
+
+        self.update()
+
+    def paintEvent(self, event):
+        for signal in self.signalPortDict.keys():
+            signal.moveLine()
+
+    def _select_all_operations(self):
+        self.logger.info("Selecting all operations in the workspace.")
+        self.pressed_operations.clear()
+        for button in self.operationDict.keys():
+            button._toggle_button(pressed=False)
+            self.pressed_operations.append(button)
+
+    def _select_operations(self):
+        selected = [button.widget() for button in self.scene.selectedItems()]
+        for button in selected:
+            button._toggle_button(pressed=False)
+
+        for button in self.pressed_operations:
+            if button not in selected:
+                button._toggle_button(pressed=True)
+
+        self.pressed_operations = selected
+
+    def _simulate_sfg(self):
+        for sfg, properties in self.dialog.properties.items():
+            self.logger.info(f"Simulating sfg with name: {sfg.name}.")
+            simulation = Simulation(sfg, input_providers=properties["input_values"], save_results=properties["all_results"])
+            l_result = simulation.run_for(properties["iteration_count"])
+
+            if properties["all_results"]:
+                print(f"{'=' * 10} {sfg.name} {'=' * 10}")
+                pprint(simulation.results)
+                print(f"{'=' * 10} /{sfg.name} {'=' * 10}")
+
+            if properties["show_plot"]:
+                self.logger.info(f"Opening plot for sfg with name: {sfg.name}.")
+                self.logger.info("To save the plot press 'Ctrl+S' when the plot is focused.")
+                self.plot = Plot(simulation, sfg, self)
+                self.plot.show()
+
+    def simulate_sfg(self):
+        self.dialog = SimulateSFGWindow(self)
+
+        for sfg in self.sfg_list:
+            self.dialog.add_sfg_to_dialog(sfg)
+
+        self.dialog.show()
+
+        # Wait for input to dialog. Kinda buggy because of the separate window in the same thread.
+        self.dialog.simulate.connect(self._simulate_sfg)
+
+    def display_faq_page(self):
+        self.faq_page = FaqWindow(self)
+        self.faq_page.scroll_area.show()
+
+    def display_about_page(self):
+        self.about_page = AboutWindow(self)
+        self.about_page.show()
+
+    def display_keybinds_page(self):
+        self.keybinds_page = KeybindsWindow(self)
+        self.keybinds_page.show()
+
+
+if __name__ == "__main__":
+    app = QApplication(sys.argv)
+    window = MainWindow()
+    window.show()
+    sys.exit(app.exec_())
diff --git a/b_asic/GUI/operation_icons/abs.png b/b_asic/GUI/operation_icons/abs.png
new file mode 100644
index 0000000000000000000000000000000000000000..6573d4d96928d32a3a59641e877108ab60d38c19
Binary files /dev/null and b/b_asic/GUI/operation_icons/abs.png differ
diff --git a/b_asic/GUI/operation_icons/abs_grey.png b/b_asic/GUI/operation_icons/abs_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..4e16da3110d3497c7dab55b9cd9edf22aef4c097
Binary files /dev/null and b/b_asic/GUI/operation_icons/abs_grey.png differ
diff --git a/b_asic/GUI/operation_icons/add.png b/b_asic/GUI/operation_icons/add.png
new file mode 100644
index 0000000000000000000000000000000000000000..504e641e4642d9c03deeea9911927bbe714f053e
Binary files /dev/null and b/b_asic/GUI/operation_icons/add.png differ
diff --git a/b_asic/GUI/operation_icons/add_grey.png b/b_asic/GUI/operation_icons/add_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..a7620d2b56c8a5d06b2c04ff994eb334777008f4
Binary files /dev/null and b/b_asic/GUI/operation_icons/add_grey.png differ
diff --git a/b_asic/GUI/operation_icons/bfly.png b/b_asic/GUI/operation_icons/bfly.png
new file mode 100644
index 0000000000000000000000000000000000000000..9948a964d353e7325c696ae7f12e1ded09cdc13f
Binary files /dev/null and b/b_asic/GUI/operation_icons/bfly.png differ
diff --git a/b_asic/GUI/operation_icons/bfly_grey.png b/b_asic/GUI/operation_icons/bfly_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..cc282efe67637dda8b5e76b0f4c35015b19ddd93
Binary files /dev/null and b/b_asic/GUI/operation_icons/bfly_grey.png differ
diff --git a/b_asic/GUI/operation_icons/c.png b/b_asic/GUI/operation_icons/c.png
new file mode 100644
index 0000000000000000000000000000000000000000..0068adae8130f7b384f7fd70277fb8f87d9dbf94
Binary files /dev/null and b/b_asic/GUI/operation_icons/c.png differ
diff --git a/b_asic/GUI/operation_icons/c_grey.png b/b_asic/GUI/operation_icons/c_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..d7d3e585e21e70aa8b89b85008e5306184ccff8c
Binary files /dev/null and b/b_asic/GUI/operation_icons/c_grey.png differ
diff --git a/b_asic/GUI/operation_icons/cmul.png b/b_asic/GUI/operation_icons/cmul.png
new file mode 100644
index 0000000000000000000000000000000000000000..7e7ff82b3aa577886da6686f62df936e8aa3572e
Binary files /dev/null and b/b_asic/GUI/operation_icons/cmul.png differ
diff --git a/b_asic/GUI/operation_icons/cmul_grey.png b/b_asic/GUI/operation_icons/cmul_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..8fe92d2606b8bc1c2bce98ed45cccb6236e36476
Binary files /dev/null and b/b_asic/GUI/operation_icons/cmul_grey.png differ
diff --git a/b_asic/GUI/operation_icons/conj.png b/b_asic/GUI/operation_icons/conj.png
new file mode 100644
index 0000000000000000000000000000000000000000..c74c9de7f45c16b972ddc072e5e0203dcb902f1b
Binary files /dev/null and b/b_asic/GUI/operation_icons/conj.png differ
diff --git a/b_asic/GUI/operation_icons/conj_grey.png b/b_asic/GUI/operation_icons/conj_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..33f1e60e686cb711644a875341c34a12b8dfe4c9
Binary files /dev/null and b/b_asic/GUI/operation_icons/conj_grey.png differ
diff --git a/b_asic/GUI/operation_icons/custom_operation.png b/b_asic/GUI/operation_icons/custom_operation.png
new file mode 100644
index 0000000000000000000000000000000000000000..a598abaaf05934222c457fc8b141431ca04f91be
Binary files /dev/null and b/b_asic/GUI/operation_icons/custom_operation.png differ
diff --git a/b_asic/GUI/operation_icons/custom_operation_grey.png b/b_asic/GUI/operation_icons/custom_operation_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..a1d72e3b7f67fb78acccf8f5fb417be62cbc2b2a
Binary files /dev/null and b/b_asic/GUI/operation_icons/custom_operation_grey.png differ
diff --git a/b_asic/GUI/operation_icons/div.png b/b_asic/GUI/operation_icons/div.png
new file mode 100644
index 0000000000000000000000000000000000000000..d7bf8908ed0acae344dc04adf6a96ae8448bb9d4
Binary files /dev/null and b/b_asic/GUI/operation_icons/div.png differ
diff --git a/b_asic/GUI/operation_icons/div_grey.png b/b_asic/GUI/operation_icons/div_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..de3a82c369dde1b6a28ea93e5362e6eb8879fe68
Binary files /dev/null and b/b_asic/GUI/operation_icons/div_grey.png differ
diff --git a/b_asic/GUI/operation_icons/in.png b/b_asic/GUI/operation_icons/in.png
new file mode 100644
index 0000000000000000000000000000000000000000..ebfd1a23e5723496e69f02bebc959d68e3c2f986
Binary files /dev/null and b/b_asic/GUI/operation_icons/in.png differ
diff --git a/b_asic/GUI/operation_icons/in_grey.png b/b_asic/GUI/operation_icons/in_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..07d3362f93039ca65966bc04d0ea481840072559
Binary files /dev/null and b/b_asic/GUI/operation_icons/in_grey.png differ
diff --git a/b_asic/GUI/operation_icons/max.png b/b_asic/GUI/operation_icons/max.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f759155f632e090a5dea49644f8dbc5dff7b4f4
Binary files /dev/null and b/b_asic/GUI/operation_icons/max.png differ
diff --git a/b_asic/GUI/operation_icons/max_grey.png b/b_asic/GUI/operation_icons/max_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..8179fb2e812cc617a012a1fb3d338e1c99e07aa7
Binary files /dev/null and b/b_asic/GUI/operation_icons/max_grey.png differ
diff --git a/b_asic/GUI/operation_icons/min.png b/b_asic/GUI/operation_icons/min.png
new file mode 100644
index 0000000000000000000000000000000000000000..5365002a74b91d91e40c95091ab8c052cfe3f3c5
Binary files /dev/null and b/b_asic/GUI/operation_icons/min.png differ
diff --git a/b_asic/GUI/operation_icons/min_grey.png b/b_asic/GUI/operation_icons/min_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..6978cd91288a9d5705903efb017e2dc3b42b1af1
Binary files /dev/null and b/b_asic/GUI/operation_icons/min_grey.png differ
diff --git a/b_asic/GUI/operation_icons/mul.png b/b_asic/GUI/operation_icons/mul.png
new file mode 100644
index 0000000000000000000000000000000000000000..2042dd16781e64ea97ed5323b79f4f310f760009
Binary files /dev/null and b/b_asic/GUI/operation_icons/mul.png differ
diff --git a/b_asic/GUI/operation_icons/mul_grey.png b/b_asic/GUI/operation_icons/mul_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..00e2304b634e02810d6a17aa2850c9afe4922eb9
Binary files /dev/null and b/b_asic/GUI/operation_icons/mul_grey.png differ
diff --git a/b_asic/GUI/operation_icons/out.png b/b_asic/GUI/operation_icons/out.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7da51bbe3640b03b9f7d89f9dd90543c3022273
Binary files /dev/null and b/b_asic/GUI/operation_icons/out.png differ
diff --git a/b_asic/GUI/operation_icons/out_grey.png b/b_asic/GUI/operation_icons/out_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..2cde317beecf019db5c4eac2a35baa8ef8e99f5e
Binary files /dev/null and b/b_asic/GUI/operation_icons/out_grey.png differ
diff --git a/b_asic/GUI/operation_icons/reg.png b/b_asic/GUI/operation_icons/reg.png
new file mode 100644
index 0000000000000000000000000000000000000000..f294072fc6d3b3567d8252aee881d035aa4913eb
Binary files /dev/null and b/b_asic/GUI/operation_icons/reg.png differ
diff --git a/b_asic/GUI/operation_icons/reg_grey.png b/b_asic/GUI/operation_icons/reg_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..88af5760169b161fdd7aa5dce7c61cdb13b69231
Binary files /dev/null and b/b_asic/GUI/operation_icons/reg_grey.png differ
diff --git a/b_asic/GUI/operation_icons/sqrt.png b/b_asic/GUI/operation_icons/sqrt.png
new file mode 100644
index 0000000000000000000000000000000000000000..8160862b675680bb5fc2f31a0a5f870b73352bbb
Binary files /dev/null and b/b_asic/GUI/operation_icons/sqrt.png differ
diff --git a/b_asic/GUI/operation_icons/sqrt_grey.png b/b_asic/GUI/operation_icons/sqrt_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..4353217de9c38d665f1364a7f7c501c444232abc
Binary files /dev/null and b/b_asic/GUI/operation_icons/sqrt_grey.png differ
diff --git a/b_asic/GUI/operation_icons/sub.png b/b_asic/GUI/operation_icons/sub.png
new file mode 100644
index 0000000000000000000000000000000000000000..73db57daf9984fd1c1eb0775a1e0535b786396a8
Binary files /dev/null and b/b_asic/GUI/operation_icons/sub.png differ
diff --git a/b_asic/GUI/operation_icons/sub_grey.png b/b_asic/GUI/operation_icons/sub_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b32557a56f08e27adcd028f080d517f6ce5bdf2
Binary files /dev/null and b/b_asic/GUI/operation_icons/sub_grey.png differ
diff --git a/b_asic/GUI/port_button.py b/b_asic/GUI/port_button.py
new file mode 100644
index 0000000000000000000000000000000000000000..11c36ef5206ae4935a5bcf675a10eee22239d8dc
--- /dev/null
+++ b/b_asic/GUI/port_button.py
@@ -0,0 +1,58 @@
+
+import sys
+
+from PySide2.QtWidgets import QPushButton, QMenu
+from PySide2.QtCore import Qt, Signal
+
+class PortButton(QPushButton):
+    connectionRequested = Signal(QPushButton)
+    moved = Signal()
+    def __init__(self, name, operation, port, window, parent=None):
+        super(PortButton, self).__init__(name, operation, parent)
+        self.pressed = False
+        self._window = window
+        self.port = port
+        self.operation = operation
+        self.clicked = 0
+        self._m_drag = False
+        self._m_press = False
+
+        self.setStyleSheet("background-color: white")
+        self.connectionRequested.connect(self._window.connectButton)
+
+    def contextMenuEvent(self, event):
+        menu = QMenu()
+        menu.addAction("Connect", lambda: self.connectionRequested.emit(self))
+        menu.exec_(self.cursor().pos())
+
+    def mousePressEvent(self, event):
+        if event.button() == Qt.LeftButton:
+            self.select_port(event.modifiers())
+
+        super(PortButton, self).mousePressEvent(event)
+
+    def mouseReleaseEvent(self, event):
+        super(PortButton, self).mouseReleaseEvent(event)
+
+    def _toggle_port(self, pressed=False):
+        self.pressed = not pressed
+        self.setStyleSheet(f"background-color: {'white' if not self.pressed else 'grey'}")
+
+    def select_port(self, modifiers=None):
+        if modifiers != Qt.ControlModifier:
+            for port in self._window.pressed_ports:
+                port._toggle_port(port.pressed)
+
+            self._toggle_port(self.pressed)
+            self._window.pressed_ports = [self]
+
+        else:
+            self._toggle_port(self.pressed)
+            if self in self._window.pressed_ports:
+                self._window.pressed_ports.remove(self)
+            else:
+                self._window.pressed_ports.append(self)
+
+        for signal in self._window.signalList:
+            signal.update()
+
diff --git a/b_asic/GUI/properties_window.py b/b_asic/GUI/properties_window.py
new file mode 100644
index 0000000000000000000000000000000000000000..22fdf7d2e9329571e98e38e4aad242b6fd50733e
--- /dev/null
+++ b/b_asic/GUI/properties_window.py
@@ -0,0 +1,63 @@
+from PySide2.QtWidgets import QDialog, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout,\
+QLabel, QCheckBox
+from PySide2.QtCore import Qt
+from PySide2.QtGui import QIntValidator
+
+class PropertiesWindow(QDialog):
+    def __init__(self, operation, main_window):
+        super(PropertiesWindow, self).__init__()
+        self.operation = operation
+        self._window = main_window
+        self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
+        self.setWindowTitle("Properties")
+
+        self.name_layout = QHBoxLayout()
+        self.name_layout.setSpacing(50)
+        self.name_label = QLabel("Name:")
+        self.edit_name = QLineEdit(self.operation.operation_path_name)
+        self.name_layout.addWidget(self.name_label)
+        self.name_layout.addWidget(self.edit_name)
+
+        self.vertical_layout = QVBoxLayout()
+        self.vertical_layout.addLayout(self.name_layout)
+
+        if self.operation.operation_path_name == "c":
+            self.constant_layout = QHBoxLayout()
+            self.constant_layout.setSpacing(50)
+            self.constant_value = QLabel("Constant:")
+            self.edit_constant = QLineEdit(str(self.operation.operation.value))
+            self.only_accept_int = QIntValidator()
+            self.edit_constant.setValidator(self.only_accept_int)
+            self.constant_layout.addWidget(self.constant_value)
+            self.constant_layout.addWidget(self.edit_constant)
+            self.vertical_layout.addLayout(self.constant_layout)
+
+        self.show_name_layout = QHBoxLayout()
+        self.check_show_name = QCheckBox("Show name?")
+        if self.operation.is_show_name:
+            self.check_show_name.setChecked(1)
+        else:
+            self.check_show_name.setChecked(0)
+        self.check_show_name.setLayoutDirection(Qt.RightToLeft)
+        self.check_show_name.setStyleSheet("spacing: 170px")
+        self.show_name_layout.addWidget(self.check_show_name)
+        self.vertical_layout.addLayout(self.show_name_layout)
+
+        self.ok = QPushButton("OK")
+        self.ok.clicked.connect(self.save_properties)
+        self.vertical_layout.addWidget(self.ok)
+        self.setLayout(self.vertical_layout)
+
+    def save_properties(self):
+        self._window.logger.info(f"Saving properties of operation: {self.operation.name}.")
+        self.operation.name = self.edit_name.text()
+        self.operation.label.setPlainText(self.operation.name)
+        if self.operation.operation_path_name == "c":
+            self.operation.operation.value = self.edit_constant.text()
+        if self.check_show_name.isChecked():
+            self.operation.label.setOpacity(1)
+            self.operation.is_show_name = True
+        else:
+            self.operation.label.setOpacity(0)
+            self.operation.is_show_name = False
+        self.reject()
\ No newline at end of file
diff --git a/b_asic/GUI/show_pc_window.py b/b_asic/GUI/show_pc_window.py
new file mode 100644
index 0000000000000000000000000000000000000000..3946ba9f821caed80893c52cb27213cbe3143edf
--- /dev/null
+++ b/b_asic/GUI/show_pc_window.py
@@ -0,0 +1,49 @@
+from PySide2.QtWidgets import QDialog, QPushButton, QVBoxLayout, QCheckBox,\
+QFrame, QFormLayout
+from PySide2.QtCore import Qt, Signal
+
+from b_asic import SFG
+
+class ShowPCWindow(QDialog):
+    pc = Signal()
+
+    def __init__(self, window):
+        super(ShowPCWindow, self).__init__()
+        self._window = window
+        self.check_box_list = []
+        self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
+        self.setWindowTitle("Show PC")
+
+        self.dialog_layout = QVBoxLayout()
+        self.pc_btn = QPushButton("Show PC")
+        self.pc_btn.clicked.connect(self.show_precedence_graph)
+        self.dialog_layout.addWidget(self.pc_btn)
+        self.setLayout(self.dialog_layout)
+
+    def add_sfg_to_dialog(self):
+        sfg_layout = QVBoxLayout()
+        options_layout = QFormLayout()
+
+        for sfg in self._window.sfg_list:
+            check_box = QCheckBox()
+            options_layout.addRow(sfg.name, check_box)
+            self.check_box_list.append(check_box)
+
+        sfg_layout.addLayout(options_layout)
+
+        frame = QFrame()
+        frame.setFrameShape(QFrame.HLine)
+        frame.setFrameShadow(QFrame.Sunken)
+        self.dialog_layout.addWidget(frame)
+
+        self.dialog_layout.addLayout(sfg_layout)
+
+    def show_precedence_graph(self):
+        for i, check_box in enumerate(self.check_box_list):
+            if check_box.isChecked():
+                self._window.logger.info("Creating a precedence chart from " + self._window.sfg_list[i].name)
+                self._window.sfg_list[i].show_precedence_graph()
+                break
+        
+        self.accept()
+        self.pc.emit()
\ No newline at end of file
diff --git a/b_asic/GUI/simulate_sfg_window.py b/b_asic/GUI/simulate_sfg_window.py
new file mode 100644
index 0000000000000000000000000000000000000000..48e584dc87e045322f5d92946b431d32d7e180b5
--- /dev/null
+++ b/b_asic/GUI/simulate_sfg_window.py
@@ -0,0 +1,135 @@
+from PySide2.QtWidgets import QDialog, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout,\
+QLabel, QCheckBox, QSpinBox, QGroupBox, QFrame, QFormLayout, QGridLayout, QSizePolicy, QFileDialog, QShortcut
+from PySide2.QtCore import Qt, Signal
+from PySide2.QtGui import QIntValidator, QKeySequence
+
+from matplotlib.backends import qt_compat
+from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.figure import Figure
+
+
+class SimulateSFGWindow(QDialog):
+    simulate = Signal()
+
+    def __init__(self, window):
+        super(SimulateSFGWindow, self).__init__()
+        self._window = window
+        self.properties = dict()
+        self.sfg_to_layout = dict()
+        self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
+        self.setWindowTitle("Simulate SFG")
+
+        self.dialog_layout = QVBoxLayout()
+        self.simulate_btn = QPushButton("Simulate")
+        self.simulate_btn.clicked.connect(self.save_properties)
+        self.dialog_layout.addWidget(self.simulate_btn)
+        self.setLayout(self.dialog_layout)
+
+    def add_sfg_to_dialog(self, sfg):
+        sfg_layout = QVBoxLayout()
+        options_layout = QFormLayout()
+
+        name_label = QLabel(f"{sfg.name}")
+        sfg_layout.addWidget(name_label)
+
+        spin_box = QSpinBox()
+        spin_box.setRange(0, 2147483647)
+        options_layout.addRow("Iteration Count: ", spin_box)
+
+        check_box = QCheckBox()
+        options_layout.addRow("Plot Results: ", check_box)
+
+        check_box = QCheckBox()
+        options_layout.addRow("Get All Results: ", check_box)
+
+        input_layout = QVBoxLayout()
+        input_label = QLabel("Input Values: ")
+        input_layout.addWidget(input_label)
+        input_grid = QGridLayout()
+        x, y = 0, 0
+        for i in range(sfg.input_count):
+            if i % 2 == 0 and i > 0:
+                x += 1
+                y = 0
+
+            input_value = QLineEdit()
+            input_value.setPlaceholderText(str(i))
+            input_value.setValidator(QIntValidator())
+            input_value.setFixedWidth(50)
+            input_grid.addWidget(input_value, x, y)
+            y += 1
+
+        input_layout.addLayout(input_grid)
+        sfg_layout.addLayout(options_layout)
+        sfg_layout.addLayout(input_layout)
+
+        frame = QFrame()
+        frame.setFrameShape(QFrame.HLine)
+        frame.setFrameShadow(QFrame.Sunken)
+        self.dialog_layout.addWidget(frame)
+
+        self.sfg_to_layout[sfg] = sfg_layout
+        self.dialog_layout.addLayout(sfg_layout)
+
+    def save_properties(self):
+        for sfg, sfg_row in self.sfg_to_layout.items():
+            _option_values = sfg_row.children()[0]
+            _input_values = sfg_row.children()[1].children()[0]
+            _, spin_box, _, check_box_plot, _, check_box_all = map(lambda x: _option_values.itemAt(x).widget(), range(_option_values.count()))
+            input_values = map(lambda x: _input_values.itemAt(x).widget(), range(_input_values.count()))
+            input_values = [int(widget.text()) if widget.text() else 0 for widget in input_values]
+            self.properties[sfg] = {
+                "iteration_count": spin_box.value(),
+                "show_plot": check_box_plot.isChecked(),
+                "all_results": check_box_all.isChecked(),
+                "input_values": input_values
+            }
+
+            # If we plot we should also print the entire data, since you can't really interact with the graph.
+            if check_box_plot.isChecked():
+                self.properties[sfg]["all_results"] = True
+
+        self.accept()
+        self.simulate.emit()
+
+
+class Plot(FigureCanvas):
+    def __init__(self, simulation, sfg, window, parent=None, width=5, height=4, dpi=100):
+        self.simulation = simulation
+        self.sfg = sfg
+        self.dpi = dpi
+        self._window = window
+
+        fig = Figure(figsize=(width, height), dpi=dpi)
+        fig.suptitle(sfg.name, fontsize=20)
+        self.axes = fig.add_subplot(111)
+
+        FigureCanvas.__init__(self, fig)
+        self.setParent(parent)
+
+        FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
+        FigureCanvas.updateGeometry(self)
+        self.save_figure = QShortcut(QKeySequence("Ctrl+S"), self)
+        self.save_figure.activated.connect(self._save_plot_figure)
+        self._plot_values_sfg()
+
+    def _save_plot_figure(self):
+        self._window.logger.info(f"Saving plot of figure: {self.sfg.name}.")
+        file_choices = "PNG (*.png)|*.png"
+        path, ext = QFileDialog.getSaveFileName(self, "Save file", "", file_choices)
+        path = path.encode("utf-8")
+        if not path[-4:] == file_choices[-4:].encode("utf-8"):
+            path += file_choices[-4:].encode("utf-8")
+
+        if path:
+            self.print_figure(path.decode(), dpi=self.dpi)
+            self._window.logger.info(f"Saved plot: {self.sfg.name} to path: {path}.")
+
+    def _plot_values_sfg(self):
+        x_axis = list(range(len(self.simulation.results.keys())))
+        for _output in range(self.sfg.output_count):
+            y_axis = list()
+            for _iter in range(len(self.simulation.results.keys())):
+                y_axis.append(self.simulation.results[_iter][str(_output)])
+
+            self.axes.plot(x_axis, y_axis)
diff --git a/b_asic/GUI/utils.py b/b_asic/GUI/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..5234c6548bbbc66eb224fb9d3d3835d67a099213
--- /dev/null
+++ b/b_asic/GUI/utils.py
@@ -0,0 +1,20 @@
+from PySide2.QtWidgets import QErrorMessage
+from traceback import format_exc
+
+def handle_error(fn):
+    def wrapper(self, *args, **kwargs):
+        try:
+            return fn(self, *args, **kwargs)
+        except Exception as e:
+            self._window.logger.error(f"Unexpected error: {format_exc()}")
+            QErrorMessage(self._window).showMessage(f"Unexpected error: {format_exc()}")
+
+    return wrapper
+
+def decorate_class(decorator):
+    def decorate(cls):
+        for attr in cls.__dict__:
+            if callable(getattr(cls, attr)):
+                setattr(cls, attr, decorator(getattr(cls, attr)))
+        return cls
+    return decorate
\ No newline at end of file
diff --git a/b_asic/__init__.py b/b_asic/__init__.py
index 254aa19b7403ad0c2b3dbb7a2b3c582cc5602e9f..537b799102f54559d15bc151e281f4f84a507120 100644
--- a/b_asic/__init__.py
+++ b/b_asic/__init__.py
@@ -6,9 +6,7 @@ from _b_asic import *
 from b_asic.core_operations import *
 from b_asic.graph_component import *
 from b_asic.operation import *
-from b_asic.precedence_chart import *
 from b_asic.port import *
-from b_asic.schema import *
 from b_asic.signal_flow_graph import *
 from b_asic.signal import *
 from b_asic.simulation import *
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index 296803e3e55b7b92d85f52b91b30533ecdfbc0b6..a70c86b7d966197850604ad7a269242d364eca36 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -18,16 +18,16 @@ class Constant(AbstractOperation):
     """
 
     def __init__(self, value: Number = 0, name: Name = ""):
-        super().__init__(input_count = 0, output_count = 1, name = name)
+        super().__init__(input_count=0, output_count=1, name=name)
         self.set_param("value", value)
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "c"
 
     def evaluate(self):
         return self.param("value")
-    
+
     @property
     def value(self) -> Number:
         """Get the constant value of this operation."""
@@ -45,10 +45,11 @@ class Addition(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
+        super().__init__(input_count=2, output_count=1,
+                         name=name, input_sources=[src0, src1])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "add"
 
     def evaluate(self, a, b):
@@ -61,10 +62,11 @@ class Subtraction(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
+        super().__init__(input_count=2, output_count=1,
+                         name=name, input_sources=[src0, src1])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "sub"
 
     def evaluate(self, a, b):
@@ -77,10 +79,11 @@ class Multiplication(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
+        super().__init__(input_count=2, output_count=1,
+                         name=name, input_sources=[src0, src1])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "mul"
 
     def evaluate(self, a, b):
@@ -93,10 +96,11 @@ class Division(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
+        super().__init__(input_count=2, output_count=1,
+                         name=name, input_sources=[src0, src1])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "div"
 
     def evaluate(self, a, b):
@@ -109,10 +113,11 @@ class Min(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
+        super().__init__(input_count=2, output_count=1,
+                         name=name, input_sources=[src0, src1])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "min"
 
     def evaluate(self, a, b):
@@ -127,10 +132,11 @@ class Max(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
+        super().__init__(input_count=2, output_count=1,
+                         name=name, input_sources=[src0, src1])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "max"
 
     def evaluate(self, a, b):
@@ -145,10 +151,11 @@ class SquareRoot(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
+        super().__init__(input_count=1, output_count=1,
+                         name=name, input_sources=[src0])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "sqrt"
 
     def evaluate(self, a):
@@ -161,10 +168,11 @@ class ComplexConjugate(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
+        super().__init__(input_count=1, output_count=1,
+                         name=name, input_sources=[src0])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "conj"
 
     def evaluate(self, a):
@@ -177,10 +185,11 @@ class Absolute(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
+        super().__init__(input_count=1, output_count=1,
+                         name=name, input_sources=[src0])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "abs"
 
     def evaluate(self, a):
@@ -193,11 +202,12 @@ class ConstantMultiplication(AbstractOperation):
     """
 
     def __init__(self, value: Number = 0, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
+        super().__init__(input_count=1, output_count=1,
+                         name=name, input_sources=[src0])
         self.set_param("value", value)
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "cmul"
 
     def evaluate(self, a):
@@ -221,11 +231,27 @@ class Butterfly(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 2, output_count = 2, name = name, input_sources = [src0, src1])
+        super().__init__(input_count=2, output_count=2,
+                         name=name, input_sources=[src0, src1])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "bfly"
 
     def evaluate(self, a, b):
         return a + b, a - b
+
+class MAD(AbstractOperation):
+    """Multiply-and-add operation.
+    TODO: More info.
+    """
+
+    def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, src2: Optional[SignalSourceProvider] = None, name: Name = ""):
+        super().__init__(input_count = 3, output_count = 1, name = name, input_sources = [src0, src1, src2])
+
+    @classmethod
+    def type_name(cls) -> TypeName:
+        return "mad"
+
+    def evaluate(self, a, b, c):
+        return a * b + c
diff --git a/b_asic/graph_component.py b/b_asic/graph_component.py
index e37997016a3276f4dbde0394c44bf5c54ecbd51c..e08422a842a84d08dcab58ab03d7f581cb1bc664 100644
--- a/b_asic/graph_component.py
+++ b/b_asic/graph_component.py
@@ -20,9 +20,9 @@ class GraphComponent(ABC):
     TODO: More info.
     """
 
-    @property
+    @classmethod
     @abstractmethod
-    def type_name(self) -> TypeName:
+    def type_name(cls) -> TypeName:
         """Get the type name of this graph component"""
         raise NotImplementedError
 
@@ -105,6 +105,10 @@ class AbstractGraphComponent(GraphComponent):
         self._graph_id = ""
         self._parameters = {}
 
+    def __str__(self):
+        return f"id: {self.graph_id if self.graph_id else 'no_id'}, \tname: {self.name if self.name else 'no_name'}" + \
+            "".join((f", \t{key}: {str(param)}" for key, param in self._parameters.items()))
+
     @property
     def name(self) -> Name:
         return self._name
@@ -112,7 +116,7 @@ class AbstractGraphComponent(GraphComponent):
     @name.setter
     def name(self, name: Name) -> None:
         self._name = name
-    
+
     @property
     def graph_id(self) -> GraphID:
         return self._graph_id
@@ -136,7 +140,7 @@ class AbstractGraphComponent(GraphComponent):
         new_component.name = copy(self.name)
         new_component.graph_id = copy(self.graph_id)
         for name, value in self.params.items():
-            new_component.set_param(copy(name), deepcopy(value)) # pylint: disable=no-member
+            new_component.set_param(copy(name), deepcopy(value))  # pylint: disable=no-member
         return new_component
 
     def traverse(self) -> Generator[GraphComponent, None, None]:
@@ -149,4 +153,4 @@ class AbstractGraphComponent(GraphComponent):
             for neighbor in component.neighbors:
                 if neighbor not in visited:
                     visited.add(neighbor)
-                    fontier.append(neighbor)
\ No newline at end of file
+                    fontier.append(neighbor)
diff --git a/b_asic/operation.py b/b_asic/operation.py
index 156c580f50e5bbc014c83257e1887810ebc37ed9..5866d8b29cd8cd26c34e5b68c5c6a83c30c6b086 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -143,8 +143,8 @@ class Operation(GraphComponent, SignalSourceProvider):
 
     @abstractmethod
     def key(self, index: int, prefix: str = "") -> ResultKey:
-        """Get the key used to access the result of a certain output of this operation
-        from the results parameter passed to current_output(s) or evaluate_output(s).
+        """Get the key used to access the output of a certain output of this operation
+        from the output parameter passed to current_output(s) or evaluate_output(s).
         """
         raise NotImplementedError
 
@@ -200,6 +200,13 @@ class Operation(GraphComponent, SignalSourceProvider):
         """Truncate the value to be used as input at the given index to a certain bit length."""
         raise NotImplementedError
 
+    @abstractmethod
+    def to_sfg(self) -> "SFG":
+        """Convert the operation into its corresponding SFG.
+        If the operation is composed by multiple operations, the operation will be split.
+        """
+        raise NotImplementedError
+
 
 class AbstractOperation(Operation, AbstractGraphComponent):
     """Generic abstract operation class which most implementations will derive from.
@@ -211,53 +218,63 @@ class AbstractOperation(Operation, AbstractGraphComponent):
 
     def __init__(self, input_count: int, output_count: int, name: Name = "", input_sources: Optional[Sequence[Optional[SignalSourceProvider]]] = None):
         super().__init__(name)
-        self._input_ports = [InputPort(self, i) for i in range(input_count)] # Allocate input ports.
-        self._output_ports = [OutputPort(self, i) for i in range(output_count)] # Allocate output ports.
+
+        self._input_ports = [InputPort(self, i) for i in range(input_count)]
+        self._output_ports = [OutputPort(self, i) for i in range(output_count)]
 
         # Connect given input sources, if any.
         if input_sources is not None:
             source_count = len(input_sources)
             if source_count != input_count:
-                raise ValueError(f"Wrong number of input sources supplied to Operation (expected {input_count}, got {source_count})")
+                raise ValueError(
+                    f"Wrong number of input sources supplied to Operation (expected {input_count}, got {source_count})")
             for i, src in enumerate(input_sources):
                 if src is not None:
                     self._input_ports[i].connect(src.source)
 
     @abstractmethod
-    def evaluate(self, *inputs) -> Any: # pylint: disable=arguments-differ
+    def evaluate(self, *inputs) -> Any:  # pylint: disable=arguments-differ
         """Evaluate the operation and generate a list of output values given a list of input values."""
         raise NotImplementedError
 
     def __add__(self, src: Union[SignalSourceProvider, Number]) -> "Addition":
-        from b_asic.core_operations import Constant, Addition # Import here to avoid circular imports.
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Constant, Addition
         return Addition(self, Constant(src) if isinstance(src, Number) else src)
-    
+
     def __radd__(self, src: Union[SignalSourceProvider, Number]) -> "Addition":
-        from b_asic.core_operations import Constant, Addition # Import here to avoid circular imports.
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Constant, Addition
         return Addition(Constant(src) if isinstance(src, Number) else src, self)
 
     def __sub__(self, src: Union[SignalSourceProvider, Number]) -> "Subtraction":
-        from b_asic.core_operations import Constant, Subtraction # Import here to avoid circular imports.
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Constant, Subtraction
         return Subtraction(self, Constant(src) if isinstance(src, Number) else src)
-    
+
     def __rsub__(self, src: Union[SignalSourceProvider, Number]) -> "Subtraction":
-        from b_asic.core_operations import Constant, Subtraction # Import here to avoid circular imports.
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Constant, Subtraction
         return Subtraction(Constant(src) if isinstance(src, Number) else src, self)
 
     def __mul__(self, src: Union[SignalSourceProvider, Number]) -> "Union[Multiplication, ConstantMultiplication]":
-        from b_asic.core_operations import Multiplication, ConstantMultiplication # Import here to avoid circular imports.
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Multiplication, ConstantMultiplication
         return ConstantMultiplication(src, self) if isinstance(src, Number) else Multiplication(self, src)
-    
+
     def __rmul__(self, src: Union[SignalSourceProvider, Number]) -> "Union[Multiplication, ConstantMultiplication]":
-        from b_asic.core_operations import Multiplication, ConstantMultiplication # Import here to avoid circular imports.
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Multiplication, ConstantMultiplication
         return ConstantMultiplication(src, self) if isinstance(src, Number) else Multiplication(src, self)
 
     def __truediv__(self, src: Union[SignalSourceProvider, Number]) -> "Division":
-        from b_asic.core_operations import Constant, Division # Import here to avoid circular imports.
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Constant, Division
         return Division(self, Constant(src) if isinstance(src, Number) else src)
-    
+
     def __rtruediv__(self, src: Union[SignalSourceProvider, Number]) -> "Division":
-        from b_asic.core_operations import Constant, Division # Import here to avoid circular imports.
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Constant, Division
         return Division(Constant(src) if isinstance(src, Number) else src, self)
 
     def __lshift__(self, src: SignalSourceProvider) -> Signal:
@@ -267,6 +284,47 @@ class AbstractOperation(Operation, AbstractGraphComponent):
                 f"{self.__class__.__name__} cannot be used as a destination because it has {diff} than 1 input")
         return self.input(0).connect(src)
     
+    def __str__(self):
+        inputs_dict = dict()
+        for i, port in enumerate(self.inputs):
+            if port.signal_count == 0:
+                inputs_dict[i] = '-'
+                break
+            dict_ele = []
+            for signal in port.signals:
+                if signal.source:
+                    if signal.source.operation.graph_id:
+                        dict_ele.append(signal.source.operation.graph_id)
+                    else:
+                        dict_ele.append("no_id")
+                else:
+                    if signal.graph_id:
+                        dict_ele.append(signal.graph_id)
+                    else:
+                        dict_ele.append("no_id")
+            inputs_dict[i] = dict_ele
+
+        outputs_dict = dict()
+        for i, port in enumerate(self.outputs):
+            if port.signal_count == 0:
+                outputs_dict[i] = '-'
+                break
+            dict_ele = []
+            for signal in port.signals:
+                if signal.destination:
+                    if signal.destination.operation.graph_id:
+                        dict_ele.append(signal.destination.operation.graph_id)
+                    else:
+                        dict_ele.append("no_id")
+                else:
+                    if signal.graph_id:
+                        dict_ele.append(signal.graph_id)
+                    else:
+                        dict_ele.append("no_id")
+            outputs_dict[i] = dict_ele
+
+        return super().__str__() + f", \tinputs: {str(inputs_dict)}, \toutputs: {str(outputs_dict)}"
+
     @property
     def input_count(self) -> int:
         return len(self._input_ports)
@@ -304,7 +362,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
             for s in p.signals:
                 result.append(s)
         return result
-    
+
     def key(self, index: int, prefix: str = "") -> ResultKey:
         key = prefix
         if self.output_count != 1:
@@ -320,20 +378,24 @@ class AbstractOperation(Operation, AbstractGraphComponent):
 
     def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, delays: Optional[MutableDelayMap] = None, prefix: str = "", bits_override: Optional[int] = None, truncate: bool = True) -> Number:
         if index < 0 or index >= self.output_count:
-            raise IndexError(f"Output index out of range (expected 0-{self.output_count - 1}, got {index})")
+            raise IndexError(
+                f"Output index out of range (expected 0-{self.output_count - 1}, got {index})")
         if len(input_values) != self.input_count:
             raise ValueError(f"Wrong number of input values supplied to operation (expected {self.input_count}, got {len(input_values)})")
 
         values = self.evaluate(*(self.truncate_inputs(input_values, bits_override) if truncate else input_values))
         if isinstance(values, collections.abc.Sequence):
             if len(values) != self.output_count:
-                raise RuntimeError(f"Operation evaluated to incorrect number of outputs (expected {self.output_count}, got {len(values)})")
+                raise RuntimeError(
+                    f"Operation evaluated to incorrect number of outputs (expected {self.output_count}, got {len(values)})")
         elif isinstance(values, Number):
             if self.output_count != 1:
-                raise RuntimeError(f"Operation evaluated to incorrect number of outputs (expected {self.output_count}, got 1)")
+                raise RuntimeError(
+                    f"Operation evaluated to incorrect number of outputs (expected {self.output_count}, got 1)")
             values = (values,)
         else:
-            raise RuntimeError(f"Operation evaluated to invalid type (expected Sequence/Number, got {values.__class__.__name__})")
+            raise RuntimeError(
+                f"Operation evaluated to invalid type (expected Sequence/Number, got {values.__class__.__name__})")
 
         if results is not None:
             for i in range(self.output_count):
@@ -350,7 +412,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
         # Import here to avoid circular imports.
         from b_asic.special_operations import Input
         try:
-            result = self.evaluate([Input()] * self.input_count)
+            result = self.evaluate(*([Input()] * self.input_count))
             if isinstance(result, collections.Sequence) and all(isinstance(e, Operation) for e in result):
                 return result
             if isinstance(result, Operation):
@@ -361,15 +423,49 @@ class AbstractOperation(Operation, AbstractGraphComponent):
             pass
         return [self]
 
+    def to_sfg(self) -> "SFG":
+        # Import here to avoid circular imports.
+        from b_asic.special_operations import Input, Output
+        from b_asic.signal_flow_graph import SFG
+
+        inputs = [Input() for i in range(self.input_count)]
+
+        try:
+            last_operations = self.evaluate(*inputs)
+            if isinstance(last_operations, Operation):
+                last_operations = [last_operations]
+            outputs = [Output(o) for o in last_operations]
+        except TypeError:
+            operation_copy = self.copy_component()
+            inputs = []
+            for i in range(self.input_count):
+                _input = Input()
+                operation_copy.input(i).connect(_input)
+                inputs.append(_input)
+
+            outputs = [Output(operation_copy)]
+
+        return SFG(inputs=inputs, outputs=outputs)
+
     def inputs_required_for_output(self, output_index: int) -> Iterable[int]:
         if output_index < 0 or output_index >= self.output_count:
             raise IndexError(f"Output index out of range (expected 0-{self.output_count - 1}, got {output_index})")
-        return [i for i in range(self.input_count)] # By default, assume each output depends on all inputs.
+        return [i for i in range(self.input_count)]  # By default, assume each output depends on all inputs.
 
     @property
     def neighbors(self) -> Iterable[GraphComponent]:
         return list(self.input_signals) + list(self.output_signals)
 
+    @property
+    def preceding_operations(self) -> Iterable[Operation]:
+        """Returns an Iterable of all Operations that are connected to this Operations input ports."""
+        return [signal.source.operation for signal in self.input_signals if signal.source]
+
+    @property
+    def subsequent_operations(self) -> Iterable[Operation]:
+        """Returns an Iterable of all Operations that are connected to this Operations output ports."""
+        return [signal.destination.operation for signal in self.output_signals if signal.destination]
+
     @property
     def source(self) -> OutputPort:
         if self.output_count != 1:
@@ -377,7 +473,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
             raise TypeError(
                 f"{self.__class__.__name__} cannot be used as an input source because it has {diff} than 1 output")
         return self.output(0)
-    
+
     def truncate_input(self, index: int, value: Number, bits: int) -> Number:
         return int(value) & ((2 ** bits) - 1)
 
diff --git a/b_asic/port.py b/b_asic/port.py
index 0b77bc7cbebbd235207c8fafb6ed1a8dd382389f..24f59b8b9433450fb669a225af1019f03635a469 100644
--- a/b_asic/port.py
+++ b/b_asic/port.py
@@ -5,7 +5,7 @@ TODO: More info.
 
 from abc import ABC, abstractmethod
 from copy import copy
-from typing import NewType, Optional, List, Iterable, TYPE_CHECKING
+from typing import Optional, List, Iterable, TYPE_CHECKING
 
 from b_asic.signal import Signal
 from b_asic.graph_component import Name
@@ -128,7 +128,7 @@ class InputPort(AbstractPort):
         signal.set_destination(self)
 
     def remove_signal(self, signal: Signal) -> None:
-        assert signal is self._source_signal, "Attempted to remove already removed signal."
+        assert signal is self._source_signal, "Attempted to remove signal that is not connected."
         self._source_signal = None
         signal.remove_destination()
 
@@ -183,7 +183,7 @@ class OutputPort(AbstractPort, SignalSourceProvider):
         signal.set_source(self)
 
     def remove_signal(self, signal: Signal) -> None:
-        assert signal in self._destination_signals, "Attempted to remove already removed signal."
+        assert signal in self._destination_signals, "Attempted to remove signal that is not connected."
         self._destination_signals.remove(signal)
         signal.remove_source()
 
diff --git a/b_asic/precedence_chart.py b/b_asic/precedence_chart.py
deleted file mode 100644
index be55a123e0ab4330057c0bb62581e45195f5e5ba..0000000000000000000000000000000000000000
--- a/b_asic/precedence_chart.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""@package docstring
-B-ASIC Precedence Chart Module.
-TODO: More info.
-"""
-
-from b_asic.signal_flow_graph import SFG
-
-
-class PrecedenceChart:
-    """Precedence chart constructed from a signal flow graph.
-    TODO: More info.
-    """
-
-    sfg: SFG
-    # TODO: More members.
-
-    def __init__(self, sfg: SFG):
-        self.sfg = sfg
-        # TODO: Implement.
-
-    # TODO: More stuff.
diff --git a/b_asic/schema.py b/b_asic/schema.py
deleted file mode 100644
index e5068cdc080c5c5004c44c885ac48f52ba44c1f3..0000000000000000000000000000000000000000
--- a/b_asic/schema.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""@package docstring
-B-ASIC Schema Module.
-TODO: More info.
-"""
-
-from b_asic.precedence_chart import PrecedenceChart
-
-
-class Schema:
-    """Schema constructed from a precedence chart.
-    TODO: More info.
-    """
-
-    pc: PrecedenceChart
-    # TODO: More members.
-
-    def __init__(self, pc: PrecedenceChart):
-        self.pc = pc
-        # TODO: Implement.
-
-    # TODO: More stuff.
diff --git a/b_asic/signal.py b/b_asic/signal.py
index d322f161b1d9c1195c2f8e5beb40f0ea7244a25b..24e8cc81566b7ba5dfb961dadb137f74afe2e111 100644
--- a/b_asic/signal.py
+++ b/b_asic/signal.py
@@ -25,14 +25,14 @@ class Signal(AbstractGraphComponent):
             self.set_destination(destination)
         self.set_param("bits", bits)
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "s"
 
     @property
     def neighbors(self) -> Iterable[GraphComponent]:
         return [p.operation for p in [self.source, self.destination] if p is not None]
-    
+
     @property
     def source(self) -> Optional["OutputPort"]:
         """Return the source OutputPort of the signal."""
@@ -103,5 +103,6 @@ class Signal(AbstractGraphComponent):
     def bits(self, bits: Optional[int]) -> None:
         """Set the number of bits that operations using this signal as an input should truncate received values to.
         None = unlimited."""
-        assert bits is None or (isinstance(bits, int) and bits >= 0), "Bits must be non-negative."
-        self.set_param("bits", bits)
\ No newline at end of file
+        assert bits is None or (isinstance(bits, int)
+                                and bits >= 0), "Bits must be non-negative."
+        self.set_param("bits", bits)
diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py
index 30dbba97c10e069b2216f44c2dd3c5771207094d..acabedf2e407caeb06a508219bc7a2a39ef446b3 100644
--- a/b_asic/signal_flow_graph.py
+++ b/b_asic/signal_flow_graph.py
@@ -6,12 +6,16 @@ TODO: More info.
 from typing import List, Iterable, Sequence, Dict, Optional, DefaultDict, MutableSet, Tuple
 from numbers import Number
 from collections import defaultdict, deque
+from io import StringIO
+from queue import PriorityQueue
+import itertools
+from graphviz import Digraph
 
 from b_asic.port import SignalSourceProvider, OutputPort
-from b_asic.operation import Operation, AbstractOperation, ResultKey, DelayMap, MutableResultMap, MutableDelayMap, ResultKey
+from b_asic.operation import Operation, AbstractOperation, ResultKey, DelayMap, MutableResultMap, MutableDelayMap
 from b_asic.signal import Signal
 from b_asic.graph_component import GraphID, GraphIDNumber, GraphComponent, Name, TypeName
-from b_asic.special_operations import Input, Output
+from b_asic.special_operations import Input, Output, Delay
 
 
 DelayQueue = List[Tuple[str, ResultKey, OutputPort]]
@@ -33,7 +37,7 @@ class GraphIDGenerator:
     @property
     def id_number_offset(self) -> GraphIDNumber:
         """Get the graph id number offset of this generator."""
-        return self._next_id_number.default_factory() # pylint: disable=not-callable
+        return self._next_id_number.default_factory()  # pylint: disable=not-callable
 
 
 class SFG(AbstractOperation):
@@ -43,37 +47,43 @@ class SFG(AbstractOperation):
 
     _components_by_id: Dict[GraphID, GraphComponent]
     _components_by_name: DefaultDict[Name, List[GraphComponent]]
-    _components_ordered: List[GraphComponent]
-    _operations_ordered: List[Operation]
+    _components_dfs_order: List[GraphComponent]
+    _operations_dfs_order: List[Operation]
+    _operations_topological_order: List[Operation]
     _graph_id_generator: GraphIDGenerator
     _input_operations: List[Input]
     _output_operations: List[Output]
     _original_components_to_new: MutableSet[GraphComponent]
     _original_input_signals_to_indices: Dict[Signal, int]
     _original_output_signals_to_indices: Dict[Signal, int]
+    _precedence_list: Optional[List[List[OutputPort]]]
 
-    def __init__(self, input_signals: Optional[Sequence[Signal]] = None, output_signals: Optional[Sequence[Signal]] = None, \
-                 inputs: Optional[Sequence[Input]] = None, outputs: Optional[Sequence[Output]] = None, \
-                 id_number_offset: GraphIDNumber = 0, name: Name = "", \
+    def __init__(self, input_signals: Optional[Sequence[Signal]] = None, output_signals: Optional[Sequence[Signal]] = None,
+                 inputs: Optional[Sequence[Input]] = None, outputs: Optional[Sequence[Output]] = None,
+                 id_number_offset: GraphIDNumber = 0, name: Name = "",
                  input_sources: Optional[Sequence[Optional[SignalSourceProvider]]] = None):
+
         input_signal_count = 0 if input_signals is None else len(input_signals)
         input_operation_count = 0 if inputs is None else len(inputs)
-        output_signal_count = 0 if output_signals is None else len(output_signals)
+        output_signal_count = 0 if output_signals is None else len(
+            output_signals)
         output_operation_count = 0 if outputs is None else len(outputs)
-        super().__init__(input_count = input_signal_count + input_operation_count,
-                         output_count = output_signal_count + output_operation_count,
-                         name = name, input_sources = input_sources)
+        super().__init__(input_count=input_signal_count + input_operation_count,
+                         output_count=output_signal_count + output_operation_count,
+                         name=name, input_sources=input_sources)
 
         self._components_by_id = dict()
         self._components_by_name = defaultdict(list)
-        self._components_ordered = []
-        self._operations_ordered = []
+        self._components_dfs_order = []
+        self._operations_dfs_order = []
+        self._operations_topological_order = []
         self._graph_id_generator = GraphIDGenerator(id_number_offset)
         self._input_operations = []
         self._output_operations = []
         self._original_components_to_new = {}
         self._original_input_signals_to_indices = {}
         self._original_output_signals_to_indices = {}
+        self._precedence_list = None
 
         # Setup input signals.
         if input_signals is not None:
@@ -126,7 +136,8 @@ class SFG(AbstractOperation):
                         new_signal = self._original_components_to_new[signal]
                     else:
                         # New signal has to be created.
-                        new_signal = self._add_component_unconnected_copy(signal)
+                        new_signal = self._add_component_unconnected_copy(
+                            signal)
 
                     new_signal.set_destination(new_output_op.input(0))
                     self._original_output_signals_to_indices[signal] = output_index
@@ -141,13 +152,17 @@ class SFG(AbstractOperation):
             new_signal = self._original_components_to_new[signal]
             if new_signal.destination is None:
                 if signal.destination is None:
-                    raise ValueError(f"Input signal #{input_index} is missing destination in SFG")
+                    raise ValueError(
+                        f"Input signal #{input_index} is missing destination in SFG")
                 if signal.destination.operation not in self._original_components_to_new:
-                    self._add_operation_connected_tree_copy(signal.destination.operation)
+                    self._add_operation_connected_tree_copy(
+                        signal.destination.operation)
             elif new_signal.destination.operation in output_operations_set:
                 # Add directly connected input to output to ordered list.
-                self._components_ordered.extend([new_signal.source.operation, new_signal, new_signal.destination.operation])
-                self._operations_ordered.extend([new_signal.source.operation, new_signal.destination.operation])
+                self._components_dfs_order.extend(
+                    [new_signal.source.operation, new_signal, new_signal.destination.operation])
+                self._operations_dfs_order.extend(
+                    [new_signal.source.operation, new_signal.destination.operation])
 
         # Search the graph inwards from each output signal.
         for signal, output_index in self._original_output_signals_to_indices.items():
@@ -155,63 +170,34 @@ class SFG(AbstractOperation):
             new_signal = self._original_components_to_new[signal]
             if new_signal.source is None:
                 if signal.source is None:
-                    raise ValueError(f"Output signal #{output_index} is missing source in SFG")
+                    raise ValueError(
+                        f"Output signal #{output_index} is missing source in SFG")
                 if signal.source.operation not in self._original_components_to_new:
-                    self._add_operation_connected_tree_copy(signal.source.operation)
-
-        # Find dependencies.
+                    self._add_operation_connected_tree_copy(
+                        signal.source.operation)
 
-    
     def __str__(self) -> str:
         """Get a string representation of this SFG."""
-        output_string = ""
-        for component in self._components_ordered:
-            if isinstance(component, Operation):
-                for key, value in self._components_by_id.items():
-                    if value is component:
-                        output_string += "id: " + key + ", name: "
-                
-                if component.name != None:
-                    output_string += component.name + ", "
-                else:
-                    output_string += "-, "
-                
-                if component.type_name == "c":
-                    output_string += "value: " + str(component.value) + ", input: ["
-                else:
-                    output_string += "input: [" 
-
-                counter_input = 0
-                for input in component.inputs:
-                    counter_input += 1
-                    for signal in input.signals:
-                        for key, value in self._components_by_id.items():
-                            if value is signal:
-                                output_string += key + ", "
-                
-                if counter_input > 0:
-                    output_string = output_string[:-2]
-                output_string += "], output: ["
-                counter_output = 0
-                for output in component.outputs:
-                    counter_output += 1
-                    for signal in output.signals:
-                        for key, value in self._components_by_id.items():
-                            if value is signal:
-                                output_string += key + ", "
-                if counter_output > 0:
-                    output_string = output_string[:-2]
-                output_string += "]\n"
-
-        return output_string
+        string_io = StringIO()
+        string_io.write(super().__str__() + "\n")
+        string_io.write("Internal Operations:\n")
+        line = "-" * 100 + "\n"
+        string_io.write(line)
+
+        for operation in self.get_operations_topological_order():
+            string_io.write(str(operation) + "\n")
+
+        string_io.write(line)
+
+        return string_io.getvalue()
 
     def __call__(self, *src: Optional[SignalSourceProvider], name: Name = "") -> "SFG":
         """Get a new independent SFG instance that is identical to this SFG except without any of its external connections."""
-        return SFG(inputs = self._input_operations, outputs = self._output_operations,
-                   id_number_offset = self.id_number_offset, name = name, input_sources = src if src else None)
+        return SFG(inputs=self._input_operations, outputs=self._output_operations,
+                   id_number_offset=self.id_number_offset, name=name, input_sources=src if src else None)
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "sfg"
 
     def evaluate(self, *args):
@@ -221,9 +207,11 @@ class SFG(AbstractOperation):
 
     def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, delays: Optional[MutableDelayMap] = None, prefix: str = "", bits_override: Optional[int] = None, truncate: bool = True) -> Number:
         if index < 0 or index >= self.output_count:
-            raise IndexError(f"Output index out of range (expected 0-{self.output_count - 1}, got {index})")
+            raise IndexError(
+                f"Output index out of range (expected 0-{self.output_count - 1}, got {index})")
         if len(input_values) != self.input_count:
-            raise ValueError(f"Wrong number of inputs supplied to SFG for evaluation (expected {self.input_count}, got {len(input_values)})")
+            raise ValueError(
+                f"Wrong number of inputs supplied to SFG for evaluation (expected {self.input_count}, got {len(input_values)})")
         if results is None:
             results = {}
         if delays is None:
@@ -243,17 +231,78 @@ class SFG(AbstractOperation):
         results[self.key(index, prefix)] = value
         return value
 
+    def connect_external_signals_to_components(self) -> bool:
+        """ Connects any external signals to this SFG's internal operations. This SFG becomes unconnected to the SFG
+        it is a component off, causing it to become invalid afterwards. Returns True if succesful, False otherwise. """
+        if len(self.inputs) != len(self.input_operations):
+            raise IndexError(f"Number of inputs does not match the number of input_operations in SFG.")
+        if len(self.outputs) != len(self.output_operations):
+            raise IndexError(f"Number of outputs does not match the number of output_operations SFG.")
+        if len(self.input_signals) == 0:
+            return False
+        if len(self.output_signals) == 0:
+            return False
+
+        # For each input_signal, connect it to the corresponding operation
+        for port, input_operation in zip(self.inputs, self.input_operations):
+            dest = input_operation.output(0).signals[0].destination
+            dest.clear()
+            port.signals[0].set_destination(dest)
+        # For each output_signal, connect it to the corresponding operation
+        for port, output_operation in zip(self.outputs, self.output_operations):
+            src = output_operation.input(0).signals[0].source
+            src.clear()
+            port.signals[0].set_source(src)
+        return True
+
+    @property
+    def input_operations(self) -> Sequence[Operation]:
+        """Get the internal input operations in the same order as their respective input ports."""
+        return self._input_operations
+
+    @property
+    def output_operations(self) -> Sequence[Operation]:
+        """Get the internal output operations in the same order as their respective output ports."""
+        return self._output_operations
+
     def split(self) -> Iterable[Operation]:
         return self.operations
 
+    def to_sfg(self) -> 'SFG':
+        return self
+
     def inputs_required_for_output(self, output_index: int) -> Iterable[int]:
         if output_index < 0 or output_index >= self.output_count:
-            raise IndexError(f"Output index out of range (expected 0-{self.output_count - 1}, got {output_index})")
-        return self._inputs_required_for_source(self._output_operations[output_index].input(0).signals[0].source, set())
-    
+            raise IndexError(
+                f"Output index out of range (expected 0-{self.output_count - 1}, got {output_index})")
+
+        input_indexes_required = []
+        sfg_input_operations_to_indexes = {
+            input_op: index for index, input_op in enumerate(self._input_operations)}
+        output_op = self._output_operations[output_index]
+        queue = deque([output_op])
+        visited = set([output_op])
+        while queue:
+            op = queue.popleft()
+            if isinstance(op, Input):
+                if op in sfg_input_operations_to_indexes:
+                    input_indexes_required.append(
+                        sfg_input_operations_to_indexes[op])
+                    del sfg_input_operations_to_indexes[op]
+
+            for input_port in op.inputs:
+                for signal in input_port.signals:
+                    if signal.source is not None:
+                        new_op = signal.source.operation
+                        if new_op not in visited:
+                            queue.append(new_op)
+                            visited.add(new_op)
+
+        return input_indexes_required
+
     def copy_component(self, *args, **kwargs) -> GraphComponent:
-        return super().copy_component(*args, **kwargs, inputs = self._input_operations, outputs = self._output_operations,
-                                      id_number_offset = self.id_number_offset, name = self.name)
+        return super().copy_component(*args, **kwargs, inputs=self._input_operations, outputs=self._output_operations,
+                                      id_number_offset=self.id_number_offset, name=self.name)
 
     @property
     def id_number_offset(self) -> GraphIDNumber:
@@ -263,28 +312,35 @@ class SFG(AbstractOperation):
     @property
     def components(self) -> Iterable[GraphComponent]:
         """Get all components of this graph in depth-first order."""
-        return self._components_ordered
+        return self._components_dfs_order
 
     @property
     def operations(self) -> Iterable[Operation]:
         """Get all operations of this graph in depth-first order."""
-        return self._operations_ordered
-    
-    @property
-    def input_operations(self) -> Sequence[Operation]:
-        """Get the internal input operations in the same order as their respective input ports."""
-        return self._input_operations
-    @property
-    def output_operations(self) -> Sequence[Operation]:
-        """Get the internal output operations in the same order as their respective output ports."""
-        return self._output_operations
+        return self._operations_dfs_order
+
+    def get_components_with_type_name(self, type_name: TypeName) -> List[GraphComponent]:
+        """Get a list with all components in this graph with the specified type_name.
+
+        Keyword arguments:
+        type_name: The type_name of the desired components.
+        """
+        i = self.id_number_offset + 1
+        components = []
+        found_comp = self.find_by_id(type_name + str(i))
+        while found_comp is not None:
+            components.append(found_comp)
+            i += 1
+            found_comp = self.find_by_id(type_name + str(i))
+
+        return components
 
     def find_by_id(self, graph_id: GraphID) -> Optional[GraphComponent]:
         """Find the graph component with the specified ID.
         Returns None if the component was not found.
 
         Keyword arguments:
-        graph_id: Graph ID of the desired component(s)
+        graph_id: Graph ID of the desired component.
         """
         return self._components_by_id.get(graph_id, None)
 
@@ -297,11 +353,276 @@ class SFG(AbstractOperation):
         """
         return self._components_by_name.get(name, [])
 
+    def replace_component(self, component: Operation, _id: GraphID):
+        """Find and replace all components matching either on GraphID, Type or both.
+        Then return a new deepcopy of the sfg with the replaced component.
+
+        Arguments:
+        component: The new component(s), e.g Multiplication
+        _id: The GraphID to match the component to replace.
+        """
+
+        _sfg_copy = self()
+        _component = _sfg_copy.find_by_id(_id)
+
+        assert _component is not None and isinstance(_component, Operation), \
+            "No operation matching the criteria found"
+        assert _component.output_count == component.output_count, \
+            "The output count may not differ between the operations"
+        assert _component.input_count == component.input_count, \
+            "The input count may not differ between the operations"
+
+        for index_in, _inp in enumerate(_component.inputs):
+            for _signal in _inp.signals:
+                _signal.remove_destination()
+                _signal.set_destination(component.input(index_in))
+
+        for index_out, _out in enumerate(_component.outputs):
+            for _signal in _out.signals:
+                _signal.remove_source()
+                _signal.set_source(component.output(index_out))
+
+        # The old SFG will be deleted by Python GC
+        return _sfg_copy()
+
+    def insert_operation(self, component: Operation, output_comp_id: GraphID) -> Optional["SFG"]:
+        """Insert an operation in the SFG after a given source operation.
+        The source operation output count must match the input count of the operation as well as the output
+        Then return a new deepcopy of the sfg with the inserted component.
+
+        Arguments:
+        component: The new component, e.g Multiplication.
+        output_comp_id: The source operation GraphID to connect from.
+        """
+
+        # Preserve the original SFG by creating a copy.
+        sfg_copy = self()
+        output_comp = sfg_copy.find_by_id(output_comp_id)
+        if output_comp is None:
+            return None
+
+        assert not isinstance(output_comp, Output), \
+            "Source operation can not be an output operation."
+        assert len(output_comp.output_signals) == component.input_count, \
+            "Source operation output count does not match input count for component."
+        assert len(output_comp.output_signals) == component.output_count, \
+            "Destination operation input count does not match output for component."
+
+        for index, signal_in in enumerate(output_comp.output_signals):
+            destination = signal_in.destination
+            signal_in.set_destination(component.input(index))
+            destination.connect(component.output(index))
+
+        # Recreate the newly coupled SFG so that all attributes are correct.
+        return sfg_copy()
+
+    def remove_operation(self, operation_id: GraphID) -> "SFG":
+        """Returns a version of the SFG where the operation with the specified GraphID removed.
+        The operation has to have the same amount of input- and output ports or a ValueError will 
+        be raised. If no operation with the entered operation_id is found then returns None and does nothing."""
+        sfg_copy = self()
+        operation = sfg_copy.find_by_id(operation_id)
+        if operation is None:
+            return None
+
+        if operation.input_count != operation.output_count:
+            raise ValueError("Different number of input and output ports of operation with the specified id")
+
+        for i, outport in enumerate(operation.outputs):
+            if outport.signal_count > 0:
+                if operation.input(i).signal_count > 0 and operation.input(i).signals[0].source is not None:
+                    in_sig = operation.input(i).signals[0]
+                    source_port = in_sig.source
+                    source_port.remove_signal(in_sig)
+                    operation.input(i).remove_signal(in_sig)
+                    for out_sig in outport.signals.copy():
+                        out_sig.set_source(source_port)
+                else:
+                    for out_sig in outport.signals.copy():
+                        out_sig.remove_source()
+            else:
+                if operation.input(i).signal_count > 0:
+                    in_sig = operation.input(i).signals[0]
+                    operation.input(i).remove_signal(in_sig)
+
+        return sfg_copy()
+
+    def get_precedence_list(self) -> List[List[OutputPort]]:
+        """Returns a Precedence list of the SFG where each element in n:th the list consists
+        of elements that are executed in the n:th step. If the precedence list already has been
+        calculated for the current SFG then returns the cached version."""
+        if self._precedence_list:
+            return self._precedence_list
+
+        # Find all operations with only outputs and no inputs.
+        no_input_ops = list(filter(lambda op: op.input_count == 0, self.operations))
+        delay_ops = self.get_components_with_type_name(Delay.type_name())
+
+        # Find all first iter output ports for precedence
+        first_iter_ports = [op.output(i) for op in (no_input_ops + delay_ops) for i in range(op.output_count)]
+
+        self._precedence_list = self._traverse_for_precedence_list(first_iter_ports)
+
+        return self._precedence_list
+
+    def _traverse_for_precedence_list(self, first_iter_ports: List[OutputPort]) -> List[List[OutputPort]]:
+        # Find dependencies of output ports and input ports.
+        remaining_inports_per_operation = {op: op.input_count for op in self.operations}
+
+        # Traverse output ports for precedence
+        curr_iter_ports = first_iter_ports
+        precedence_list = []
+
+        while curr_iter_ports:
+            # Add the found ports to the current iter
+            precedence_list.append(curr_iter_ports)
+
+            next_iter_ports = []
+
+            for outport in curr_iter_ports:
+                for signal in outport.signals:
+                    new_inport = signal.destination
+                    # Don't traverse over delays.
+                    if new_inport is not None and not isinstance(new_inport.operation, Delay):
+                        new_op = new_inport.operation
+                        remaining_inports_per_operation[new_op] -= 1
+                        if remaining_inports_per_operation[new_op] == 0:
+                            next_iter_ports.extend(new_op.outputs)
+
+            curr_iter_ports = next_iter_ports
+
+        return precedence_list
+
+    def show_precedence_graph(self) -> None:
+        p_list = self.get_precedence_list()
+        pg = Digraph()
+        pg.attr(rankdir = 'LR')
+
+        # Creates nodes for each output port in the precedence list
+        for i in range(len(p_list)):
+            ports = p_list[i]
+            with pg.subgraph(name='cluster_' + str(i)) as sub:
+                sub.attr(label='N' + str(i + 1))
+                for port in ports:
+                    sub.node(port.operation.graph_id + '.' + str(port.index))
+        # Creates edges for each output port and creates nodes for each operation and edges for them as well
+        for i in range(len(p_list)):
+            ports = p_list[i]
+            for port in ports:
+                for signal in port.signals:
+                    pg.edge(port.operation.graph_id + '.' + str(port.index), signal.destination.operation.graph_id)
+                    pg.node(signal.destination.operation.graph_id, shape = 'square')
+                pg.edge(port.operation.graph_id, port.operation.graph_id + '.' + str(port.index))
+                pg.node(port.operation.graph_id, shape = 'square')
+
+        pg.view()  
+
+    def print_precedence_graph(self) -> None:
+        """Prints a representation of the SFG's precedence list to the standard out.
+        If the precedence list already has been calculated then it uses the cached version,
+        otherwise it calculates the precedence list and then prints it."""
+        precedence_list = self.get_precedence_list()
+
+        line = "-" * 120
+        out_str = StringIO()
+        out_str.write(line)
+
+        printed_ops = set()
+
+        for iter_num, iter in enumerate(precedence_list, start=1):
+            for outport_num, outport in enumerate(iter, start=1):
+                if outport not in printed_ops:
+                    # Only print once per operation, even if it has multiple outports
+                    out_str.write("\n")
+                    out_str.write(str(iter_num))
+                    out_str.write(".")
+                    out_str.write(str(outport_num))
+                    out_str.write(" \t")
+                    out_str.write(str(outport.operation))
+                    printed_ops.add(outport)
+
+            out_str.write("\n")
+            out_str.write(line)
+
+        print(out_str.getvalue())
+
+    def get_operations_topological_order(self) -> Iterable[Operation]:
+        """Returns an Iterable of the Operations in the SFG in Topological Order.
+        Feedback loops makes an absolutely correct Topological order impossible, so an 
+        approximative Topological Order is returned in such cases in this implementation."""
+        if self._operations_topological_order:
+            return self._operations_topological_order
+
+        no_inputs_queue = deque(list(filter(lambda op: op.input_count == 0, self.operations)))
+        remaining_inports_per_operation = {op: op.input_count for op in self.operations}
+
+        # Maps number of input counts to a queue of seen objects with such a size.
+        seen_with_inputs_dict = defaultdict(deque)
+        seen = set()
+        top_order = []
+
+        assert len(no_inputs_queue) > 0, "Illegal SFG state, dangling signals in SFG."
+
+        first_op = no_inputs_queue.popleft()
+        visited = set([first_op])
+        p_queue = PriorityQueue()
+        p_queue.put((-first_op.output_count, first_op))  # Negative priority as max-heap popping is wanted
+        operations_left = len(self.operations) - 1
+
+        seen_but_not_visited_count = 0
+
+        while operations_left > 0:
+            while not p_queue.empty():
+                op = p_queue.get()[1]
+
+                operations_left -= 1
+                top_order.append(op)
+                visited.add(op)
+
+                for neighbor_op in op.subsequent_operations:
+                    if neighbor_op not in visited:
+                        remaining_inports_per_operation[neighbor_op] -= 1
+                        remaining_inports = remaining_inports_per_operation[neighbor_op]
+
+                        if remaining_inports == 0:
+                            p_queue.put((-neighbor_op.output_count, neighbor_op))
+
+                        elif remaining_inports > 0:
+                            if neighbor_op in seen:
+                                seen_with_inputs_dict[remaining_inports + 1].remove(neighbor_op)
+                            else:
+                                seen.add(neighbor_op)
+                                seen_but_not_visited_count += 1
+
+                            seen_with_inputs_dict[remaining_inports].append(neighbor_op)
+
+            # Check if have to fetch Operations from somewhere else since p_queue is empty
+            if operations_left > 0:
+                # First check if can fetch from Operations with no input ports
+                if no_inputs_queue:
+                    new_op = no_inputs_queue.popleft()
+                    p_queue.put((new_op.output_count, new_op))
+
+                # Else fetch operation with lowest input count that is not zero
+                elif seen_but_not_visited_count > 0:
+                    for i in itertools.count(start=1):
+                        seen_inputs_queue = seen_with_inputs_dict[i]
+                        if seen_inputs_queue:
+                            new_op = seen_inputs_queue.popleft()
+                            p_queue.put((-new_op.output_count, new_op))
+                            seen_but_not_visited_count -= 1
+                            break
+                else:
+                    raise RuntimeError("Unallowed structure in SFG detected")
+
+        self._operations_topological_order = top_order
+        return self._operations_topological_order
+
     def _add_component_unconnected_copy(self, original_component: GraphComponent) -> GraphComponent:
         assert original_component not in self._original_components_to_new, "Tried to add duplicate SFG component"
         new_component = original_component.copy_component()
         self._original_components_to_new[original_component] = new_component
-        new_id = self._graph_id_generator.next_id(new_component.type_name)
+        new_id = self._graph_id_generator.next_id(new_component.type_name())
         new_component.graph_id = new_id
         self._components_by_id[new_id] = new_component
         self._components_by_name[new_component.name].append(new_component)
@@ -315,8 +636,8 @@ class SFG(AbstractOperation):
             new_op = None
             if original_op not in self._original_components_to_new:
                 new_op = self._add_component_unconnected_copy(original_op)
-                self._components_ordered.append(new_op)
-                self._operations_ordered.append(new_op)
+                self._components_dfs_order.append(new_op)
+                self._operations_dfs_order.append(new_op)
             else:
                 new_op = self._original_components_to_new[original_op]
 
@@ -331,29 +652,33 @@ class SFG(AbstractOperation):
                         # New signal already created during first step of constructor.
                         new_signal = self._original_components_to_new[original_signal]
                         new_signal.set_destination(new_op.input(original_input_port.index))
-                        self._components_ordered.extend([new_signal, new_signal.source.operation])
-                        self._operations_ordered.append(new_signal.source.operation)
+
+                        self._components_dfs_order.extend([new_signal, new_signal.source.operation])
+                        self._operations_dfs_order.append(new_signal.source.operation)
 
                     # Check if the signal has not been added before.
                     elif original_signal not in self._original_components_to_new:
                         if original_signal.source is None:
                             raise ValueError("Dangling signal without source in SFG")
-                        
+
                         new_signal = self._add_component_unconnected_copy(original_signal)
                         new_signal.set_destination(new_op.input(original_input_port.index))
-                        self._components_ordered.append(new_signal)
+
+                        self._components_dfs_order.append(new_signal)
 
                         original_connected_op = original_signal.source.operation
                         # Check if connected Operation has been added before.
                         if original_connected_op in self._original_components_to_new:
                             # Set source to the already added operations port.
-                            new_signal.set_source(self._original_components_to_new[original_connected_op].output(original_signal.source.index))
+                            new_signal.set_source(self._original_components_to_new[original_connected_op].output(
+                                original_signal.source.index))
                         else:
                             # Create new operation, set signal source to it.
                             new_connected_op = self._add_component_unconnected_copy(original_connected_op)
                             new_signal.set_source(new_connected_op.output(original_signal.source.index))
-                            self._components_ordered.append(new_connected_op)
-                            self._operations_ordered.append(new_connected_op)
+
+                            self._components_dfs_order.append(new_connected_op)
+                            self._operations_dfs_order.append(new_connected_op)
 
                             # Add connected operation to queue of operations to visit.
                             op_stack.append(original_connected_op)
@@ -366,8 +691,9 @@ class SFG(AbstractOperation):
                         # New signal already created during first step of constructor.
                         new_signal = self._original_components_to_new[original_signal]
                         new_signal.set_source(new_op.output(original_output_port.index))
-                        self._components_ordered.extend([new_signal, new_signal.destination.operation])
-                        self._operations_ordered.append(new_signal.destination.operation)
+
+                        self._components_dfs_order.extend([new_signal, new_signal.destination.operation])
+                        self._operations_dfs_order.append(new_signal.destination.operation)
 
                     # Check if signal has not been added before.
                     elif original_signal not in self._original_components_to_new:
@@ -376,61 +702,26 @@ class SFG(AbstractOperation):
 
                         new_signal = self._add_component_unconnected_copy(original_signal)
                         new_signal.set_source(new_op.output(original_output_port.index))
-                        self._components_ordered.append(new_signal)
+
+                        self._components_dfs_order.append(new_signal)
 
                         original_connected_op = original_signal.destination.operation
                         # Check if connected operation has been added.
                         if original_connected_op in self._original_components_to_new:
                             # Set destination to the already connected operations port.
-                            new_signal.set_destination(self._original_components_to_new[original_connected_op].input(original_signal.destination.index))
+                            new_signal.set_destination(self._original_components_to_new[original_connected_op].input(
+                                original_signal.destination.index))
                         else:
                             # Create new operation, set destination to it.
                             new_connected_op = self._add_component_unconnected_copy(original_connected_op)
                             new_signal.set_destination(new_connected_op.input(original_signal.destination.index))
-                            self._components_ordered.append(new_connected_op)
-                            self._operations_ordered.append(new_connected_op)
+
+                            self._components_dfs_order.append(new_connected_op)
+                            self._operations_dfs_order.append(new_connected_op)
 
                             # Add connected operation to the queue of operations to visit.
                             op_stack.append(original_connected_op)
 
-    def replace_component(self, component: Operation, _component: Operation = None, _id: GraphID = None):
-        """Find and replace all components matching either on GraphID, Type or both.
-        Then return a new deepcopy of the sfg with the replaced component.
-
-        Arguments:
-        component: The new component(s), e.g Multiplication
-
-        Keyword arguments:
-        _component: The specific component to replace.
-        _id: The GraphID to match the component to replace.
-        """
-
-        assert _component is not None or _id is not None, \
-            "Define either operation to replace or GraphID of operation"
-
-        if _id is not None:
-            _component = self.find_by_id(_id)
-
-        assert _component is not None and isinstance(_component, Operation), \
-            "No operation matching the criteria found"
-        assert _component.output_count == component.output_count, \
-            "The output count may not differ between the operations"
-        assert _component.input_count == component.input_count, \
-            "The input count may not differ between the operations"
-
-        for index_in, _inp in enumerate(_component.inputs):
-            for _signal in _inp.signals:
-                _signal.remove_destination()
-                _signal.set_destination(component.input(index_in))
-        
-        for index_out, _out in enumerate(_component.outputs):
-            for _signal in _out.signals:
-                _signal.remove_source()
-                _signal.set_source(component.output(index_out))
-
-        # The old SFG will be deleted by Python GC
-        return self()
-
     def _evaluate_source(self, src: OutputPort, results: MutableResultMap, delays: MutableDelayMap, prefix: str, bits_override: Optional[int], truncate: bool, deferred_delays: DelayQueue) -> Number:
         key_base = (prefix + "." + src.operation.graph_id) if prefix else src.operation.graph_id
         key = src.operation.key(src.index, key_base)
diff --git a/b_asic/simulation.py b/b_asic/simulation.py
index decc6f4da2c1feefde82d9c7430319d92dcf2d17..7829d171a38ff74c9a90b513e5a21034727e7492 100644
--- a/b_asic/simulation.py
+++ b/b_asic/simulation.py
@@ -44,7 +44,8 @@ class Simulation:
     def set_input(self, index: int, input_provider: InputProvider) -> None:
         """Set the input function used to get values for the specific input at the given index to the internal SFG."""
         if index < 0 or index >= len(self._input_functions):
-            raise IndexError(f"Input index out of range (expected 0-{len(self._input_functions) - 1}, got {index})")
+            raise IndexError(
+                f"Input index out of range (expected 0-{len(self._input_functions) - 1}, got {index})")
         if callable(input_provider):
             self._input_functions[index] = input_provider
         elif isinstance(input_provider, Number):
@@ -113,4 +114,4 @@ class Simulation:
 
     def clear_state(self) -> None:
         """Clear all current state of the simulation, except for the results and iteration."""
-        self._delays.clear()
\ No newline at end of file
+        self._delays.clear()
diff --git a/b_asic/special_operations.py b/b_asic/special_operations.py
index 32c6d022facd80c268ef14e7ff46f83a108ac8e7..8c15c55aed2ab7821467b51e84750a86743bde7a 100644
--- a/b_asic/special_operations.py
+++ b/b_asic/special_operations.py
@@ -17,13 +17,13 @@ class Input(AbstractOperation):
     """
 
     def __init__(self, name: Name = ""):
-        super().__init__(input_count = 0, output_count = 1, name = name)
+        super().__init__(input_count=0, output_count=1, name=name)
         self.set_param("value", 0)
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "in"
-    
+
     def evaluate(self):
         return self.param("value")
 
@@ -44,10 +44,11 @@ class Output(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
-        super().__init__(input_count = 1, output_count = 0, name = name, input_sources = [src0])
+        super().__init__(input_count=1, output_count=0,
+                         name=name, input_sources=[src0])
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "out"
 
     def evaluate(self, _):
@@ -60,11 +61,12 @@ class Delay(AbstractOperation):
     """
 
     def __init__(self, src0: Optional[SignalSourceProvider] = None, initial_value: Number = 0, name: Name = ""):
-        super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
+        super().__init__(input_count=1, output_count=1,
+                         name=name, input_sources=[src0])
         self.set_param("initial_value", initial_value)
 
-    @property
-    def type_name(self) -> TypeName:
+    @classmethod
+    def type_name(cls) -> TypeName:
         return "t"
 
     def evaluate(self, a):
@@ -77,10 +79,12 @@ class Delay(AbstractOperation):
     
     def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, delays: Optional[MutableDelayMap] = None, prefix: str = "", bits_override: Optional[int] = None, truncate: bool = True) -> Number:
         if index != 0:
-            raise IndexError(f"Output index out of range (expected 0-0, got {index})")
+            raise IndexError(
+                f"Output index out of range (expected 0-0, got {index})")
         if len(input_values) != 1:
-            raise ValueError(f"Wrong number of inputs supplied to SFG for evaluation (expected 1, got {len(input_values)})")
-        
+            raise ValueError(
+                f"Wrong number of inputs supplied to SFG for evaluation (expected 1, got {len(input_values)})")
+
         key = self.key(index, prefix)
         value = self.param("initial_value")
         if delays is not None:
@@ -98,4 +102,4 @@ class Delay(AbstractOperation):
     @initial_value.setter
     def initial_value(self, value: Number) -> None:
         """Set the initial value of this delay."""
-        self.set_param("initial_value", value)
\ No newline at end of file
+        self.set_param("initial_value", value)
diff --git a/legacy/simulation_oop/signal_flow_graph.cpp b/legacy/simulation_oop/signal_flow_graph.cpp
index d62ff6d33c09f04abee63659e8fcf4c0f18ac5c7..4c3763c81e8f97f22caf42a47d88c1186b9a874d 100644
--- a/legacy/simulation_oop/signal_flow_graph.cpp
+++ b/legacy/simulation_oop/signal_flow_graph.cpp
@@ -85,7 +85,7 @@ std::shared_ptr<operation> signal_flow_graph_operation::make_operation(pybind11:
 		return it->second;
 	}
 	auto const graph_id = op.attr("graph_id").cast<std::string_view>();
-	auto const type_name = op.attr("type_name").cast<std::string_view>();
+	auto const type_name = op.attr("type_name")().cast<std::string_view>();
 	auto key = (prefix.empty()) ? result_key{graph_id} : fmt::format("{}.{}", prefix, graph_id);
 	if (type_name == "c") {
 		auto const value = op.attr("value").cast<number>();
diff --git a/setup.py b/setup.py
index f9d4cf7e6a6f8c5b2895e63da824bcb71994701d..0568514756180c02b81a7b323789fb72a6e5bf26 100644
--- a/setup.py
+++ b/setup.py
@@ -37,9 +37,9 @@ class CMakeBuild(build_ext):
 
         if not os.path.exists(self.build_temp):
             os.makedirs(self.build_temp)
-        
+
         env = os.environ.copy()
-        
+
         print(f"=== Configuring {ext.name} ===")
         print(f"Temp dir: {self.build_temp}")
         print(f"Output dir: {cmake_output_dir}")
@@ -72,7 +72,9 @@ setuptools.setup(
     install_requires = [
         "pybind11>=2.3.0",
         "numpy",
-        "install_qt_binding"
+        "pyside2",
+        "graphviz",
+        "matplotlib"
     ],
     packages = ["b_asic"],
     ext_modules = [CMakeExtension("b_asic")],
diff --git a/src/simulation/compile.cpp b/src/simulation/compile.cpp
index 33f9cc0c1f5f26dc5f4b2d0b70d7cc0dbae8a163..95ab167bf73117a1d56e2c26e28aa7ff4352d421 100644
--- a/src/simulation/compile.cpp
+++ b/src/simulation/compile.cpp
@@ -139,7 +139,7 @@ private:
 		auto const pointer = op.attr("outputs")[py::int_{output_index}].ptr();
 		if (m_incomplete_outputs.count(pointer) != 0) {
 			// Make sure the output doesn't depend on its own value, unless it's a delay operation.
-			if (op.attr("type_name").cast<std::string_view>() != "t") {
+			if (op.attr("type_name")().cast<std::string_view>() != "t") {
 				throw py::value_error{"Direct feedback loop detected in simulation SFG"};
 			}
 		}
@@ -217,7 +217,7 @@ private:
 
 	void add_operation_output(py::handle op, std::size_t output_index, std::string_view prefix, sfg_info_stack const& sfg_stack,
 							  delay_queue& deferred_delays) {
-		auto const type_name = op.attr("type_name").cast<std::string_view>();
+		auto const type_name = op.attr("type_name")().cast<std::string_view>();
 		if (type_name == "out") {
 			this->add_source(op, 0, prefix, sfg_stack, deferred_delays);
 		} else if (auto const result_index = this->begin_operation_output(op, output_index, prefix)) {
diff --git a/test/fixtures/operation_tree.py b/test/fixtures/operation_tree.py
index fc8008fa4098ca488e23766f5ff7d05711300685..695979c65ab56eda3baf992b3b99963ee1fe7c9a 100644
--- a/test/fixtures/operation_tree.py
+++ b/test/fixtures/operation_tree.py
@@ -1,6 +1,6 @@
 import pytest
 
-from b_asic import Addition, Constant, Signal
+from b_asic import Addition, Constant, Signal, Butterfly
 
 
 @pytest.fixture
@@ -41,6 +41,41 @@ def large_operation_tree():
     """
     return Addition(Addition(Constant(2), Constant(3)), Addition(Constant(4), Constant(5)))
 
+@pytest.fixture
+def large_operation_tree_names():
+    """Valid addition operation connected with a large operation tree with 2 other additions and 4 constants.
+    With names.
+    2---+
+        |
+        v
+       add---+
+        ^    |
+        |    |
+    3---+    v
+            add = (2 + 3) + (4 + 5) = 14
+    4---+    ^
+        |    |
+        v    |
+       add---+
+        ^
+        |
+    5---+
+    """
+    return Addition(Addition(Constant(2, name="constant2"), Constant(3, name="constant3")), Addition(Constant(4, name="constant4"), Constant(5, name="constant5")))
+
+@pytest.fixture
+def butterfly_operation_tree():
+    """Valid butterfly operations connected to eachother with 3 butterfly operations and 2 constants as inputs and 2 outputs.
+    2 ---+       +--- (2 + 4) ---+       +--- (6 + (-2)) ---+       +--- (4 + 8) ---> out1 = 12
+         |       |               |       |                  |       |
+         v       ^               v       ^                  v       ^
+         butterfly               butterfly                  butterfly
+         ^       v               ^       v                  ^       v
+         |       |               |       |                  |       |               
+    4 ---+       +--- (2 - 4) ---+       +--- (6 - (-2)) ---+       +--- (4 - 8) ---> out2 = -4
+    """
+    return Butterfly(*(Butterfly(*(Butterfly(Constant(2), Constant(4), name="bfly3").outputs), name="bfly2").outputs), name="bfly1")
+
 @pytest.fixture
 def operation_graph_with_cycle():
     """Invalid addition operation connected with an operation graph containing a cycle.
diff --git a/test/fixtures/signal_flow_graph.py b/test/fixtures/signal_flow_graph.py
index 959fd944db5ddc12eba5fba3019bee9b2b8eb254..c18924d7f5e78e3af93a478774b500755b4c11a5 100644
--- a/test/fixtures/signal_flow_graph.py
+++ b/test/fixtures/signal_flow_graph.py
@@ -1,13 +1,13 @@
 import pytest
 
-from b_asic import SFG, Input, Output, Constant, Delay, ConstantMultiplication, AbstractOperation, Name, TypeName, SignalSourceProvider
+from b_asic import SFG, Input, Output, Constant, Delay, Addition, ConstantMultiplication, Butterfly, AbstractOperation, Name, TypeName, SignalSourceProvider
 from typing import Optional
 
 
 @pytest.fixture
 def sfg_two_inputs_two_outputs():
     """Valid SFG with two inputs and two outputs.
-	     .               .
+             .               .
     in1-------+  +--------->out1
          .    |  |       .
          .    v  |       .
@@ -18,9 +18,9 @@ def sfg_two_inputs_two_outputs():
        | .          ^    .
        | .          |    .
        +------------+    .
-	     .               .
+             .               .
     out1 = in1 + in2
-	out2 = in1 + 2 * in2
+        out2 = in1 + 2 * in2
     """
     in1 = Input()
     in2 = Input()
@@ -28,13 +28,14 @@ def sfg_two_inputs_two_outputs():
     add2 = add1 + in2
     out1 = Output(add1)
     out2 = Output(add2)
-    return SFG(inputs = [in1, in2], outputs = [out1, out2])
+    return SFG(inputs=[in1, in2], outputs=[out1, out2])
+
 
 @pytest.fixture
 def sfg_two_inputs_two_outputs_independent():
     """Valid SFG with two inputs and two outputs, where the first output only depends
     on the first input and the second output only depends on the second input.
-	     .               .
+             .               .
     in1-------------------->out1
          .               .
          .               .
@@ -45,17 +46,18 @@ def sfg_two_inputs_two_outputs_independent():
          .   |      ^    .
          .   |      |    .
          .   +------+    .
-	     .               .
+             .               .
     out1 = in1
-	out2 = in2 + 3
+        out2 = in2 + 3
     """
-    in1 = Input()
-    in2 = Input()
-    c1 = Constant(3)
-    add1 = in2 + c1
-    out1 = Output(in1)
-    out2 = Output(add1)
-    return SFG(inputs = [in1, in2], outputs = [out1, out2])
+    in1 = Input("IN1")
+    in2 = Input("IN2")
+    c1 = Constant(3, "C1")
+    add1 = Addition(in2, c1, "ADD1")
+    out1 = Output(in1, "OUT1")
+    out2 = Output(add1, "OUT2")
+    return SFG(inputs=[in1, in2], outputs=[out1, out2])
+
 
 @pytest.fixture
 def sfg_nested():
@@ -66,7 +68,7 @@ def sfg_nested():
     mac_in2 = Input()
     mac_in3 = Input()
     mac_out1 = Output(mac_in1 + mac_in2 * mac_in3)
-    MAC = SFG(inputs = [mac_in1, mac_in2, mac_in3], outputs = [mac_out1])
+    MAC = SFG(inputs=[mac_in1, mac_in2, mac_in3], outputs=[mac_out1])
 
     in1 = Input()
     in2 = Input()
@@ -74,7 +76,8 @@ def sfg_nested():
     mac2 = MAC(in1, in2, mac1)
     mac3 = MAC(in1, mac1, mac2)
     out1 = Output(mac3)
-    return SFG(inputs = [in1, in2], outputs = [out1])
+    return SFG(inputs=[in1, in2], outputs=[out1])
+
 
 @pytest.fixture
 def sfg_delay():
@@ -126,13 +129,14 @@ def sfg_simple_filter():
     in1---->add1----->t1+---->out1
          .                 .
     """
-    in1 = Input()
-    t1 = Delay()
-    cmul1 = ConstantMultiplication(0.5, t1)
-    add1 = in1 + cmul1
-    t1 << add1
-    out1 = Output(t1)
-    return SFG(inputs=[in1], outputs=[out1])
+    in1 = Input("IN1")
+    cmul1 = ConstantMultiplication(0.5, name="CMUL1")
+    add1 = Addition(in1, cmul1, "ADD1")
+    add1.input(1).signals[0].name = "S2"
+    t1 = Delay(add1, name="T1")
+    cmul1.input(0).connect(t1, "S1")
+    out1 = Output(t1, "OUT1")
+    return SFG(inputs=[in1], outputs=[out1], name="simple_filter")
 
 @pytest.fixture
 def sfg_custom_operation():
@@ -141,7 +145,7 @@ def sfg_custom_operation():
         def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
             super().__init__(input_count = 1, output_count = 2, name = name, input_sources = [src0])
         
-        @property
+        @classmethod
         def type_name(self) -> TypeName:
             return "custom"
 
@@ -152,4 +156,64 @@ def sfg_custom_operation():
     custom1 = CustomOperation(in1)
     out1 = Output(custom1.output(0))
     out2 = Output(custom1.output(1))
-    return SFG(inputs=[in1], outputs=[out1, out2])
\ No newline at end of file
+    return SFG(inputs=[in1], outputs=[out1, out2])
+
+
+@pytest.fixture
+def precedence_sfg_delays():
+    """A sfg with delays and interesting layout for precednce list generation.
+
+    IN1>--->C0>--->ADD1>--->Q1>---+--->A0>--->ADD4>--->OUT1
+                     ^            |            ^
+                     |            T1           |
+                     |            |            |
+                   ADD2<---<B1<---+--->A1>--->ADD3
+                     ^            |            ^
+                     |            T2           |
+                     |            |            |
+                     +-----<B2<---+--->A2>-----+
+    """
+    in1 = Input("IN1")
+    c0 = ConstantMultiplication(5, in1, "C0")
+    add1 = Addition(c0, None, "ADD1")
+    # Not sure what operation "Q" is supposed to be in the example
+    Q1 = ConstantMultiplication(1, add1, "Q1")
+    T1 = Delay(Q1, 0, "T1")
+    T2 = Delay(T1, 0, "T2")
+    b2 = ConstantMultiplication(2, T2, "B2")
+    b1 = ConstantMultiplication(3, T1, "B1")
+    add2 = Addition(b1, b2, "ADD2")
+    add1.input(1).connect(add2)
+    a1 = ConstantMultiplication(4, T1, "A1")
+    a2 = ConstantMultiplication(6, T2, "A2")
+    add3 = Addition(a1, a2, "ADD3")
+    a0 = ConstantMultiplication(7, Q1, "A0")
+    add4 = Addition(a0, add3, "ADD4")
+    out1 = Output(add4, "OUT1")
+
+    return SFG(inputs=[in1], outputs=[out1], name="SFG")
+
+
+@pytest.fixture
+def precedence_sfg_delays_and_constants():
+    in1 = Input("IN1")
+    c0 = ConstantMultiplication(5, in1, "C0")
+    add1 = Addition(c0, None, "ADD1")
+    # Not sure what operation "Q" is supposed to be in the example
+    Q1 = ConstantMultiplication(1, add1, "Q1")
+    T1 = Delay(Q1, 0, "T1")
+    const1 = Constant(10, "CONST1")  # Replace T2 delay with a constant
+    b2 = ConstantMultiplication(2, const1, "B2")
+    b1 = ConstantMultiplication(3, T1, "B1")
+    add2 = Addition(b1, b2, "ADD2")
+    add1.input(1).connect(add2)
+    a1 = ConstantMultiplication(4, T1, "A1")
+    a2 = ConstantMultiplication(10, const1, "A2")
+    add3 = Addition(a1, a2, "ADD3")
+    a0 = ConstantMultiplication(7, Q1, "A0")
+    # Replace ADD4 with a butterfly to test multiple output ports
+    bfly1 = Butterfly(a0, add3, "BFLY1")
+    out1 = Output(bfly1.output(0), "OUT1")
+    Output(bfly1.output(1), "OUT2")
+
+    return SFG(inputs=[in1], outputs=[out1], name="SFG")
diff --git a/test/test_abstract_operation.py b/test/test_abstract_operation.py
index 5423ecdf08c420df5dccc6393c3ad6637961172b..9163fce2a955c7fbc68d5d24de86896d251934da 100644
--- a/test/test_abstract_operation.py
+++ b/test/test_abstract_operation.py
@@ -89,4 +89,3 @@ def test_division_overload():
     assert isinstance(div3, Division)
     assert div3.input(0).signals[0].source.operation.value == 5
     assert div3.input(1).signals == div2.output(0).signals
-
diff --git a/test/test_core_operations.py b/test/test_core_operations.py
index 4d0039b558e81c5cd74f151f93f0bc0194a702d5..6a0493c60965579bd843e0b514bd7f9b9a0e4707 100644
--- a/test/test_core_operations.py
+++ b/test/test_core_operations.py
@@ -6,7 +6,6 @@ from b_asic import \
     Constant, Addition, Subtraction, Multiplication, ConstantMultiplication, Division, \
     SquareRoot, ComplexConjugate, Max, Min, Absolute, Butterfly
 
-
 class TestConstant:
     def test_constant_positive(self):
         test_operation = Constant(3)
@@ -164,3 +163,14 @@ class TestButterfly:
         test_operation = Butterfly()
         assert test_operation.evaluate_output(0, [2+1j, 3-2j]) == 5-1j
         assert test_operation.evaluate_output(1, [2+1j, 3-2j]) == -1+3j
+
+
+class TestDepends:
+    def test_depends_addition(self):
+        add1 = Addition()
+        assert set(add1.inputs_required_for_output(0)) == {0, 1}
+
+    def test_depends_butterfly(self):
+        bfly1 = Butterfly()
+        assert set(bfly1.inputs_required_for_output(0)) == {0, 1}
+        assert set(bfly1.inputs_required_for_output(1)) == {0, 1}
diff --git a/test/test_depends.py b/test/test_depends.py
deleted file mode 100644
index e26911054a9604db2f08998f6ecfccd81a012e5a..0000000000000000000000000000000000000000
--- a/test/test_depends.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from b_asic import Addition, Butterfly
-
-class TestDepends:
-    def test_depends_addition(self):
-        add1 = Addition()
-        assert set(add1.inputs_required_for_output(0)) == {0, 1}
-
-    def test_depends_butterfly(self):
-        bfly1 = Butterfly()
-        assert set(bfly1.inputs_required_for_output(0)) == {0, 1}
-        assert set(bfly1.inputs_required_for_output(1)) == {0, 1}
-
-    def test_depends_sfg(self, sfg_two_inputs_two_outputs):
-        assert set(sfg_two_inputs_two_outputs.inputs_required_for_output(0)) == {0, 1}
-        assert set(sfg_two_inputs_two_outputs.inputs_required_for_output(1)) == {0, 1}
-
-    def test_depends_sfg_independent(self, sfg_two_inputs_two_outputs_independent):
-        assert set(sfg_two_inputs_two_outputs_independent.inputs_required_for_output(0)) == {0}
-        assert set(sfg_two_inputs_two_outputs_independent.inputs_required_for_output(1)) == {1}
\ No newline at end of file
diff --git a/test/test_fast_simulation.py b/test/test_fast_simulation.py
index ece9add5891cb47cba7b63f80682bc65d970470e..6eb3b251bec6a2c556230dbedcb8f712c9f56d48 100644
--- a/test/test_fast_simulation.py
+++ b/test/test_fast_simulation.py
@@ -8,13 +8,11 @@ class TestRunFor:
     def test_with_lambdas_as_input(self, sfg_two_inputs_two_outputs):
         simulation = FastSimulation(sfg_two_inputs_two_outputs, [lambda n: n + 3, lambda n: 1 + n * 2])
 
-        output = simulation.run_for(101)
+        output = simulation.run_for(101, save_results = True)
 
         assert output[0] == 304
         assert output[1] == 505
 
-        print(simulation.results)
-
         assert simulation.results["0"][100] == 304
         assert simulation.results["1"][100] == 505
 
@@ -51,7 +49,7 @@ class TestRunFor:
         input1 = np.array([7, 3, 3,  54, 2])
         simulation = FastSimulation(sfg_two_inputs_two_outputs, [input0, input1])
 
-        output = simulation.run_for(5)
+        output = simulation.run_for(5, save_results = True)
 
         assert output[0] == 9
         assert output[1] == 11
@@ -115,7 +113,7 @@ class TestRunFor:
     def test_delay(self, sfg_delay):
         simulation = FastSimulation(sfg_delay)
         simulation.set_input(0, [5, -2, 25, -6, 7, 0])
-        simulation.run_for(6)
+        simulation.run_for(6, save_results = True)
 
         assert simulation.results["0"][0] == 0
         assert simulation.results["0"][1] == 5
@@ -178,17 +176,17 @@ class TestRun:
         assert output3[0] == 28
         assert output4[0] == 0
         assert output5[0] == 7
-        
+
     def test_simple_accumulator(self, sfg_simple_accumulator):
         data_in = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
         simulation = FastSimulation(sfg_simple_accumulator, [data_in])
         simulation.run()
         assert list(simulation.results["0"]) == [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
-
+        
     def test_simple_filter(self, sfg_simple_filter):
         input0 = np.array([1, 2, 3, 4, 5])
         simulation = FastSimulation(sfg_simple_filter, [input0])
-        simulation.run_for(len(input0))
+        simulation.run_for(len(input0), save_results = True)
         assert all(simulation.results["0"] == np.array([0, 1.0, 2.5, 4.25, 6.125]))
 
     def test_custom_operation(self, sfg_custom_operation):
@@ -197,6 +195,7 @@ class TestRun:
         assert all(simulation.results["0"] == np.array([2, 4, 6, 8, 10]))
         assert all(simulation.results["1"] == np.array([2, 4, 8, 16, 32]))
 
+
 class TestLarge:
     def test_1k_additions(self):
         prev_op = Addition(Constant(1), Constant(1))
diff --git a/test/test_operation.py b/test/test_operation.py
index b76ba16d11425c0ce868e4fa0b4c88d9f862e23f..77e9ba3cbd0eaa75886b5a7e5d11f00f6cfeb479 100644
--- a/test/test_operation.py
+++ b/test/test_operation.py
@@ -1,6 +1,6 @@
 import pytest
 
-from b_asic import Constant, Addition
+from b_asic import Constant, Addition, MAD, Butterfly, SquareRoot
 
 class TestTraverse:
     def test_traverse_single_tree(self, operation):
@@ -22,4 +22,32 @@ class TestTraverse:
         assert len(list(filter(lambda type_: isinstance(type_, Constant), result))) == 4
 
     def test_traverse_loop(self, operation_graph_with_cycle):
-        assert len(list(operation_graph_with_cycle.traverse())) == 8
\ No newline at end of file
+        assert len(list(operation_graph_with_cycle.traverse())) == 8
+
+class TestToSfg:
+    def test_convert_mad_to_sfg(self):
+        mad1 = MAD()
+        mad1_sfg = mad1.to_sfg()
+
+        assert mad1.evaluate(1,1,1) == mad1_sfg.evaluate(1,1,1)
+        assert len(mad1_sfg.operations) == 6
+
+    def test_butterfly_to_sfg(self):
+        but1 = Butterfly()
+        but1_sfg = but1.to_sfg()
+
+        assert but1.evaluate(1,1)[0] == but1_sfg.evaluate(1,1)[0]
+        assert but1.evaluate(1,1)[1] == but1_sfg.evaluate(1,1)[1]
+        assert len(but1_sfg.operations) == 8
+
+    def test_add_to_sfg(self):
+        add1 = Addition()
+        add1_sfg = add1.to_sfg()
+
+        assert len(add1_sfg.operations) == 4
+
+    def test_sqrt_to_sfg(self):
+        sqrt1 = SquareRoot()
+        sqrt1_sfg = sqrt1.to_sfg()
+
+        assert len(sqrt1_sfg.operations) == 3
diff --git a/test/test_sfg.py b/test/test_sfg.py
index 9e6c07cf98b49d24c73995080fbaccd225574041..e93427bac9fca6b2f3dd71d70a0c5cbf0ebec56f 100644
--- a/test/test_sfg.py
+++ b/test/test_sfg.py
@@ -1,6 +1,10 @@
 import pytest
+import io
+import sys
 
-from b_asic import SFG, Signal, Input, Output, Constant, Addition, Multiplication
+
+from b_asic import SFG, Signal, Input, Output, Constant, ConstantMultiplication, Addition, Multiplication, Delay, \
+    Butterfly, Subtraction, SquareRoot
 
 
 class TestInit:
@@ -9,7 +13,7 @@ class TestInit:
         out1 = Output(None, "OUT1")
         out1.input(0).connect(in1, "S1")
 
-        sfg = SFG(inputs = [in1], outputs = [out1]) # in1 ---s1---> out1
+        sfg = SFG(inputs=[in1], outputs=[out1])  # in1 ---s1---> out1
 
         assert len(list(sfg.components)) == 3
         assert len(list(sfg.operations)) == 2
@@ -22,7 +26,8 @@ class TestInit:
 
         s1 = add2.input(0).connect(add1, "S1")
 
-        sfg = SFG(input_signals = [s1], output_signals = [s1]) # in1 ---s1---> out1
+        # in1 ---s1---> out1
+        sfg = SFG(input_signals=[s1], output_signals=[s1])
 
         assert len(list(sfg.components)) == 3
         assert len(list(sfg.operations)) == 2
@@ -30,7 +35,7 @@ class TestInit:
         assert sfg.output_count == 1
 
     def test_outputs_construction(self, operation_tree):
-        sfg = SFG(outputs = [Output(operation_tree)])
+        sfg = SFG(outputs=[Output(operation_tree)])
 
         assert len(list(sfg.components)) == 7
         assert len(list(sfg.operations)) == 4
@@ -38,26 +43,31 @@ class TestInit:
         assert sfg.output_count == 1
 
     def test_signals_construction(self, operation_tree):
-        sfg = SFG(output_signals = [Signal(source = operation_tree.output(0))])
+        sfg = SFG(output_signals=[Signal(source=operation_tree.output(0))])
 
         assert len(list(sfg.components)) == 7
         assert len(list(sfg.operations)) == 4
         assert sfg.input_count == 0
         assert sfg.output_count == 1
 
+
 class TestPrintSfg:
     def test_one_addition(self):
         inp1 = Input("INP1")
         inp2 = Input("INP2")
         add1 = Addition(inp1, inp2, "ADD1")
         out1 = Output(add1, "OUT1")
-        sfg = SFG(inputs=[inp1, inp2], outputs=[out1], name="sf1")
+        sfg = SFG(inputs=[inp1, inp2], outputs=[out1], name="SFG1")
 
         assert sfg.__str__() == \
-            "id: add1, name: ADD1, input: [s1, s2], output: [s3]\n" + \
-            "id: in1, name: INP1, input: [], output: [s1]\n" + \
-            "id: in2, name: INP2, input: [], output: [s2]\n" + \
-            "id: out1, name: OUT1, input: [s3], output: []\n"
+            "id: no_id, \tname: SFG1, \tinputs: {0: '-'}, \toutputs: {0: '-'}\n" + \
+            "Internal Operations:\n" + \
+            "----------------------------------------------------------------------------------------------------\n" + \
+            str(sfg.find_by_name("INP1")[0]) + "\n" + \
+            str(sfg.find_by_name("INP2")[0]) + "\n" + \
+            str(sfg.find_by_name("ADD1")[0]) + "\n" + \
+            str(sfg.find_by_name("OUT1")[0]) + "\n" + \
+            "----------------------------------------------------------------------------------------------------\n"
 
     def test_add_mul(self):
         inp1 = Input("INP1")
@@ -69,12 +79,16 @@ class TestPrintSfg:
         sfg = SFG(inputs=[inp1, inp2, inp3], outputs=[out1], name="mac_sfg")
 
         assert sfg.__str__() == \
-            "id: add1, name: ADD1, input: [s1, s2], output: [s5]\n" + \
-            "id: in1, name: INP1, input: [], output: [s1]\n" + \
-            "id: in2, name: INP2, input: [], output: [s2]\n" + \
-            "id: mul1, name: MUL1, input: [s5, s3], output: [s4]\n" + \
-            "id: in3, name: INP3, input: [], output: [s3]\n" + \
-            "id: out1, name: OUT1, input: [s4], output: []\n"
+            "id: no_id, \tname: mac_sfg, \tinputs: {0: '-'}, \toutputs: {0: '-'}\n" + \
+            "Internal Operations:\n" + \
+            "----------------------------------------------------------------------------------------------------\n" + \
+            str(sfg.find_by_name("INP1")[0]) + "\n" + \
+            str(sfg.find_by_name("INP2")[0]) + "\n" + \
+            str(sfg.find_by_name("ADD1")[0]) + "\n" + \
+            str(sfg.find_by_name("INP3")[0]) + "\n" + \
+            str(sfg.find_by_name("MUL1")[0]) + "\n" + \
+            str(sfg.find_by_name("OUT1")[0]) + "\n" + \
+            "----------------------------------------------------------------------------------------------------\n"
 
     def test_constant(self):
         inp1 = Input("INP1")
@@ -85,18 +99,27 @@ class TestPrintSfg:
         sfg = SFG(inputs=[inp1], outputs=[out1], name="sfg")
 
         assert sfg.__str__() == \
-            "id: add1, name: ADD1, input: [s3, s1], output: [s2]\n" + \
-            "id: c1, name: CONST, value: 3, input: [], output: [s3]\n" + \
-            "id: in1, name: INP1, input: [], output: [s1]\n" + \
-            "id: out1, name: OUT1, input: [s2], output: []\n"
+            "id: no_id, \tname: sfg, \tinputs: {0: '-'}, \toutputs: {0: '-'}\n" + \
+            "Internal Operations:\n" + \
+            "----------------------------------------------------------------------------------------------------\n" + \
+            str(sfg.find_by_name("CONST")[0]) + "\n" + \
+            str(sfg.find_by_name("INP1")[0]) + "\n" + \
+            str(sfg.find_by_name("ADD1")[0]) + "\n" + \
+            str(sfg.find_by_name("OUT1")[0]) + "\n" + \
+            "----------------------------------------------------------------------------------------------------\n"
 
     def test_simple_filter(self, sfg_simple_filter):
-         assert sfg_simple_filter.__str__() == \
-            'id: add1, name: , input: [s1, s3], output: [s4]\n' + \
-            'id: in1, name: , input: [], output: [s1]\n' + \
-            'id: cmul1, name: , input: [s5], output: [s3]\n' + \
-            'id: t1, name: , input: [s4], output: [s5, s2]\n' + \
-            'id: out1, name: , input: [s2], output: []\n'
+        assert sfg_simple_filter.__str__() == \
+            "id: no_id, \tname: simple_filter, \tinputs: {0: '-'}, \toutputs: {0: '-'}\n" + \
+            "Internal Operations:\n" + \
+            "----------------------------------------------------------------------------------------------------\n" + \
+            str(sfg_simple_filter.find_by_name("IN1")[0]) + "\n" + \
+            str(sfg_simple_filter.find_by_name("ADD1")[0]) + "\n" + \
+            str(sfg_simple_filter.find_by_name("T1")[0]) + "\n" + \
+            str(sfg_simple_filter.find_by_name("CMUL1")[0]) + "\n" + \
+            str(sfg_simple_filter.find_by_name("OUT1")[0]) + "\n" + \
+            "----------------------------------------------------------------------------------------------------\n"
+
 
 class TestDeepCopy:
     def test_deep_copy_no_duplicates(self):
@@ -107,7 +130,7 @@ class TestDeepCopy:
         mul1 = Multiplication(add1, inp3, "MUL1")
         out1 = Output(mul1, "OUT1")
 
-        mac_sfg = SFG(inputs = [inp1, inp2], outputs = [out1], name = "mac_sfg")
+        mac_sfg = SFG(inputs=[inp1, inp2], outputs=[out1], name="mac_sfg")
         mac_sfg_new = mac_sfg()
 
         assert mac_sfg.name == "mac_sfg"
@@ -134,8 +157,9 @@ class TestDeepCopy:
         mul1.input(1).connect(add2, "S6")
         out1.input(0).connect(mul1, "S7")
 
-        mac_sfg = SFG(inputs = [inp1, inp2], outputs = [out1], id_number_offset = 100, name = "mac_sfg")
-        mac_sfg_new = mac_sfg(name = "mac_sfg2")
+        mac_sfg = SFG(inputs=[inp1, inp2], outputs=[out1],
+                      id_number_offset=100, name="mac_sfg")
+        mac_sfg_new = mac_sfg(name="mac_sfg2")
 
         assert mac_sfg.name == "mac_sfg"
         assert mac_sfg_new.name == "mac_sfg2"
@@ -145,7 +169,7 @@ class TestDeepCopy:
         for g_id, component in mac_sfg._components_by_id.items():
             component_copy = mac_sfg_new.find_by_id(g_id)
             assert component.name == component_copy.name
-    
+
     def test_deep_copy_with_new_sources(self):
         inp1 = Input("INP1")
         inp2 = Input("INP2")
@@ -154,7 +178,7 @@ class TestDeepCopy:
         mul1 = Multiplication(add1, inp3, "MUL1")
         out1 = Output(mul1, "OUT1")
 
-        mac_sfg = SFG(inputs = [inp1, inp2], outputs = [out1], name = "mac_sfg")
+        mac_sfg = SFG(inputs=[inp1, inp2], outputs=[out1], name="mac_sfg")
 
         a = Addition(Constant(3), Constant(5))
         b = Constant(2)
@@ -162,20 +186,22 @@ class TestDeepCopy:
         assert mac_sfg_new.input(0).signals[0].source.operation is a
         assert mac_sfg_new.input(1).signals[0].source.operation is b
 
+
 class TestEvaluateOutput:
     def test_evaluate_output(self, operation_tree):
-        sfg = SFG(outputs = [Output(operation_tree)])
+        sfg = SFG(outputs=[Output(operation_tree)])
         assert sfg.evaluate_output(0, []) == 5
 
     def test_evaluate_output_large(self, large_operation_tree):
-        sfg = SFG(outputs = [Output(large_operation_tree)])
+        sfg = SFG(outputs=[Output(large_operation_tree)])
         assert sfg.evaluate_output(0, []) == 14
 
     def test_evaluate_output_cycle(self, operation_graph_with_cycle):
-        sfg = SFG(outputs = [Output(operation_graph_with_cycle)])
+        sfg = SFG(outputs=[Output(operation_graph_with_cycle)])
         with pytest.raises(Exception):
             sfg.evaluate_output(0, [])
 
+
 class TestComponents:
     def test_advanced_components(self):
         inp1 = Input("INP1")
@@ -194,9 +220,10 @@ class TestComponents:
         mul1.input(1).connect(add2, "S6")
         out1.input(0).connect(mul1, "S7")
 
-        mac_sfg = SFG(inputs = [inp1, inp2], outputs = [out1], name = "mac_sfg")
+        mac_sfg = SFG(inputs=[inp1, inp2], outputs=[out1], name="mac_sfg")
 
-        assert set([comp.name for comp in mac_sfg.components]) == {"INP1", "INP2", "INP3", "ADD1", "ADD2", "MUL1", "OUT1", "S1", "S2", "S3", "S4", "S5", "S6", "S7"}
+        assert set([comp.name for comp in mac_sfg.components]) == {
+            "INP1", "INP2", "INP3", "ADD1", "ADD2", "MUL1", "OUT1", "S1", "S2", "S3", "S4", "S5", "S6", "S7"}
 
 
 class TestReplaceComponents:
@@ -204,16 +231,8 @@ class TestReplaceComponents:
         sfg = SFG(outputs=[Output(operation_tree)])
         component_id = "add1"
 
-        sfg = sfg.replace_component(Multiplication(name="Multi"), _id=component_id)
-        assert component_id not in sfg._components_by_id.keys()
-        assert "Multi" in sfg._components_by_name.keys()
-
-    def test_replace_addition_by_component(self, operation_tree):
-        sfg = SFG(outputs=[Output(operation_tree)])
-        component_id = "add1"
-        component = sfg.find_by_id(component_id)
-
-        sfg = sfg.replace_component(Multiplication(name="Multi"), _component=component)
+        sfg = sfg.replace_component(
+            Multiplication(name="Multi"), _id=component_id)
         assert component_id not in sfg._components_by_id.keys()
         assert "Multi" in sfg._components_by_name.keys()
 
@@ -221,15 +240,16 @@ class TestReplaceComponents:
         sfg = SFG(outputs=[Output(large_operation_tree)])
         component_id = "add3"
 
-        sfg = sfg.replace_component(Multiplication(name="Multi"), _id=component_id)
+        sfg = sfg.replace_component(
+            Multiplication(name="Multi"), _id=component_id)
         assert "Multi" in sfg._components_by_name.keys()
         assert component_id not in sfg._components_by_id.keys()
-    
+
     def test_replace_no_input_component(self, operation_tree):
         sfg = SFG(outputs=[Output(operation_tree)])
         component_id = "c1"
         _const = sfg.find_by_id(component_id)
-        
+
         sfg = sfg.replace_component(Constant(1), _id=component_id)
         assert _const is not sfg.find_by_id(component_id)
 
@@ -238,7 +258,8 @@ class TestReplaceComponents:
         component_id = "addd1"
 
         try:
-            sfg = sfg.replace_component(Multiplication(name="Multi"), _id=component_id)
+            sfg = sfg.replace_component(
+                Multiplication(name="Multi"), _id=component_id)
         except AssertionError:
             assert True
         else:
@@ -249,8 +270,508 @@ class TestReplaceComponents:
         component_id = "c1"
 
         try:
-            sfg = sfg.replace_component(Multiplication(name="Multi"), _id=component_id)
+            sfg = sfg.replace_component(
+                Multiplication(name="Multi"), _id=component_id)
         except AssertionError:
             assert True
         else:
             assert False
+
+
+class TestInsertComponent:
+
+    def test_insert_component_in_sfg(self, large_operation_tree_names):
+        sfg = SFG(outputs=[Output(large_operation_tree_names)])
+        sqrt = SquareRoot()
+
+        _sfg = sfg.insert_operation(sqrt, sfg.find_by_name("constant4")[0].graph_id)
+        assert _sfg.evaluate() != sfg.evaluate()
+
+        assert any([isinstance(comp, SquareRoot) for comp in _sfg.operations])
+        assert not any([isinstance(comp, SquareRoot) for comp in sfg.operations])
+
+        assert not isinstance(sfg.find_by_name("constant4")[0].output(0).signals[0].destination.operation, SquareRoot)
+        assert isinstance(_sfg.find_by_name("constant4")[0].output(0).signals[0].destination.operation, SquareRoot)
+
+        assert sfg.find_by_name("constant4")[0].output(0).signals[0].destination.operation is sfg.find_by_id("add3")
+        assert _sfg.find_by_name("constant4")[0].output(
+            0).signals[0].destination.operation is not _sfg.find_by_id("add3")
+        assert _sfg.find_by_id("sqrt1").output(0).signals[0].destination.operation is _sfg.find_by_id("add3")
+
+    def test_insert_invalid_component_in_sfg(self, large_operation_tree):
+        sfg = SFG(outputs=[Output(large_operation_tree)])
+
+        # Should raise an exception for not matching input count to output count.
+        add4 = Addition()
+        with pytest.raises(Exception):
+            sfg.insert_operation(add4, "c1")
+
+    def test_insert_at_output(self, large_operation_tree):
+        sfg = SFG(outputs=[Output(large_operation_tree)])
+
+        # Should raise an exception for trying to insert an operation after an output.
+        sqrt = SquareRoot()
+        with pytest.raises(Exception):
+            _sfg = sfg.insert_operation(sqrt, "out1")
+
+    def test_insert_multiple_output_ports(self, butterfly_operation_tree):
+        sfg = SFG(outputs=list(map(Output, butterfly_operation_tree.outputs)))
+        _sfg = sfg.insert_operation(Butterfly(name="n_bfly"), "bfly3")
+
+        assert sfg.evaluate() != _sfg.evaluate()
+
+        assert len(sfg.find_by_name("n_bfly")) == 0
+        assert len(_sfg.find_by_name("n_bfly")) == 1
+
+        # Correctly connected old output -> new input
+        assert _sfg.find_by_name("bfly3")[0].output(
+            0).signals[0].destination.operation is _sfg.find_by_name("n_bfly")[0]
+        assert _sfg.find_by_name("bfly3")[0].output(
+            1).signals[0].destination.operation is _sfg.find_by_name("n_bfly")[0]
+
+        # Correctly connected new input -> old output
+        assert _sfg.find_by_name("n_bfly")[0].input(0).signals[0].source.operation is _sfg.find_by_name("bfly3")[0]
+        assert _sfg.find_by_name("n_bfly")[0].input(1).signals[0].source.operation is _sfg.find_by_name("bfly3")[0]
+
+        # Correctly connected new output -> next input
+        assert _sfg.find_by_name("n_bfly")[0].output(
+            0).signals[0].destination.operation is _sfg.find_by_name("bfly2")[0]
+        assert _sfg.find_by_name("n_bfly")[0].output(
+            1).signals[0].destination.operation is _sfg.find_by_name("bfly2")[0]
+
+        # Correctly connected next input -> new output
+        assert _sfg.find_by_name("bfly2")[0].input(0).signals[0].source.operation is _sfg.find_by_name("n_bfly")[0]
+        assert _sfg.find_by_name("bfly2")[0].input(1).signals[0].source.operation is _sfg.find_by_name("n_bfly")[0]
+
+
+class TestFindComponentsWithTypeName:
+    def test_mac_components(self):
+        inp1 = Input("INP1")
+        inp2 = Input("INP2")
+        inp3 = Input("INP3")
+        add1 = Addition(None, None, "ADD1")
+        add2 = Addition(None, None, "ADD2")
+        mul1 = Multiplication(None, None, "MUL1")
+        out1 = Output(None, "OUT1")
+
+        add1.input(0).connect(inp1, "S1")
+        add1.input(1).connect(inp2, "S2")
+        add2.input(0).connect(add1, "S4")
+        add2.input(1).connect(inp3, "S3")
+        mul1.input(0).connect(add1, "S5")
+        mul1.input(1).connect(add2, "S6")
+        out1.input(0).connect(mul1, "S7")
+
+        mac_sfg = SFG(inputs=[inp1, inp2], outputs=[out1], name="mac_sfg")
+
+        assert {comp.name for comp in mac_sfg.get_components_with_type_name(
+            inp1.type_name())} == {"INP1", "INP2", "INP3"}
+
+        assert {comp.name for comp in mac_sfg.get_components_with_type_name(
+            add1.type_name())} == {"ADD1", "ADD2"}
+
+        assert {comp.name for comp in mac_sfg.get_components_with_type_name(
+            mul1.type_name())} == {"MUL1"}
+
+        assert {comp.name for comp in mac_sfg.get_components_with_type_name(
+            out1.type_name())} == {"OUT1"}
+
+        assert {comp.name for comp in mac_sfg.get_components_with_type_name(
+            Signal.type_name())} == {"S1", "S2", "S3", "S4", "S5", "S6", "S7"}
+
+
+class TestGetPrecedenceList:
+
+    def test_inputs_delays(self, precedence_sfg_delays):
+
+        precedence_list = precedence_sfg_delays.get_precedence_list()
+
+        assert len(precedence_list) == 7
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[0]]) == {"IN1", "T1", "T2"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[1]]) == {"C0", "B1", "B2", "A1", "A2"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[2]]) == {"ADD2", "ADD3"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[3]]) == {"ADD1"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[4]]) == {"Q1"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[5]]) == {"A0"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[6]]) == {"ADD4"}
+
+    def test_inputs_constants_delays_multiple_outputs(self, precedence_sfg_delays_and_constants):
+
+        precedence_list = precedence_sfg_delays_and_constants.get_precedence_list()
+
+        assert len(precedence_list) == 7
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[0]]) == {"IN1", "T1", "CONST1"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[1]]) == {"C0", "B1", "B2", "A1", "A2"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[2]]) == {"ADD2", "ADD3"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[3]]) == {"ADD1"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[4]]) == {"Q1"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[5]]) == {"A0"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[6]]) == {"BFLY1.0", "BFLY1.1"}
+
+    def test_precedence_multiple_outputs_same_precedence(self, sfg_two_inputs_two_outputs):
+        sfg_two_inputs_two_outputs.name = "NESTED_SFG"
+
+        in1 = Input("IN1")
+        sfg_two_inputs_two_outputs.input(0).connect(in1, "S1")
+        in2 = Input("IN2")
+        cmul1 = ConstantMultiplication(10, None, "CMUL1")
+        cmul1.input(0).connect(in2, "S2")
+        sfg_two_inputs_two_outputs.input(1).connect(cmul1, "S3")
+
+        out1 = Output(sfg_two_inputs_two_outputs.output(0), "OUT1")
+        out2 = Output(sfg_two_inputs_two_outputs.output(1), "OUT2")
+
+        sfg = SFG(inputs=[in1, in2], outputs=[out1, out2])
+
+        precedence_list = sfg.get_precedence_list()
+
+        assert len(precedence_list) == 3
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[0]]) == {"IN1", "IN2"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[1]]) == {"CMUL1"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[2]]) == {"NESTED_SFG.0", "NESTED_SFG.1"}
+
+    def test_precedence_sfg_multiple_outputs_different_precedences(self, sfg_two_inputs_two_outputs_independent):
+        sfg_two_inputs_two_outputs_independent.name = "NESTED_SFG"
+
+        in1 = Input("IN1")
+        in2 = Input("IN2")
+        sfg_two_inputs_two_outputs_independent.input(0).connect(in1, "S1")
+        cmul1 = ConstantMultiplication(10, None, "CMUL1")
+        cmul1.input(0).connect(in2, "S2")
+        sfg_two_inputs_two_outputs_independent.input(1).connect(cmul1, "S3")
+        out1 = Output(sfg_two_inputs_two_outputs_independent.output(0), "OUT1")
+        out2 = Output(sfg_two_inputs_two_outputs_independent.output(1), "OUT2")
+
+        sfg = SFG(inputs=[in1, in2], outputs=[out1, out2])
+
+        precedence_list = sfg.get_precedence_list()
+
+        assert len(precedence_list) == 3
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[0]]) == {"IN1", "IN2"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[1]]) == {"CMUL1"}
+
+        assert set([port.operation.key(port.index, port.operation.name)
+                    for port in precedence_list[2]]) == {"NESTED_SFG.0", "NESTED_SFG.1"}
+
+
+class TestPrintPrecedence:
+    def test_delays(self, precedence_sfg_delays):
+        sfg = precedence_sfg_delays
+
+        captured_output = io.StringIO()
+        sys.stdout = captured_output
+
+        sfg.print_precedence_graph()
+
+        sys.stdout = sys.__stdout__
+
+        captured_output = captured_output.getvalue()
+
+        assert captured_output == \
+            "-" * 120 + "\n" + \
+            "1.1 \t" + str(sfg.find_by_name("IN1")[0]) + "\n" + \
+            "1.2 \t" + str(sfg.find_by_name("T1")[0]) + "\n" + \
+            "1.3 \t" + str(sfg.find_by_name("T2")[0]) + "\n" + \
+            "-" * 120 + "\n" + \
+            "2.1 \t" + str(sfg.find_by_name("C0")[0]) + "\n" + \
+            "2.2 \t" + str(sfg.find_by_name("A1")[0]) + "\n" + \
+            "2.3 \t" + str(sfg.find_by_name("B1")[0]) + "\n" + \
+            "2.4 \t" + str(sfg.find_by_name("A2")[0]) + "\n" + \
+            "2.5 \t" + str(sfg.find_by_name("B2")[0]) + "\n" + \
+            "-" * 120 + "\n" + \
+            "3.1 \t" + str(sfg.find_by_name("ADD3")[0]) + "\n" + \
+            "3.2 \t" + str(sfg.find_by_name("ADD2")[0]) + "\n" + \
+            "-" * 120 + "\n" + \
+            "4.1 \t" + str(sfg.find_by_name("ADD1")[0]) + "\n" + \
+            "-" * 120 + "\n" + \
+            "5.1 \t" + str(sfg.find_by_name("Q1")[0]) + "\n" + \
+            "-" * 120 + "\n" + \
+            "6.1 \t" + str(sfg.find_by_name("A0")[0]) + "\n" + \
+            "-" * 120 + "\n" + \
+            "7.1 \t" + str(sfg.find_by_name("ADD4")[0]) + "\n" + \
+            "-" * 120 + "\n"
+
+
+class TestDepends:
+    def test_depends_sfg(self, sfg_two_inputs_two_outputs):
+        assert set(sfg_two_inputs_two_outputs.inputs_required_for_output(0)) == {
+            0, 1}
+        assert set(sfg_two_inputs_two_outputs.inputs_required_for_output(1)) == {
+            0, 1}
+
+    def test_depends_sfg_independent(self, sfg_two_inputs_two_outputs_independent):
+        assert set(
+            sfg_two_inputs_two_outputs_independent.inputs_required_for_output(0)) == {0}
+        assert set(
+            sfg_two_inputs_two_outputs_independent.inputs_required_for_output(1)) == {1}
+
+
+class TestConnectExternalSignalsToComponentsSoloComp:
+
+    def test_connect_external_signals_to_components_mac(self):
+        """ Replace a MAC with inner components in an SFG """
+        inp1 = Input("INP1")
+        inp2 = Input("INP2")
+        inp3 = Input("INP3")
+        add1 = Addition(None, None, "ADD1")
+        add2 = Addition(None, None, "ADD2")
+        mul1 = Multiplication(None, None, "MUL1")
+        out1 = Output(None, "OUT1")
+
+        add1.input(0).connect(inp1, "S1")
+        add1.input(1).connect(inp2, "S2")
+        add2.input(0).connect(add1, "S3")
+        add2.input(1).connect(inp3, "S4")
+        mul1.input(0).connect(add1, "S5")
+        mul1.input(1).connect(add2, "S6")
+        out1.input(0).connect(mul1, "S7")
+
+        mac_sfg = SFG(inputs=[inp1, inp2], outputs=[out1])
+
+        inp4 = Input("INP4")
+        inp5 = Input("INP5")
+        out2 = Output(None, "OUT2")
+
+        mac_sfg.input(0).connect(inp4, "S8")
+        mac_sfg.input(1).connect(inp5, "S9")
+        out2.input(0).connect(mac_sfg.outputs[0], "S10")
+
+        test_sfg = SFG(inputs=[inp4, inp5], outputs=[out2])
+        assert test_sfg.evaluate(1, 2) == 9
+        mac_sfg.connect_external_signals_to_components()
+        assert test_sfg.evaluate(1, 2) == 9
+        assert not test_sfg.connect_external_signals_to_components()
+
+    def test_connect_external_signals_to_components_operation_tree(self, operation_tree):
+        """ Replaces an SFG with only a operation_tree component with its inner components """
+        sfg1 = SFG(outputs=[Output(operation_tree)])
+        out1 = Output(None, "OUT1")
+        out1.input(0).connect(sfg1.outputs[0], "S1")
+        test_sfg = SFG(outputs=[out1])
+        assert test_sfg.evaluate_output(0, []) == 5
+        sfg1.connect_external_signals_to_components()
+        assert test_sfg.evaluate_output(0, []) == 5
+        assert not test_sfg.connect_external_signals_to_components()
+
+    def test_connect_external_signals_to_components_large_operation_tree(self, large_operation_tree):
+        """ Replaces an SFG with only a large_operation_tree component with its inner components """
+        sfg1 = SFG(outputs=[Output(large_operation_tree)])
+        out1 = Output(None, "OUT1")
+        out1.input(0).connect(sfg1.outputs[0], "S1")
+        test_sfg = SFG(outputs=[out1])
+        assert test_sfg.evaluate_output(0, []) == 14
+        sfg1.connect_external_signals_to_components()
+        assert test_sfg.evaluate_output(0, []) == 14
+        assert not test_sfg.connect_external_signals_to_components()
+
+
+class TestConnectExternalSignalsToComponentsMultipleComp:
+
+    def test_connect_external_signals_to_components_operation_tree(self, operation_tree):
+        """ Replaces a operation_tree in an SFG with other components """
+        sfg1 = SFG(outputs=[Output(operation_tree)])
+
+        inp1 = Input("INP1")
+        inp2 = Input("INP2")
+        out1 = Output(None, "OUT1")
+
+        add1 = Addition(None, None, "ADD1")
+        add2 = Addition(None, None, "ADD2")
+
+        add1.input(0).connect(inp1, "S1")
+        add1.input(1).connect(inp2, "S2")
+        add2.input(0).connect(add1, "S3")
+        add2.input(1).connect(sfg1.outputs[0], "S4")
+        out1.input(0).connect(add2, "S5")
+
+        test_sfg = SFG(inputs=[inp1, inp2], outputs=[out1])
+        assert test_sfg.evaluate(1, 2) == 8
+        sfg1.connect_external_signals_to_components()
+        assert test_sfg.evaluate(1, 2) == 8
+        assert not test_sfg.connect_external_signals_to_components()
+
+    def test_connect_external_signals_to_components_large_operation_tree(self, large_operation_tree):
+        """ Replaces a large_operation_tree in an SFG with other components """
+        sfg1 = SFG(outputs=[Output(large_operation_tree)])
+
+        inp1 = Input("INP1")
+        inp2 = Input("INP2")
+        out1 = Output(None, "OUT1")
+        add1 = Addition(None, None, "ADD1")
+        add2 = Addition(None, None, "ADD2")
+
+        add1.input(0).connect(inp1, "S1")
+        add1.input(1).connect(inp2, "S2")
+        add2.input(0).connect(add1, "S3")
+        add2.input(1).connect(sfg1.outputs[0], "S4")
+        out1.input(0).connect(add2, "S5")
+
+        test_sfg = SFG(inputs=[inp1, inp2], outputs=[out1])
+        assert test_sfg.evaluate(1, 2) == 17
+        sfg1.connect_external_signals_to_components()
+        assert test_sfg.evaluate(1, 2) == 17
+        assert not test_sfg.connect_external_signals_to_components()
+
+    def create_sfg(self, op_tree):
+        """ Create a simple SFG with either operation_tree or large_operation_tree """
+        sfg1 = SFG(outputs=[Output(op_tree)])
+
+        inp1 = Input("INP1")
+        inp2 = Input("INP2")
+        out1 = Output(None, "OUT1")
+        add1 = Addition(None, None, "ADD1")
+        add2 = Addition(None, None, "ADD2")
+
+        add1.input(0).connect(inp1, "S1")
+        add1.input(1).connect(inp2, "S2")
+        add2.input(0).connect(add1, "S3")
+        add2.input(1).connect(sfg1.outputs[0], "S4")
+        out1.input(0).connect(add2, "S5")
+
+        return SFG(inputs=[inp1, inp2], outputs=[out1])
+
+    def test_connect_external_signals_to_components_many_op(self, large_operation_tree):
+        """ Replaces an sfg component in a larger SFG with several component operations """
+        inp1 = Input("INP1")
+        inp2 = Input("INP2")
+        inp3 = Input("INP3")
+        inp4 = Input("INP4")
+        out1 = Output(None, "OUT1")
+        add1 = Addition(None, None, "ADD1")
+        sub1 = Subtraction(None, None, "SUB1")
+
+        add1.input(0).connect(inp1, "S1")
+        add1.input(1).connect(inp2, "S2")
+
+        sfg1 = self.create_sfg(large_operation_tree)
+
+        sfg1.input(0).connect(add1, "S3")
+        sfg1.input(1).connect(inp3, "S4")
+        sub1.input(0).connect(sfg1.outputs[0], "S5")
+        sub1.input(1).connect(inp4, "S6")
+        out1.input(0).connect(sub1, "S7")
+
+        test_sfg = SFG(inputs=[inp1, inp2, inp3, inp4], outputs=[out1])
+        
+        assert test_sfg.evaluate(1, 2, 3, 4) == 16
+        sfg1.connect_external_signals_to_components()
+        assert test_sfg.evaluate(1, 2, 3, 4) == 16
+        assert not test_sfg.connect_external_signals_to_components()
+
+class TestTopologicalOrderOperations:
+    def test_feedback_sfg(self, sfg_simple_filter):
+        topological_order = sfg_simple_filter.get_operations_topological_order()
+
+        assert [comp.name for comp in topological_order] == ["IN1", "ADD1", "T1", "CMUL1", "OUT1"]
+
+    def test_multiple_independent_inputs(self, sfg_two_inputs_two_outputs_independent):
+        topological_order = sfg_two_inputs_two_outputs_independent.get_operations_topological_order()
+
+        assert [comp.name for comp in topological_order] == ["IN1", "OUT1", "IN2", "C1", "ADD1", "OUT2"]
+
+
+class TestRemove:
+    def test_remove_single_input_outputs(self, sfg_simple_filter):
+        new_sfg = sfg_simple_filter.remove_operation("cmul1")
+
+        assert set(op.name for op in sfg_simple_filter.find_by_name("T1")[0].subsequent_operations) == {"CMUL1", "OUT1"}
+        assert set(op.name for op in new_sfg.find_by_name("T1")[0].subsequent_operations) == {"ADD1", "OUT1"}
+
+        assert set(op.name for op in sfg_simple_filter.find_by_name("ADD1")[0].preceding_operations) == {"CMUL1", "IN1"}
+        assert set(op.name for op in new_sfg.find_by_name("ADD1")[0].preceding_operations) == {"T1", "IN1"}
+
+        assert "S1" in set([sig.name for sig in sfg_simple_filter.find_by_name("T1")[0].output(0).signals])
+        assert "S2" in set([sig.name for sig in new_sfg.find_by_name("T1")[0].output(0).signals])
+
+    def test_remove_multiple_inputs_outputs(self, butterfly_operation_tree):
+        out1 = Output(butterfly_operation_tree.output(0), "OUT1")
+        out2 = Output(butterfly_operation_tree.output(1), "OUT2")
+
+        sfg = SFG(outputs=[out1, out2])
+
+        new_sfg = sfg.remove_operation(sfg.find_by_name("bfly2")[0].graph_id)
+
+        assert sfg.find_by_name("bfly3")[0].output(0).signal_count == 1
+        assert new_sfg.find_by_name("bfly3")[0].output(0).signal_count == 1
+
+        sfg_dest_0 = sfg.find_by_name("bfly3")[0].output(0).signals[0].destination
+        new_sfg_dest_0 = new_sfg.find_by_name("bfly3")[0].output(0).signals[0].destination
+
+        assert sfg_dest_0.index == 0
+        assert new_sfg_dest_0.index == 0
+        assert sfg_dest_0.operation.name == "bfly2"
+        assert new_sfg_dest_0.operation.name == "bfly1"
+
+        assert sfg.find_by_name("bfly3")[0].output(1).signal_count == 1
+        assert new_sfg.find_by_name("bfly3")[0].output(1).signal_count == 1
+
+        sfg_dest_1 = sfg.find_by_name("bfly3")[0].output(1).signals[0].destination
+        new_sfg_dest_1 = new_sfg.find_by_name("bfly3")[0].output(1).signals[0].destination
+
+        assert sfg_dest_1.index == 1
+        assert new_sfg_dest_1.index == 1
+        assert sfg_dest_1.operation.name == "bfly2"
+        assert new_sfg_dest_1.operation.name == "bfly1"
+
+        assert sfg.find_by_name("bfly1")[0].input(0).signal_count == 1
+        assert new_sfg.find_by_name("bfly1")[0].input(0).signal_count == 1
+
+        sfg_source_0 = sfg.find_by_name("bfly1")[0].input(0).signals[0].source
+        new_sfg_source_0 = new_sfg.find_by_name("bfly1")[0].input(0).signals[0].source
+
+        assert sfg_source_0.index == 0
+        assert new_sfg_source_0.index == 0
+        assert sfg_source_0.operation.name == "bfly2"
+        assert new_sfg_source_0.operation.name == "bfly3"
+
+        sfg_source_1 = sfg.find_by_name("bfly1")[0].input(1).signals[0].source
+        new_sfg_source_1 = new_sfg.find_by_name("bfly1")[0].input(1).signals[0].source
+
+        assert sfg_source_1.index == 1
+        assert new_sfg_source_1.index == 1
+        assert sfg_source_1.operation.name == "bfly2"
+        assert new_sfg_source_1.operation.name == "bfly3"
+
+        assert "bfly2" not in set(op.name for op in new_sfg.operations)
+
+    def remove_different_number_inputs_outputs(self, sfg_simple_filter):
+        with pytest.raises(ValueError):
+            sfg_simple_filter.remove_operation("add1")