From 7b263393b2a51b24f16f31fa02302c58cf6e72fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivarhar@outlook.com>
Date: Wed, 19 Feb 2020 21:49:22 +0100
Subject: [PATCH 01/50] add gitignore

---
 .gitignore | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100755 .gitignore

diff --git a/.gitignore b/.gitignore
new file mode 100755
index 00000000..bf81ea19
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,28 @@
+.vs/
+.vscode/
+build*/
+bin*/
+logs/
+CMakeLists.txt.user*
+*.autosave
+*.creator
+*.creator.user*
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+*.elc
+auto-save-list
+tramp
+.\#*
+*~
+.fuse_hudden*
+.directory
+.Trash-*
+.nfs*
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+$RECYCLE.BIN/
+*.stackdump
+[Dd]esktop.ini
\ No newline at end of file
-- 
GitLab


From 1b46d8f122474d2e110b19f23c27bbd7641778df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivarhar@outlook.com>
Date: Thu, 20 Feb 2020 20:46:36 +0100
Subject: [PATCH 02/50] add readme, logo, CMakeLists, setup boilerplate and
 example code

---
 .gitignore         |   4 ++-
 .gitlab-ci.yml     |  10 ------
 CMakeLists.txt     |  77 ++++++++++++++++++++++++++++++++++++++++++
 LICENSE            |   0
 MANIFEST.in        |   3 ++
 README.md          |  82 +++++++++++++++++++++++++++++++++++++++++++--
 b_asic/__init__.py |   6 ++++
 build.sh           |   5 ---
 helloworld.py      |   1 -
 logo.png           | Bin 0 -> 70697 bytes
 setup.py           |  75 +++++++++++++++++++++++++++++++++++++++++
 src/main.cpp       |  21 ++++++++++++
 testbuild.py       |   1 -
 13 files changed, 264 insertions(+), 21 deletions(-)
 delete mode 100644 .gitlab-ci.yml
 create mode 100755 CMakeLists.txt
 mode change 100644 => 100755 LICENSE
 create mode 100755 MANIFEST.in
 mode change 100644 => 100755 README.md
 create mode 100755 b_asic/__init__.py
 delete mode 100644 build.sh
 delete mode 100644 helloworld.py
 create mode 100755 logo.png
 create mode 100755 setup.py
 create mode 100755 src/main.cpp
 delete mode 100644 testbuild.py

diff --git a/.gitignore b/.gitignore
index bf81ea19..473e0e34 100755
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
 build*/
 bin*/
 logs/
+dist/
 CMakeLists.txt.user*
 *.autosave
 *.creator
@@ -25,4 +26,5 @@ ehthumbs.db
 ehthumbs_vista.db
 $RECYCLE.BIN/
 *.stackdump
-[Dd]esktop.ini
\ No newline at end of file
+[Dd]esktop.ini
+*.egg-info
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 6c51653c..00000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-stages:
-  - build
-
-PythonBuild:
-  stage: build
-  artifacts:
-    untracked: true
-  script:
-    - apt-get update && apt-get install python3 -y
-    - bash build.sh
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755
index 00000000..6b6dafb3
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 3.8)
+
+project(
+	"B-ASIC"
+	VERSION 0.0.1
+	DESCRIPTION "Better ASIC Toolbox for python3"
+	LANGUAGES C CXX
+)
+
+find_package(fmt 6.1.2 REQUIRED)
+find_package(pybind11 CONFIG REQUIRED)
+
+set(LIBRARY_NAME "b_asic")
+set(TARGET_NAME "_${LIBRARY_NAME}")
+if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
+	include(GNUInstallDirs)
+	set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+add_library(
+	"${TARGET_NAME}" MODULE
+	"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp"
+)
+add_library(
+	"${TARGET_NAME}:${TARGET_NAME}"
+	ALIAS "${TARGET_NAME}"
+)
+
+set_target_properties(
+	"${TARGET_NAME}"
+	PROPERTIES
+		PREFIX ""
+)
+
+target_include_directories(
+	"${TARGET_NAME}"
+    PRIVATE
+        "${CMAKE_CURRENT_SOURCE_DIR}/src"
+)
+
+target_compile_features(
+	"${TARGET_NAME}"
+	PRIVATE
+		cxx_std_17
+)
+target_compile_options(
+	"${TARGET_NAME}"
+	PRIVATE
+		$<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:
+			-W -Wall -Wextra -Werror -Wno-psabi -fvisibility=hidden
+			$<$<CONFIG:Debug>:-g>
+			$<$<NOT:$<CONFIG:Debug>>:-O3>
+		>
+		$<$<CXX_COMPILER_ID:MSVC>:
+			/W3 /WX /permissive- /utf-8
+			$<$<CONFIG:Debug>:/Od>
+			$<$<NOT:$<CONFIG:Debug>>:/Ot>
+		>
+)
+
+target_link_libraries(
+	"${TARGET_NAME}"
+	PRIVATE
+		fmt::fmt-header-only
+		pybind11::module
+)
+
+add_custom_target(
+	copy_python_files ALL
+	COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
+	COMMENT "Copying python files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
+)
+add_custom_target(
+	copy_misc_files ALL
+	COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/README.md" "${CMAKE_CURRENT_LIST_DIR}/LICENSE" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
+	COMMENT "Copying misc. files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
+)
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
old mode 100644
new mode 100755
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100755
index 00000000..89a500ee
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include README.md
+include LICENSE
+recursive-include src *.cpp *.h
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index f126999b..78df523e
--- a/README.md
+++ b/README.md
@@ -1,3 +1,79 @@
-<img src="https://files.slack.com/files-pri/TSHPRJY83-FTTRW9MQ8/b-asic-logo-opaque.png"  width="318" height="100">
-<br>
-<h3>The leading company in circuit design<h3>
\ No newline at end of file
+<img src="logo.png" width="278" height="100">
+
+# B-ASIC - Better ASIC Toolbox
+B-ASIC is an ASIC toolbox for Python 3 that simplifies circuit design and optimization.
+
+## Prerequisites
+The following packages are required in order to build the library:
+* cmake 3.8+
+  * gcc 7+/clang 7+/msvc 16+
+  * fmtlib 6.1.2+
+  * pybind11 2.3.0+
+* python 3.6+
+  * setuptools
+  * wheel
+  * pybind11
+
+## Development
+How to build and debug the library during development.
+
+### Using CMake directly
+How to build using CMake.
+
+#### Configuring
+In `B-ASIC`:
+```
+mkdir build
+cd build
+cmake ..
+```
+
+#### Building (Debug)
+In `B-ASIC/build`:
+```
+cmake --build .
+```
+The output gets written to `B-ASIC/build/lib`.
+
+#### Building (Release)
+In `B-ASIC/build`:
+```
+cmake --build . --config Release
+```
+The output gets written to `B-ASIC/build/lib`.
+
+### Using setuptools to create a package
+How to create a package using setuptools.
+
+#### Setup (Binary distribution)
+In `B-ASIC`:
+```
+python3 setup.py bdist_wheel
+```
+The output gets written to `B-ASIC/dist`.
+
+#### Setup (Source distribution)
+In `B-ASIC`:
+```
+python3 setup.py sdist
+```
+The output gets written to `B-ASIC/dist`.
+
+## Usage
+How to build and use the library as a user.
+
+### Installation
+```
+python3 -m pip install b_asic
+```
+
+### Importing
+```
+python3
+>>> import b_asic as asic
+>>> help(asic)
+```
+
+## License
+B-ASIC is distributed under the MIT license.
+See the included LICENSE file for more information.
\ No newline at end of file
diff --git a/b_asic/__init__.py b/b_asic/__init__.py
new file mode 100755
index 00000000..598e2cbf
--- /dev/null
+++ b/b_asic/__init__.py
@@ -0,0 +1,6 @@
+"""Better ASIC Toolbox"""
+from _b_asic import *
+
+def mul(a, b):
+	"""A function that multiplies two numbers"""
+	return a * b
diff --git a/build.sh b/build.sh
deleted file mode 100644
index 1015ced2..00000000
--- a/build.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#! bin/sh
-for n in `find . -name "*.py"`
-do
-  python3 $n
-done
\ No newline at end of file
diff --git a/helloworld.py b/helloworld.py
deleted file mode 100644
index 6d95fe97..00000000
--- a/helloworld.py
+++ /dev/null
@@ -1 +0,0 @@
-print("Hello world")
\ No newline at end of file
diff --git a/logo.png b/logo.png
new file mode 100755
index 0000000000000000000000000000000000000000..b1bd78fe7eb8a737ac8153576078d61492c2e9a3
GIT binary patch
literal 70697
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

literal 0
HcmV?d00001

diff --git a/setup.py b/setup.py
new file mode 100755
index 00000000..aa6d7a6c
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,75 @@
+import os
+import sys
+import shutil
+import subprocess
+import setuptools
+from setuptools import Extension
+from setuptools.command.build_ext import build_ext
+
+CMAKE_EXE = os.environ.get('CMAKE_EXE', shutil.which('cmake'))
+
+class CMakeExtension(Extension):
+    def __init__(self, name, sourcedir = ""):
+        super().__init__(name, sources=[])
+        self.sourcedir = os.path.abspath(sourcedir)
+
+class CMakeBuild(build_ext):
+    def build_extension(self, ext):
+        if not isinstance(ext, CMakeExtension):
+            return super().build_extension(ext)
+
+        if not CMAKE_EXE:
+            raise RuntimeError(f"Cannot build extension {ext.name}: CMake executable not found! Set the CMAKE_EXE environment variable or update your path.")
+
+        cmake_build_type = "Debug" if self.debug else "Release"
+        cmake_output_dir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
+        cmake_configure_argv = [
+            CMAKE_EXE, ext.sourcedir,
+            "-DCMAKE_BUILD_TYPE=" + cmake_build_type,
+            "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + cmake_output_dir,
+            "-DPYTHON_EXECUTABLE=" + sys.executable,
+        ]
+        cmake_build_argv = [
+            CMAKE_EXE, "--build", ".",
+            "--config", cmake_build_type
+        ]
+
+        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}")
+        subprocess.check_call(cmake_configure_argv, cwd=self.build_temp, env=env)
+
+        print(f"=== Building {ext.name} ===")
+        print(f"Temp dir: {self.build_temp}")
+        print(f"Output dir: {cmake_output_dir}")
+        print(f"Build type: {cmake_build_type}")
+        subprocess.check_call(cmake_build_argv, cwd=self.build_temp, env=env)
+
+        print()
+
+setuptools.setup(
+    name = "b-asic",
+    version = "0.0.1",
+    author = "Adam Jakobsson, Angus Lothian, Arvid Westerlund, Felix Goding, Ivar Härnqvist, Jacob Wahlman, Kevin Scott, Rasmus Karlsson",
+    author_email = "adaja901@student.liu.se, anglo547@student.liu.se, arvwe160@student.liu.se, felgo673@student.liu.se, ivaha717@student.liu.se, jacwa448@student.liu.se, kevsc634@student.liu.se, raska119@student.liu.se",
+    description = "Better ASIC Toolbox",
+    long_description = open("README.md", "r").read(),
+    long_description_content_type = "text/markdown",
+    url = "https://gitlab.liu.se/PUM_TDDD96/B-ASIC",
+    classifiers = [
+        "Programming Language :: Python :: 3",
+        "License :: OSI Approved :: MIT License",
+        "Operating System :: OS Independent",
+    ],
+    python_requires = ">=3.6",
+    install_requires = ["pybind11>=2.3.0"],
+    packages = ["b_asic"],
+    ext_modules = [CMakeExtension("b_asic")],
+    cmdclass = {"build_ext": CMakeBuild},
+    zip_safe = False
+)
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100755
index 00000000..c3184d1b
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,21 @@
+#include <pybind11/pybind11.h>
+
+namespace py = pybind11;
+
+namespace asic {
+
+int add(int a, int b) {
+	return a + b;
+}
+
+int sub(int a, int b) {
+	return a - b;
+}
+
+} // namespace asic
+
+PYBIND11_MODULE(_b_asic, m) {
+	m.doc() = "Better ASIC Toolbox Extension Module";
+	m.def("add", &asic::add, "A function which adds two numbers", py::arg("a"), py::arg("b"));
+	m.def("sub", &asic::sub, "A function which subtracts two numbers", py::arg("a"), py::arg("b"));
+}
\ No newline at end of file
diff --git a/testbuild.py b/testbuild.py
deleted file mode 100644
index 48f3864f..00000000
--- a/testbuild.py
+++ /dev/null
@@ -1 +0,0 @@
-print("Worked to build this file")
\ No newline at end of file
-- 
GitLab


From 0e74e6a625e15d652d48f512323053f9306cbaaa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivarhar@outlook.com>
Date: Thu, 20 Feb 2020 20:55:45 +0100
Subject: [PATCH 03/50] fix CMake python file copy destination

---
 CMakeLists.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6b6dafb3..7f6908ed 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,8 +67,8 @@ target_link_libraries(
 
 add_custom_target(
 	copy_python_files ALL
-	COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
-	COMMENT "Copying python files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
+	COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
+	COMMENT "Copying python files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
 )
 add_custom_target(
 	copy_misc_files ALL
-- 
GitLab


From dce323929a2c467ec1ab6383d6850dd2410d1604 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivarhar@outlook.com>
Date: Fri, 21 Feb 2020 00:45:57 +0100
Subject: [PATCH 04/50] add python system shell

---
 b_asic/__init__.py   |  17 ++--
 b_asic/operation.py  | 210 +++++++++++++++++++++++++++++++++++++++++++
 b_asic/ops.py        |  75 ++++++++++++++++
 b_asic/pc.py         |  24 +++++
 b_asic/port.py       | 147 ++++++++++++++++++++++++++++++
 b_asic/schema.py     |  24 +++++
 b_asic/sfg.py        |  37 ++++++++
 b_asic/signal.py     |  68 ++++++++++++++
 b_asic/simulation.py |  42 +++++++++
 src/main.cpp         |   6 +-
 10 files changed, 642 insertions(+), 8 deletions(-)
 create mode 100755 b_asic/operation.py
 create mode 100755 b_asic/ops.py
 create mode 100755 b_asic/pc.py
 create mode 100755 b_asic/port.py
 create mode 100755 b_asic/schema.py
 create mode 100755 b_asic/sfg.py
 create mode 100755 b_asic/signal.py
 create mode 100755 b_asic/simulation.py

diff --git a/b_asic/__init__.py b/b_asic/__init__.py
index 598e2cbf..8bbc17ab 100755
--- a/b_asic/__init__.py
+++ b/b_asic/__init__.py
@@ -1,6 +1,13 @@
-"""Better ASIC Toolbox"""
+"""
+Better ASIC Toolbox.
+TODO: More info.
+"""
 from _b_asic import *
-
-def mul(a, b):
-	"""A function that multiplies two numbers"""
-	return a * b
+from b_asic.operation import *
+from b_asic.ops import *
+from b_asic.pc import *
+from b_asic.port import *
+from b_asic.schema import *
+from b_asic.sfg import *
+from b_asic.signal import *
+from b_asic.simulation import *
\ No newline at end of file
diff --git a/b_asic/operation.py b/b_asic/operation.py
new file mode 100755
index 00000000..4bea5047
--- /dev/null
+++ b/b_asic/operation.py
@@ -0,0 +1,210 @@
+"""
+B-ASIC Operation Module.
+TODO: More info.
+"""
+from b_asic.port import InputPort, OutputPort
+from b_asic.signal import SignalSource, SignalDestination
+from b_asic.simulation import SimulationState, OperationState
+from abc import ABC, abstractmethod
+from numbers import Number
+from typing import NewType, List, Dict, Optional, final
+
+OperationId = NewType("OperationId", int)
+
+class Operation(ABC):
+	"""
+	Operation interface.
+	TODO: More info.
+	"""
+
+	@abstractmethod
+	def identifier(self) -> OperationId:
+		"""
+		Get the unique identifier.
+		"""
+		pass
+
+	@abstractmethod
+	def inputs(self) -> List[InputPort]:
+		"""
+		Get a list of all input ports.
+		"""
+		pass
+
+	@abstractmethod
+	def outputs(self) -> List[OutputPort]:
+		"""
+		Get a list of all output ports.
+		"""
+		pass
+
+	@abstractmethod
+	def input_count(self) -> int:
+		"""
+		Get the number of input ports.
+		"""
+		pass
+
+	@abstractmethod
+	def output_count(self) -> int:
+		"""
+		Get the number of output ports.
+		"""
+		pass
+
+	@abstractmethod
+	def input(self, i: int) -> InputPort:
+		"""
+		Get the input port at index i.
+		"""
+		pass
+
+	@abstractmethod
+	def output(self, i: int) -> OutputPort:
+		"""
+		Get the output port at index i.
+		"""
+		pass
+
+	@abstractmethod
+	def params(self) -> Dict[str, Optional[Any]]:
+		"""
+		Get a dictionary of all parameter values.
+		"""
+		pass
+
+	@abstractmethod
+	def param(self, name: str) -> Optional[Any]:
+		"""
+		Get the value of a parameter.
+		Returns None if the parameter is not defined.
+		"""
+		pass
+
+	@abstractmethod
+	def set_param(self, name: str, value: Any) -> None:
+		"""
+		Set the value of a parameter.
+		The parameter must be defined.
+		"""
+		pass
+
+	@abstractmethod
+	def evaluate_outputs(self, state: SimulationState) -> List[Number]:
+		"""
+		Simulate the circuit until its iteration count matches that of the simulation state,
+		then return the resulting output vector.
+		"""
+		pass
+
+	@abstractmethod
+	def split(self) -> List[Operation]:
+		"""
+		Split the operation into multiple operations.
+		If splitting is not possible, this may return a list containing only the operation itself.
+		"""
+		pass
+	
+	# TODO: More stuff.
+
+class BasicOperation(ABC, Operation):
+	"""
+	Generic abstract operation class which most implementations will derive from.
+	TODO: More info.
+	"""
+
+	_identifier: OperationId
+	_input_ports: List[InputPort]
+	_output_ports: List[OutputPort]
+	_parameters: Dict[str, Optional[Any]]
+
+	def __init__(self, identifier: OperationId):
+		"""
+		Construct a BasicOperation.
+		"""
+		self._identifier = identifier
+		self._input_ports = []
+		self._output_ports = []
+		self._parameters = {}
+
+	@abstractmethod
+	def evaluate(self, inputs: list) -> list:
+		"""
+		Evaluate the operation and generate a list of output values given a list of input values.
+		"""
+		pass
+
+	@final
+	def id(self) -> OperationId:
+		return self._identifier
+		
+	@final
+	def inputs(self) -> List[InputPort]:
+		return self._input_ports.copy()
+
+	@final
+	def outputs(self) -> List[OutputPort]:
+		return self._output_ports.copy()
+
+	@final
+	def input_count(self) -> int:
+		return len(self._input_ports)
+
+	@final
+	def output_count(self) -> int:
+		return len(self._output_ports)
+
+	@final
+	def input(self, i: int) -> InputPort:
+		return self._input_ports[i]
+
+	@final
+	def output(self, i: int) -> OutputPort:
+		return self._output_ports[i]
+
+	@final
+	def params(self) -> Dict[str, Optional[Any]]:
+		return self._parameters.copy()
+	
+	@final
+	def param(self, name: str) -> Optional[Any]:
+		return self._parameters.get(name)
+
+	@final
+	def set_param(self, name: str, value: Any) -> None:
+		assert name in self._parameters # TODO: Error message.
+		self._parameters[name] = value
+
+	def evaluate_outputs(self, state: SimulationState) -> List[Number]:
+		# TODO: Check implementation.
+		input_count: int = self.input_count()
+		output_count: int = self.output_count()
+		assert input_count == len(self._input_ports) # TODO: Error message.
+		assert output_count == len(self._output_ports) # TODO: Error message.
+
+		self_state: OperationState = state.operation_states[self.identifier()]
+
+		while self_state.iteration < state.iteration:
+			input_values: List[Number] = [0] * input_count
+			for i in range(input_count):
+				source: SignalSource = self._input_ports[i].signal().source
+				input_values[i] = source.operation.evaluate_outputs(state)[source.port_index]
+
+			self_state.output_values = self.evaluate(input_values)
+			assert len(self_state.output_values) == output_count # TODO: Error message.
+			self_state.iteration += 1
+			for i in range(output_count):
+				for signal in self._output_ports[i].signals():
+					destination: SignalDestination = signal.destination
+					destination.evaluate_outputs(state)
+
+		return self_state.output_values
+
+	def split(self) -> List[Operation]:
+		# TODO: Check implementation.
+		results = self.evaluate(self._input_ports)
+		if all(isinstance(e, Operation) for e in results):
+			return results
+		return [self]
+		
+	# TODO: More stuff.
\ No newline at end of file
diff --git a/b_asic/ops.py b/b_asic/ops.py
new file mode 100755
index 00000000..6b370725
--- /dev/null
+++ b/b_asic/ops.py
@@ -0,0 +1,75 @@
+"""
+B-ASIC Core Operations Module.
+TODO: More info.
+"""
+
+from b_asic.operation import OperationId, Operation, BasicOperation
+from numbers import Number
+from typing import final
+
+class Input(Operation):
+	"""
+	Input operation.
+	TODO: More info.
+	"""
+
+	# TODO: Implement.
+	pass
+
+class Constant(BasicOperation):
+	"""
+	Constant value operation.
+	TODO: More info.
+	"""
+
+	def __init__(self, identifier: OperationId, value: Number):
+		"""
+		Construct a Constant.
+		"""
+		super().__init__(identifier)
+		self._output_ports = [OutputPort()] # TODO: Generate appropriate ID for ports.
+		self._parameters["value"] = value
+
+	@final
+	def evaluate(self, inputs: list) -> list:
+		return [self.param("value")]
+
+class Addition(BasicOperation):
+	"""
+	Binary addition operation.
+	TODO: More info.
+	"""
+
+	def __init__(self, identifier: OperationId):
+		"""
+		Construct an Addition.
+		"""
+		super().__init__(identifier)
+		self._input_ports = [InputPort(), InputPort()] # TODO: Generate appropriate ID for ports.
+		self._output_ports = [OutputPort()] # TODO: Generate appropriate ID for ports.
+
+	@final
+	def evaluate(self, inputs: list) -> list:
+		return [inputs[0] + inputs[1]]
+
+
+class ConstantMultiplication(BasicOperation):
+	"""
+	Unary constant multiplication operation.
+	TODO: More info.
+	"""
+
+	def __init__(self, identifier: OperationId, coefficient: Number):
+		"""
+		Construct a ConstantMultiplication.
+		"""
+		super().__init__(identifier)
+		self._input_ports = [InputPort()] # TODO: Generate appropriate ID for ports.
+		self._output_ports = [OutputPort()] # TODO: Generate appropriate ID for ports.
+		self._parameters["coefficient"] = coefficient
+
+	@final
+	def evaluate(self, inputs: list) -> list:
+		return [inputs[0] * self.param("coefficient")]
+
+# TODO: More operations.
\ No newline at end of file
diff --git a/b_asic/pc.py b/b_asic/pc.py
new file mode 100755
index 00000000..cc18d6af
--- /dev/null
+++ b/b_asic/pc.py
@@ -0,0 +1,24 @@
+"""
+B-ASIC Precedence Chart Module.
+TODO: More info.
+"""
+
+from b_asic.sfg 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):
+		"""
+		Construct a PrecedenceChart.
+		"""
+		self.sfg = sfg
+		# TODO: Implement.
+
+	# TODO: More stuff.
\ No newline at end of file
diff --git a/b_asic/port.py b/b_asic/port.py
new file mode 100755
index 00000000..6b1e8d20
--- /dev/null
+++ b/b_asic/port.py
@@ -0,0 +1,147 @@
+"""
+B-ASIC Port Module.
+TODO: More info.
+"""
+
+from b_asic.signal import Signal
+from abc import ABC, abstractmethod
+from typing import NewType, Optional, List, Dict, final
+
+PortId = NewType("PortId", int)
+
+class Port(ABC):
+	"""
+	Abstract port class.
+	TODO: More info.
+	"""
+
+	_identifier: PortId
+
+	def __init__(self, identifier: PortId):
+		"""
+		Construct a Port.
+		"""
+		self._identifier = identifier
+	
+	@final
+	def identifier(self) -> PortId:
+		"""
+		Get the unique identifier.
+		"""
+		return self._identifier
+
+	@abstractmethod
+	def signals(self) -> List[Signal]:
+		"""
+		Get a list of all connected signals.
+		"""
+		pass
+	
+	@abstractmethod
+	def signal_count(self) -> int:
+		"""
+		Get the number of connected signals.
+		"""
+		pass
+
+	@abstractmethod
+	def signal(self, i: int = 0) -> Signal:
+		"""
+		Get the connected signal at index i.
+		"""
+		pass
+
+	@abstractmethod
+	def connect(self, signal: Signal) -> None:
+		"""
+		Connect a signal.
+		"""
+		pass
+
+	@abstractmethod
+	def disconnect(self, i: int = 0) -> None:
+		"""
+		Disconnect a signal.
+		"""
+		pass
+
+	# TODO: More stuff.
+
+class InputPort(Port):
+	"""
+	Input port.
+	TODO: More info.
+	"""
+	_source_signal: Optional[Signal]
+
+	def __init__(self, identifier: PortId):
+		"""
+		Construct an InputPort.
+		"""
+		super().__init__(identifier)
+		self._source_signal = None
+
+	@final
+	def signals(self) -> List[Signal]:
+		return [] if self._source_signal == None else [self._source_signal]
+	
+	@final
+	def signal_count(self) -> int:
+		return 0 if self._source_signal == None else 1
+
+	@final
+	def signal(self, i: int = 0) -> Signal:
+		assert i >= 0 and i < self.signal_count() # TODO: Error message.
+		assert self._source_signal != None # TODO: Error message.
+		return self._source_signal
+
+	@final
+	def connect(self, signal: Signal) -> None:
+		self._source_signal = signal
+
+	@final
+	def disconnect(self, i: int = 0) -> None:
+		assert i >= 0 and i < self.signal_count() # TODO: Error message.
+		self._source_signal = None
+
+	# TODO: More stuff.
+
+class OutputPort(Port):
+	"""
+	Output port.
+	TODO: More info.
+	"""
+
+	_destination_signals: List[Signal]
+
+	def __init__(self, identifier: PortId):
+		"""
+		Construct an OutputPort.
+		"""
+		super().__init__(identifier)
+		self._destination_signals = []
+
+	@final
+	def signals(self) -> List[Signal]:
+		return self._destination_signals.copy()
+
+	@final
+	def signal_count(self) -> int:
+		return len(self._destination_signals)
+
+	@final
+	def signal(self, i: int = 0) -> Signal:
+		assert i >= 0 and i < self.signal_count() # TODO: Error message.
+		return self._destination_signals[i]
+
+	@final
+	def connect(self, signal: Signal) -> None:
+		assert signal not in self._destination_signals # TODO: Error message.
+		self._destination_signals.append(signal)
+	
+	@final
+	def disconnect(self, i: int = 0) -> None:
+		assert i >= 0 and i < self.signal_count() # TODO: Error message.
+		del self._destination_signals[i]
+		
+	# TODO: More stuff.
\ No newline at end of file
diff --git a/b_asic/schema.py b/b_asic/schema.py
new file mode 100755
index 00000000..a7642f49
--- /dev/null
+++ b/b_asic/schema.py
@@ -0,0 +1,24 @@
+"""
+B-ASIC Schema Module.
+TODO: More info.
+"""
+
+from b_asic.pc import PrecedenceChart
+
+class Schema:
+	"""
+	Schema constructed from a precedence chart.
+	TODO: More info.
+	"""
+
+	pc: PrecedenceChart
+	# TODO: More members.
+
+	def __init__(self, pc: PrecedenceChart):
+		"""
+		Construct a Schema.
+		"""
+		self.pc = pc
+		# TODO: Implement.
+
+	# TODO: More stuff.
\ No newline at end of file
diff --git a/b_asic/sfg.py b/b_asic/sfg.py
new file mode 100755
index 00000000..d39cc524
--- /dev/null
+++ b/b_asic/sfg.py
@@ -0,0 +1,37 @@
+"""
+B-ASIC Signal Flow Graph Module.
+TODO: More info.
+"""
+
+from b_asic.operation import OperationId, Operation, BasicOperation
+from b_asic.signal import SignalSource, SignalDestination
+from b_asic.simulation import SimulationState, OperationState
+from typing import List, final
+
+class SFG(BasicOperation):
+	"""
+	Signal flow graph.
+	TODO: More info.
+	"""
+
+	_operations: List[Operation]
+
+	def __init__(self, identifier: OperationId, input_destinations: List[SignalDestination], output_sources: List[SignalSource]):
+		"""
+		Construct a SFG.
+		"""
+		super().__init__(identifier)
+		# TODO: Allocate input/output ports with appropriate IDs.
+		self._operations = []
+		# TODO: Traverse the graph between the inputs/outputs and add to self._operations.
+		# TODO: Connect ports with signals with appropriate IDs.
+
+	@final
+	def evaluate(self, inputs: list) -> list:
+		return [] # TODO: Implement
+
+	@final
+	def split(self) -> List[Operation]:
+		return self._operations
+
+	# TODO: More stuff.
\ No newline at end of file
diff --git a/b_asic/signal.py b/b_asic/signal.py
new file mode 100755
index 00000000..36be9f58
--- /dev/null
+++ b/b_asic/signal.py
@@ -0,0 +1,68 @@
+"""
+B-ASIC Signal Module.
+TODO: More info.
+"""
+
+from b_asic.operation import Operation
+from typing import NewType
+
+SignalId = NewType("SignalId", int)
+
+class SignalSource:
+	"""
+	Handle to a signal source.
+	TODO: More info.
+	"""
+	operation: Operation
+	port_index: int
+
+	def __init__(self, operation: Operation, port_index: int):
+		"""
+		Construct a SignalSource.
+		"""
+		self.operation = operation
+		self.port_index = port_index
+
+	# TODO: More stuff.
+
+class SignalDestination:
+	"""
+	Handle to a signal destination.
+	TODO: More info.
+	"""
+	operation: Operation
+	port_index: int
+
+	def __init__(self, operation: Operation, port_index: int):
+		"""
+		Construct a SignalDestination.
+		"""
+		self.operation = operation
+		self.port_index = port_index
+
+	# TODO: More stuff.
+
+class Signal:
+	"""
+	A connection between two operations consisting of a source and destination handle.
+	TODO: More info.
+	"""
+	_identifier: SignalId
+	source: SignalSource
+	destination: SignalDestination
+
+	def __init__(self, identifier: SignalId, source: SignalSource, destination: SignalDestination):
+		"""
+		Construct a Signal.
+		"""
+		self._identifier = identifier
+		self.source = source
+		self.destination = destination
+
+	def identifier(self) -> SignalId:
+		"""
+		Get the unique identifier.
+		"""
+		return self._identifier
+
+	# TODO: More stuff.
\ No newline at end of file
diff --git a/b_asic/simulation.py b/b_asic/simulation.py
new file mode 100755
index 00000000..aa33cb33
--- /dev/null
+++ b/b_asic/simulation.py
@@ -0,0 +1,42 @@
+"""
+B-ASIC Simulation Module.
+TODO: More info.
+"""
+
+from b_asic.operation import OperationId
+from numbers import Number
+from typing import List, Dict
+
+class OperationState:
+	"""
+	Simulation state of an operation.
+	TODO: More info.
+	"""
+
+	output_values: List[Number]
+	iteration: int
+
+	def __init__(self):
+		"""
+		Construct an OperationState.
+		"""
+		self.output_values = []
+		self.iteration = 0
+
+class SimulationState:
+	"""
+	Simulation state.
+	TODO: More info.
+	"""
+
+	operation_states: Dict[OperationId, OperationState]
+	iteration: int
+
+	def __init__(self):
+		"""
+		Construct a SimulationState.
+		"""
+		self.operation_states = {}
+		self.iteration = 0
+
+	# TODO: More stuff.
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index c3184d1b..75a77ef5 100755
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -15,7 +15,7 @@ int sub(int a, int b) {
 } // namespace asic
 
 PYBIND11_MODULE(_b_asic, m) {
-	m.doc() = "Better ASIC Toolbox Extension Module";
-	m.def("add", &asic::add, "A function which adds two numbers", py::arg("a"), py::arg("b"));
-	m.def("sub", &asic::sub, "A function which subtracts two numbers", py::arg("a"), py::arg("b"));
+	m.doc() = "Better ASIC Toolbox Extension Module.";
+	m.def("add", &asic::add, "A function which adds two numbers.", py::arg("a"), py::arg("b"));
+	m.def("sub", &asic::sub, "A function which subtracts two numbers.", py::arg("a"), py::arg("b"));
 }
\ No newline at end of file
-- 
GitLab


From 8509024bd55d3b07183f7ba93d56a0112b7b3dfe Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Fri, 21 Feb 2020 08:22:25 +0100
Subject: [PATCH 05/50] Update README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index f126999b..20c8c4a2 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
 <img src="https://files.slack.com/files-pri/TSHPRJY83-FTTRW9MQ8/b-asic-logo-opaque.png"  width="318" height="100">
 <br>
-<h3>The leading company in circuit design<h3>
\ No newline at end of file
+<h3>B-ASIC is an ASIC toolbox for Python 3 that simplifies circuit design and optimization.<h3>
\ No newline at end of file
-- 
GitLab


From acc9f36e4d6f5def4d3b2e4c382023f811ac5e8f Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Fri, 21 Feb 2020 09:05:25 +0100
Subject: [PATCH 06/50] Delete helloworld.py

---
 helloworld.py | 1 -
 1 file changed, 1 deletion(-)
 delete mode 100644 helloworld.py

diff --git a/helloworld.py b/helloworld.py
deleted file mode 100644
index 6d95fe97..00000000
--- a/helloworld.py
+++ /dev/null
@@ -1 +0,0 @@
-print("Hello world")
\ No newline at end of file
-- 
GitLab


From b164384a32d0e3ee35b2b192651720581790b2d4 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Fri, 21 Feb 2020 09:07:06 +0100
Subject: [PATCH 07/50] Update .gitlab-ci.yml

---
 .gitlab-ci.yml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2130f26d..78499e52 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,5 +6,4 @@ PythonBuild:
   artifacts:
     untracked: true
   script:
-    - apt-get update && apt-get install python3 -y
-    - python3 helloworld.py
\ No newline at end of file
+    - apt-get update && apt-get install python3 -y
\ No newline at end of file
-- 
GitLab


From 613e90981a08500a1215f80a98ae7b8dcf34d7b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivaha717@student.liu.se>
Date: Mon, 24 Feb 2020 10:20:44 +0100
Subject: [PATCH 08/50] fix sdist by adding CMakeLists.txt to manifest, update
 README.md

---
 .gitignore           |   0
 CMakeLists.txt       |   0
 LICENSE              |   0
 MANIFEST.in          |   1 +
 README.md            |  12 ++++++++++++
 b_asic/__init__.py   |   0
 b_asic/operation.py  |   0
 b_asic/ops.py        |   0
 b_asic/pc.py         |   0
 b_asic/port.py       |   0
 b_asic/schema.py     |   0
 b_asic/sfg.py        |   0
 b_asic/signal.py     |   0
 b_asic/simulation.py |   0
 logo.png             | Bin
 setup.py             |   0
 src/main.cpp         |   0
 17 files changed, 13 insertions(+)
 mode change 100755 => 100644 .gitignore
 mode change 100755 => 100644 CMakeLists.txt
 mode change 100755 => 100644 LICENSE
 mode change 100755 => 100644 MANIFEST.in
 mode change 100755 => 100644 README.md
 mode change 100755 => 100644 b_asic/__init__.py
 mode change 100755 => 100644 b_asic/operation.py
 mode change 100755 => 100644 b_asic/ops.py
 mode change 100755 => 100644 b_asic/pc.py
 mode change 100755 => 100644 b_asic/port.py
 mode change 100755 => 100644 b_asic/schema.py
 mode change 100755 => 100644 b_asic/sfg.py
 mode change 100755 => 100644 b_asic/signal.py
 mode change 100755 => 100644 b_asic/simulation.py
 mode change 100755 => 100644 logo.png
 mode change 100755 => 100644 setup.py
 mode change 100755 => 100644 src/main.cpp

diff --git a/.gitignore b/.gitignore
old mode 100755
new mode 100644
diff --git a/CMakeLists.txt b/CMakeLists.txt
old mode 100755
new mode 100644
diff --git a/LICENSE b/LICENSE
old mode 100755
new mode 100644
diff --git a/MANIFEST.in b/MANIFEST.in
old mode 100755
new mode 100644
index 89a500ee..ce996f6c
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,4 @@
 include README.md
 include LICENSE
+include CMakeLists.txt
 recursive-include src *.cpp *.h
diff --git a/README.md b/README.md
old mode 100755
new mode 100644
index 78df523e..4917b55e
--- a/README.md
+++ b/README.md
@@ -59,6 +59,18 @@ python3 setup.py sdist
 ```
 The output gets written to `B-ASIC/dist`.
 
+#### Installation (Binary distribution)
+In `B-ASIC`:
+```
+python3 -m pip install b_asic-<version>-<cpver>-<cpver>_<arch>.whl
+```
+
+#### Installation (Source distribution)
+In `B-ASIC`:
+```
+python3 -m pip install b-asic-<version>.tar.gz
+```
+
 ## Usage
 How to build and use the library as a user.
 
diff --git a/b_asic/__init__.py b/b_asic/__init__.py
old mode 100755
new mode 100644
diff --git a/b_asic/operation.py b/b_asic/operation.py
old mode 100755
new mode 100644
diff --git a/b_asic/ops.py b/b_asic/ops.py
old mode 100755
new mode 100644
diff --git a/b_asic/pc.py b/b_asic/pc.py
old mode 100755
new mode 100644
diff --git a/b_asic/port.py b/b_asic/port.py
old mode 100755
new mode 100644
diff --git a/b_asic/schema.py b/b_asic/schema.py
old mode 100755
new mode 100644
diff --git a/b_asic/sfg.py b/b_asic/sfg.py
old mode 100755
new mode 100644
diff --git a/b_asic/signal.py b/b_asic/signal.py
old mode 100755
new mode 100644
diff --git a/b_asic/simulation.py b/b_asic/simulation.py
old mode 100755
new mode 100644
diff --git a/logo.png b/logo.png
old mode 100755
new mode 100644
diff --git a/setup.py b/setup.py
old mode 100755
new mode 100644
diff --git a/src/main.cpp b/src/main.cpp
old mode 100755
new mode 100644
-- 
GitLab


From 21124444435a48f821538c4747afb8e92c6536a1 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Mon, 24 Feb 2020 10:55:55 +0100
Subject: [PATCH 09/50] Deleted README.md

---
 README.md | 3 ---
 1 file changed, 3 deletions(-)
 delete mode 100644 README.md

diff --git a/README.md b/README.md
deleted file mode 100644
index 20c8c4a2..00000000
--- a/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-<img src="https://files.slack.com/files-pri/TSHPRJY83-FTTRW9MQ8/b-asic-logo-opaque.png"  width="318" height="100">
-<br>
-<h3>B-ASIC is an ASIC toolbox for Python 3 that simplifies circuit design and optimization.<h3>
\ No newline at end of file
-- 
GitLab


From f1b026aa955bb5baed73467159fa8bafb2b751ad Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Mon, 24 Feb 2020 11:00:59 +0100
Subject: [PATCH 10/50] Revert "Deleted README.md"

This reverts commit 21124444435a48f821538c4747afb8e92c6536a1
---
 README.md | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 README.md

diff --git a/README.md b/README.md
new file mode 100644
index 00000000..20c8c4a2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+<img src="https://files.slack.com/files-pri/TSHPRJY83-FTTRW9MQ8/b-asic-logo-opaque.png"  width="318" height="100">
+<br>
+<h3>B-ASIC is an ASIC toolbox for Python 3 that simplifies circuit design and optimization.<h3>
\ No newline at end of file
-- 
GitLab


From 66a79f9c4a20a5c50d461a8ca9c58f56149f245b Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Mon, 24 Feb 2020 11:15:53 +0100
Subject: [PATCH 11/50] Readded yaml file

---
 .gitlab-ci.yml | 9 +++++++++
 1 file changed, 9 insertions(+)
 create mode 100644 .gitlab-ci.yml

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000..78499e52
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,9 @@
+stages:
+  - build
+
+PythonBuild:
+  stage: build
+  artifacts:
+    untracked: true
+  script:
+    - apt-get update && apt-get install python3 -y
\ No newline at end of file
-- 
GitLab


From 415be01e3c073e4081ff1f8dec338796fae6d896 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivaha717@student.liu.se>
Date: Tue, 25 Feb 2020 00:04:55 +0100
Subject: [PATCH 12/50] fix cmake build output directory on windows

---
 .gitignore     |  3 ++-
 CMakeLists.txt | 35 ++++++++++++++++++-----------------
 LICENSE        |  2 +-
 README.md      |  8 ++++----
 4 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/.gitignore b/.gitignore
index 473e0e34..36987299 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,4 +27,5 @@ ehthumbs_vista.db
 $RECYCLE.BIN/
 *.stackdump
 [Dd]esktop.ini
-*.egg-info
\ No newline at end of file
+*.egg-info
+__pycache__/
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7f6908ed..c2381e4d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,24 +12,26 @@ find_package(pybind11 CONFIG REQUIRED)
 
 set(LIBRARY_NAME "b_asic")
 set(TARGET_NAME "_${LIBRARY_NAME}")
+
 if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
 	include(GNUInstallDirs)
 	set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_INSTALL_LIBDIR}")
 endif()
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
 
-add_library(
-	"${TARGET_NAME}" MODULE
-	"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp"
-)
-add_library(
-	"${TARGET_NAME}:${TARGET_NAME}"
-	ALIAS "${TARGET_NAME}"
-)
-
-set_target_properties(
+pybind11_add_module(
 	"${TARGET_NAME}"
-	PROPERTIES
-		PREFIX ""
+	"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp"
 )
 
 target_include_directories(
@@ -47,14 +49,14 @@ target_compile_options(
 	"${TARGET_NAME}"
 	PRIVATE
 		$<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:
-			-W -Wall -Wextra -Werror -Wno-psabi -fvisibility=hidden
+			-W -Wall -Wextra -Werror -Wno-psabi
 			$<$<CONFIG:Debug>:-g>
-			$<$<NOT:$<CONFIG:Debug>>:-O3>
+			$<$<NOT:$<CONFIG:Debug>>:-O3 -flto>
 		>
 		$<$<CXX_COMPILER_ID:MSVC>:
-			/W3 /WX /permissive- /utf-8
+			/W3 /WX /permissive- /utf-8 /bigobj
 			$<$<CONFIG:Debug>:/Od>
-			$<$<NOT:$<CONFIG:Debug>>:/Ot>
+			$<$<NOT:$<CONFIG:Debug>>:/Ot /GL /LTCG>
 		>
 )
 
@@ -62,7 +64,6 @@ target_link_libraries(
 	"${TARGET_NAME}"
 	PRIVATE
 		fmt::fmt-header-only
-		pybind11::module
 )
 
 add_custom_target(
diff --git a/LICENSE b/LICENSE
index 17010bd5..669ce41e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2020 TDDD96
+Copyright (c) 2020 TDDD96 PUM4
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 4917b55e..b36a5d82 100644
--- a/README.md
+++ b/README.md
@@ -43,26 +43,26 @@ cmake --build . --config Release
 The output gets written to `B-ASIC/build/lib`.
 
 ### Using setuptools to create a package
-How to create a package using setuptools.
+How to create a package using setuptools that can be installed using pip.
 
 #### Setup (Binary distribution)
 In `B-ASIC`:
 ```
 python3 setup.py bdist_wheel
 ```
-The output gets written to `B-ASIC/dist`.
+The output gets written to `B-ASIC/dist/b_asic-<version>-<python_tag>-<abi_tag>-<platform_tag>.whl`.
 
 #### Setup (Source distribution)
 In `B-ASIC`:
 ```
 python3 setup.py sdist
 ```
-The output gets written to `B-ASIC/dist`.
+The output gets written to `B-ASIC/dist/b-asic-<version>.tar.gz`.
 
 #### Installation (Binary distribution)
 In `B-ASIC`:
 ```
-python3 -m pip install b_asic-<version>-<cpver>-<cpver>_<arch>.whl
+python3 -m pip install b_asic-<version>-<python_tag>-<abi_tag>-<platform_tag>.whl
 ```
 
 #### Installation (Source distribution)
-- 
GitLab


From 46082d46e89aa63349e02dd9ea5106d592a1aedb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivaha717@student.liu.se>
Date: Tue, 25 Feb 2020 00:06:41 +0100
Subject: [PATCH 13/50] remove bad compiler flags

---
 CMakeLists.txt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c2381e4d..8ba064fd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,12 +51,12 @@ target_compile_options(
 		$<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:
 			-W -Wall -Wextra -Werror -Wno-psabi
 			$<$<CONFIG:Debug>:-g>
-			$<$<NOT:$<CONFIG:Debug>>:-O3 -flto>
+			$<$<NOT:$<CONFIG:Debug>>:-O3>
 		>
 		$<$<CXX_COMPILER_ID:MSVC>:
-			/W3 /WX /permissive- /utf-8 /bigobj
+			/W3 /WX /permissive- /utf-8
 			$<$<CONFIG:Debug>:/Od>
-			$<$<NOT:$<CONFIG:Debug>>:/Ot /GL /LTCG>
+			$<$<NOT:$<CONFIG:Debug>>:/Ot>
 		>
 )
 
-- 
GitLab


From a5069646bea7bb132ada81ddec26334be6410915 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivaha717@student.liu.se>
Date: Tue, 25 Feb 2020 01:05:00 +0100
Subject: [PATCH 14/50] fix circular module import

---
 CMakeLists.txt                          |   8 +-
 README.md                               |   2 +
 b_asic/__init__.py                      |   7 +-
 b_asic/basic_operation.py               | 114 ++++++++++++++++++++++
 b_asic/{ops.py => core_operations.py}   |   4 +-
 b_asic/operation.py                     | 124 +++---------------------
 b_asic/{pc.py => precedence_chart.py}   |   2 +-
 b_asic/schema.py                        |   2 +-
 b_asic/{sfg.py => signal_flow_graph.py} |   3 +-
 setup.py                                |   6 +-
 10 files changed, 152 insertions(+), 120 deletions(-)
 create mode 100644 b_asic/basic_operation.py
 rename b_asic/{ops.py => core_operations.py} (91%)
 rename b_asic/{pc.py => precedence_chart.py} (89%)
 rename b_asic/{sfg.py => signal_flow_graph.py} (89%)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8ba064fd..433d2746 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,9 +67,15 @@ target_link_libraries(
 )
 
 add_custom_target(
-	copy_python_files ALL
+	remove_old_python_dir ALL
+	COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
+	COMMENT "Removing old python directory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
+)
+add_custom_target(
+	copy_python_dir ALL
 	COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
 	COMMENT "Copying python files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
+	DEPENDS remove_old_python_dir
 )
 add_custom_target(
 	copy_misc_files ALL
diff --git a/README.md b/README.md
index b36a5d82..bcd09857 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,8 @@ The following packages are required in order to build the library:
   * setuptools
   * wheel
   * pybind11
+  * numpy
+  * pyside2/pyqt5
 
 ## Development
 How to build and debug the library during development.
diff --git a/b_asic/__init__.py b/b_asic/__init__.py
index 8bbc17ab..8a84a945 100644
--- a/b_asic/__init__.py
+++ b/b_asic/__init__.py
@@ -3,11 +3,12 @@ Better ASIC Toolbox.
 TODO: More info.
 """
 from _b_asic import *
+from b_asic.basic_operation import *
+from b_asic.core_operations import *
 from b_asic.operation import *
-from b_asic.ops import *
-from b_asic.pc import *
+from b_asic.precedence_chart import *
 from b_asic.port import *
 from b_asic.schema import *
-from b_asic.sfg import *
+from b_asic.signal_flow_graph import *
 from b_asic.signal import *
 from b_asic.simulation import *
\ No newline at end of file
diff --git a/b_asic/basic_operation.py b/b_asic/basic_operation.py
new file mode 100644
index 00000000..e87860c6
--- /dev/null
+++ b/b_asic/basic_operation.py
@@ -0,0 +1,114 @@
+"""
+B-ASIC Basic Operation Module.
+TODO: More info.
+"""
+
+from b_asic.port import InputPort, OutputPort
+from b_asic.signal import SignalSource, SignalDestination
+from b_asic.operation import OperationId, Operation
+from b_asic.simulation import SimulationState, OperationState
+from abc import ABC, abstractmethod
+from typing import List, Dict, Optional, Any, final
+from numbers import Number
+
+class BasicOperation(Operation):
+	"""
+	Generic abstract operation class which most implementations will derive from.
+	TODO: More info.
+	"""
+
+	_identifier: OperationId
+	_input_ports: List[InputPort]
+	_output_ports: List[OutputPort]
+	_parameters: Dict[str, Optional[Any]]
+
+	def __init__(self, identifier: OperationId):
+		"""
+		Construct a BasicOperation.
+		"""
+		self._identifier = identifier
+		self._input_ports = []
+		self._output_ports = []
+		self._parameters = {}
+
+	@abstractmethod
+	def evaluate(self, inputs: list) -> list:
+		"""
+		Evaluate the operation and generate a list of output values given a list of input values.
+		"""
+		pass
+
+	@final
+	def id(self) -> OperationId:
+		return self._identifier
+		
+	@final
+	def inputs(self) -> List[InputPort]:
+		return self._input_ports.copy()
+
+	@final
+	def outputs(self) -> List[OutputPort]:
+		return self._output_ports.copy()
+
+	@final
+	def input_count(self) -> int:
+		return len(self._input_ports)
+
+	@final
+	def output_count(self) -> int:
+		return len(self._output_ports)
+
+	@final
+	def input(self, i: int) -> InputPort:
+		return self._input_ports[i]
+
+	@final
+	def output(self, i: int) -> OutputPort:
+		return self._output_ports[i]
+
+	@final
+	def params(self) -> Dict[str, Optional[Any]]:
+		return self._parameters.copy()
+	
+	@final
+	def param(self, name: str) -> Optional[Any]:
+		return self._parameters.get(name)
+
+	@final
+	def set_param(self, name: str, value: Any) -> None:
+		assert name in self._parameters # TODO: Error message.
+		self._parameters[name] = value
+
+	def evaluate_outputs(self, state: SimulationState) -> List[Number]:
+		# TODO: Check implementation.
+		input_count: int = self.input_count()
+		output_count: int = self.output_count()
+		assert input_count == len(self._input_ports) # TODO: Error message.
+		assert output_count == len(self._output_ports) # TODO: Error message.
+
+		self_state: OperationState = state.operation_states[self.identifier()]
+
+		while self_state.iteration < state.iteration:
+			input_values: List[Number] = [0] * input_count
+			for i in range(input_count):
+				source: SignalSource = self._input_ports[i].signal().source
+				input_values[i] = source.operation.evaluate_outputs(state)[source.port_index]
+
+			self_state.output_values = self.evaluate(input_values)
+			assert len(self_state.output_values) == output_count # TODO: Error message.
+			self_state.iteration += 1
+			for i in range(output_count):
+				for signal in self._output_ports[i].signals():
+					destination: SignalDestination = signal.destination
+					destination.evaluate_outputs(state)
+
+		return self_state.output_values
+
+	def split(self) -> List[Operation]:
+		# TODO: Check implementation.
+		results = self.evaluate(self._input_ports)
+		if all(isinstance(e, Operation) for e in results):
+			return results
+		return [self]
+		
+	# TODO: More stuff.
\ No newline at end of file
diff --git a/b_asic/ops.py b/b_asic/core_operations.py
similarity index 91%
rename from b_asic/ops.py
rename to b_asic/core_operations.py
index 6b370725..553d7cff 100644
--- a/b_asic/ops.py
+++ b/b_asic/core_operations.py
@@ -3,7 +3,9 @@ B-ASIC Core Operations Module.
 TODO: More info.
 """
 
-from b_asic.operation import OperationId, Operation, BasicOperation
+from b_asic.port import InputPort, OutputPort
+from b_asic.operation import OperationId, Operation
+from b_asic.basic_operation import BasicOperation
 from numbers import Number
 from typing import final
 
diff --git a/b_asic/operation.py b/b_asic/operation.py
index 4bea5047..310491bc 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -2,12 +2,14 @@
 B-ASIC Operation Module.
 TODO: More info.
 """
-from b_asic.port import InputPort, OutputPort
-from b_asic.signal import SignalSource, SignalDestination
-from b_asic.simulation import SimulationState, OperationState
+
 from abc import ABC, abstractmethod
 from numbers import Number
-from typing import NewType, List, Dict, Optional, final
+from typing import NewType, List, Dict, Optional, Any, TYPE_CHECKING
+
+if TYPE_CHECKING:
+	from b_asic.port import InputPort, OutputPort
+	from b_asic.simulation import SimulationState
 
 OperationId = NewType("OperationId", int)
 
@@ -21,18 +23,19 @@ class Operation(ABC):
 	def identifier(self) -> OperationId:
 		"""
 		Get the unique identifier.
+		TODO: Move id info to SFG, remove id class members.
 		"""
 		pass
 
 	@abstractmethod
-	def inputs(self) -> List[InputPort]:
+	def inputs(self) -> "List[InputPort]":
 		"""
 		Get a list of all input ports.
 		"""
 		pass
 
 	@abstractmethod
-	def outputs(self) -> List[OutputPort]:
+	def outputs(self) -> "List[OutputPort]":
 		"""
 		Get a list of all output ports.
 		"""
@@ -53,14 +56,14 @@ class Operation(ABC):
 		pass
 
 	@abstractmethod
-	def input(self, i: int) -> InputPort:
+	def input(self, i: int) -> "InputPort":
 		"""
 		Get the input port at index i.
 		"""
 		pass
 
 	@abstractmethod
-	def output(self, i: int) -> OutputPort:
+	def output(self, i: int) -> "OutputPort":
 		"""
 		Get the output port at index i.
 		"""
@@ -90,7 +93,7 @@ class Operation(ABC):
 		pass
 
 	@abstractmethod
-	def evaluate_outputs(self, state: SimulationState) -> List[Number]:
+	def evaluate_outputs(self, state: "SimulationState") -> List[Number]:
 		"""
 		Simulate the circuit until its iteration count matches that of the simulation state,
 		then return the resulting output vector.
@@ -98,7 +101,7 @@ class Operation(ABC):
 		pass
 
 	@abstractmethod
-	def split(self) -> List[Operation]:
+	def split(self) -> "List[Operation]":
 		"""
 		Split the operation into multiple operations.
 		If splitting is not possible, this may return a list containing only the operation itself.
@@ -107,104 +110,3 @@ class Operation(ABC):
 	
 	# TODO: More stuff.
 
-class BasicOperation(ABC, Operation):
-	"""
-	Generic abstract operation class which most implementations will derive from.
-	TODO: More info.
-	"""
-
-	_identifier: OperationId
-	_input_ports: List[InputPort]
-	_output_ports: List[OutputPort]
-	_parameters: Dict[str, Optional[Any]]
-
-	def __init__(self, identifier: OperationId):
-		"""
-		Construct a BasicOperation.
-		"""
-		self._identifier = identifier
-		self._input_ports = []
-		self._output_ports = []
-		self._parameters = {}
-
-	@abstractmethod
-	def evaluate(self, inputs: list) -> list:
-		"""
-		Evaluate the operation and generate a list of output values given a list of input values.
-		"""
-		pass
-
-	@final
-	def id(self) -> OperationId:
-		return self._identifier
-		
-	@final
-	def inputs(self) -> List[InputPort]:
-		return self._input_ports.copy()
-
-	@final
-	def outputs(self) -> List[OutputPort]:
-		return self._output_ports.copy()
-
-	@final
-	def input_count(self) -> int:
-		return len(self._input_ports)
-
-	@final
-	def output_count(self) -> int:
-		return len(self._output_ports)
-
-	@final
-	def input(self, i: int) -> InputPort:
-		return self._input_ports[i]
-
-	@final
-	def output(self, i: int) -> OutputPort:
-		return self._output_ports[i]
-
-	@final
-	def params(self) -> Dict[str, Optional[Any]]:
-		return self._parameters.copy()
-	
-	@final
-	def param(self, name: str) -> Optional[Any]:
-		return self._parameters.get(name)
-
-	@final
-	def set_param(self, name: str, value: Any) -> None:
-		assert name in self._parameters # TODO: Error message.
-		self._parameters[name] = value
-
-	def evaluate_outputs(self, state: SimulationState) -> List[Number]:
-		# TODO: Check implementation.
-		input_count: int = self.input_count()
-		output_count: int = self.output_count()
-		assert input_count == len(self._input_ports) # TODO: Error message.
-		assert output_count == len(self._output_ports) # TODO: Error message.
-
-		self_state: OperationState = state.operation_states[self.identifier()]
-
-		while self_state.iteration < state.iteration:
-			input_values: List[Number] = [0] * input_count
-			for i in range(input_count):
-				source: SignalSource = self._input_ports[i].signal().source
-				input_values[i] = source.operation.evaluate_outputs(state)[source.port_index]
-
-			self_state.output_values = self.evaluate(input_values)
-			assert len(self_state.output_values) == output_count # TODO: Error message.
-			self_state.iteration += 1
-			for i in range(output_count):
-				for signal in self._output_ports[i].signals():
-					destination: SignalDestination = signal.destination
-					destination.evaluate_outputs(state)
-
-		return self_state.output_values
-
-	def split(self) -> List[Operation]:
-		# TODO: Check implementation.
-		results = self.evaluate(self._input_ports)
-		if all(isinstance(e, Operation) for e in results):
-			return results
-		return [self]
-		
-	# TODO: More stuff.
\ No newline at end of file
diff --git a/b_asic/pc.py b/b_asic/precedence_chart.py
similarity index 89%
rename from b_asic/pc.py
rename to b_asic/precedence_chart.py
index cc18d6af..76a22c9f 100644
--- a/b_asic/pc.py
+++ b/b_asic/precedence_chart.py
@@ -3,7 +3,7 @@ B-ASIC Precedence Chart Module.
 TODO: More info.
 """
 
-from b_asic.sfg import SFG
+from b_asic.signal_flow_graph import SFG
 
 class PrecedenceChart:
 	"""
diff --git a/b_asic/schema.py b/b_asic/schema.py
index a7642f49..c7a72526 100644
--- a/b_asic/schema.py
+++ b/b_asic/schema.py
@@ -3,7 +3,7 @@ B-ASIC Schema Module.
 TODO: More info.
 """
 
-from b_asic.pc import PrecedenceChart
+from b_asic.precedence_chart import PrecedenceChart
 
 class Schema:
 	"""
diff --git a/b_asic/sfg.py b/b_asic/signal_flow_graph.py
similarity index 89%
rename from b_asic/sfg.py
rename to b_asic/signal_flow_graph.py
index d39cc524..8fbbebff 100644
--- a/b_asic/sfg.py
+++ b/b_asic/signal_flow_graph.py
@@ -3,7 +3,8 @@ B-ASIC Signal Flow Graph Module.
 TODO: More info.
 """
 
-from b_asic.operation import OperationId, Operation, BasicOperation
+from b_asic.operation import OperationId, Operation
+from b_asic.basic_operation import BasicOperation
 from b_asic.signal import SignalSource, SignalDestination
 from b_asic.simulation import SimulationState, OperationState
 from typing import List, final
diff --git a/setup.py b/setup.py
index aa6d7a6c..71ae4e4a 100644
--- a/setup.py
+++ b/setup.py
@@ -67,7 +67,11 @@ setuptools.setup(
         "Operating System :: OS Independent",
     ],
     python_requires = ">=3.6",
-    install_requires = ["pybind11>=2.3.0"],
+    install_requires = [
+        "pybind11>=2.3.0",
+        "numpy",
+        "install_qt_binding"
+    ],
     packages = ["b_asic"],
     ext_modules = [CMakeExtension("b_asic")],
     cmdclass = {"build_ext": CMakeBuild},
-- 
GitLab


From 706132608ed1021c6e556b8281123a7d5fc51b1e Mon Sep 17 00:00:00 2001
From: Jacob Wahlman <jacwa448@student.liu.se>
Date: Tue, 25 Feb 2020 10:30:57 +0100
Subject: [PATCH 15/50] Removed final to support < 3.8, spacing between classes

---
 b_asic/basic_operation.py   | 21 ++++++---------------
 b_asic/core_operations.py   |  9 ++++-----
 b_asic/operation.py         |  3 ++-
 b_asic/port.py              | 28 ++++++++++------------------
 b_asic/precedence_chart.py  |  3 ++-
 b_asic/schema.py            |  3 ++-
 b_asic/signal.py            |  5 ++++-
 b_asic/signal_flow_graph.py |  7 +++----
 b_asic/simulation.py        |  4 +++-
 9 files changed, 36 insertions(+), 47 deletions(-)

diff --git a/b_asic/basic_operation.py b/b_asic/basic_operation.py
index e87860c6..6810a9a7 100644
--- a/b_asic/basic_operation.py
+++ b/b_asic/basic_operation.py
@@ -8,9 +8,10 @@ from b_asic.signal import SignalSource, SignalDestination
 from b_asic.operation import OperationId, Operation
 from b_asic.simulation import SimulationState, OperationState
 from abc import ABC, abstractmethod
-from typing import List, Dict, Optional, Any, final
+from typing import List, Dict, Optional, Any
 from numbers import Number
 
+
 class BasicOperation(Operation):
 	"""
 	Generic abstract operation class which most implementations will derive from.
@@ -38,43 +39,33 @@ class BasicOperation(Operation):
 		"""
 		pass
 
-	@final
 	def id(self) -> OperationId:
 		return self._identifier
-		
-	@final
+
 	def inputs(self) -> List[InputPort]:
 		return self._input_ports.copy()
 
-	@final
 	def outputs(self) -> List[OutputPort]:
 		return self._output_ports.copy()
 
-	@final
 	def input_count(self) -> int:
 		return len(self._input_ports)
 
-	@final
 	def output_count(self) -> int:
 		return len(self._output_ports)
 
-	@final
 	def input(self, i: int) -> InputPort:
 		return self._input_ports[i]
 
-	@final
 	def output(self, i: int) -> OutputPort:
 		return self._output_ports[i]
 
-	@final
 	def params(self) -> Dict[str, Optional[Any]]:
 		return self._parameters.copy()
-	
-	@final
+
 	def param(self, name: str) -> Optional[Any]:
 		return self._parameters.get(name)
 
-	@final
 	def set_param(self, name: str, value: Any) -> None:
 		assert name in self._parameters # TODO: Error message.
 		self._parameters[name] = value
@@ -110,5 +101,5 @@ class BasicOperation(Operation):
 		if all(isinstance(e, Operation) for e in results):
 			return results
 		return [self]
-		
-	# TODO: More stuff.
\ No newline at end of file
+
+	# TODO: More stuff.
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index 553d7cff..c766d06c 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -7,7 +7,7 @@ from b_asic.port import InputPort, OutputPort
 from b_asic.operation import OperationId, Operation
 from b_asic.basic_operation import BasicOperation
 from numbers import Number
-from typing import final
+
 
 class Input(Operation):
 	"""
@@ -18,6 +18,7 @@ class Input(Operation):
 	# TODO: Implement.
 	pass
 
+
 class Constant(BasicOperation):
 	"""
 	Constant value operation.
@@ -32,10 +33,10 @@ class Constant(BasicOperation):
 		self._output_ports = [OutputPort()] # TODO: Generate appropriate ID for ports.
 		self._parameters["value"] = value
 
-	@final
 	def evaluate(self, inputs: list) -> list:
 		return [self.param("value")]
 
+
 class Addition(BasicOperation):
 	"""
 	Binary addition operation.
@@ -50,7 +51,6 @@ class Addition(BasicOperation):
 		self._input_ports = [InputPort(), InputPort()] # TODO: Generate appropriate ID for ports.
 		self._output_ports = [OutputPort()] # TODO: Generate appropriate ID for ports.
 
-	@final
 	def evaluate(self, inputs: list) -> list:
 		return [inputs[0] + inputs[1]]
 
@@ -70,8 +70,7 @@ class ConstantMultiplication(BasicOperation):
 		self._output_ports = [OutputPort()] # TODO: Generate appropriate ID for ports.
 		self._parameters["coefficient"] = coefficient
 
-	@final
 	def evaluate(self, inputs: list) -> list:
 		return [inputs[0] * self.param("coefficient")]
 
-# TODO: More operations.
\ No newline at end of file
+# TODO: More operations.
diff --git a/b_asic/operation.py b/b_asic/operation.py
index 310491bc..731822f1 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -13,6 +13,7 @@ if TYPE_CHECKING:
 
 OperationId = NewType("OperationId", int)
 
+
 class Operation(ABC):
 	"""
 	Operation interface.
@@ -107,6 +108,6 @@ class Operation(ABC):
 		If splitting is not possible, this may return a list containing only the operation itself.
 		"""
 		pass
-	
+
 	# TODO: More stuff.
 
diff --git a/b_asic/port.py b/b_asic/port.py
index 6b1e8d20..2d5405ed 100644
--- a/b_asic/port.py
+++ b/b_asic/port.py
@@ -5,10 +5,11 @@ TODO: More info.
 
 from b_asic.signal import Signal
 from abc import ABC, abstractmethod
-from typing import NewType, Optional, List, Dict, final
+from typing import NewType, Optional, List, Dict
 
 PortId = NewType("PortId", int)
 
+
 class Port(ABC):
 	"""
 	Abstract port class.
@@ -22,8 +23,7 @@ class Port(ABC):
 		Construct a Port.
 		"""
 		self._identifier = identifier
-	
-	@final
+
 	def identifier(self) -> PortId:
 		"""
 		Get the unique identifier.
@@ -36,7 +36,7 @@ class Port(ABC):
 		Get a list of all connected signals.
 		"""
 		pass
-	
+
 	@abstractmethod
 	def signal_count(self) -> int:
 		"""
@@ -67,6 +67,7 @@ class Port(ABC):
 
 	# TODO: More stuff.
 
+
 class InputPort(Port):
 	"""
 	Input port.
@@ -81,31 +82,27 @@ class InputPort(Port):
 		super().__init__(identifier)
 		self._source_signal = None
 
-	@final
 	def signals(self) -> List[Signal]:
 		return [] if self._source_signal == None else [self._source_signal]
-	
-	@final
+
 	def signal_count(self) -> int:
 		return 0 if self._source_signal == None else 1
 
-	@final
 	def signal(self, i: int = 0) -> Signal:
 		assert i >= 0 and i < self.signal_count() # TODO: Error message.
 		assert self._source_signal != None # TODO: Error message.
 		return self._source_signal
 
-	@final
 	def connect(self, signal: Signal) -> None:
 		self._source_signal = signal
 
-	@final
 	def disconnect(self, i: int = 0) -> None:
 		assert i >= 0 and i < self.signal_count() # TODO: Error message.
 		self._source_signal = None
 
 	# TODO: More stuff.
 
+
 class OutputPort(Port):
 	"""
 	Output port.
@@ -121,27 +118,22 @@ class OutputPort(Port):
 		super().__init__(identifier)
 		self._destination_signals = []
 
-	@final
 	def signals(self) -> List[Signal]:
 		return self._destination_signals.copy()
 
-	@final
 	def signal_count(self) -> int:
 		return len(self._destination_signals)
 
-	@final
 	def signal(self, i: int = 0) -> Signal:
 		assert i >= 0 and i < self.signal_count() # TODO: Error message.
 		return self._destination_signals[i]
 
-	@final
 	def connect(self, signal: Signal) -> None:
 		assert signal not in self._destination_signals # TODO: Error message.
 		self._destination_signals.append(signal)
-	
-	@final
+
 	def disconnect(self, i: int = 0) -> None:
 		assert i >= 0 and i < self.signal_count() # TODO: Error message.
 		del self._destination_signals[i]
-		
-	# TODO: More stuff.
\ No newline at end of file
+
+	# TODO: More stuff.
diff --git a/b_asic/precedence_chart.py b/b_asic/precedence_chart.py
index 76a22c9f..329c78d2 100644
--- a/b_asic/precedence_chart.py
+++ b/b_asic/precedence_chart.py
@@ -5,6 +5,7 @@ TODO: More info.
 
 from b_asic.signal_flow_graph import SFG
 
+
 class PrecedenceChart:
 	"""
 	Precedence chart constructed from a signal flow graph.
@@ -21,4 +22,4 @@ class PrecedenceChart:
 		self.sfg = sfg
 		# TODO: Implement.
 
-	# TODO: More stuff.
\ No newline at end of file
+	# TODO: More stuff.
diff --git a/b_asic/schema.py b/b_asic/schema.py
index c7a72526..56eb47ff 100644
--- a/b_asic/schema.py
+++ b/b_asic/schema.py
@@ -5,6 +5,7 @@ TODO: More info.
 
 from b_asic.precedence_chart import PrecedenceChart
 
+
 class Schema:
 	"""
 	Schema constructed from a precedence chart.
@@ -21,4 +22,4 @@ class Schema:
 		self.pc = pc
 		# TODO: Implement.
 
-	# TODO: More stuff.
\ No newline at end of file
+	# TODO: More stuff.
diff --git a/b_asic/signal.py b/b_asic/signal.py
index 36be9f58..4fac563f 100644
--- a/b_asic/signal.py
+++ b/b_asic/signal.py
@@ -8,6 +8,7 @@ from typing import NewType
 
 SignalId = NewType("SignalId", int)
 
+
 class SignalSource:
 	"""
 	Handle to a signal source.
@@ -25,6 +26,7 @@ class SignalSource:
 
 	# TODO: More stuff.
 
+
 class SignalDestination:
 	"""
 	Handle to a signal destination.
@@ -42,6 +44,7 @@ class SignalDestination:
 
 	# TODO: More stuff.
 
+
 class Signal:
 	"""
 	A connection between two operations consisting of a source and destination handle.
@@ -65,4 +68,4 @@ class Signal:
 		"""
 		return self._identifier
 
-	# TODO: More stuff.
\ No newline at end of file
+	# TODO: More stuff.
diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py
index 8fbbebff..f9671636 100644
--- a/b_asic/signal_flow_graph.py
+++ b/b_asic/signal_flow_graph.py
@@ -7,7 +7,8 @@ from b_asic.operation import OperationId, Operation
 from b_asic.basic_operation import BasicOperation
 from b_asic.signal import SignalSource, SignalDestination
 from b_asic.simulation import SimulationState, OperationState
-from typing import List, final
+from typing import List
+
 
 class SFG(BasicOperation):
 	"""
@@ -27,12 +28,10 @@ class SFG(BasicOperation):
 		# TODO: Traverse the graph between the inputs/outputs and add to self._operations.
 		# TODO: Connect ports with signals with appropriate IDs.
 
-	@final
 	def evaluate(self, inputs: list) -> list:
 		return [] # TODO: Implement
 
-	@final
 	def split(self) -> List[Operation]:
 		return self._operations
 
-	# TODO: More stuff.
\ No newline at end of file
+	# TODO: More stuff.
diff --git a/b_asic/simulation.py b/b_asic/simulation.py
index aa33cb33..e219445b 100644
--- a/b_asic/simulation.py
+++ b/b_asic/simulation.py
@@ -7,6 +7,7 @@ from b_asic.operation import OperationId
 from numbers import Number
 from typing import List, Dict
 
+
 class OperationState:
 	"""
 	Simulation state of an operation.
@@ -23,6 +24,7 @@ class OperationState:
 		self.output_values = []
 		self.iteration = 0
 
+
 class SimulationState:
 	"""
 	Simulation state.
@@ -39,4 +41,4 @@ class SimulationState:
 		self.operation_states = {}
 		self.iteration = 0
 
-	# TODO: More stuff.
\ No newline at end of file
+	# TODO: More stuff.
-- 
GitLab


From aacf7a8385a79d1dd494dfeba9d09d3a797c70ef Mon Sep 17 00:00:00 2001
From: Kevin Scott <kevsc634@student.liu.se>
Date: Tue, 25 Feb 2020 11:54:30 +0100
Subject: [PATCH 16/50] Added support for pytest and updated readme

---
 README.md          | 13 ++++++++++++-
 setup.py           |  3 ++-
 tests/__init__.py  |  0
 tests/test_port.py | 10 ++++++++++
 4 files changed, 24 insertions(+), 2 deletions(-)
 create mode 100644 tests/__init__.py
 create mode 100644 tests/test_port.py

diff --git a/README.md b/README.md
index bcd09857..97c65156 100644
--- a/README.md
+++ b/README.md
@@ -88,6 +88,17 @@ python3
 >>> help(asic)
 ```
 
+## Running tests
+### pytest
+Run all tests against installed version off b_asic using a virtual enviroment
+In `B-ASIC`:
+```
+python3 -m venv .venv
+source .venv/bin/activate
+pip install .
+pytest
+```
+
 ## License
 B-ASIC is distributed under the MIT license.
-See the included LICENSE file for more information.
\ No newline at end of file
+See the included LICENSE file for more information.
diff --git a/setup.py b/setup.py
index 71ae4e4a..0b591cbb 100644
--- a/setup.py
+++ b/setup.py
@@ -70,7 +70,8 @@ setuptools.setup(
     install_requires = [
         "pybind11>=2.3.0",
         "numpy",
-        "install_qt_binding"
+        "install_qt_binding",
+        "pytest==5.3.4"
     ],
     packages = ["b_asic"],
     ext_modules = [CMakeExtension("b_asic")],
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/test_port.py b/tests/test_port.py
new file mode 100644
index 00000000..498233bb
--- /dev/null
+++ b/tests/test_port.py
@@ -0,0 +1,10 @@
+from b_asic import InputPort
+import pytest
+
+@pytest.fixture
+def outp_port():
+    return InputPort(0)
+
+
+def test_port(outp_port):
+    assert outp_port.signals() == []
-- 
GitLab


From 94a50a8e9abf35c607f128c1fc124cbe279f8fe3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivaha717@student.liu.se>
Date: Tue, 25 Feb 2020 12:29:58 +0100
Subject: [PATCH 17/50] add clang-format, add basic ctest files, fix README

---
 .clang-format       | 151 ++++++++++++++++++++++++++++++++++++++++++++
 CMakeLists.txt      |   8 ++-
 README.md           |   4 +-
 test/CMakeLists.txt |   9 +++
 test/test_main.cpp  |   5 ++
 5 files changed, 173 insertions(+), 4 deletions(-)
 create mode 100644 .clang-format
 create mode 100644 test/CMakeLists.txt
 create mode 100644 test/test_main.cpp

diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..7548f76b
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,151 @@
+AccessModifierOffset: -4
+
+AlignAfterOpenBracket: DontAlign
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignConsecutiveMacros: true
+AlignEscapedNewlines: DontAlign
+AlignOperands: false
+AlignTrailingComments: true
+
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Empty
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLambdasOnASingleLine: Inline
+AllowShortLoopsOnASingleLine: false
+
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: Yes
+
+BinPackArguments: false
+BinPackParameters: true
+
+BreakBeforeBraces: Custom
+BraceWrapping:
+  AfterCaseLabel: false
+  AfterClass: false
+  AfterControlStatement: false
+  AfterEnum: false
+  AfterFunction: false
+  AfterNamespace: false
+  AfterObjCDeclaration: false
+  AfterStruct: false
+  AfterUnion: false
+  AfterExternBlock: false
+  BeforeCatch: false
+  BeforeElse: false
+  IndentBraces: false
+  SplitEmptyFunction: false
+  SplitEmptyRecord: false
+  SplitEmptyNamespace: true
+
+BreakBeforeBinaryOperators: None
+BreakBeforeTernaryOperators: false
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: BeforeComma
+BreakStringLiterals: true
+
+ColumnLimit: 140
+
+CommentPragmas: ''
+
+CompactNamespaces: false
+
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 4
+
+ContinuationIndentWidth: 4
+
+Cpp11BracedListStyle: true
+
+DerivePointerAlignment: false
+
+DisableFormat: false
+
+FixNamespaceComments: true
+
+ForEachMacros:
+  - Q_FOREACH
+  - BOOST_FOREACH
+  - FOREACH
+  - FOR_EACH
+
+IncludeBlocks: Regroup
+IncludeCategories:
+  - Regex: '^<'
+    Priority: 2
+  - Regex: '.*'
+    Priority: 1
+IncludeIsMainRegex: '(_test)?$'
+
+IndentCaseLabels: true
+IndentGotoLabels: false
+IndentPPDirectives: None
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+
+KeepEmptyLinesAtTheStartOfBlocks: false
+
+Language: Cpp
+
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+
+MaxEmptyLinesToKeep: 1
+
+NamespaceIndentation: None
+NamespaceMacros:
+  - NAMESPACE
+
+PenaltyBreakAssignment: 100
+PenaltyBreakBeforeFirstCallParameter: 10
+PenaltyBreakComment: 10
+PenaltyBreakFirstLessLess: 100
+PenaltyBreakString: 10
+PenaltyBreakTemplateDeclaration: 10000
+PenaltyExcessCharacter: 999999
+PenaltyReturnTypeOnItsOwnLine: 10000
+
+PointerAlignment: Left
+
+ReflowComments: false
+
+SortIncludes: true
+SortUsingDeclarations: true
+
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyBlock: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInContainerLiterals: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+
+Standard: Cpp11
+
+StatementMacros:
+  - Q_UNUSED
+
+TabWidth: 4
+
+TypenameMacros:
+  - STACK_OF
+  - LIST
+  - LIST_ENTRY
+
+UseTab: ForContinuationAndIndentation
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 433d2746..ab4d1bf5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,10 +75,14 @@ add_custom_target(
 	copy_python_dir ALL
 	COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
 	COMMENT "Copying python files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
-	DEPENDS remove_old_python_dir
+	DEPENDS "${TARGET_NAME}" remove_old_python_dir
 )
 add_custom_target(
 	copy_misc_files ALL
 	COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/README.md" "${CMAKE_CURRENT_LIST_DIR}/LICENSE" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
 	COMMENT "Copying misc. files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
-)
\ No newline at end of file
+	DEPENDS "${TARGET_NAME}"
+)
+
+enable_testing()
+add_subdirectory(test)
\ No newline at end of file
diff --git a/README.md b/README.md
index bcd09857..54440d52 100644
--- a/README.md
+++ b/README.md
@@ -62,13 +62,13 @@ python3 setup.py sdist
 The output gets written to `B-ASIC/dist/b-asic-<version>.tar.gz`.
 
 #### Installation (Binary distribution)
-In `B-ASIC`:
+In `B-ASIC/dist`:
 ```
 python3 -m pip install b_asic-<version>-<python_tag>-<abi_tag>-<platform_tag>.whl
 ```
 
 #### Installation (Source distribution)
-In `B-ASIC`:
+In `B-ASIC/dist`:
 ```
 python3 -m pip install b-asic-<version>.tar.gz
 ```
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 00000000..c9fd7f20
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.13)
+
+project("B-ASIC-test")
+
+add_executable(
+	test-main
+	"${CMAKE_CURRENT_SOURCE_DIR}/test_main.cpp"
+)
+add_test(NAME test-main COMMAND test-main)
\ No newline at end of file
diff --git a/test/test_main.cpp b/test/test_main.cpp
new file mode 100644
index 00000000..30ea92f7
--- /dev/null
+++ b/test/test_main.cpp
@@ -0,0 +1,5 @@
+// TODO: Tests
+
+int main() {
+	return 0;
+}
\ No newline at end of file
-- 
GitLab


From 7175f248593299ec06353bbf2ab5f719fc2419b8 Mon Sep 17 00:00:00 2001
From: Jacob Wahlman <jacwa448@student.liu.se>
Date: Tue, 25 Feb 2020 14:41:29 +0100
Subject: [PATCH 18/50] Fixed invalid abstract method override

---
 b_asic/basic_operation.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/b_asic/basic_operation.py b/b_asic/basic_operation.py
index 6810a9a7..d5d03045 100644
--- a/b_asic/basic_operation.py
+++ b/b_asic/basic_operation.py
@@ -39,7 +39,7 @@ class BasicOperation(Operation):
 		"""
 		pass
 
-	def id(self) -> OperationId:
+	def identifier(self) -> OperationId:
 		return self._identifier
 
 	def inputs(self) -> List[InputPort]:
-- 
GitLab


From efcd8394cc64fbd490899e8a2ff559301ecfa56f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivaha717@student.liu.se>
Date: Tue, 25 Feb 2020 16:35:57 +0100
Subject: [PATCH 19/50] fix test setup

---
 .gitignore                  |  4 +++-
 CMakeLists.txt              | 11 +--------
 README.md                   | 46 ++++++++++++++++++++++---------------
 test/CMakeLists.txt         |  9 --------
 {tests => test}/__init__.py |  0
 test/test_main.cpp          |  5 ----
 test/test_port.py           |  9 ++++++++
 tests/test_port.py          | 10 --------
 8 files changed, 41 insertions(+), 53 deletions(-)
 delete mode 100644 test/CMakeLists.txt
 rename {tests => test}/__init__.py (100%)
 delete mode 100644 test/test_main.cpp
 create mode 100644 test/test_port.py
 delete mode 100644 tests/test_port.py

diff --git a/.gitignore b/.gitignore
index 36987299..0bdf5476 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,4 +28,6 @@ $RECYCLE.BIN/
 *.stackdump
 [Dd]esktop.ini
 *.egg-info
-__pycache__/
\ No newline at end of file
+__pycache__/
+env/
+venv/
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ab4d1bf5..bcd7af2e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -76,13 +76,4 @@ add_custom_target(
 	COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
 	COMMENT "Copying python files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
 	DEPENDS "${TARGET_NAME}" remove_old_python_dir
-)
-add_custom_target(
-	copy_misc_files ALL
-	COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/README.md" "${CMAKE_CURRENT_LIST_DIR}/LICENSE" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
-	COMMENT "Copying misc. files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
-	DEPENDS "${TARGET_NAME}"
-)
-
-enable_testing()
-add_subdirectory(test)
\ No newline at end of file
+)
\ No newline at end of file
diff --git a/README.md b/README.md
index 4552c3b6..58025fbb 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,10 @@
 # B-ASIC - Better ASIC Toolbox
 B-ASIC is an ASIC toolbox for Python 3 that simplifies circuit design and optimization.
 
-## Prerequisites
+## Development
+How to build and debug the library during development.
+
+### Prerequisites
 The following packages are required in order to build the library:
 * cmake 3.8+
   * gcc 7+/clang 7+/msvc 16+
@@ -16,9 +19,6 @@ The following packages are required in order to build the library:
   * numpy
   * pyside2/pyqt5
 
-## Development
-How to build and debug the library during development.
-
 ### Using CMake directly
 How to build using CMake.
 
@@ -64,13 +64,34 @@ The output gets written to `B-ASIC/dist/b-asic-<version>.tar.gz`.
 #### Installation (Binary distribution)
 In `B-ASIC/dist`:
 ```
-python3 -m pip install b_asic-<version>-<python_tag>-<abi_tag>-<platform_tag>.whl
+pip install b_asic-<version>-<python_tag>-<abi_tag>-<platform_tag>.whl
 ```
 
 #### Installation (Source distribution)
 In `B-ASIC/dist`:
 ```
-python3 -m pip install b-asic-<version>.tar.gz
+pip install b-asic-<version>.tar.gz
+```
+
+### Running tests
+How to run the tests using pytest in a virtual environment.
+
+#### Linux/OS X
+In `B-ASIC`:
+```
+python3 -m venv env
+source env/bin/activate
+pip install .
+pytest
+```
+
+#### Windows
+In `B-ASIC` (as admin):
+```
+python3 -m venv env
+.\env\Scripts\activate.bat
+pip install .
+pytest
 ```
 
 ## Usage
@@ -78,7 +99,7 @@ How to build and use the library as a user.
 
 ### Installation
 ```
-python3 -m pip install b_asic
+pip install b_asic
 ```
 
 ### Importing
@@ -88,17 +109,6 @@ python3
 >>> help(asic)
 ```
 
-## Running tests
-### pytest
-Run all tests against installed version off b_asic using a virtual enviroment
-In `B-ASIC`:
-```
-python3 -m venv .venv
-source .venv/bin/activate
-pip install .
-pytest
-```
-
 ## License
 B-ASIC is distributed under the MIT license.
 See the included LICENSE file for more information.
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
deleted file mode 100644
index c9fd7f20..00000000
--- a/test/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-cmake_minimum_required(VERSION 3.13)
-
-project("B-ASIC-test")
-
-add_executable(
-	test-main
-	"${CMAKE_CURRENT_SOURCE_DIR}/test_main.cpp"
-)
-add_test(NAME test-main COMMAND test-main)
\ No newline at end of file
diff --git a/tests/__init__.py b/test/__init__.py
similarity index 100%
rename from tests/__init__.py
rename to test/__init__.py
diff --git a/test/test_main.cpp b/test/test_main.cpp
deleted file mode 100644
index 30ea92f7..00000000
--- a/test/test_main.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// TODO: Tests
-
-int main() {
-	return 0;
-}
\ No newline at end of file
diff --git a/test/test_port.py b/test/test_port.py
new file mode 100644
index 00000000..62e17ea5
--- /dev/null
+++ b/test/test_port.py
@@ -0,0 +1,9 @@
+import b_asic
+import pytest
+
+@pytest.fixture
+def outp_port():
+    return b_asic.InputPort(0)
+
+def test_port(outp_port):
+    assert outp_port.signals() == []
\ No newline at end of file
diff --git a/tests/test_port.py b/tests/test_port.py
deleted file mode 100644
index 498233bb..00000000
--- a/tests/test_port.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from b_asic import InputPort
-import pytest
-
-@pytest.fixture
-def outp_port():
-    return InputPort(0)
-
-
-def test_port(outp_port):
-    assert outp_port.signals() == []
-- 
GitLab


From faf0f394dd6973e86fcb914c8c669ab463f2caf4 Mon Sep 17 00:00:00 2001
From: Kevin Scott <kevsc634@student.liu.se>
Date: Wed, 26 Feb 2020 15:57:24 +0100
Subject: [PATCH 20/50] Updated README, added package for test coverage and
 added some test files

---
 README.md               |  5 +++++
 setup.py                |  3 ++-
 test/test_inputport.py  | 28 ++++++++++++++++++++++++++++
 test/test_outputport.py | 28 ++++++++++++++++++++++++++++
 test/test_port.py       | 32 +++++++++++++++++++++++++++-----
 test/test_signal.py     | 28 ++++++++++++++++++++++++++++
 6 files changed, 118 insertions(+), 6 deletions(-)
 create mode 100644 test/test_inputport.py
 create mode 100644 test/test_outputport.py
 create mode 100644 test/test_signal.py

diff --git a/README.md b/README.md
index 58025fbb..bb71c4d7 100644
--- a/README.md
+++ b/README.md
@@ -94,6 +94,11 @@ pip install .
 pytest
 ```
 
+#### Test with coverage
+```
+pytest --cov=b_asic --cov-report html test
+```
+
 ## Usage
 How to build and use the library as a user.
 
diff --git a/setup.py b/setup.py
index 0b591cbb..cfe8605f 100644
--- a/setup.py
+++ b/setup.py
@@ -71,7 +71,8 @@ setuptools.setup(
         "pybind11>=2.3.0",
         "numpy",
         "install_qt_binding",
-        "pytest==5.3.4"
+        "pytest==5.3.4",
+        "pytest-cov==2.8.1"
     ],
     packages = ["b_asic"],
     ext_modules = [CMakeExtension("b_asic")],
diff --git a/test/test_inputport.py b/test/test_inputport.py
new file mode 100644
index 00000000..0cb3cab9
--- /dev/null
+++ b/test/test_inputport.py
@@ -0,0 +1,28 @@
+"""
+B-ASIC test suite for Inputport
+TODO: More info
+"""
+from b_asic import InputPort, Signal, SignalSource, SignalDestination, Addition
+import pytest
+
+@pytest.fixture
+def signal():
+    source = SignalSource(Addition(0), 1)
+    dest = SignalDestination(Addition(1),2)
+    return Signal(source, dest)
+
+def test_connect_multple_signals(signal):
+    """
+    make sure we can only connect one signal to an input port
+    """
+    inp_port = InputPort(0)
+    inp_port.connect(signal)
+
+    # create new signal
+    source = SignalSource(Addition(0), 3)
+    dest = SignalDestination(Addition(1),4)
+    new_signal = Signal(source, dest)
+
+    inp_port.connect(new_signal)
+    inp_port.signal_count() == 1
+    assert inp_port.signals() == [new_signal]
\ No newline at end of file
diff --git a/test/test_outputport.py b/test/test_outputport.py
new file mode 100644
index 00000000..35331670
--- /dev/null
+++ b/test/test_outputport.py
@@ -0,0 +1,28 @@
+"""
+B-ASIC test suite for InputPort
+TODO: More info
+"""
+from b_asic import OutputPort, Signal, SignalSource, SignalDestination, Addition
+import pytest
+
+@pytest.fixture
+def signal():
+    source = SignalSource(Addition(0), 1)
+    dest = SignalDestination(Addition(1),2)
+    return Signal(source, dest)
+
+def test_connect_multiple_signals(signal):
+    """
+    make sure we can connect multiple signals to an output port
+    """
+    outp_port = OutputPort(0)
+    outp_port.connect(signal)
+
+    # create new signal
+    source = SignalSource(Addition(0), 3)
+    dest = SignalDestination(Addition(1),4)
+    new_signal = Signal(source, dest)
+
+    outp_port.connect(new_signal)
+    outp_port.signal_count() == 2
+    assert outp_port.signals() == [signal, new_signal]
\ No newline at end of file
diff --git a/test/test_port.py b/test/test_port.py
index 62e17ea5..f4e039a9 100644
--- a/test/test_port.py
+++ b/test/test_port.py
@@ -1,9 +1,31 @@
-import b_asic
+"""
+B-ASIC test suite for Port interface, place all general test cases for abstract class Port here
+TODO: More info
+"""
+
+from b_asic import InputPort, OutputPort, Signal, SignalSource, SignalDestination, Addition
 import pytest
 
 @pytest.fixture
-def outp_port():
-    return b_asic.InputPort(0)
+def signal():
+    source = SignalSource(Addition(0), 1)
+    dest = SignalDestination(Addition(1),2)
+    return Signal(source, dest)
+
+def test_connect_one_signal_to_port(signal):
+    port = InputPort(0)
+    port.connect(signal)
+    assert len(port.signals()) == 1
+    assert port.signal() == signal
+
+def test_change_port_signal():
+    source = SignalSource(Addition(0), 1)
+    dest = SignalDestination(Addition(1),2)
+    signal1 = Signal(source, dest)
+    signal2 = Signal(source, dest)
 
-def test_port(outp_port):
-    assert outp_port.signals() == []
\ No newline at end of file
+    port = InputPort(0)
+    port.connect(signal1)
+    assert port.signal() == signal1
+    port.connect(signal2)
+    assert port.signal() == signal2
\ No newline at end of file
diff --git a/test/test_signal.py b/test/test_signal.py
new file mode 100644
index 00000000..35d51eb5
--- /dev/null
+++ b/test/test_signal.py
@@ -0,0 +1,28 @@
+from b_asic import Signal, SignalSource, SignalDestination
+from b_asic.core_operations import Addition 
+import pytest
+
+# TODO mock operation
+# Use port index
+
+@pytest.fixture
+def operation():
+    return Addition(0)
+
+@pytest.fixture
+def signal_dest(operation):
+    return SignalDestination(operation, 0)
+
+@pytest.fixture
+def signal_src(operation):
+    return SignalSource(operation, 0)
+
+def test_construct_source_signal(operation):
+    s = SignalSource(operation, 0)
+    assert True
+
+
+def test_construct_signal():
+    s = Signal(signal_src, signal_dest)
+    assert True
+
-- 
GitLab


From 541b13945e439d8d5a1007a64da47c572310f471 Mon Sep 17 00:00:00 2001
From: Kevin Scott <kevsc634@student.liu.se>
Date: Thu, 27 Feb 2020 11:57:12 +0100
Subject: [PATCH 21/50] fixed error when running tests and restructured tests

---
 test/conftest.py             |  2 ++
 test/fixtures/__init__.py    |  0
 test/fixtures/signal.py      | 20 ++++++++++++++++++++
 test/port/__init__.py        |  0
 test/port/test_inputport.py  | 23 +++++++++++++++++++++++
 test/port/test_outputport.py | 18 ++++++++++++++++++
 test/{ => port}/test_port.py | 10 ++--------
 test/test_inputport.py       | 28 ----------------------------
 test/test_outputport.py      | 28 ----------------------------
 test/test_signal.py          | 28 ----------------------------
 10 files changed, 65 insertions(+), 92 deletions(-)
 create mode 100644 test/conftest.py
 create mode 100644 test/fixtures/__init__.py
 create mode 100644 test/fixtures/signal.py
 create mode 100644 test/port/__init__.py
 create mode 100644 test/port/test_inputport.py
 create mode 100644 test/port/test_outputport.py
 rename test/{ => port}/test_port.py (73%)
 delete mode 100644 test/test_inputport.py
 delete mode 100644 test/test_outputport.py
 delete mode 100644 test/test_signal.py

diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644
index 00000000..986af94c
--- /dev/null
+++ b/test/conftest.py
@@ -0,0 +1,2 @@
+import pytest
+from test.fixtures.signal import *
\ No newline at end of file
diff --git a/test/fixtures/__init__.py b/test/fixtures/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/fixtures/signal.py b/test/fixtures/signal.py
new file mode 100644
index 00000000..5fbdcf2b
--- /dev/null
+++ b/test/fixtures/signal.py
@@ -0,0 +1,20 @@
+import pytest
+from b_asic import Signal, SignalSource, SignalDestination, Addition
+
+"""
+Use a fixture for initializing objects and pass them as argument to a test function
+"""
+@pytest.fixture
+def signal():
+    source = SignalSource(Addition(0), 1)
+    dest = SignalDestination(Addition(1), 2)
+    return Signal(0, source, dest)
+
+@pytest.fixture
+def signals():
+    ret = []
+    for i in range(0,3):
+        source = SignalSource(Addition(0), 1)
+        dest = SignalDestination(Addition(1), 2)
+        ret.append(Signal(i, source, dest))
+    return ret
\ No newline at end of file
diff --git a/test/port/__init__.py b/test/port/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/port/test_inputport.py b/test/port/test_inputport.py
new file mode 100644
index 00000000..d761900a
--- /dev/null
+++ b/test/port/test_inputport.py
@@ -0,0 +1,23 @@
+"""
+B-ASIC test suite for Inputport
+"""
+
+# import module we are testing
+from b_asic import InputPort
+
+# import dependencies
+from b_asic import Signal, SignalSource, SignalDestination, Addition
+
+import pytest
+
+def test_connect_multiple_signals(signals):
+    """
+    test if only one signal can connect to an input port
+    """
+    inp_port = InputPort(0)
+
+    for s in signals:
+        inp_port.connect(s)
+
+    assert inp_port.signal_count() == 1
+    assert inp_port.signals()[0] == signals[-1]
\ No newline at end of file
diff --git a/test/port/test_outputport.py b/test/port/test_outputport.py
new file mode 100644
index 00000000..5f7b8f49
--- /dev/null
+++ b/test/port/test_outputport.py
@@ -0,0 +1,18 @@
+"""
+B-ASIC test suite for InputPort
+TODO: More info
+"""
+from b_asic import OutputPort, Signal, SignalSource, SignalDestination, Addition
+import pytest
+
+def test_connect_multiple_signals(signals):
+    """
+    test if multiple signals can connect to an output port
+    """
+    outp_port = OutputPort(0)
+
+    for s in signals:
+        outp_port.connect(s)
+
+    assert outp_port.signal_count() == 3
+    assert outp_port.signals() == signals
\ No newline at end of file
diff --git a/test/test_port.py b/test/port/test_port.py
similarity index 73%
rename from test/test_port.py
rename to test/port/test_port.py
index f4e039a9..56cb9be2 100644
--- a/test/test_port.py
+++ b/test/port/test_port.py
@@ -1,16 +1,10 @@
 """
 B-ASIC test suite for Port interface, place all general test cases for abstract class Port here
-TODO: More info
 """
 
 from b_asic import InputPort, OutputPort, Signal, SignalSource, SignalDestination, Addition
 import pytest
 
-@pytest.fixture
-def signal():
-    source = SignalSource(Addition(0), 1)
-    dest = SignalDestination(Addition(1),2)
-    return Signal(source, dest)
 
 def test_connect_one_signal_to_port(signal):
     port = InputPort(0)
@@ -21,8 +15,8 @@ def test_connect_one_signal_to_port(signal):
 def test_change_port_signal():
     source = SignalSource(Addition(0), 1)
     dest = SignalDestination(Addition(1),2)
-    signal1 = Signal(source, dest)
-    signal2 = Signal(source, dest)
+    signal1 = Signal(1, source, dest)
+    signal2 = Signal(2, source, dest)
 
     port = InputPort(0)
     port.connect(signal1)
diff --git a/test/test_inputport.py b/test/test_inputport.py
deleted file mode 100644
index 0cb3cab9..00000000
--- a/test/test_inputport.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""
-B-ASIC test suite for Inputport
-TODO: More info
-"""
-from b_asic import InputPort, Signal, SignalSource, SignalDestination, Addition
-import pytest
-
-@pytest.fixture
-def signal():
-    source = SignalSource(Addition(0), 1)
-    dest = SignalDestination(Addition(1),2)
-    return Signal(source, dest)
-
-def test_connect_multple_signals(signal):
-    """
-    make sure we can only connect one signal to an input port
-    """
-    inp_port = InputPort(0)
-    inp_port.connect(signal)
-
-    # create new signal
-    source = SignalSource(Addition(0), 3)
-    dest = SignalDestination(Addition(1),4)
-    new_signal = Signal(source, dest)
-
-    inp_port.connect(new_signal)
-    inp_port.signal_count() == 1
-    assert inp_port.signals() == [new_signal]
\ No newline at end of file
diff --git a/test/test_outputport.py b/test/test_outputport.py
deleted file mode 100644
index 35331670..00000000
--- a/test/test_outputport.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""
-B-ASIC test suite for InputPort
-TODO: More info
-"""
-from b_asic import OutputPort, Signal, SignalSource, SignalDestination, Addition
-import pytest
-
-@pytest.fixture
-def signal():
-    source = SignalSource(Addition(0), 1)
-    dest = SignalDestination(Addition(1),2)
-    return Signal(source, dest)
-
-def test_connect_multiple_signals(signal):
-    """
-    make sure we can connect multiple signals to an output port
-    """
-    outp_port = OutputPort(0)
-    outp_port.connect(signal)
-
-    # create new signal
-    source = SignalSource(Addition(0), 3)
-    dest = SignalDestination(Addition(1),4)
-    new_signal = Signal(source, dest)
-
-    outp_port.connect(new_signal)
-    outp_port.signal_count() == 2
-    assert outp_port.signals() == [signal, new_signal]
\ No newline at end of file
diff --git a/test/test_signal.py b/test/test_signal.py
deleted file mode 100644
index 35d51eb5..00000000
--- a/test/test_signal.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from b_asic import Signal, SignalSource, SignalDestination
-from b_asic.core_operations import Addition 
-import pytest
-
-# TODO mock operation
-# Use port index
-
-@pytest.fixture
-def operation():
-    return Addition(0)
-
-@pytest.fixture
-def signal_dest(operation):
-    return SignalDestination(operation, 0)
-
-@pytest.fixture
-def signal_src(operation):
-    return SignalSource(operation, 0)
-
-def test_construct_source_signal(operation):
-    s = SignalSource(operation, 0)
-    assert True
-
-
-def test_construct_signal():
-    s = Signal(signal_src, signal_dest)
-    assert True
-
-- 
GitLab


From 3dbd937da8ac0695430851f84441525708843e1e Mon Sep 17 00:00:00 2001
From: Jacob Wahlman <jacwa448@student.liu.se>
Date: Mon, 2 Mar 2020 13:34:15 +0100
Subject: [PATCH 22/50] Move OperationID from operation to signal_flow_graph
 and change sfg operations datastructure to a dict

---
 b_asic/basic_operation.py                     | 18 ++--
 b_asic/core_operations.py                     | 35 +++++---
 b_asic/graph_id.py                            | 30 +++++++
 b_asic/operation.py                           | 24 +++---
 b_asic/signal.py                              | 13 +--
 b_asic/signal_flow_graph.py                   | 65 +++++++++++---
 b_asic/simulation.py                          |  3 +-
 b_asic/traverse_tree.py                       | 54 ++++++++++++
 test/fixtures/signal.py                       | 14 +--
 test/graph_id/conftest.py                     |  1 +
 test/graph_id/test_graph_id_generator.py      | 29 +++++++
 test/port/test_port.py                        |  8 +-
 test/signal_flow_graph/conftest.py            |  1 +
 .../test_signal_flow_graph.py                 |  3 +
 test/traverse/test_traverse_tree.py           | 85 +++++++++++++++++++
 15 files changed, 314 insertions(+), 69 deletions(-)
 create mode 100644 b_asic/graph_id.py
 create mode 100644 b_asic/traverse_tree.py
 create mode 100644 test/graph_id/conftest.py
 create mode 100644 test/graph_id/test_graph_id_generator.py
 create mode 100644 test/signal_flow_graph/conftest.py
 create mode 100644 test/signal_flow_graph/test_signal_flow_graph.py
 create mode 100644 test/traverse/test_traverse_tree.py

diff --git a/b_asic/basic_operation.py b/b_asic/basic_operation.py
index d5d03045..4f7b426b 100644
--- a/b_asic/basic_operation.py
+++ b/b_asic/basic_operation.py
@@ -5,7 +5,7 @@ TODO: More info.
 
 from b_asic.port import InputPort, OutputPort
 from b_asic.signal import SignalSource, SignalDestination
-from b_asic.operation import OperationId, Operation
+from b_asic.operation import Operation
 from b_asic.simulation import SimulationState, OperationState
 from abc import ABC, abstractmethod
 from typing import List, Dict, Optional, Any
@@ -18,16 +18,14 @@ class BasicOperation(Operation):
 	TODO: More info.
 	"""
 
-	_identifier: OperationId
 	_input_ports: List[InputPort]
 	_output_ports: List[OutputPort]
 	_parameters: Dict[str, Optional[Any]]
 
-	def __init__(self, identifier: OperationId):
+	def __init__(self):
 		"""
 		Construct a BasicOperation.
 		"""
-		self._identifier = identifier
 		self._input_ports = []
 		self._output_ports = []
 		self._parameters = {}
@@ -39,9 +37,6 @@ class BasicOperation(Operation):
 		"""
 		pass
 
-	def identifier(self) -> OperationId:
-		return self._identifier
-
 	def inputs(self) -> List[InputPort]:
 		return self._input_ports.copy()
 
@@ -102,4 +97,13 @@ class BasicOperation(Operation):
 			return results
 		return [self]
 
+	@property
+	def neighbours(self) -> List[Operation]:
+		neighbours: List[Operation] = []
+		for port in self._output_ports + self._input_ports:
+			for signal in port.signals():
+				neighbours += [signal.source.operation, signal.destination.operation]
+
+		return neighbours
+
 	# TODO: More stuff.
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index c766d06c..bca344cf 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -4,8 +4,9 @@ TODO: More info.
 """
 
 from b_asic.port import InputPort, OutputPort
-from b_asic.operation import OperationId, Operation
+from b_asic.operation import Operation
 from b_asic.basic_operation import BasicOperation
+from b_asic.graph_id import GraphIDType
 from numbers import Number
 
 
@@ -15,7 +16,7 @@ class Input(Operation):
 	TODO: More info.
 	"""
 
-	# TODO: Implement.
+	# TODO: Implement all functions.
 	pass
 
 
@@ -25,17 +26,19 @@ class Constant(BasicOperation):
 	TODO: More info.
 	"""
 
-	def __init__(self, identifier: OperationId, value: Number):
+	def __init__(self, value: Number):
 		"""
 		Construct a Constant.
 		"""
-		super().__init__(identifier)
-		self._output_ports = [OutputPort()] # TODO: Generate appropriate ID for ports.
+		super().__init__()
+		self._output_ports = [OutputPort(1)] # TODO: Generate appropriate ID for ports.
 		self._parameters["value"] = value
 
 	def evaluate(self, inputs: list) -> list:
 		return [self.param("value")]
 
+	def get_op_name(self) -> GraphIDType:
+		return "const"
 
 class Addition(BasicOperation):
 	"""
@@ -43,17 +46,20 @@ class Addition(BasicOperation):
 	TODO: More info.
 	"""
 
-	def __init__(self, identifier: OperationId):
+	def __init__(self):
 		"""
 		Construct an Addition.
 		"""
-		super().__init__(identifier)
-		self._input_ports = [InputPort(), InputPort()] # TODO: Generate appropriate ID for ports.
-		self._output_ports = [OutputPort()] # TODO: Generate appropriate ID for ports.
+		super().__init__()
+		self._input_ports = [InputPort(1), InputPort(1)] # TODO: Generate appropriate ID for ports.
+		self._output_ports = [OutputPort(1)] # TODO: Generate appropriate ID for ports.
 
 	def evaluate(self, inputs: list) -> list:
 		return [inputs[0] + inputs[1]]
 
+	def get_op_name(self) -> GraphIDType:
+		return "add"
+
 
 class ConstantMultiplication(BasicOperation):
 	"""
@@ -61,16 +67,19 @@ class ConstantMultiplication(BasicOperation):
 	TODO: More info.
 	"""
 
-	def __init__(self, identifier: OperationId, coefficient: Number):
+	def __init__(self, coefficient: Number):
 		"""
 		Construct a ConstantMultiplication.
 		"""
-		super().__init__(identifier)
-		self._input_ports = [InputPort()] # TODO: Generate appropriate ID for ports.
-		self._output_ports = [OutputPort()] # TODO: Generate appropriate ID for ports.
+		super().__init__()
+		self._input_ports = [InputPort(1)] # TODO: Generate appropriate ID for ports.
+		self._output_ports = [OutputPort(1)] # TODO: Generate appropriate ID for ports.
 		self._parameters["coefficient"] = coefficient
 
 	def evaluate(self, inputs: list) -> list:
 		return [inputs[0] * self.param("coefficient")]
 
+	def get_op_name(self) -> GraphIDType:
+		return "const_mul"
+
 # TODO: More operations.
diff --git a/b_asic/graph_id.py b/b_asic/graph_id.py
new file mode 100644
index 00000000..3f25f513
--- /dev/null
+++ b/b_asic/graph_id.py
@@ -0,0 +1,30 @@
+"""
+B-ASIC Graph ID module for handling IDs of different objects in a graph.
+TODO: More info
+"""
+
+from collections import defaultdict
+from typing import NewType, Union, DefaultDict
+
+GraphID = NewType("GraphID", str)
+GraphIDType = NewType("GraphIDType", str)
+GraphIDNumber = NewType("GraphIDNumber", int)
+
+class GraphIDGenerator:
+    """
+    A class that generates Graph IDs for objects.
+    """
+
+    _next_id_number: DefaultDict[GraphIDType, GraphIDNumber]
+
+    def __init__(self):
+        self._next_id_number = defaultdict(lambda: 1)       # Initalises every key element to 1
+
+    def get_next_id(self, graph_id_type: GraphIDType) -> GraphID:
+        """
+        Returns the next graph id for a certain graph id type.
+        """
+        graph_id = graph_id_type + str(self._next_id_number[graph_id_type])
+        self._next_id_number[graph_id_type] += 1            # Increase the current id number
+        return graph_id
+
diff --git a/b_asic/operation.py b/b_asic/operation.py
index 731822f1..f02cd700 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -10,9 +10,7 @@ from typing import NewType, List, Dict, Optional, Any, TYPE_CHECKING
 if TYPE_CHECKING:
 	from b_asic.port import InputPort, OutputPort
 	from b_asic.simulation import SimulationState
-
-OperationId = NewType("OperationId", int)
-
+	from b_asic.graph_id import GraphIDType
 
 class Operation(ABC):
 	"""
@@ -20,14 +18,6 @@ class Operation(ABC):
 	TODO: More info.
 	"""
 
-	@abstractmethod
-	def identifier(self) -> OperationId:
-		"""
-		Get the unique identifier.
-		TODO: Move id info to SFG, remove id class members.
-		"""
-		pass
-
 	@abstractmethod
 	def inputs(self) -> "List[InputPort]":
 		"""
@@ -109,5 +99,17 @@ class Operation(ABC):
 		"""
 		pass
 
+	@abstractmethod
+	def get_op_name(self) -> "GraphIDType":
+		"""Returns a string representing the operation name of the operation."""
+		pass
+
+	@abstractmethod
+	def neighbours(self) -> "List[Operation]":
+		"""
+		Return all operations that are connected by signals to this operation.
+		If no neighbours are found this returns an empty list
+		"""
+
 	# TODO: More stuff.
 
diff --git a/b_asic/signal.py b/b_asic/signal.py
index 4fac563f..6ef55c8d 100644
--- a/b_asic/signal.py
+++ b/b_asic/signal.py
@@ -6,9 +6,6 @@ TODO: More info.
 from b_asic.operation import Operation
 from typing import NewType
 
-SignalId = NewType("SignalId", int)
-
-
 class SignalSource:
 	"""
 	Handle to a signal source.
@@ -50,22 +47,14 @@ class Signal:
 	A connection between two operations consisting of a source and destination handle.
 	TODO: More info.
 	"""
-	_identifier: SignalId
 	source: SignalSource
 	destination: SignalDestination
 
-	def __init__(self, identifier: SignalId, source: SignalSource, destination: SignalDestination):
+	def __init__(self, source: SignalSource, destination: SignalDestination):
 		"""
 		Construct a Signal.
 		"""
-		self._identifier = identifier
 		self.source = source
 		self.destination = destination
 
-	def identifier(self) -> SignalId:
-		"""
-		Get the unique identifier.
-		"""
-		return self._identifier
-
 	# TODO: More stuff.
diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py
index f9671636..9d31b04b 100644
--- a/b_asic/signal_flow_graph.py
+++ b/b_asic/signal_flow_graph.py
@@ -3,12 +3,13 @@ B-ASIC Signal Flow Graph Module.
 TODO: More info.
 """
 
-from b_asic.operation import OperationId, Operation
+from b_asic.operation import Operation
 from b_asic.basic_operation import BasicOperation
-from b_asic.signal import SignalSource, SignalDestination
+from b_asic.signal import Signal, SignalSource, SignalDestination
 from b_asic.simulation import SimulationState, OperationState
-from typing import List
+from b_asic.graph_id import GraphIDGenerator, GraphID
 
+from typing import List, Dict, Union, Optional
 
 class SFG(BasicOperation):
 	"""
@@ -16,22 +17,60 @@ class SFG(BasicOperation):
 	TODO: More info.
 	"""
 
-	_operations: List[Operation]
+	_graph_objects_by_id: Dict[GraphID, Union[Operation, Signal]]
+	_graph_id_generator: GraphIDGenerator
 
-	def __init__(self, identifier: OperationId, input_destinations: List[SignalDestination], output_sources: List[SignalSource]):
-		"""
-		Construct a SFG.
-		"""
-		super().__init__(identifier)
+	def __init__(self, input_destinations: List[SignalDestination], output_sources: List[SignalSource]):
+		"""Constructs an SFG."""
+		super().__init__()
 		# TODO: Allocate input/output ports with appropriate IDs.
-		self._operations = []
+		
+		self._graph_objects_by_id = dict # Map Operation ID to Operation objects
+		self._graph_id_generator = GraphIDGenerator() 
+
 		# TODO: Traverse the graph between the inputs/outputs and add to self._operations.
 		# TODO: Connect ports with signals with appropriate IDs.
 
 	def evaluate(self, inputs: list) -> list:
 		return [] # TODO: Implement
 
-	def split(self) -> List[Operation]:
-		return self._operations
+	def add_operation(self, operation: Operation) -> GraphID:
+		"""Adds the entered operation to the SFG's dictionary of graph objects and
+	 	returns a generated GraphID for it.
+		
+		Keyword arguments:
+		operation: Operation to add to the graph.
+		"""
+		return self._add_graph_obj(operation, operation.get_op_name())
+
+
+	def add_signal(self, signal: Signal) -> GraphID:
+		"""Adds the entered signal to the SFG's dictionary of graph objects and returns
+		a generated GraphID for it.
+		
+		Keyword argumentst:
+		signal: Signal to add to the graph.
+		"""
+		return self._add_graph_obj(signal, 'sig')
+
+
+	def find_by_id(self, graph_id: GraphID) -> Optional[Operation]:
+		"""Finds a graph object based on the entered Graph ID and returns it. If no graph
+		object with the entered ID was found then returns None.
+
+		Keyword arguments:
+		graph_id: Graph ID of the wanted object.
+		"""
+		if graph_id in self._graph_objects_by_id:
+			return self._graph_objects_by_id[graph_id]
+		else:
+			return None
+			
+
+
+	def _add_graph_obj(self, obj: Union[Operation, Signal], operation_id_type: str):
+		graph_id = self._graph_id_generator.get_next_id(operation_id_type)
+		self._graph_objects_by_id[graph_id] = obj
+		return graph_id 
+
 
-	# TODO: More stuff.
diff --git a/b_asic/simulation.py b/b_asic/simulation.py
index e219445b..d3d3aaf6 100644
--- a/b_asic/simulation.py
+++ b/b_asic/simulation.py
@@ -3,7 +3,6 @@ B-ASIC Simulation Module.
 TODO: More info.
 """
 
-from b_asic.operation import OperationId
 from numbers import Number
 from typing import List, Dict
 
@@ -31,7 +30,7 @@ class SimulationState:
 	TODO: More info.
 	"""
 
-	operation_states: Dict[OperationId, OperationState]
+	# operation_states: Dict[OperationId, OperationState]
 	iteration: int
 
 	def __init__(self):
diff --git a/b_asic/traverse_tree.py b/b_asic/traverse_tree.py
new file mode 100644
index 00000000..024a542d
--- /dev/null
+++ b/b_asic/traverse_tree.py
@@ -0,0 +1,54 @@
+"""
+B-ASIC Operation Tree Traversing Module.
+TODO:
+    - Get a first operation or? an entire operation tree
+    - For each start point, follow it to the next operation from it's out port.
+    - If we are searching for a specific operation end.
+    - If we are searching for a specific type of operation add the operation to a list and continue.
+    - When we no more out ports can be traversed return results and end.
+"""
+
+from typing import List, Optional
+from collections import deque
+
+from b_asic.operation import Operation
+
+
+class Traverse:
+    """Traverse operation tree.
+    TODO:
+        - More info.
+        - Check if a datastructure other than list suits better as return value.
+        - Implement the type check for operation.
+    """
+
+    def __init__(self, operation: Operation):
+        """Construct a TraverseTree."""
+        self._initial_operation = operation
+
+    def _breadth_first_search(self, start: Operation) -> List[Operation]:
+        """Use breadth first search to traverse the operation tree."""
+        visited: List[Operation] = [start]
+        queue = deque([start])
+        while queue:
+            operation = queue.popleft()
+            for n_operation in operation.neighbours:
+                if n_operation not in visited:
+                    visited.append(n_operation)
+                    queue.append(n_operation)
+
+        return visited
+
+    def traverse(self, type_: Optional[Operation] = None) -> List[Operation]:
+        """Traverse the the operation tree and return operation where type matches.
+        If the type is None then return the entire tree.
+
+        Keyword arguments:
+        type_-- the operation type to search for (default None)
+        """
+
+        operations: List[Operation] = self._breadth_first_search(self._initial_operation)
+        if type_ is not None:
+            operations = [oper for oper in operations if isinstance(oper, type_)]
+
+        return operations
diff --git a/test/fixtures/signal.py b/test/fixtures/signal.py
index 5fbdcf2b..64b96f55 100644
--- a/test/fixtures/signal.py
+++ b/test/fixtures/signal.py
@@ -6,15 +6,15 @@ Use a fixture for initializing objects and pass them as argument to a test funct
 """
 @pytest.fixture
 def signal():
-    source = SignalSource(Addition(0), 1)
-    dest = SignalDestination(Addition(1), 2)
-    return Signal(0, source, dest)
+    source = SignalSource(Addition(), 1)
+    dest = SignalDestination(Addition(), 2)
+    return Signal(source, dest)
 
 @pytest.fixture
 def signals():
     ret = []
-    for i in range(0,3):
-        source = SignalSource(Addition(0), 1)
-        dest = SignalDestination(Addition(1), 2)
-        ret.append(Signal(i, source, dest))
+    for _ in range(0,3):
+        source = SignalSource(Addition(), 1)
+        dest = SignalDestination(Addition(), 2)
+        ret.append(Signal(source, dest))
     return ret
\ No newline at end of file
diff --git a/test/graph_id/conftest.py b/test/graph_id/conftest.py
new file mode 100644
index 00000000..5871ed8e
--- /dev/null
+++ b/test/graph_id/conftest.py
@@ -0,0 +1 @@
+import pytest
diff --git a/test/graph_id/test_graph_id_generator.py b/test/graph_id/test_graph_id_generator.py
new file mode 100644
index 00000000..7aeb6cad
--- /dev/null
+++ b/test/graph_id/test_graph_id_generator.py
@@ -0,0 +1,29 @@
+"""
+B-ASIC test suite for graph id generator.
+"""
+
+from b_asic.graph_id import GraphIDGenerator, GraphID
+
+import pytest
+
+def test_empty_string_generator():
+    """Test the graph id generator for an empty string type."""
+    graph_id_generator = GraphIDGenerator()
+    assert graph_id_generator.get_next_id("") == "1"
+    assert graph_id_generator.get_next_id("") == "2"
+
+
+def test_normal_string_generator():
+    """"Test the graph id generator for a normal string type."""
+    graph_id_generator = GraphIDGenerator()
+    assert graph_id_generator.get_next_id("add") == "add1"
+    assert graph_id_generator.get_next_id("add") == "add2"
+
+def test_different_strings_generator():
+    """Test the graph id generator for different strings."""
+    graph_id_generator = GraphIDGenerator()
+    assert graph_id_generator.get_next_id("sub") == "sub1"
+    assert graph_id_generator.get_next_id("mul") == "mul1"
+    assert graph_id_generator.get_next_id("sub") == "sub2"
+    assert graph_id_generator.get_next_id("mul") == "mul2"
+    
\ No newline at end of file
diff --git a/test/port/test_port.py b/test/port/test_port.py
index 56cb9be2..7e1fc9b7 100644
--- a/test/port/test_port.py
+++ b/test/port/test_port.py
@@ -13,10 +13,10 @@ def test_connect_one_signal_to_port(signal):
     assert port.signal() == signal
 
 def test_change_port_signal():
-    source = SignalSource(Addition(0), 1)
-    dest = SignalDestination(Addition(1),2)
-    signal1 = Signal(1, source, dest)
-    signal2 = Signal(2, source, dest)
+    source = SignalSource(Addition, 1)
+    dest = SignalDestination(Addition,2)
+    signal1 = Signal(source, dest)
+    signal2 = Signal(source, dest)
 
     port = InputPort(0)
     port.connect(signal1)
diff --git a/test/signal_flow_graph/conftest.py b/test/signal_flow_graph/conftest.py
new file mode 100644
index 00000000..5871ed8e
--- /dev/null
+++ b/test/signal_flow_graph/conftest.py
@@ -0,0 +1 @@
+import pytest
diff --git a/test/signal_flow_graph/test_signal_flow_graph.py b/test/signal_flow_graph/test_signal_flow_graph.py
new file mode 100644
index 00000000..921e8906
--- /dev/null
+++ b/test/signal_flow_graph/test_signal_flow_graph.py
@@ -0,0 +1,3 @@
+from b_asic.signal_flow_graph import SFG
+from b_asic.core_operations import Addition, Constant
+from b_asic.signal import Signal
diff --git a/test/traverse/test_traverse_tree.py b/test/traverse/test_traverse_tree.py
new file mode 100644
index 00000000..d218a6e7
--- /dev/null
+++ b/test/traverse/test_traverse_tree.py
@@ -0,0 +1,85 @@
+"""
+TODO:
+    - Rewrite to more clean code, not so repetitive
+    - Update when signals and id's has been merged.
+"""
+
+from b_asic.core_operations import Constant, Addition
+from b_asic.signal import Signal, SignalSource, SignalDestination
+from b_asic.port import InputPort, OutputPort
+from b_asic.traverse_tree import Traverse
+
+import pytest
+
+@pytest.fixture
+def operation():
+    return Constant(2)
+
+def create_operation(_type, dest_oper, index, **kwargs):
+    oper = _type(**kwargs)
+    oper_signal_source = SignalSource(oper, 0)
+    oper_signal_dest = SignalDestination(dest_oper, index)
+    oper_signal = Signal(oper_signal_source, oper_signal_dest)
+    oper._output_ports[0].connect(oper_signal)
+    dest_oper._input_ports[index].connect(oper_signal)
+    return oper
+
+@pytest.fixture
+def operation_tree():
+    add_oper = Addition()
+
+    const_oper = create_operation(Constant, add_oper, 0, value=2)
+    const_oper_2 = create_operation(Constant, add_oper, 1, value=3)
+
+    return add_oper
+
+@pytest.fixture
+def large_operation_tree():
+    add_oper = Addition()
+    add_oper_2 = Addition()
+
+    const_oper = create_operation(Constant, add_oper, 0, value=2)
+    const_oper_2 = create_operation(Constant, add_oper, 1, value=3)
+
+    const_oper_3 = create_operation(Constant, add_oper_2, 0, value=4)
+    const_oper_4 = create_operation(Constant, add_oper_2, 1, value=5)
+
+    add_oper_3 = Addition()
+    add_oper_signal_source = SignalSource(add_oper, 0)
+    add_oper_signal_dest = SignalDestination(add_oper_3, 0)
+    add_oper_signal = Signal(add_oper_signal_source, add_oper_signal_dest)
+    add_oper._output_ports[0].connect(add_oper_signal)
+    add_oper_3._input_ports[0].connect(add_oper_signal)
+
+    add_oper_2_signal_source = SignalSource(add_oper_2, 0)
+    add_oper_2_signal_dest = SignalDestination(add_oper_3, 1)
+    add_oper_2_signal = Signal(add_oper_2_signal_source, add_oper_2_signal_dest)
+    add_oper_2._output_ports[0].connect(add_oper_2_signal)
+    add_oper_3._input_ports[1].connect(add_oper_2_signal)
+    return const_oper
+
+def test_traverse_single_tree(operation):
+    traverse = Traverse(operation)
+    assert traverse.traverse() == [operation]
+
+def test_traverse_tree(operation_tree):
+    traverse = Traverse(operation_tree)
+    assert len(traverse.traverse()) == 3
+
+def test_traverse_large_tree(large_operation_tree):
+    traverse = Traverse(large_operation_tree)
+    assert len(traverse.traverse()) == 7
+
+def test_traverse_type(large_operation_tree):
+    traverse = Traverse(large_operation_tree)
+    assert len(traverse.traverse(Addition)) == 3
+    assert len(traverse.traverse(Constant)) == 4
+
+def test_traverse_loop(operation_tree):
+    add_oper_signal_source = SignalSource(operation_tree, 0)
+    add_oper_signal_dest = SignalDestination(operation_tree, 0)
+    add_oper_signal = Signal(add_oper_signal_source, add_oper_signal_dest)
+    operation_tree._output_ports[0].connect(add_oper_signal)
+    operation_tree._input_ports[0].connect(add_oper_signal)
+    traverse = Traverse(operation_tree)
+    assert len(traverse.traverse()) == 2
\ No newline at end of file
-- 
GitLab


From bacb62bd474a6a12e3b8e8daaa897589e7b33d2f Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 11:06:53 +0100
Subject: [PATCH 23/50] Update .gitlab-ci.yml to use pytest

---
 .gitlab-ci.yml | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 78499e52..a10b243e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,9 +1,18 @@
+image: python:3.6
+
 stages:
-  - build
+ - test
+
+before_script:
+  - pip3 install .
+  - pip3 show myapp
 
-PythonBuild:
-  stage: build
-  artifacts:
-    untracked: true
+run tests:
+  stage: test
+  only:
+    - develop
+  tags:
+    - python3
+    - test
   script:
-    - apt-get update && apt-get install python3 -y
\ No newline at end of file
+    - pytest test
\ No newline at end of file
-- 
GitLab


From c80b391875e029d5ef97ff312e395cb5f03db3bf Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 11:15:18 +0100
Subject: [PATCH 24/50] Removed tags from .gitlab-ci.yml

---
 .gitlab-ci.yml | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a10b243e..f9766baf 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,8 +11,5 @@ run tests:
   stage: test
   only:
     - develop
-  tags:
-    - python3
-    - test
   script:
     - pytest test
\ No newline at end of file
-- 
GitLab


From 71f1c87b05f93ecde4c830767fcda50e1028ac21 Mon Sep 17 00:00:00 2001
From: Jacob Wahlman <jacwa448@student.liu.se>
Date: Tue, 3 Mar 2020 11:33:03 +0100
Subject: [PATCH 25/50] doxygen docstrings and some pep stuff

---
 b_asic/__init__.py          |  2 +-
 b_asic/basic_operation.py   | 20 +++++-------
 b_asic/core_operations.py   | 35 ++++++++------------
 b_asic/graph_id.py          | 14 +++-----
 b_asic/operation.py         | 54 ++++++++++---------------------
 b_asic/port.py              | 64 ++++++++++++-------------------------
 b_asic/precedence_chart.py  |  8 ++---
 b_asic/schema.py            |  8 ++---
 b_asic/signal.py            | 22 +++----------
 b_asic/signal_flow_graph.py | 29 +++++++----------
 b_asic/simulation.py        | 16 +++-------
 b_asic/traverse_tree.py     | 15 ++-------
 12 files changed, 91 insertions(+), 196 deletions(-)

diff --git a/b_asic/__init__.py b/b_asic/__init__.py
index 8a84a945..752bac07 100644
--- a/b_asic/__init__.py
+++ b/b_asic/__init__.py
@@ -11,4 +11,4 @@ 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 *
\ No newline at end of file
+from b_asic.simulation import *
diff --git a/b_asic/basic_operation.py b/b_asic/basic_operation.py
index 4f7b426b..54aaaebe 100644
--- a/b_asic/basic_operation.py
+++ b/b_asic/basic_operation.py
@@ -1,20 +1,20 @@
-"""
+"""@package docstring
 B-ASIC Basic Operation Module.
 TODO: More info.
 """
 
+from abc import abstractmethod
+from typing import List, Dict, Optional, Any
+from numbers import Number
+
 from b_asic.port import InputPort, OutputPort
 from b_asic.signal import SignalSource, SignalDestination
 from b_asic.operation import Operation
 from b_asic.simulation import SimulationState, OperationState
-from abc import ABC, abstractmethod
-from typing import List, Dict, Optional, Any
-from numbers import Number
 
 
 class BasicOperation(Operation):
-	"""
-	Generic abstract operation class which most implementations will derive from.
+	"""Generic abstract operation class which most implementations will derive from.
 	TODO: More info.
 	"""
 
@@ -23,18 +23,14 @@ class BasicOperation(Operation):
 	_parameters: Dict[str, Optional[Any]]
 
 	def __init__(self):
-		"""
-		Construct a BasicOperation.
-		"""
+		"""Construct a BasicOperation."""
 		self._input_ports = []
 		self._output_ports = []
 		self._parameters = {}
 
 	@abstractmethod
 	def evaluate(self, inputs: list) -> list:
-		"""
-		Evaluate the operation and generate a list of output values given a list of input values.
-		"""
+		"""Evaluate the operation and generate a list of output values given a list of input values."""
 		pass
 
 	def inputs(self) -> List[InputPort]:
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index bca344cf..513fe7cf 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -1,18 +1,18 @@
-"""
+"""@package docstring
 B-ASIC Core Operations Module.
 TODO: More info.
 """
 
+from numbers import Number
+
 from b_asic.port import InputPort, OutputPort
 from b_asic.operation import Operation
 from b_asic.basic_operation import BasicOperation
 from b_asic.graph_id import GraphIDType
-from numbers import Number
 
 
 class Input(Operation):
-	"""
-	Input operation.
+	"""Input operation.
 	TODO: More info.
 	"""
 
@@ -21,15 +21,12 @@ class Input(Operation):
 
 
 class Constant(BasicOperation):
-	"""
-	Constant value operation.
+	"""Constant value operation.
 	TODO: More info.
 	"""
 
 	def __init__(self, value: Number):
-		"""
-		Construct a Constant.
-		"""
+		"""Construct a Constant."""
 		super().__init__()
 		self._output_ports = [OutputPort(1)] # TODO: Generate appropriate ID for ports.
 		self._parameters["value"] = value
@@ -37,19 +34,16 @@ class Constant(BasicOperation):
 	def evaluate(self, inputs: list) -> list:
 		return [self.param("value")]
 
-	def get_op_name(self) -> GraphIDType:
+	def type_name(self) -> GraphIDType:
 		return "const"
 
 class Addition(BasicOperation):
-	"""
-	Binary addition operation.
+	"""Binary addition operation.
 	TODO: More info.
 	"""
 
 	def __init__(self):
-		"""
-		Construct an Addition.
-		"""
+		"""Construct an Addition."""
 		super().__init__()
 		self._input_ports = [InputPort(1), InputPort(1)] # TODO: Generate appropriate ID for ports.
 		self._output_ports = [OutputPort(1)] # TODO: Generate appropriate ID for ports.
@@ -57,20 +51,17 @@ class Addition(BasicOperation):
 	def evaluate(self, inputs: list) -> list:
 		return [inputs[0] + inputs[1]]
 
-	def get_op_name(self) -> GraphIDType:
+	def type_name(self) -> GraphIDType:
 		return "add"
 
 
 class ConstantMultiplication(BasicOperation):
-	"""
-	Unary constant multiplication operation.
+	"""Unary constant multiplication operation.
 	TODO: More info.
 	"""
 
 	def __init__(self, coefficient: Number):
-		"""
-		Construct a ConstantMultiplication.
-		"""
+		"""Construct a ConstantMultiplication."""
 		super().__init__()
 		self._input_ports = [InputPort(1)] # TODO: Generate appropriate ID for ports.
 		self._output_ports = [OutputPort(1)] # TODO: Generate appropriate ID for ports.
@@ -79,7 +70,7 @@ class ConstantMultiplication(BasicOperation):
 	def evaluate(self, inputs: list) -> list:
 		return [inputs[0] * self.param("coefficient")]
 
-	def get_op_name(self) -> GraphIDType:
+	def type_name(self) -> GraphIDType:
 		return "const_mul"
 
 # TODO: More operations.
diff --git a/b_asic/graph_id.py b/b_asic/graph_id.py
index 3f25f513..0fd1855b 100644
--- a/b_asic/graph_id.py
+++ b/b_asic/graph_id.py
@@ -1,19 +1,18 @@
-"""
+"""@package docstring
 B-ASIC Graph ID module for handling IDs of different objects in a graph.
 TODO: More info
 """
 
 from collections import defaultdict
-from typing import NewType, Union, DefaultDict
+from typing import NewType, DefaultDict
 
 GraphID = NewType("GraphID", str)
 GraphIDType = NewType("GraphIDType", str)
 GraphIDNumber = NewType("GraphIDNumber", int)
 
+
 class GraphIDGenerator:
-    """
-    A class that generates Graph IDs for objects.
-    """
+    """A class that generates Graph IDs for objects."""
 
     _next_id_number: DefaultDict[GraphIDType, GraphIDNumber]
 
@@ -21,10 +20,7 @@ class GraphIDGenerator:
         self._next_id_number = defaultdict(lambda: 1)       # Initalises every key element to 1
 
     def get_next_id(self, graph_id_type: GraphIDType) -> GraphID:
-        """
-        Returns the next graph id for a certain graph id type.
-        """
+        """Returns the next graph id for a certain graph id type."""
         graph_id = graph_id_type + str(self._next_id_number[graph_id_type])
         self._next_id_number[graph_id_type] += 1            # Increase the current id number
         return graph_id
-
diff --git a/b_asic/operation.py b/b_asic/operation.py
index f02cd700..923690aa 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -1,115 +1,95 @@
-"""
+"""@package docstring
 B-ASIC Operation Module.
 TODO: More info.
 """
 
 from abc import ABC, abstractmethod
 from numbers import Number
-from typing import NewType, List, Dict, Optional, Any, TYPE_CHECKING
+from typing import List, Dict, Optional, Any, TYPE_CHECKING
 
 if TYPE_CHECKING:
 	from b_asic.port import InputPort, OutputPort
 	from b_asic.simulation import SimulationState
 	from b_asic.graph_id import GraphIDType
 
+
 class Operation(ABC):
-	"""
-	Operation interface.
+	"""Operation interface.
 	TODO: More info.
 	"""
 
 	@abstractmethod
 	def inputs(self) -> "List[InputPort]":
-		"""
-		Get a list of all input ports.
-		"""
+		"""Get a list of all input ports."""
 		pass
 
 	@abstractmethod
 	def outputs(self) -> "List[OutputPort]":
-		"""
-		Get a list of all output ports.
-		"""
+		"""Get a list of all output ports."""
 		pass
 
 	@abstractmethod
 	def input_count(self) -> int:
-		"""
-		Get the number of input ports.
-		"""
+		"""Get the number of input ports."""
 		pass
 
 	@abstractmethod
 	def output_count(self) -> int:
-		"""
-		Get the number of output ports.
-		"""
+		"""Get the number of output ports."""
 		pass
 
 	@abstractmethod
 	def input(self, i: int) -> "InputPort":
-		"""
-		Get the input port at index i.
-		"""
+		"""Get the input port at index i."""
 		pass
 
 	@abstractmethod
 	def output(self, i: int) -> "OutputPort":
-		"""
-		Get the output port at index i.
-		"""
+		"""Get the output port at index i."""
 		pass
 
 	@abstractmethod
 	def params(self) -> Dict[str, Optional[Any]]:
-		"""
-		Get a dictionary of all parameter values.
-		"""
+		"""Get a dictionary of all parameter values."""
 		pass
 
 	@abstractmethod
 	def param(self, name: str) -> Optional[Any]:
-		"""
-		Get the value of a parameter.
+		"""Get the value of a parameter.
 		Returns None if the parameter is not defined.
 		"""
 		pass
 
 	@abstractmethod
 	def set_param(self, name: str, value: Any) -> None:
-		"""
-		Set the value of a parameter.
+		"""Set the value of a parameter.
 		The parameter must be defined.
 		"""
 		pass
 
 	@abstractmethod
 	def evaluate_outputs(self, state: "SimulationState") -> List[Number]:
-		"""
-		Simulate the circuit until its iteration count matches that of the simulation state,
+		"""Simulate the circuit until its iteration count matches that of the simulation state,
 		then return the resulting output vector.
 		"""
 		pass
 
 	@abstractmethod
 	def split(self) -> "List[Operation]":
-		"""
-		Split the operation into multiple operations.
+		"""Split the operation into multiple operations.
 		If splitting is not possible, this may return a list containing only the operation itself.
 		"""
 		pass
 
 	@abstractmethod
-	def get_op_name(self) -> "GraphIDType":
+	def type_name(self) -> "GraphIDType":
 		"""Returns a string representing the operation name of the operation."""
 		pass
 
 	@abstractmethod
 	def neighbours(self) -> "List[Operation]":
-		"""
-		Return all operations that are connected by signals to this operation.
+		"""Return all operations that are connected by signals to this operation.
 		If no neighbours are found this returns an empty list
 		"""
 
 	# TODO: More stuff.
-
diff --git a/b_asic/port.py b/b_asic/port.py
index 2d5405ed..f67defb7 100644
--- a/b_asic/port.py
+++ b/b_asic/port.py
@@ -1,120 +1,98 @@
-"""
+"""@package docstring
 B-ASIC Port Module.
 TODO: More info.
 """
 
-from b_asic.signal import Signal
 from abc import ABC, abstractmethod
-from typing import NewType, Optional, List, Dict
+from typing import NewType, Optional, List
+
+from b_asic.signal import Signal
 
 PortId = NewType("PortId", int)
 
 
 class Port(ABC):
-	"""
-	Abstract port class.
+	"""Abstract port class.
 	TODO: More info.
 	"""
 
 	_identifier: PortId
 
 	def __init__(self, identifier: PortId):
-		"""
-		Construct a Port.
-		"""
+		"""Construct a Port."""
 		self._identifier = identifier
 
 	def identifier(self) -> PortId:
-		"""
-		Get the unique identifier.
-		"""
+		"""Get the unique identifier."""
 		return self._identifier
 
 	@abstractmethod
 	def signals(self) -> List[Signal]:
-		"""
-		Get a list of all connected signals.
-		"""
+		"""Get a list of all connected signals."""
 		pass
 
 	@abstractmethod
 	def signal_count(self) -> int:
-		"""
-		Get the number of connected signals.
-		"""
+		"""Get the number of connected signals."""
 		pass
 
 	@abstractmethod
 	def signal(self, i: int = 0) -> Signal:
-		"""
-		Get the connected signal at index i.
-		"""
+		"""Get the connected signal at index i."""
 		pass
 
 	@abstractmethod
 	def connect(self, signal: Signal) -> None:
-		"""
-		Connect a signal.
-		"""
+		"""Connect a signal."""
 		pass
 
 	@abstractmethod
 	def disconnect(self, i: int = 0) -> None:
-		"""
-		Disconnect a signal.
-		"""
+		"""Disconnect a signal."""
 		pass
 
 	# TODO: More stuff.
 
 
 class InputPort(Port):
-	"""
-	Input port.
+	"""Input port.
 	TODO: More info.
 	"""
 	_source_signal: Optional[Signal]
 
 	def __init__(self, identifier: PortId):
-		"""
-		Construct an InputPort.
-		"""
 		super().__init__(identifier)
 		self._source_signal = None
 
 	def signals(self) -> List[Signal]:
-		return [] if self._source_signal == None else [self._source_signal]
+		return [] if self._source_signal is None else [self._source_signal]
 
 	def signal_count(self) -> int:
-		return 0 if self._source_signal == None else 1
+		return 0 if self._source_signal is None else 1
 
 	def signal(self, i: int = 0) -> Signal:
-		assert i >= 0 and i < self.signal_count() # TODO: Error message.
-		assert self._source_signal != None # TODO: Error message.
+		assert 0 <= i < self.signal_count() # TODO: Error message.
+		assert self._source_signal is not None # TODO: Error message.
 		return self._source_signal
 
 	def connect(self, signal: Signal) -> None:
 		self._source_signal = signal
 
 	def disconnect(self, i: int = 0) -> None:
-		assert i >= 0 and i < self.signal_count() # TODO: Error message.
+		assert 0 <= i < self.signal_count() # TODO: Error message.
 		self._source_signal = None
 
 	# TODO: More stuff.
 
 
 class OutputPort(Port):
-	"""
-	Output port.
+	"""Output port.
 	TODO: More info.
 	"""
 
 	_destination_signals: List[Signal]
 
 	def __init__(self, identifier: PortId):
-		"""
-		Construct an OutputPort.
-		"""
 		super().__init__(identifier)
 		self._destination_signals = []
 
@@ -125,7 +103,7 @@ class OutputPort(Port):
 		return len(self._destination_signals)
 
 	def signal(self, i: int = 0) -> Signal:
-		assert i >= 0 and i < self.signal_count() # TODO: Error message.
+		assert 0 <= i < self.signal_count() # TODO: Error message.
 		return self._destination_signals[i]
 
 	def connect(self, signal: Signal) -> None:
@@ -133,7 +111,7 @@ class OutputPort(Port):
 		self._destination_signals.append(signal)
 
 	def disconnect(self, i: int = 0) -> None:
-		assert i >= 0 and i < self.signal_count() # TODO: Error message.
+		assert 0 <= i < self.signal_count() # TODO: Error message.
 		del self._destination_signals[i]
 
 	# TODO: More stuff.
diff --git a/b_asic/precedence_chart.py b/b_asic/precedence_chart.py
index 329c78d2..93b86164 100644
--- a/b_asic/precedence_chart.py
+++ b/b_asic/precedence_chart.py
@@ -1,4 +1,4 @@
-"""
+"""@package docstring
 B-ASIC Precedence Chart Module.
 TODO: More info.
 """
@@ -7,8 +7,7 @@ from b_asic.signal_flow_graph import SFG
 
 
 class PrecedenceChart:
-	"""
-	Precedence chart constructed from a signal flow graph.
+	"""Precedence chart constructed from a signal flow graph.
 	TODO: More info.
 	"""
 
@@ -16,9 +15,6 @@ class PrecedenceChart:
 	# TODO: More members.
 
 	def __init__(self, sfg: SFG):
-		"""
-		Construct a PrecedenceChart.
-		"""
 		self.sfg = sfg
 		# TODO: Implement.
 
diff --git a/b_asic/schema.py b/b_asic/schema.py
index 56eb47ff..41938263 100644
--- a/b_asic/schema.py
+++ b/b_asic/schema.py
@@ -1,4 +1,4 @@
-"""
+"""@package docstring
 B-ASIC Schema Module.
 TODO: More info.
 """
@@ -7,8 +7,7 @@ from b_asic.precedence_chart import PrecedenceChart
 
 
 class Schema:
-	"""
-	Schema constructed from a precedence chart.
+	"""Schema constructed from a precedence chart.
 	TODO: More info.
 	"""
 
@@ -16,9 +15,6 @@ class Schema:
 	# TODO: More members.
 
 	def __init__(self, pc: PrecedenceChart):
-		"""
-		Construct a Schema.
-		"""
 		self.pc = pc
 		# TODO: Implement.
 
diff --git a/b_asic/signal.py b/b_asic/signal.py
index 6ef55c8d..7e63ebfb 100644
--- a/b_asic/signal.py
+++ b/b_asic/signal.py
@@ -1,23 +1,19 @@
-"""
+"""@package docstring
 B-ASIC Signal Module.
 TODO: More info.
 """
 
 from b_asic.operation import Operation
-from typing import NewType
+
 
 class SignalSource:
-	"""
-	Handle to a signal source.
+	"""Handle to a signal source.
 	TODO: More info.
 	"""
 	operation: Operation
 	port_index: int
 
 	def __init__(self, operation: Operation, port_index: int):
-		"""
-		Construct a SignalSource.
-		"""
 		self.operation = operation
 		self.port_index = port_index
 
@@ -25,17 +21,13 @@ class SignalSource:
 
 
 class SignalDestination:
-	"""
-	Handle to a signal destination.
+	"""Handle to a signal destination.
 	TODO: More info.
 	"""
 	operation: Operation
 	port_index: int
 
 	def __init__(self, operation: Operation, port_index: int):
-		"""
-		Construct a SignalDestination.
-		"""
 		self.operation = operation
 		self.port_index = port_index
 
@@ -43,17 +35,13 @@ class SignalDestination:
 
 
 class Signal:
-	"""
-	A connection between two operations consisting of a source and destination handle.
+	"""A connection between two operations consisting of a source and destination handle.
 	TODO: More info.
 	"""
 	source: SignalSource
 	destination: SignalDestination
 
 	def __init__(self, source: SignalSource, destination: SignalDestination):
-		"""
-		Construct a Signal.
-		"""
 		self.source = source
 		self.destination = destination
 
diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py
index 9d31b04b..914cf390 100644
--- a/b_asic/signal_flow_graph.py
+++ b/b_asic/signal_flow_graph.py
@@ -1,19 +1,18 @@
-"""
+"""@package docstring
 B-ASIC Signal Flow Graph Module.
 TODO: More info.
 """
 
+from typing import List, Dict, Union, Optional
+
 from b_asic.operation import Operation
 from b_asic.basic_operation import BasicOperation
 from b_asic.signal import Signal, SignalSource, SignalDestination
-from b_asic.simulation import SimulationState, OperationState
 from b_asic.graph_id import GraphIDGenerator, GraphID
 
-from typing import List, Dict, Union, Optional
 
 class SFG(BasicOperation):
-	"""
-	Signal flow graph.
+	"""Signal flow graph.
 	TODO: More info.
 	"""
 
@@ -21,12 +20,11 @@ class SFG(BasicOperation):
 	_graph_id_generator: GraphIDGenerator
 
 	def __init__(self, input_destinations: List[SignalDestination], output_sources: List[SignalSource]):
-		"""Constructs an SFG."""
 		super().__init__()
 		# TODO: Allocate input/output ports with appropriate IDs.
-		
+
 		self._graph_objects_by_id = dict # Map Operation ID to Operation objects
-		self._graph_id_generator = GraphIDGenerator() 
+		self._graph_id_generator = GraphIDGenerator()
 
 		# TODO: Traverse the graph between the inputs/outputs and add to self._operations.
 		# TODO: Connect ports with signals with appropriate IDs.
@@ -37,23 +35,21 @@ class SFG(BasicOperation):
 	def add_operation(self, operation: Operation) -> GraphID:
 		"""Adds the entered operation to the SFG's dictionary of graph objects and
 	 	returns a generated GraphID for it.
-		
+
 		Keyword arguments:
 		operation: Operation to add to the graph.
 		"""
-		return self._add_graph_obj(operation, operation.get_op_name())
-
+		return self._add_graph_obj(operation, operation.type_name())
 
 	def add_signal(self, signal: Signal) -> GraphID:
 		"""Adds the entered signal to the SFG's dictionary of graph objects and returns
 		a generated GraphID for it.
-		
+
 		Keyword argumentst:
 		signal: Signal to add to the graph.
 		"""
 		return self._add_graph_obj(signal, 'sig')
 
-
 	def find_by_id(self, graph_id: GraphID) -> Optional[Operation]:
 		"""Finds a graph object based on the entered Graph ID and returns it. If no graph
 		object with the entered ID was found then returns None.
@@ -63,14 +59,11 @@ class SFG(BasicOperation):
 		"""
 		if graph_id in self._graph_objects_by_id:
 			return self._graph_objects_by_id[graph_id]
-		else:
-			return None
-			
 
+		return None
 
 	def _add_graph_obj(self, obj: Union[Operation, Signal], operation_id_type: str):
 		graph_id = self._graph_id_generator.get_next_id(operation_id_type)
 		self._graph_objects_by_id[graph_id] = obj
-		return graph_id 
-
+		return graph_id
 
diff --git a/b_asic/simulation.py b/b_asic/simulation.py
index d3d3aaf6..c4f7f8f3 100644
--- a/b_asic/simulation.py
+++ b/b_asic/simulation.py
@@ -1,15 +1,14 @@
-"""
+"""@package docstring
 B-ASIC Simulation Module.
 TODO: More info.
 """
 
 from numbers import Number
-from typing import List, Dict
+from typing import List
 
 
 class OperationState:
-	"""
-	Simulation state of an operation.
+	"""Simulation state of an operation.
 	TODO: More info.
 	"""
 
@@ -17,16 +16,12 @@ class OperationState:
 	iteration: int
 
 	def __init__(self):
-		"""
-		Construct an OperationState.
-		"""
 		self.output_values = []
 		self.iteration = 0
 
 
 class SimulationState:
-	"""
-	Simulation state.
+	"""Simulation state.
 	TODO: More info.
 	"""
 
@@ -34,9 +29,6 @@ class SimulationState:
 	iteration: int
 
 	def __init__(self):
-		"""
-		Construct a SimulationState.
-		"""
 		self.operation_states = {}
 		self.iteration = 0
 
diff --git a/b_asic/traverse_tree.py b/b_asic/traverse_tree.py
index 024a542d..dc00371e 100644
--- a/b_asic/traverse_tree.py
+++ b/b_asic/traverse_tree.py
@@ -1,11 +1,5 @@
-"""
+"""@package docstring
 B-ASIC Operation Tree Traversing Module.
-TODO:
-    - Get a first operation or? an entire operation tree
-    - For each start point, follow it to the next operation from it's out port.
-    - If we are searching for a specific operation end.
-    - If we are searching for a specific type of operation add the operation to a list and continue.
-    - When we no more out ports can be traversed return results and end.
 """
 
 from typing import List, Optional
@@ -15,12 +9,7 @@ from b_asic.operation import Operation
 
 
 class Traverse:
-    """Traverse operation tree.
-    TODO:
-        - More info.
-        - Check if a datastructure other than list suits better as return value.
-        - Implement the type check for operation.
-    """
+    """Traverse operation tree."""
 
     def __init__(self, operation: Operation):
         """Construct a TraverseTree."""
-- 
GitLab


From 5d95d67b2f3e33a852fc9b05c26fd8488e304f54 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 11:37:03 +0100
Subject: [PATCH 26/50] Update .gitlab-ci.yml

---
 .gitlab-ci.yml | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f9766baf..bbed8d34 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,8 +4,11 @@ stages:
  - test
 
 before_script:
-  - pip3 install .
-  - pip3 show myapp
+  - apt-get update --yes
+  - apt-cache search fmt
+#  - apt-get install --yes cmake pybind11
+#  - pip3 install .
+#  - pip3 show b_asic
 
 run tests:
   stage: test
-- 
GitLab


From 78b73849d135a37e4b7d7ca780a049856fbf6196 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 11:46:23 +0100
Subject: [PATCH 27/50] Update .gitlab-ci.yml

---
 .gitlab-ci.yml | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bbed8d34..daa60558 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,10 +5,9 @@ stages:
 
 before_script:
   - apt-get update --yes
-  - apt-cache search fmt
-#  - apt-get install --yes cmake pybind11
-#  - pip3 install .
-#  - pip3 show b_asic
+  - apt-get install --yes build-essential cmake libfmt-dev pybind11-dev
+  - pip3 install .
+  - pip3 show b_asic
 
 run tests:
   stage: test
-- 
GitLab


From 2a81edff903f27315358393151e7faf495762942 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:04:23 +0100
Subject: [PATCH 28/50] Update .gitlab-ci.yml

---
 .gitlab-ci.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index daa60558..e5cebcda 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,7 +5,9 @@ stages:
 
 before_script:
   - apt-get update --yes
-  - apt-get install --yes build-essential cmake libfmt-dev pybind11-dev
+  - apt-get install --yes build-essential cmake pybind11-dev
+  - wget http://launchpadlibrarian.net/466750967/libfmt-dev_6.1.2+ds-2_amd64.deb
+  - apt install http://launchpadlibrarian.net/466750967/libfmt-dev_6.1.2+ds-2_amd64.deb
   - pip3 install .
   - pip3 show b_asic
 
-- 
GitLab


From 65d26595458953937f780510eb7b51eba27f5309 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:05:53 +0100
Subject: [PATCH 29/50] Update .gitlab-ci.yml

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e5cebcda..46fc191c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,7 +7,7 @@ before_script:
   - apt-get update --yes
   - apt-get install --yes build-essential cmake pybind11-dev
   - wget http://launchpadlibrarian.net/466750967/libfmt-dev_6.1.2+ds-2_amd64.deb
-  - apt install http://launchpadlibrarian.net/466750967/libfmt-dev_6.1.2+ds-2_amd64.deb
+  - apt install libfmt-dev_6.1.2+ds-2_amd64.deb
   - pip3 install .
   - pip3 show b_asic
 
-- 
GitLab


From 8d9cc29a710453723c65640830319b372d35187c Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:07:24 +0100
Subject: [PATCH 30/50] Update .gitlab-ci.yml

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 46fc191c..fdec79f5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,7 +7,7 @@ before_script:
   - apt-get update --yes
   - apt-get install --yes build-essential cmake pybind11-dev
   - wget http://launchpadlibrarian.net/466750967/libfmt-dev_6.1.2+ds-2_amd64.deb
-  - apt install libfmt-dev_6.1.2+ds-2_amd64.deb
+  - apt install ./libfmt-dev_6.1.2+ds-2_amd64.deb
   - pip3 install .
   - pip3 show b_asic
 
-- 
GitLab


From 9869138862bce68c184957ae4fa775e904613e3f Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:11:57 +0100
Subject: [PATCH 31/50] Update .gitlab-ci.yml

---
 .gitlab-ci.yml | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fdec79f5..daa60558 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,9 +5,7 @@ stages:
 
 before_script:
   - apt-get update --yes
-  - apt-get install --yes build-essential cmake pybind11-dev
-  - wget http://launchpadlibrarian.net/466750967/libfmt-dev_6.1.2+ds-2_amd64.deb
-  - apt install ./libfmt-dev_6.1.2+ds-2_amd64.deb
+  - apt-get install --yes build-essential cmake libfmt-dev pybind11-dev
   - pip3 install .
   - pip3 show b_asic
 
-- 
GitLab


From 6b6e6b1544029ca284a3153f58b89e06a6a3c4e4 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:12:38 +0100
Subject: [PATCH 32/50] Update CMakeLists.txt

---
 CMakeLists.txt | 156 ++++++++++++++++++++++++-------------------------
 1 file changed, 78 insertions(+), 78 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index bcd7af2e..a5ee5840 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,79 +1,79 @@
-cmake_minimum_required(VERSION 3.8)
-
-project(
-	"B-ASIC"
-	VERSION 0.0.1
-	DESCRIPTION "Better ASIC Toolbox for python3"
-	LANGUAGES C CXX
-)
-
-find_package(fmt 6.1.2 REQUIRED)
-find_package(pybind11 CONFIG REQUIRED)
-
-set(LIBRARY_NAME "b_asic")
-set(TARGET_NAME "_${LIBRARY_NAME}")
-
-if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
-	include(GNUInstallDirs)
-	set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_INSTALL_LIBDIR}")
-endif()
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-
-pybind11_add_module(
-	"${TARGET_NAME}"
-	"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp"
-)
-
-target_include_directories(
-	"${TARGET_NAME}"
-    PRIVATE
-        "${CMAKE_CURRENT_SOURCE_DIR}/src"
-)
-
-target_compile_features(
-	"${TARGET_NAME}"
-	PRIVATE
-		cxx_std_17
-)
-target_compile_options(
-	"${TARGET_NAME}"
-	PRIVATE
-		$<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:
-			-W -Wall -Wextra -Werror -Wno-psabi
-			$<$<CONFIG:Debug>:-g>
-			$<$<NOT:$<CONFIG:Debug>>:-O3>
-		>
-		$<$<CXX_COMPILER_ID:MSVC>:
-			/W3 /WX /permissive- /utf-8
-			$<$<CONFIG:Debug>:/Od>
-			$<$<NOT:$<CONFIG:Debug>>:/Ot>
-		>
-)
-
-target_link_libraries(
-	"${TARGET_NAME}"
-	PRIVATE
-		fmt::fmt-header-only
-)
-
-add_custom_target(
-	remove_old_python_dir ALL
-	COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
-	COMMENT "Removing old python directory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
-)
-add_custom_target(
-	copy_python_dir ALL
-	COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
-	COMMENT "Copying python files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
-	DEPENDS "${TARGET_NAME}" remove_old_python_dir
+cmake_minimum_required(VERSION 3.8)
+
+project(
+	"B-ASIC"
+	VERSION 0.0.1
+	DESCRIPTION "Better ASIC Toolbox for python3"
+	LANGUAGES C CXX
+)
+
+find_package(fmt 5.2.1 REQUIRED)
+find_package(pybind11 CONFIG REQUIRED)
+
+set(LIBRARY_NAME "b_asic")
+set(TARGET_NAME "_${LIBRARY_NAME}")
+
+if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
+	include(GNUInstallDirs)
+	set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_INSTALL_LIBDIR}")
+endif()
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+
+pybind11_add_module(
+	"${TARGET_NAME}"
+	"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp"
+)
+
+target_include_directories(
+	"${TARGET_NAME}"
+    PRIVATE
+        "${CMAKE_CURRENT_SOURCE_DIR}/src"
+)
+
+target_compile_features(
+	"${TARGET_NAME}"
+	PRIVATE
+		cxx_std_17
+)
+target_compile_options(
+	"${TARGET_NAME}"
+	PRIVATE
+		$<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:
+			-W -Wall -Wextra -Werror -Wno-psabi
+			$<$<CONFIG:Debug>:-g>
+			$<$<NOT:$<CONFIG:Debug>>:-O3>
+		>
+		$<$<CXX_COMPILER_ID:MSVC>:
+			/W3 /WX /permissive- /utf-8
+			$<$<CONFIG:Debug>:/Od>
+			$<$<NOT:$<CONFIG:Debug>>:/Ot>
+		>
+)
+
+target_link_libraries(
+	"${TARGET_NAME}"
+	PRIVATE
+		fmt::fmt-header-only
+)
+
+add_custom_target(
+	remove_old_python_dir ALL
+	COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
+	COMMENT "Removing old python directory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
+)
+add_custom_target(
+	copy_python_dir ALL
+	COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
+	COMMENT "Copying python files to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${LIBRARY_NAME}"
+	DEPENDS "${TARGET_NAME}" remove_old_python_dir
 )
\ No newline at end of file
-- 
GitLab


From 590bba7e05546f758017656e8cd01ebc08a49ebf Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:13:12 +0100
Subject: [PATCH 33/50] Update README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index bb71c4d7..fd98f919 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ How to build and debug the library during development.
 The following packages are required in order to build the library:
 * cmake 3.8+
   * gcc 7+/clang 7+/msvc 16+
-  * fmtlib 6.1.2+
+  * fmtlib 5.2.1+
   * pybind11 2.3.0+
 * python 3.6+
   * setuptools
-- 
GitLab


From bd27492d72afbca3c0a50a0573d56e9c575de569 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:18:23 +0100
Subject: [PATCH 34/50] Update CMakeLists.txt

---
 CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a5ee5840..713d4b80 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -63,7 +63,7 @@ target_compile_options(
 target_link_libraries(
 	"${TARGET_NAME}"
 	PRIVATE
-		fmt::fmt-header-only
+		fmt-header-only
 )
 
 add_custom_target(
-- 
GitLab


From 216d817d2561da31ef01a5e450e2ec5993f891c4 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:22:48 +0100
Subject: [PATCH 35/50] Update CMakeLists.txt

---
 CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 713d4b80..3471fb4f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -63,7 +63,7 @@ target_compile_options(
 target_link_libraries(
 	"${TARGET_NAME}"
 	PRIVATE
-		fmt-header-only
+		fmt::fmt
 )
 
 add_custom_target(
-- 
GitLab


From 957cb6901e4d7a1347045c3ca733e64a5cd2af8e Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:27:58 +0100
Subject: [PATCH 36/50] Update setup.py

---
 setup.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/setup.py b/setup.py
index cfe8605f..8dc782c8 100644
--- a/setup.py
+++ b/setup.py
@@ -61,6 +61,7 @@ setuptools.setup(
     long_description = open("README.md", "r").read(),
     long_description_content_type = "text/markdown",
     url = "https://gitlab.liu.se/PUM_TDDD96/B-ASIC",
+    license = "MIT",
     classifiers = [
         "Programming Language :: Python :: 3",
         "License :: OSI Approved :: MIT License",
-- 
GitLab


From d893d74b0c4180cdb8b3381396a11be23c29f897 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:37:38 +0100
Subject: [PATCH 37/50] Update setup.py

---
 setup.py | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/setup.py b/setup.py
index 8dc782c8..43d55d40 100644
--- a/setup.py
+++ b/setup.py
@@ -71,9 +71,7 @@ setuptools.setup(
     install_requires = [
         "pybind11>=2.3.0",
         "numpy",
-        "install_qt_binding",
-        "pytest==5.3.4",
-        "pytest-cov==2.8.1"
+        "install_qt_binding"
     ],
     packages = ["b_asic"],
     ext_modules = [CMakeExtension("b_asic")],
-- 
GitLab


From aa4f7bc54db3ef641cb73e40e4edf8155792a0b3 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Tue, 3 Mar 2020 12:42:48 +0100
Subject: [PATCH 38/50] Update .gitlab-ci.yml

---
 .gitlab-ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index daa60558..752a2af2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,6 +6,7 @@ stages:
 before_script:
   - apt-get update --yes
   - apt-get install --yes build-essential cmake libfmt-dev pybind11-dev
+  - pip3 install pytest pytest-cov
   - pip3 install .
   - pip3 show b_asic
 
-- 
GitLab


From 09e8cc0ab15f53451afd8b43ca17dfe4c469ccd6 Mon Sep 17 00:00:00 2001
From: Felix Goding <felgo673@student.liu.se>
Date: Wed, 4 Mar 2020 13:50:20 +0100
Subject: [PATCH 39/50] Update .gitlab-ci.yml

---
 .gitlab-ci.yml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 752a2af2..3b2095bb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,7 +12,5 @@ before_script:
 
 run tests:
   stage: test
-  only:
-    - develop
   script:
     - pytest test
\ No newline at end of file
-- 
GitLab


From 35dfc3ed3bb5fd64166ee9c54bbd5a7444f25185 Mon Sep 17 00:00:00 2001
From: Kevin Scott <kevsc634@student.liu.se>
Date: Wed, 4 Mar 2020 16:18:39 +0100
Subject: [PATCH 40/50] Resolve "Signal Interface"

---
 b_asic/basic_operation.py           | 16 +++++---
 b_asic/core_operations.py           | 10 ++---
 b_asic/port.py                      | 56 +++++++++++++++++---------
 b_asic/signal.py                    | 62 ++++++++++++-----------------
 b_asic/signal_flow_graph.py         |  6 ++-
 test/fixtures/__init__.py           |  0
 test/fixtures/signal.py             | 16 ++------
 test/port/__init__.py               |  0
 test/port/test_inputport.py         |  9 ++---
 test/port/test_outputport.py        |  6 +--
 test/port/test_port.py              | 25 ------------
 test/traverse/test_traverse_tree.py | 19 +++------
 12 files changed, 97 insertions(+), 128 deletions(-)
 delete mode 100644 test/fixtures/__init__.py
 delete mode 100644 test/port/__init__.py
 delete mode 100644 test/port/test_port.py

diff --git a/b_asic/basic_operation.py b/b_asic/basic_operation.py
index 54aaaebe..93a27222 100644
--- a/b_asic/basic_operation.py
+++ b/b_asic/basic_operation.py
@@ -8,7 +8,7 @@ from typing import List, Dict, Optional, Any
 from numbers import Number
 
 from b_asic.port import InputPort, OutputPort
-from b_asic.signal import SignalSource, SignalDestination
+from b_asic.signal import Signal
 from b_asic.operation import Operation
 from b_asic.simulation import SimulationState, OperationState
 
@@ -73,7 +73,7 @@ class BasicOperation(Operation):
 		while self_state.iteration < state.iteration:
 			input_values: List[Number] = [0] * input_count
 			for i in range(input_count):
-				source: SignalSource = self._input_ports[i].signal().source
+				source: Signal = self._input_ports[i].signal
 				input_values[i] = source.operation.evaluate_outputs(state)[source.port_index]
 
 			self_state.output_values = self.evaluate(input_values)
@@ -81,7 +81,7 @@ class BasicOperation(Operation):
 			self_state.iteration += 1
 			for i in range(output_count):
 				for signal in self._output_ports[i].signals():
-					destination: SignalDestination = signal.destination
+					destination: Signal = signal.destination
 					destination.evaluate_outputs(state)
 
 		return self_state.output_values
@@ -96,9 +96,13 @@ class BasicOperation(Operation):
 	@property
 	def neighbours(self) -> List[Operation]:
 		neighbours: List[Operation] = []
-		for port in self._output_ports + self._input_ports:
-			for signal in port.signals():
-				neighbours += [signal.source.operation, signal.destination.operation]
+		for port in self._input_ports:
+			for signal in port.signals:
+				neighbours.append(signal.source.operation)
+
+		for port in self._output_ports:
+			for signal in port.signals:
+				neighbours.append(signal.destination.operation)
 
 		return neighbours
 
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index 513fe7cf..45919b8b 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -28,7 +28,7 @@ class Constant(BasicOperation):
 	def __init__(self, value: Number):
 		"""Construct a Constant."""
 		super().__init__()
-		self._output_ports = [OutputPort(1)] # TODO: Generate appropriate ID for ports.
+		self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
 		self._parameters["value"] = value
 
 	def evaluate(self, inputs: list) -> list:
@@ -45,8 +45,8 @@ class Addition(BasicOperation):
 	def __init__(self):
 		"""Construct an Addition."""
 		super().__init__()
-		self._input_ports = [InputPort(1), InputPort(1)] # TODO: Generate appropriate ID for ports.
-		self._output_ports = [OutputPort(1)] # TODO: Generate appropriate ID for ports.
+		self._input_ports = [InputPort(1, self), InputPort(1, self)] # TODO: Generate appropriate ID for ports.
+		self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
 
 	def evaluate(self, inputs: list) -> list:
 		return [inputs[0] + inputs[1]]
@@ -63,8 +63,8 @@ class ConstantMultiplication(BasicOperation):
 	def __init__(self, coefficient: Number):
 		"""Construct a ConstantMultiplication."""
 		super().__init__()
-		self._input_ports = [InputPort(1)] # TODO: Generate appropriate ID for ports.
-		self._output_ports = [OutputPort(1)] # TODO: Generate appropriate ID for ports.
+		self._input_ports = [InputPort(1), self] # TODO: Generate appropriate ID for ports.
+		self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
 		self._parameters["coefficient"] = coefficient
 
 	def evaluate(self, inputs: list) -> list:
diff --git a/b_asic/port.py b/b_asic/port.py
index f67defb7..4c6fb244 100644
--- a/b_asic/port.py
+++ b/b_asic/port.py
@@ -7,6 +7,7 @@ from abc import ABC, abstractmethod
 from typing import NewType, Optional, List
 
 from b_asic.signal import Signal
+from b_asic.operation import Operation
 
 PortId = NewType("PortId", int)
 
@@ -16,29 +17,38 @@ class Port(ABC):
 	TODO: More info.
 	"""
 
-	_identifier: PortId
+	_port_id: PortId
+	_operation: Operation
 
-	def __init__(self, identifier: PortId):
-		"""Construct a Port."""
-		self._identifier = identifier
+	def __init__(self, port_id: PortId, operation: Operation):
+		self._port_id = port_id
+		self._operation = operation
 
+	@property
 	def identifier(self) -> PortId:
 		"""Get the unique identifier."""
-		return self._identifier
+		return self._port_id
 
+	@property
+	def operation(self) -> Operation:
+		"""Get the connected operation."""
+		return self._operation
+
+	@property
 	@abstractmethod
 	def signals(self) -> List[Signal]:
 		"""Get a list of all connected signals."""
 		pass
 
+	@property
 	@abstractmethod
-	def signal_count(self) -> int:
-		"""Get the number of connected signals."""
+	def signal(self, i: int = 0) -> Signal:
+		"""Get the connected signal at index i."""
 		pass
 
 	@abstractmethod
-	def signal(self, i: int = 0) -> Signal:
-		"""Get the connected signal at index i."""
+	def signal_count(self) -> int:
+		"""Get the number of connected signals."""
 		pass
 
 	@abstractmethod
@@ -51,6 +61,7 @@ class Port(ABC):
 		"""Disconnect a signal."""
 		pass
 
+
 	# TODO: More stuff.
 
 
@@ -60,26 +71,30 @@ class InputPort(Port):
 	"""
 	_source_signal: Optional[Signal]
 
-	def __init__(self, identifier: PortId):
-		super().__init__(identifier)
+	def __init__(self, port_id: PortId, operation: Operation):
+		super().__init__(port_id, operation)
 		self._source_signal = None
 
+	@property
 	def signals(self) -> List[Signal]:
 		return [] if self._source_signal is None else [self._source_signal]
 
-	def signal_count(self) -> int:
-		return 0 if self._source_signal is None else 1
-
+	@property
 	def signal(self, i: int = 0) -> Signal:
 		assert 0 <= i < self.signal_count() # TODO: Error message.
 		assert self._source_signal is not None # TODO: Error message.
 		return self._source_signal
 
+	def signal_count(self) -> int:
+		return 0 if self._source_signal is None else 1
+
 	def connect(self, signal: Signal) -> None:
 		self._source_signal = signal
+		signal.destination = self
 
 	def disconnect(self, i: int = 0) -> None:
 		assert 0 <= i < self.signal_count() # TODO: Error message.
+		self._source_signal.disconnect_source()
 		self._source_signal = None
 
 	# TODO: More stuff.
@@ -92,23 +107,26 @@ class OutputPort(Port):
 
 	_destination_signals: List[Signal]
 
-	def __init__(self, identifier: PortId):
-		super().__init__(identifier)
+	def __init__(self, port_id: PortId, operation: Operation):
+		super().__init__(port_id, operation)
 		self._destination_signals = []
 
+	@property
 	def signals(self) -> List[Signal]:
 		return self._destination_signals.copy()
 
-	def signal_count(self) -> int:
-		return len(self._destination_signals)
-
+	@property
 	def signal(self, i: int = 0) -> Signal:
 		assert 0 <= i < self.signal_count() # TODO: Error message.
 		return self._destination_signals[i]
 
+	def signal_count(self) -> int:
+		return len(self._destination_signals)
+
 	def connect(self, signal: Signal) -> None:
 		assert signal not in self._destination_signals # TODO: Error message.
 		self._destination_signals.append(signal)
+		signal.source = self
 
 	def disconnect(self, i: int = 0) -> None:
 		assert 0 <= i < self.signal_count() # TODO: Error message.
diff --git a/b_asic/signal.py b/b_asic/signal.py
index 7e63ebfb..17078138 100644
--- a/b_asic/signal.py
+++ b/b_asic/signal.py
@@ -1,48 +1,38 @@
 """@package docstring
 B-ASIC Signal Module.
-TODO: More info.
 """
+from typing import TYPE_CHECKING, Optional
+if TYPE_CHECKING:
+	from b_asic import OutputPort, InputPort
 
-from b_asic.operation import Operation
-
-
-class SignalSource:
-	"""Handle to a signal source.
-	TODO: More info.
-	"""
-	operation: Operation
-	port_index: int
-
-	def __init__(self, operation: Operation, port_index: int):
-		self.operation = operation
-		self.port_index = port_index
-
-	# TODO: More stuff.
+class Signal:
+	"""A connection between two ports."""
+	_source: "OutputPort"
+	_destination: "InputPort"
 
+	def __init__(self, src: Optional["OutputPort"] = None, dest: Optional["InputPort"] = None):
+		self._source = src
+		self._destination = dest
 
-class SignalDestination:
-	"""Handle to a signal destination.
-	TODO: More info.
-	"""
-	operation: Operation
-	port_index: int
+	@property	
+	def source(self) -> "InputPort":
+		return self._source
 
-	def __init__(self, operation: Operation, port_index: int):
-		self.operation = operation
-		self.port_index = port_index
+	@property
+	def destination(self) -> "OutputPort":
+		return self._destination
 
-	# TODO: More stuff.
+	@source.setter
+	def source(self, src: "Outputport") -> None:
+		self._source = src
 
+	@destination.setter
+	def destination(self, dest: "InputPort") -> None:
+		self._destination = dest
 
-class Signal:
-	"""A connection between two operations consisting of a source and destination handle.
-	TODO: More info.
-	"""
-	source: SignalSource
-	destination: SignalDestination
+	def disconnect_source(self) -> None:
+		self._source = None
 
-	def __init__(self, source: SignalSource, destination: SignalDestination):
-		self.source = source
-		self.destination = destination
+	def disconnect_destination(self) -> None:
+		self._destination = None
 
-	# TODO: More stuff.
diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py
index 914cf390..f7d4be64 100644
--- a/b_asic/signal_flow_graph.py
+++ b/b_asic/signal_flow_graph.py
@@ -7,7 +7,9 @@ from typing import List, Dict, Union, Optional
 
 from b_asic.operation import Operation
 from b_asic.basic_operation import BasicOperation
-from b_asic.signal import Signal, SignalSource, SignalDestination
+from b_asic.signal import Signal
+from b_asic.simulation import SimulationState, OperationState
+from typing import List
 from b_asic.graph_id import GraphIDGenerator, GraphID
 
 
@@ -19,7 +21,7 @@ class SFG(BasicOperation):
 	_graph_objects_by_id: Dict[GraphID, Union[Operation, Signal]]
 	_graph_id_generator: GraphIDGenerator
 
-	def __init__(self, input_destinations: List[SignalDestination], output_sources: List[SignalSource]):
+	def __init__(self, input_destinations: List[Signal], output_sources: List[Signal]):
 		super().__init__()
 		# TODO: Allocate input/output ports with appropriate IDs.
 
diff --git a/test/fixtures/__init__.py b/test/fixtures/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/test/fixtures/signal.py b/test/fixtures/signal.py
index 64b96f55..9139e93a 100644
--- a/test/fixtures/signal.py
+++ b/test/fixtures/signal.py
@@ -1,20 +1,10 @@
 import pytest
-from b_asic import Signal, SignalSource, SignalDestination, Addition
+from b_asic import Signal
 
-"""
-Use a fixture for initializing objects and pass them as argument to a test function
-"""
 @pytest.fixture
 def signal():
-    source = SignalSource(Addition(), 1)
-    dest = SignalDestination(Addition(), 2)
-    return Signal(source, dest)
+    return Signal()
 
 @pytest.fixture
 def signals():
-    ret = []
-    for _ in range(0,3):
-        source = SignalSource(Addition(), 1)
-        dest = SignalDestination(Addition(), 2)
-        ret.append(Signal(source, dest))
-    return ret
\ No newline at end of file
+    return [Signal() for _ in range(0,3)]
diff --git a/test/port/__init__.py b/test/port/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/test/port/test_inputport.py b/test/port/test_inputport.py
index d761900a..f0e70cb7 100644
--- a/test/port/test_inputport.py
+++ b/test/port/test_inputport.py
@@ -2,22 +2,19 @@
 B-ASIC test suite for Inputport
 """
 
-# import module we are testing
 from b_asic import InputPort
 
-# import dependencies
-from b_asic import Signal, SignalSource, SignalDestination, Addition
-
 import pytest
 
 def test_connect_multiple_signals(signals):
     """
     test if only one signal can connect to an input port
     """
-    inp_port = InputPort(0)
+    inp_port = InputPort(0, None)
 
     for s in signals:
         inp_port.connect(s)
 
     assert inp_port.signal_count() == 1
-    assert inp_port.signals()[0] == signals[-1]
\ No newline at end of file
+    assert inp_port.signals[0] == signals[-1]
+
diff --git a/test/port/test_outputport.py b/test/port/test_outputport.py
index 5f7b8f49..5c76bb48 100644
--- a/test/port/test_outputport.py
+++ b/test/port/test_outputport.py
@@ -2,17 +2,17 @@
 B-ASIC test suite for InputPort
 TODO: More info
 """
-from b_asic import OutputPort, Signal, SignalSource, SignalDestination, Addition
+from b_asic import OutputPort
 import pytest
 
 def test_connect_multiple_signals(signals):
     """
     test if multiple signals can connect to an output port
     """
-    outp_port = OutputPort(0)
+    outp_port = OutputPort(0, None)
 
     for s in signals:
         outp_port.connect(s)
 
     assert outp_port.signal_count() == 3
-    assert outp_port.signals() == signals
\ No newline at end of file
+    assert outp_port.signals == signals
\ No newline at end of file
diff --git a/test/port/test_port.py b/test/port/test_port.py
deleted file mode 100644
index 7e1fc9b7..00000000
--- a/test/port/test_port.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""
-B-ASIC test suite for Port interface, place all general test cases for abstract class Port here
-"""
-
-from b_asic import InputPort, OutputPort, Signal, SignalSource, SignalDestination, Addition
-import pytest
-
-
-def test_connect_one_signal_to_port(signal):
-    port = InputPort(0)
-    port.connect(signal)
-    assert len(port.signals()) == 1
-    assert port.signal() == signal
-
-def test_change_port_signal():
-    source = SignalSource(Addition, 1)
-    dest = SignalDestination(Addition,2)
-    signal1 = Signal(source, dest)
-    signal2 = Signal(source, dest)
-
-    port = InputPort(0)
-    port.connect(signal1)
-    assert port.signal() == signal1
-    port.connect(signal2)
-    assert port.signal() == signal2
\ No newline at end of file
diff --git a/test/traverse/test_traverse_tree.py b/test/traverse/test_traverse_tree.py
index d218a6e7..57e8a67b 100644
--- a/test/traverse/test_traverse_tree.py
+++ b/test/traverse/test_traverse_tree.py
@@ -5,7 +5,7 @@ TODO:
 """
 
 from b_asic.core_operations import Constant, Addition
-from b_asic.signal import Signal, SignalSource, SignalDestination
+from b_asic.signal import Signal 
 from b_asic.port import InputPort, OutputPort
 from b_asic.traverse_tree import Traverse
 
@@ -17,10 +17,9 @@ def operation():
 
 def create_operation(_type, dest_oper, index, **kwargs):
     oper = _type(**kwargs)
-    oper_signal_source = SignalSource(oper, 0)
-    oper_signal_dest = SignalDestination(dest_oper, index)
-    oper_signal = Signal(oper_signal_source, oper_signal_dest)
+    oper_signal = Signal()
     oper._output_ports[0].connect(oper_signal)
+
     dest_oper._input_ports[index].connect(oper_signal)
     return oper
 
@@ -45,15 +44,11 @@ def large_operation_tree():
     const_oper_4 = create_operation(Constant, add_oper_2, 1, value=5)
 
     add_oper_3 = Addition()
-    add_oper_signal_source = SignalSource(add_oper, 0)
-    add_oper_signal_dest = SignalDestination(add_oper_3, 0)
-    add_oper_signal = Signal(add_oper_signal_source, add_oper_signal_dest)
+    add_oper_signal = Signal(add_oper, add_oper_3)
     add_oper._output_ports[0].connect(add_oper_signal)
     add_oper_3._input_ports[0].connect(add_oper_signal)
 
-    add_oper_2_signal_source = SignalSource(add_oper_2, 0)
-    add_oper_2_signal_dest = SignalDestination(add_oper_3, 1)
-    add_oper_2_signal = Signal(add_oper_2_signal_source, add_oper_2_signal_dest)
+    add_oper_2_signal = Signal(add_oper_2, add_oper_3)
     add_oper_2._output_ports[0].connect(add_oper_2_signal)
     add_oper_3._input_ports[1].connect(add_oper_2_signal)
     return const_oper
@@ -76,9 +71,7 @@ def test_traverse_type(large_operation_tree):
     assert len(traverse.traverse(Constant)) == 4
 
 def test_traverse_loop(operation_tree):
-    add_oper_signal_source = SignalSource(operation_tree, 0)
-    add_oper_signal_dest = SignalDestination(operation_tree, 0)
-    add_oper_signal = Signal(add_oper_signal_source, add_oper_signal_dest)
+    add_oper_signal = Signal()
     operation_tree._output_ports[0].connect(add_oper_signal)
     operation_tree._input_ports[0].connect(add_oper_signal)
     traverse = Traverse(operation_tree)
-- 
GitLab


From 5fffc69bcaae6b81ba570e2295f40914c24c5c6e Mon Sep 17 00:00:00 2001
From: Angus Lothian <anglo547@student.liu.se>
Date: Thu, 5 Mar 2020 10:57:04 +0100
Subject: [PATCH 41/50] Change so that Basic Operation also inherits from
 abstract graph component and removed name property implementation

---
 b_asic/__init__.py                            |  7 +-
 b_asic/abstract_graph_component.py            | 25 ++++++
 ...sic_operation.py => abstract_operation.py} | 14 ++--
 b_asic/core_operations.py                     | 42 +++++-----
 b_asic/graph_component.py                     | 34 ++++++++
 b_asic/operation.py                           | 41 +++++-----
 b_asic/port.py                                | 16 ++--
 b_asic/signal.py                              | 28 +++++--
 b_asic/signal_flow_graph.py                   | 80 ++++++++++++-------
 .../test_signal_flow_graph.py                 |  7 ++
 10 files changed, 197 insertions(+), 97 deletions(-)
 create mode 100644 b_asic/abstract_graph_component.py
 rename b_asic/{basic_operation.py => abstract_operation.py} (90%)
 create mode 100644 b_asic/graph_component.py

diff --git a/b_asic/__init__.py b/b_asic/__init__.py
index 752bac07..4ae6652b 100644
--- a/b_asic/__init__.py
+++ b/b_asic/__init__.py
@@ -2,9 +2,11 @@
 Better ASIC Toolbox.
 TODO: More info.
 """
-from _b_asic import *
-from b_asic.basic_operation import *
+from b_asic.abstract_graph_component import *
+from b_asic.abstract_operation import *
 from b_asic.core_operations import *
+from b_asic.graph_component import *
+from b_asic.graph_id import *
 from b_asic.operation import *
 from b_asic.precedence_chart import *
 from b_asic.port import *
@@ -12,3 +14,4 @@ from b_asic.schema import *
 from b_asic.signal_flow_graph import *
 from b_asic.signal import *
 from b_asic.simulation import *
+from b_asic.traverse_tree import *
diff --git a/b_asic/abstract_graph_component.py b/b_asic/abstract_graph_component.py
new file mode 100644
index 00000000..6efb94e3
--- /dev/null
+++ b/b_asic/abstract_graph_component.py
@@ -0,0 +1,25 @@
+"""@package docstring
+B-ASIC module for Graph Components of a signal flow graph.
+TODO: More info.
+"""
+
+from b_asic.graph_component import GraphComponent, Name
+
+class AbstractGraphComponent(GraphComponent):
+    """Abstract Graph Component class which is a component of a signal flow graph.
+
+    TODO: More info.
+    """
+
+    _name: Name
+
+    def __init__(self, name: Name = ""):
+        self._name = name
+
+    @property
+    def name(self) -> Name:
+        return self._name
+
+    @name.setter
+    def name(self, name: Name) -> None:
+        self._name = name
diff --git a/b_asic/basic_operation.py b/b_asic/abstract_operation.py
similarity index 90%
rename from b_asic/basic_operation.py
rename to b_asic/abstract_operation.py
index 93a27222..ab8438a5 100644
--- a/b_asic/basic_operation.py
+++ b/b_asic/abstract_operation.py
@@ -1,5 +1,5 @@
 """@package docstring
-B-ASIC Basic Operation Module.
+B-ASIC Abstract Operation Module.
 TODO: More info.
 """
 
@@ -11,9 +11,9 @@ from b_asic.port import InputPort, OutputPort
 from b_asic.signal import Signal
 from b_asic.operation import Operation
 from b_asic.simulation import SimulationState, OperationState
+from b_asic.abstract_graph_component import AbstractGraphComponent
 
-
-class BasicOperation(Operation):
+class AbstractOperation(Operation, AbstractGraphComponent):
 	"""Generic abstract operation class which most implementations will derive from.
 	TODO: More info.
 	"""
@@ -22,8 +22,8 @@ class BasicOperation(Operation):
 	_output_ports: List[OutputPort]
 	_parameters: Dict[str, Optional[Any]]
 
-	def __init__(self):
-		"""Construct a BasicOperation."""
+	def __init__(self, **kwds):
+		super().__init__(**kwds)
 		self._input_ports = []
 		self._output_ports = []
 		self._parameters = {}
@@ -31,7 +31,7 @@ class BasicOperation(Operation):
 	@abstractmethod
 	def evaluate(self, inputs: list) -> list:
 		"""Evaluate the operation and generate a list of output values given a list of input values."""
-		pass
+		raise NotImplementedError
 
 	def inputs(self) -> List[InputPort]:
 		return self._input_ports.copy()
@@ -68,7 +68,7 @@ class BasicOperation(Operation):
 		assert input_count == len(self._input_ports) # TODO: Error message.
 		assert output_count == len(self._output_ports) # TODO: Error message.
 
-		self_state: OperationState = state.operation_states[self.identifier()]
+		self_state: OperationState = state.operation_states[self]
 
 		while self_state.iteration < state.iteration:
 			input_values: List[Number] = [0] * input_count
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index 45919b8b..52f18361 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -7,62 +7,69 @@ from numbers import Number
 
 from b_asic.port import InputPort, OutputPort
 from b_asic.operation import Operation
-from b_asic.basic_operation import BasicOperation
-from b_asic.graph_id import GraphIDType
+from b_asic.abstract_operation import AbstractOperation
+from b_asic.abstract_graph_component import AbstractGraphComponent
+from b_asic.graph_component import Name, TypeName
 
 
-class Input(Operation):
+class Input(Operation, AbstractGraphComponent):
 	"""Input operation.
 	TODO: More info.
 	"""
 
 	# TODO: Implement all functions.
-	pass
 
+	@property
+	def type_name(self) -> TypeName:
+		return "in"
 
-class Constant(BasicOperation):
+
+class Constant(AbstractOperation):
 	"""Constant value operation.
 	TODO: More info.
 	"""
 
-	def __init__(self, value: Number):
+	def __init__(self, value: Number, **kwds):
 		"""Construct a Constant."""
-		super().__init__()
+		super().__init__(**kwds)
 		self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
 		self._parameters["value"] = value
 
 	def evaluate(self, inputs: list) -> list:
 		return [self.param("value")]
 
-	def type_name(self) -> GraphIDType:
+	@property
+	def type_name(self) -> TypeName:
 		return "const"
 
-class Addition(BasicOperation):
+
+class Addition(AbstractOperation):
 	"""Binary addition operation.
 	TODO: More info.
 	"""
 
-	def __init__(self):
+	def __init__(self, **kwds):
 		"""Construct an Addition."""
-		super().__init__()
+		super().__init__(**kwds)
 		self._input_ports = [InputPort(1, self), InputPort(1, self)] # TODO: Generate appropriate ID for ports.
 		self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
 
 	def evaluate(self, inputs: list) -> list:
 		return [inputs[0] + inputs[1]]
 
-	def type_name(self) -> GraphIDType:
+	@property
+	def type_name(self) -> TypeName:
 		return "add"
 
 
-class ConstantMultiplication(BasicOperation):
+class ConstantMultiplication(AbstractOperation):
 	"""Unary constant multiplication operation.
 	TODO: More info.
 	"""
 
-	def __init__(self, coefficient: Number):
+	def __init__(self, coefficient: Number, **kwds):
 		"""Construct a ConstantMultiplication."""
-		super().__init__()
+		super().__init__(**kwds)
 		self._input_ports = [InputPort(1), self] # TODO: Generate appropriate ID for ports.
 		self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
 		self._parameters["coefficient"] = coefficient
@@ -70,7 +77,6 @@ class ConstantMultiplication(BasicOperation):
 	def evaluate(self, inputs: list) -> list:
 		return [inputs[0] * self.param("coefficient")]
 
-	def type_name(self) -> GraphIDType:
+	@property
+	def type_name(self) -> TypeName:
 		return "const_mul"
-
-# TODO: More operations.
diff --git a/b_asic/graph_component.py b/b_asic/graph_component.py
new file mode 100644
index 00000000..4bce63f2
--- /dev/null
+++ b/b_asic/graph_component.py
@@ -0,0 +1,34 @@
+"""@package docstring
+B-ASIC Operation Module.
+TODO: More info.
+"""
+
+from abc import ABC, abstractmethod
+from typing import NewType
+
+Name = NewType("Name", str)
+TypeName = NewType("TypeName", str)
+
+
+class GraphComponent(ABC):
+    """Graph component interface.
+    TODO: More info.
+    """
+
+    @property
+    @abstractmethod
+    def type_name(self) -> TypeName:
+        """Returns the type name of the graph component"""
+        raise NotImplementedError
+
+    @property
+    @abstractmethod
+    def name(self) -> Name:
+        """Returns the name of the graph component."""
+        raise NotImplementedError
+
+    @name.setter
+    @abstractmethod
+    def name(self, name: Name) -> None:
+        """Sets the name of the graph component to the entered name."""
+        raise NotImplementedError
diff --git a/b_asic/operation.py b/b_asic/operation.py
index 923690aa..5d4b404d 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -3,17 +3,17 @@ B-ASIC Operation Module.
 TODO: More info.
 """
 
-from abc import ABC, abstractmethod
+from abc import abstractmethod
 from numbers import Number
 from typing import List, Dict, Optional, Any, TYPE_CHECKING
 
+from b_asic.graph_component import GraphComponent
+
 if TYPE_CHECKING:
 	from b_asic.port import InputPort, OutputPort
 	from b_asic.simulation import SimulationState
-	from b_asic.graph_id import GraphIDType
-
 
-class Operation(ABC):
+class Operation(GraphComponent):
 	"""Operation interface.
 	TODO: More info.
 	"""
@@ -21,75 +21,72 @@ class Operation(ABC):
 	@abstractmethod
 	def inputs(self) -> "List[InputPort]":
 		"""Get a list of all input ports."""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def outputs(self) -> "List[OutputPort]":
 		"""Get a list of all output ports."""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def input_count(self) -> int:
 		"""Get the number of input ports."""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def output_count(self) -> int:
 		"""Get the number of output ports."""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def input(self, i: int) -> "InputPort":
 		"""Get the input port at index i."""
-		pass
+		raise NotImplementedError
+
 
 	@abstractmethod
 	def output(self, i: int) -> "OutputPort":
 		"""Get the output port at index i."""
-		pass
+		raise NotImplementedError
+
 
 	@abstractmethod
 	def params(self) -> Dict[str, Optional[Any]]:
 		"""Get a dictionary of all parameter values."""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def param(self, name: str) -> Optional[Any]:
 		"""Get the value of a parameter.
 		Returns None if the parameter is not defined.
 		"""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def set_param(self, name: str, value: Any) -> None:
 		"""Set the value of a parameter.
 		The parameter must be defined.
 		"""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def evaluate_outputs(self, state: "SimulationState") -> List[Number]:
 		"""Simulate the circuit until its iteration count matches that of the simulation state,
 		then return the resulting output vector.
 		"""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def split(self) -> "List[Operation]":
 		"""Split the operation into multiple operations.
 		If splitting is not possible, this may return a list containing only the operation itself.
 		"""
-		pass
-
-	@abstractmethod
-	def type_name(self) -> "GraphIDType":
-		"""Returns a string representing the operation name of the operation."""
-		pass
+		raise NotImplementedError
 
+	@property
 	@abstractmethod
 	def neighbours(self) -> "List[Operation]":
 		"""Return all operations that are connected by signals to this operation.
 		If no neighbours are found this returns an empty list
 		"""
-
-	# TODO: More stuff.
+		raise NotImplementedError
diff --git a/b_asic/port.py b/b_asic/port.py
index 4c6fb244..a8a062fc 100644
--- a/b_asic/port.py
+++ b/b_asic/port.py
@@ -38,31 +38,27 @@ class Port(ABC):
 	@abstractmethod
 	def signals(self) -> List[Signal]:
 		"""Get a list of all connected signals."""
-		pass
+		raise NotImplementedError
 
-	@property
 	@abstractmethod
 	def signal(self, i: int = 0) -> Signal:
 		"""Get the connected signal at index i."""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def signal_count(self) -> int:
 		"""Get the number of connected signals."""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def connect(self, signal: Signal) -> None:
 		"""Connect a signal."""
-		pass
+		raise NotImplementedError
 
 	@abstractmethod
 	def disconnect(self, i: int = 0) -> None:
 		"""Disconnect a signal."""
-		pass
-
-
-	# TODO: More stuff.
+		raise NotImplementedError
 
 
 class InputPort(Port):
@@ -79,7 +75,6 @@ class InputPort(Port):
 	def signals(self) -> List[Signal]:
 		return [] if self._source_signal is None else [self._source_signal]
 
-	@property
 	def signal(self, i: int = 0) -> Signal:
 		assert 0 <= i < self.signal_count() # TODO: Error message.
 		assert self._source_signal is not None # TODO: Error message.
@@ -115,7 +110,6 @@ class OutputPort(Port):
 	def signals(self) -> List[Signal]:
 		return self._destination_signals.copy()
 
-	@property
 	def signal(self, i: int = 0) -> Signal:
 		assert 0 <= i < self.signal_count() # TODO: Error message.
 		return self._destination_signals[i]
diff --git a/b_asic/signal.py b/b_asic/signal.py
index 17078138..810c00dc 100644
--- a/b_asic/signal.py
+++ b/b_asic/signal.py
@@ -2,37 +2,51 @@
 B-ASIC Signal Module.
 """
 from typing import TYPE_CHECKING, Optional
+
+from b_asic.graph_component import TypeName
+from b_asic.abstract_graph_component import AbstractGraphComponent
+
 if TYPE_CHECKING:
 	from b_asic import OutputPort, InputPort
 
-class Signal:
+class Signal(AbstractGraphComponent):
 	"""A connection between two ports."""
 	_source: "OutputPort"
 	_destination: "InputPort"
 
-	def __init__(self, src: Optional["OutputPort"] = None, dest: Optional["InputPort"] = None):
+	def __init__(self, src: Optional["OutputPort"] = None, dest: Optional["InputPort"] = None, **kwds):
+		super().__init__(**kwds)
 		self._source = src
 		self._destination = dest
 
-	@property	
-	def source(self) -> "InputPort":
+	@property
+	def source(self) -> "OutputPort":
+		"""Returns the source OutputPort of the signal."""
 		return self._source
 
 	@property
-	def destination(self) -> "OutputPort":
+	def destination(self) -> "InputPort":
+		"""Returns the destination InputPort of the signal."""
 		return self._destination
 
 	@source.setter
-	def source(self, src: "Outputport") -> None:
+	def source(self, src: "OutputPort") -> None:
+		"""Sets the value of the source OutputPort of the signal."""
 		self._source = src
 
 	@destination.setter
 	def destination(self, dest: "InputPort") -> None:
+		"""Sets the value of the destination InputPort of the signal."""
 		self._destination = dest
 
+	@property
+	def type_name(self) -> TypeName:
+		return "s"
+
 	def disconnect_source(self) -> None:
+		"""Disconnects the source OutputPort of the signal."""
 		self._source = None
 
 	def disconnect_destination(self) -> None:
+		"""Disconnects the destination InputPort of the signal."""
 		self._destination = None
-
diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py
index f7d4be64..38c46697 100644
--- a/b_asic/signal_flow_graph.py
+++ b/b_asic/signal_flow_graph.py
@@ -3,69 +3,89 @@ B-ASIC Signal Flow Graph Module.
 TODO: More info.
 """
 
-from typing import List, Dict, Union, Optional
+from typing import List, Dict, Optional, DefaultDict
+from collections import defaultdict
 
 from b_asic.operation import Operation
-from b_asic.basic_operation import BasicOperation
+from b_asic.abstract_operation import AbstractOperation
 from b_asic.signal import Signal
-from b_asic.simulation import SimulationState, OperationState
-from typing import List
 from b_asic.graph_id import GraphIDGenerator, GraphID
+from b_asic.graph_component import GraphComponent, Name, TypeName
 
 
-class SFG(BasicOperation):
+class SFG(AbstractOperation):
 	"""Signal flow graph.
 	TODO: More info.
 	"""
 
-	_graph_objects_by_id: Dict[GraphID, Union[Operation, Signal]]
+	_graph_components_by_id: Dict[GraphID, GraphComponent]
+	_graph_components_by_name: DefaultDict[Name, List[GraphComponent]]
 	_graph_id_generator: GraphIDGenerator
 
-	def __init__(self, input_destinations: List[Signal], output_sources: List[Signal]):
-		super().__init__()
-		# TODO: Allocate input/output ports with appropriate IDs.
-
-		self._graph_objects_by_id = dict # Map Operation ID to Operation objects
+	def __init__(self, input_signals: List[Signal] = None, output_signals: List[Signal] = None, \
+				ops: List[Operation] = None, **kwds):
+		super().__init__(**kwds)
+		if input_signals is None:
+			input_signals = []
+		if output_signals is None:
+			output_signals = []
+		if ops is None:
+			ops = []
+
+		self._graph_components_by_id = dict() # Maps Graph ID to objects
+		self._graph_components_by_name = defaultdict(list) # Maps Name to objects
 		self._graph_id_generator = GraphIDGenerator()
 
+		for operation in ops:
+			self._add_graph_component(operation)
+
+		for input_signal in input_signals:
+			self._add_graph_component(input_signal)
+
+		# TODO: Construct SFG based on what inputs that were given
 		# TODO: Traverse the graph between the inputs/outputs and add to self._operations.
 		# TODO: Connect ports with signals with appropriate IDs.
 
 	def evaluate(self, inputs: list) -> list:
 		return [] # TODO: Implement
 
-	def add_operation(self, operation: Operation) -> GraphID:
-		"""Adds the entered operation to the SFG's dictionary of graph objects and
+	def _add_graph_component(self, graph_component: GraphComponent) -> GraphID:
+		"""Adds the entered graph component to the SFG's dictionary of graph objects and
 	 	returns a generated GraphID for it.
 
 		Keyword arguments:
-		operation: Operation to add to the graph.
+		graph_component: Graph component to add to the graph.
 		"""
-		return self._add_graph_obj(operation, operation.type_name())
+		# Add to name dict
+		self._graph_components_by_name[graph_component.name].append(graph_component)
 
-	def add_signal(self, signal: Signal) -> GraphID:
-		"""Adds the entered signal to the SFG's dictionary of graph objects and returns
-		a generated GraphID for it.
-
-		Keyword argumentst:
-		signal: Signal to add to the graph.
-		"""
-		return self._add_graph_obj(signal, 'sig')
+		# Add to ID dict
+		graph_id: GraphID = self._graph_id_generator.get_next_id(graph_component.type_name)
+		self._graph_components_by_id[graph_id] = graph_component
+		return graph_id
 
-	def find_by_id(self, graph_id: GraphID) -> Optional[Operation]:
+	def find_by_id(self, graph_id: GraphID) -> Optional[GraphComponent]:
 		"""Finds a graph object based on the entered Graph ID and returns it. If no graph
 		object with the entered ID was found then returns None.
 
 		Keyword arguments:
 		graph_id: Graph ID of the wanted object.
 		"""
-		if graph_id in self._graph_objects_by_id:
-			return self._graph_objects_by_id[graph_id]
+		if graph_id in self._graph_components_by_id:
+			return self._graph_components_by_id[graph_id]
 
 		return None
 
-	def _add_graph_obj(self, obj: Union[Operation, Signal], operation_id_type: str):
-		graph_id = self._graph_id_generator.get_next_id(operation_id_type)
-		self._graph_objects_by_id[graph_id] = obj
-		return graph_id
+	def find_by_name(self, name: Name) -> List[GraphComponent]:
+		"""Finds all graph objects that have the entered name and returns them
+		in a list. If no graph object with the entered name was found then returns an
+		empty list.
+
+		Keyword arguments:
+		name: Name of the wanted object.
+		"""
+		return self._graph_components_by_name[name]
 
+	@property
+	def type_name(self) -> TypeName:
+		return "sfg"
diff --git a/test/signal_flow_graph/test_signal_flow_graph.py b/test/signal_flow_graph/test_signal_flow_graph.py
index 921e8906..d18d2da5 100644
--- a/test/signal_flow_graph/test_signal_flow_graph.py
+++ b/test/signal_flow_graph/test_signal_flow_graph.py
@@ -1,3 +1,10 @@
 from b_asic.signal_flow_graph import SFG
 from b_asic.core_operations import Addition, Constant
 from b_asic.signal import Signal
+from b_asic.signal_flow_graph import SFG
+
+import pytest
+
+def test_adding_to_sfg():
+    pass
+
-- 
GitLab


From 72cb843b1447226589a05ffa67437d57fc801bcc Mon Sep 17 00:00:00 2001
From: Jacob Wahlman <jacwa448@student.liu.se>
Date: Thu, 5 Mar 2020 11:40:09 +0100
Subject: [PATCH 42/50] Resolve "Operation Traversing"

---
 b_asic/__init__.py                  |  2 +-
 b_asic/abstract_operation.py        |  7 ++-
 b_asic/traverse_tree.py             | 43 -----------------
 b_asic/utilities.py                 | 21 +++++++++
 test/conftest.py                    |  3 +-
 test/fixtures/operation_tree.py     | 44 +++++++++++++++++
 test/traverse/test_traverse_tree.py | 73 +++++------------------------
 7 files changed, 86 insertions(+), 107 deletions(-)
 delete mode 100644 b_asic/traverse_tree.py
 create mode 100644 b_asic/utilities.py
 create mode 100644 test/fixtures/operation_tree.py

diff --git a/b_asic/__init__.py b/b_asic/__init__.py
index 4ae6652b..fc787edf 100644
--- a/b_asic/__init__.py
+++ b/b_asic/__init__.py
@@ -14,4 +14,4 @@ from b_asic.schema import *
 from b_asic.signal_flow_graph import *
 from b_asic.signal import *
 from b_asic.simulation import *
-from b_asic.traverse_tree import *
+from b_asic.utilities import *
diff --git a/b_asic/abstract_operation.py b/b_asic/abstract_operation.py
index ab8438a5..558022a7 100644
--- a/b_asic/abstract_operation.py
+++ b/b_asic/abstract_operation.py
@@ -4,13 +4,14 @@ TODO: More info.
 """
 
 from abc import abstractmethod
-from typing import List, Dict, Optional, Any
+from typing import List, Set, Dict, Optional, Any
 from numbers import Number
 
 from b_asic.port import InputPort, OutputPort
 from b_asic.signal import Signal
 from b_asic.operation import Operation
 from b_asic.simulation import SimulationState, OperationState
+from b_asic.utilities import breadth_first_search
 from b_asic.abstract_graph_component import AbstractGraphComponent
 
 class AbstractOperation(Operation, AbstractGraphComponent):
@@ -106,4 +107,8 @@ class AbstractOperation(Operation, AbstractGraphComponent):
 
 		return neighbours
 
+	def traverse(self) -> Operation:
+		"""Traverse the operation tree and return a generator with start point in the operation."""
+		return breadth_first_search(self)
+
 	# TODO: More stuff.
diff --git a/b_asic/traverse_tree.py b/b_asic/traverse_tree.py
deleted file mode 100644
index dc00371e..00000000
--- a/b_asic/traverse_tree.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""@package docstring
-B-ASIC Operation Tree Traversing Module.
-"""
-
-from typing import List, Optional
-from collections import deque
-
-from b_asic.operation import Operation
-
-
-class Traverse:
-    """Traverse operation tree."""
-
-    def __init__(self, operation: Operation):
-        """Construct a TraverseTree."""
-        self._initial_operation = operation
-
-    def _breadth_first_search(self, start: Operation) -> List[Operation]:
-        """Use breadth first search to traverse the operation tree."""
-        visited: List[Operation] = [start]
-        queue = deque([start])
-        while queue:
-            operation = queue.popleft()
-            for n_operation in operation.neighbours:
-                if n_operation not in visited:
-                    visited.append(n_operation)
-                    queue.append(n_operation)
-
-        return visited
-
-    def traverse(self, type_: Optional[Operation] = None) -> List[Operation]:
-        """Traverse the the operation tree and return operation where type matches.
-        If the type is None then return the entire tree.
-
-        Keyword arguments:
-        type_-- the operation type to search for (default None)
-        """
-
-        operations: List[Operation] = self._breadth_first_search(self._initial_operation)
-        if type_ is not None:
-            operations = [oper for oper in operations if isinstance(oper, type_)]
-
-        return operations
diff --git a/b_asic/utilities.py b/b_asic/utilities.py
new file mode 100644
index 00000000..25707ff8
--- /dev/null
+++ b/b_asic/utilities.py
@@ -0,0 +1,21 @@
+"""@package docstring
+B-ASIC Operation Module.
+TODO: More info.
+"""
+
+from typing import Set
+from collections import deque
+
+from b_asic.operation import Operation
+
+def breadth_first_search(start: Operation) -> Operation:
+    """Use breadth first search to traverse the operation tree."""
+    visited: Set[Operation] = {start}
+    queue = deque([start])
+    while queue:
+        operation = queue.popleft()
+        yield operation
+        for n_operation in operation.neighbours:
+            if n_operation not in visited:
+                visited.add(n_operation)
+                queue.append(n_operation)
diff --git a/test/conftest.py b/test/conftest.py
index 986af94c..66ee9630 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -1,2 +1,3 @@
 import pytest
-from test.fixtures.signal import *
\ No newline at end of file
+from test.fixtures.signal import *
+from test.fixtures.operation_tree import *
\ No newline at end of file
diff --git a/test/fixtures/operation_tree.py b/test/fixtures/operation_tree.py
new file mode 100644
index 00000000..bcc0bb27
--- /dev/null
+++ b/test/fixtures/operation_tree.py
@@ -0,0 +1,44 @@
+from b_asic.core_operations import Addition, Constant
+from b_asic.signal import Signal
+
+import pytest
+
+@pytest.fixture
+def operation():
+    return Constant(2)
+
+def create_operation(_type, dest_oper, index, **kwargs):
+    oper = _type(**kwargs)
+    oper_signal = Signal()
+    oper._output_ports[0].connect(oper_signal)
+
+    dest_oper._input_ports[index].connect(oper_signal)
+    return oper
+
+@pytest.fixture
+def operation_tree():
+    add_oper = Addition()
+    create_operation(Constant, add_oper, 0, value=2)
+    create_operation(Constant, add_oper, 1, value=3)
+    return add_oper
+
+@pytest.fixture
+def large_operation_tree():
+    add_oper = Addition()
+    add_oper_2 = Addition()
+
+    const_oper = create_operation(Constant, add_oper, 0, value=2)
+    create_operation(Constant, add_oper, 1, value=3)
+
+    create_operation(Constant, add_oper_2, 0, value=4)
+    create_operation(Constant, add_oper_2, 1, value=5)
+
+    add_oper_3 = Addition()
+    add_oper_signal = Signal(add_oper, add_oper_3)
+    add_oper._output_ports[0].connect(add_oper_signal)
+    add_oper_3._input_ports[0].connect(add_oper_signal)
+
+    add_oper_2_signal = Signal(add_oper_2, add_oper_3)
+    add_oper_2._output_ports[0].connect(add_oper_2_signal)
+    add_oper_3._input_ports[1].connect(add_oper_2_signal)
+    return const_oper
diff --git a/test/traverse/test_traverse_tree.py b/test/traverse/test_traverse_tree.py
index 57e8a67b..2c1d08fe 100644
--- a/test/traverse/test_traverse_tree.py
+++ b/test/traverse/test_traverse_tree.py
@@ -1,78 +1,29 @@
-"""
-TODO:
-    - Rewrite to more clean code, not so repetitive
-    - Update when signals and id's has been merged.
-"""
-
 from b_asic.core_operations import Constant, Addition
-from b_asic.signal import Signal 
+from b_asic.signal import Signal
 from b_asic.port import InputPort, OutputPort
-from b_asic.traverse_tree import Traverse
 
 import pytest
 
-@pytest.fixture
-def operation():
-    return Constant(2)
-
-def create_operation(_type, dest_oper, index, **kwargs):
-    oper = _type(**kwargs)
-    oper_signal = Signal()
-    oper._output_ports[0].connect(oper_signal)
-
-    dest_oper._input_ports[index].connect(oper_signal)
-    return oper
-
-@pytest.fixture
-def operation_tree():
-    add_oper = Addition()
-
-    const_oper = create_operation(Constant, add_oper, 0, value=2)
-    const_oper_2 = create_operation(Constant, add_oper, 1, value=3)
-
-    return add_oper
-
-@pytest.fixture
-def large_operation_tree():
-    add_oper = Addition()
-    add_oper_2 = Addition()
-
-    const_oper = create_operation(Constant, add_oper, 0, value=2)
-    const_oper_2 = create_operation(Constant, add_oper, 1, value=3)
-
-    const_oper_3 = create_operation(Constant, add_oper_2, 0, value=4)
-    const_oper_4 = create_operation(Constant, add_oper_2, 1, value=5)
-
-    add_oper_3 = Addition()
-    add_oper_signal = Signal(add_oper, add_oper_3)
-    add_oper._output_ports[0].connect(add_oper_signal)
-    add_oper_3._input_ports[0].connect(add_oper_signal)
-
-    add_oper_2_signal = Signal(add_oper_2, add_oper_3)
-    add_oper_2._output_ports[0].connect(add_oper_2_signal)
-    add_oper_3._input_ports[1].connect(add_oper_2_signal)
-    return const_oper
-
 def test_traverse_single_tree(operation):
-    traverse = Traverse(operation)
-    assert traverse.traverse() == [operation]
+    """Traverse a tree consisting of one operation."""
+    constant = Constant(None)
+    assert list(constant.traverse()) == [constant]
 
 def test_traverse_tree(operation_tree):
-    traverse = Traverse(operation_tree)
-    assert len(traverse.traverse()) == 3
+    """Traverse a basic addition tree with two constants."""
+    assert len(list(operation_tree.traverse())) == 3
 
 def test_traverse_large_tree(large_operation_tree):
-    traverse = Traverse(large_operation_tree)
-    assert len(traverse.traverse()) == 7
+    """Traverse a larger tree."""
+    assert len(list(large_operation_tree.traverse())) == 7
 
 def test_traverse_type(large_operation_tree):
-    traverse = Traverse(large_operation_tree)
-    assert len(traverse.traverse(Addition)) == 3
-    assert len(traverse.traverse(Constant)) == 4
+    traverse = list(large_operation_tree.traverse())
+    assert len(list(filter(lambda type_: isinstance(type_, Addition), traverse))) == 3
+    assert len(list(filter(lambda type_: isinstance(type_, Constant), traverse))) == 4
 
 def test_traverse_loop(operation_tree):
     add_oper_signal = Signal()
     operation_tree._output_ports[0].connect(add_oper_signal)
     operation_tree._input_ports[0].connect(add_oper_signal)
-    traverse = Traverse(operation_tree)
-    assert len(traverse.traverse()) == 2
\ No newline at end of file
+    assert len(list(operation_tree.traverse())) == 2
-- 
GitLab


From 75204fba11ef17ee4f322445a980633e4e4d2c5e Mon Sep 17 00:00:00 2001
From: Jacob Wahlman <jacwa448@student.liu.se>
Date: Thu, 5 Mar 2020 11:56:35 +0100
Subject: [PATCH 43/50] tabs to spaces

---
 b_asic/abstract_operation.py             | 194 +++++++++++------------
 b_asic/core_operations.py                | 100 ++++++------
 b_asic/operation.py                      | 156 +++++++++---------
 b_asic/port.py                           | 186 +++++++++++-----------
 b_asic/precedence_chart.py               |  18 +--
 b_asic/schema.py                         |  18 +--
 b_asic/signal.py                         |  82 +++++-----
 b_asic/signal_flow_graph.py              | 150 +++++++++---------
 b_asic/simulation.py                     |  34 ++--
 test/graph_id/test_graph_id_generator.py |   1 -
 test/port/test_outputport.py             |   2 +-
 11 files changed, 470 insertions(+), 471 deletions(-)

diff --git a/b_asic/abstract_operation.py b/b_asic/abstract_operation.py
index 558022a7..7cee1790 100644
--- a/b_asic/abstract_operation.py
+++ b/b_asic/abstract_operation.py
@@ -15,100 +15,100 @@ from b_asic.utilities import breadth_first_search
 from b_asic.abstract_graph_component import AbstractGraphComponent
 
 class AbstractOperation(Operation, AbstractGraphComponent):
-	"""Generic abstract operation class which most implementations will derive from.
-	TODO: More info.
-	"""
-
-	_input_ports: List[InputPort]
-	_output_ports: List[OutputPort]
-	_parameters: Dict[str, Optional[Any]]
-
-	def __init__(self, **kwds):
-		super().__init__(**kwds)
-		self._input_ports = []
-		self._output_ports = []
-		self._parameters = {}
-
-	@abstractmethod
-	def evaluate(self, inputs: list) -> list:
-		"""Evaluate the operation and generate a list of output values given a list of input values."""
-		raise NotImplementedError
-
-	def inputs(self) -> List[InputPort]:
-		return self._input_ports.copy()
-
-	def outputs(self) -> List[OutputPort]:
-		return self._output_ports.copy()
-
-	def input_count(self) -> int:
-		return len(self._input_ports)
-
-	def output_count(self) -> int:
-		return len(self._output_ports)
-
-	def input(self, i: int) -> InputPort:
-		return self._input_ports[i]
-
-	def output(self, i: int) -> OutputPort:
-		return self._output_ports[i]
-
-	def params(self) -> Dict[str, Optional[Any]]:
-		return self._parameters.copy()
-
-	def param(self, name: str) -> Optional[Any]:
-		return self._parameters.get(name)
-
-	def set_param(self, name: str, value: Any) -> None:
-		assert name in self._parameters # TODO: Error message.
-		self._parameters[name] = value
-
-	def evaluate_outputs(self, state: SimulationState) -> List[Number]:
-		# TODO: Check implementation.
-		input_count: int = self.input_count()
-		output_count: int = self.output_count()
-		assert input_count == len(self._input_ports) # TODO: Error message.
-		assert output_count == len(self._output_ports) # TODO: Error message.
-
-		self_state: OperationState = state.operation_states[self]
-
-		while self_state.iteration < state.iteration:
-			input_values: List[Number] = [0] * input_count
-			for i in range(input_count):
-				source: Signal = self._input_ports[i].signal
-				input_values[i] = source.operation.evaluate_outputs(state)[source.port_index]
-
-			self_state.output_values = self.evaluate(input_values)
-			assert len(self_state.output_values) == output_count # TODO: Error message.
-			self_state.iteration += 1
-			for i in range(output_count):
-				for signal in self._output_ports[i].signals():
-					destination: Signal = signal.destination
-					destination.evaluate_outputs(state)
-
-		return self_state.output_values
-
-	def split(self) -> List[Operation]:
-		# TODO: Check implementation.
-		results = self.evaluate(self._input_ports)
-		if all(isinstance(e, Operation) for e in results):
-			return results
-		return [self]
-
-	@property
-	def neighbours(self) -> List[Operation]:
-		neighbours: List[Operation] = []
-		for port in self._input_ports:
-			for signal in port.signals:
-				neighbours.append(signal.source.operation)
-
-		for port in self._output_ports:
-			for signal in port.signals:
-				neighbours.append(signal.destination.operation)
-
-		return neighbours
-
-	def traverse(self) -> Operation:
-		"""Traverse the operation tree and return a generator with start point in the operation."""
-		return breadth_first_search(self)
-
-	# TODO: More stuff.
+    """Generic abstract operation class which most implementations will derive from.
+    TODO: More info.
+    """
+
+    _input_ports: List[InputPort]
+    _output_ports: List[OutputPort]
+    _parameters: Dict[str, Optional[Any]]
+
+    def __init__(self, **kwds):
+        super().__init__(**kwds)
+        self._input_ports = []
+        self._output_ports = []
+        self._parameters = {}
+
+    @abstractmethod
+    def evaluate(self, inputs: list) -> list:
+        """Evaluate the operation and generate a list of output values given a list of input values."""
+        raise NotImplementedError
+
+    def inputs(self) -> List[InputPort]:
+        return self._input_ports.copy()
+
+    def outputs(self) -> List[OutputPort]:
+        return self._output_ports.copy()
+
+    def input_count(self) -> int:
+        return len(self._input_ports)
+
+    def output_count(self) -> int:
+        return len(self._output_ports)
+
+    def input(self, i: int) -> InputPort:
+        return self._input_ports[i]
+
+    def output(self, i: int) -> OutputPort:
+        return self._output_ports[i]
+
+    def params(self) -> Dict[str, Optional[Any]]:
+        return self._parameters.copy()
+
+    def param(self, name: str) -> Optional[Any]:
+        return self._parameters.get(name)
+
+    def set_param(self, name: str, value: Any) -> None:
+        assert name in self._parameters # TODO: Error message.
+        self._parameters[name] = value
+
+    def evaluate_outputs(self, state: SimulationState) -> List[Number]:
+        # TODO: Check implementation.
+        input_count: int = self.input_count()
+        output_count: int = self.output_count()
+        assert input_count == len(self._input_ports) # TODO: Error message.
+        assert output_count == len(self._output_ports) # TODO: Error message.
+
+        self_state: OperationState = state.operation_states[self]
+
+        while self_state.iteration < state.iteration:
+            input_values: List[Number] = [0] * input_count
+            for i in range(input_count):
+                source: Signal = self._input_ports[i].signal
+                input_values[i] = source.operation.evaluate_outputs(state)[source.port_index]
+
+            self_state.output_values = self.evaluate(input_values)
+            assert len(self_state.output_values) == output_count # TODO: Error message.
+            self_state.iteration += 1
+            for i in range(output_count):
+                for signal in self._output_ports[i].signals():
+                    destination: Signal = signal.destination
+                    destination.evaluate_outputs(state)
+
+        return self_state.output_values
+
+    def split(self) -> List[Operation]:
+        # TODO: Check implementation.
+        results = self.evaluate(self._input_ports)
+        if all(isinstance(e, Operation) for e in results):
+            return results
+        return [self]
+
+    @property
+    def neighbours(self) -> List[Operation]:
+        neighbours: List[Operation] = []
+        for port in self._input_ports:
+            for signal in port.signals:
+                neighbours.append(signal.source.operation)
+
+        for port in self._output_ports:
+            for signal in port.signals:
+                neighbours.append(signal.destination.operation)
+
+        return neighbours
+
+    def traverse(self) -> Operation:
+        """Traverse the operation tree and return a generator with start point in the operation."""
+        return breadth_first_search(self)
+
+    # TODO: More stuff.
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index 52f18361..f023e1a5 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -13,70 +13,70 @@ from b_asic.graph_component import Name, TypeName
 
 
 class Input(Operation, AbstractGraphComponent):
-	"""Input operation.
-	TODO: More info.
-	"""
+    """Input operation.
+    TODO: More info.
+    """
 
-	# TODO: Implement all functions.
+    # TODO: Implement all functions.
 
-	@property
-	def type_name(self) -> TypeName:
-		return "in"
+    @property
+    def type_name(self) -> TypeName:
+        return "in"
 
 
 class Constant(AbstractOperation):
-	"""Constant value operation.
-	TODO: More info.
-	"""
+    """Constant value operation.
+    TODO: More info.
+    """
 
-	def __init__(self, value: Number, **kwds):
-		"""Construct a Constant."""
-		super().__init__(**kwds)
-		self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
-		self._parameters["value"] = value
+    def __init__(self, value: Number, **kwds):
+        """Construct a Constant."""
+        super().__init__(**kwds)
+        self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
+        self._parameters["value"] = value
 
-	def evaluate(self, inputs: list) -> list:
-		return [self.param("value")]
+    def evaluate(self, inputs: list) -> list:
+        return [self.param("value")]
 
-	@property
-	def type_name(self) -> TypeName:
-		return "const"
+    @property
+    def type_name(self) -> TypeName:
+        return "const"
 
 
 class Addition(AbstractOperation):
-	"""Binary addition operation.
-	TODO: More info.
-	"""
+    """Binary addition operation.
+    TODO: More info.
+    """
 
-	def __init__(self, **kwds):
-		"""Construct an Addition."""
-		super().__init__(**kwds)
-		self._input_ports = [InputPort(1, self), InputPort(1, self)] # TODO: Generate appropriate ID for ports.
-		self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
+    def __init__(self, **kwds):
+        """Construct an Addition."""
+        super().__init__(**kwds)
+        self._input_ports = [InputPort(1, self), InputPort(1, self)] # TODO: Generate appropriate ID for ports.
+        self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
 
-	def evaluate(self, inputs: list) -> list:
-		return [inputs[0] + inputs[1]]
+    def evaluate(self, inputs: list) -> list:
+        return [inputs[0] + inputs[1]]
 
-	@property
-	def type_name(self) -> TypeName:
-		return "add"
+    @property
+    def type_name(self) -> TypeName:
+        return "add"
 
 
 class ConstantMultiplication(AbstractOperation):
-	"""Unary constant multiplication operation.
-	TODO: More info.
-	"""
-
-	def __init__(self, coefficient: Number, **kwds):
-		"""Construct a ConstantMultiplication."""
-		super().__init__(**kwds)
-		self._input_ports = [InputPort(1), self] # TODO: Generate appropriate ID for ports.
-		self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
-		self._parameters["coefficient"] = coefficient
-
-	def evaluate(self, inputs: list) -> list:
-		return [inputs[0] * self.param("coefficient")]
-
-	@property
-	def type_name(self) -> TypeName:
-		return "const_mul"
+    """Unary constant multiplication operation.
+    TODO: More info.
+    """
+
+    def __init__(self, coefficient: Number, **kwds):
+        """Construct a ConstantMultiplication."""
+        super().__init__(**kwds)
+        self._input_ports = [InputPort(1), self] # TODO: Generate appropriate ID for ports.
+        self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
+        self._parameters["coefficient"] = coefficient
+
+    def evaluate(self, inputs: list) -> list:
+        return [inputs[0] * self.param("coefficient")]
+
+    @property
+    def type_name(self) -> TypeName:
+        return "const_mul"
diff --git a/b_asic/operation.py b/b_asic/operation.py
index 5d4b404d..4a716b79 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -10,83 +10,83 @@ from typing import List, Dict, Optional, Any, TYPE_CHECKING
 from b_asic.graph_component import GraphComponent
 
 if TYPE_CHECKING:
-	from b_asic.port import InputPort, OutputPort
-	from b_asic.simulation import SimulationState
+    from b_asic.port import InputPort, OutputPort
+    from b_asic.simulation import SimulationState
 
 class Operation(GraphComponent):
-	"""Operation interface.
-	TODO: More info.
-	"""
-
-	@abstractmethod
-	def inputs(self) -> "List[InputPort]":
-		"""Get a list of all input ports."""
-		raise NotImplementedError
-
-	@abstractmethod
-	def outputs(self) -> "List[OutputPort]":
-		"""Get a list of all output ports."""
-		raise NotImplementedError
-
-	@abstractmethod
-	def input_count(self) -> int:
-		"""Get the number of input ports."""
-		raise NotImplementedError
-
-	@abstractmethod
-	def output_count(self) -> int:
-		"""Get the number of output ports."""
-		raise NotImplementedError
-
-	@abstractmethod
-	def input(self, i: int) -> "InputPort":
-		"""Get the input port at index i."""
-		raise NotImplementedError
-
-
-	@abstractmethod
-	def output(self, i: int) -> "OutputPort":
-		"""Get the output port at index i."""
-		raise NotImplementedError
-
-
-	@abstractmethod
-	def params(self) -> Dict[str, Optional[Any]]:
-		"""Get a dictionary of all parameter values."""
-		raise NotImplementedError
-
-	@abstractmethod
-	def param(self, name: str) -> Optional[Any]:
-		"""Get the value of a parameter.
-		Returns None if the parameter is not defined.
-		"""
-		raise NotImplementedError
-
-	@abstractmethod
-	def set_param(self, name: str, value: Any) -> None:
-		"""Set the value of a parameter.
-		The parameter must be defined.
-		"""
-		raise NotImplementedError
-
-	@abstractmethod
-	def evaluate_outputs(self, state: "SimulationState") -> List[Number]:
-		"""Simulate the circuit until its iteration count matches that of the simulation state,
-		then return the resulting output vector.
-		"""
-		raise NotImplementedError
-
-	@abstractmethod
-	def split(self) -> "List[Operation]":
-		"""Split the operation into multiple operations.
-		If splitting is not possible, this may return a list containing only the operation itself.
-		"""
-		raise NotImplementedError
-
-	@property
-	@abstractmethod
-	def neighbours(self) -> "List[Operation]":
-		"""Return all operations that are connected by signals to this operation.
-		If no neighbours are found this returns an empty list
-		"""
-		raise NotImplementedError
+    """Operation interface.
+    TODO: More info.
+    """
+
+    @abstractmethod
+    def inputs(self) -> "List[InputPort]":
+        """Get a list of all input ports."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def outputs(self) -> "List[OutputPort]":
+        """Get a list of all output ports."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def input_count(self) -> int:
+        """Get the number of input ports."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def output_count(self) -> int:
+        """Get the number of output ports."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def input(self, i: int) -> "InputPort":
+        """Get the input port at index i."""
+        raise NotImplementedError
+
+
+    @abstractmethod
+    def output(self, i: int) -> "OutputPort":
+        """Get the output port at index i."""
+        raise NotImplementedError
+
+
+    @abstractmethod
+    def params(self) -> Dict[str, Optional[Any]]:
+        """Get a dictionary of all parameter values."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def param(self, name: str) -> Optional[Any]:
+        """Get the value of a parameter.
+        Returns None if the parameter is not defined.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def set_param(self, name: str, value: Any) -> None:
+        """Set the value of a parameter.
+        The parameter must be defined.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def evaluate_outputs(self, state: "SimulationState") -> List[Number]:
+        """Simulate the circuit until its iteration count matches that of the simulation state,
+        then return the resulting output vector.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def split(self) -> "List[Operation]":
+        """Split the operation into multiple operations.
+        If splitting is not possible, this may return a list containing only the operation itself.
+        """
+        raise NotImplementedError
+
+    @property
+    @abstractmethod
+    def neighbours(self) -> "List[Operation]":
+        """Return all operations that are connected by signals to this operation.
+        If no neighbours are found this returns an empty list
+        """
+        raise NotImplementedError
diff --git a/b_asic/port.py b/b_asic/port.py
index a8a062fc..6cbd59ba 100644
--- a/b_asic/port.py
+++ b/b_asic/port.py
@@ -13,117 +13,117 @@ PortId = NewType("PortId", int)
 
 
 class Port(ABC):
-	"""Abstract port class.
-	TODO: More info.
-	"""
-
-	_port_id: PortId
-	_operation: Operation
-
-	def __init__(self, port_id: PortId, operation: Operation):
-		self._port_id = port_id
-		self._operation = operation
-
-	@property
-	def identifier(self) -> PortId:
-		"""Get the unique identifier."""
-		return self._port_id
-
-	@property
-	def operation(self) -> Operation:
-		"""Get the connected operation."""
-		return self._operation
-
-	@property
-	@abstractmethod
-	def signals(self) -> List[Signal]:
-		"""Get a list of all connected signals."""
-		raise NotImplementedError
-
-	@abstractmethod
-	def signal(self, i: int = 0) -> Signal:
-		"""Get the connected signal at index i."""
-		raise NotImplementedError
-
-	@abstractmethod
-	def signal_count(self) -> int:
-		"""Get the number of connected signals."""
-		raise NotImplementedError
-
-	@abstractmethod
-	def connect(self, signal: Signal) -> None:
-		"""Connect a signal."""
-		raise NotImplementedError
-
-	@abstractmethod
-	def disconnect(self, i: int = 0) -> None:
-		"""Disconnect a signal."""
-		raise NotImplementedError
+    """Abstract port class.
+    TODO: More info.
+    """
+
+    _port_id: PortId
+    _operation: Operation
+
+    def __init__(self, port_id: PortId, operation: Operation):
+        self._port_id = port_id
+        self._operation = operation
+
+    @property
+    def identifier(self) -> PortId:
+        """Get the unique identifier."""
+        return self._port_id
+
+    @property
+    def operation(self) -> Operation:
+        """Get the connected operation."""
+        return self._operation
+
+    @property
+    @abstractmethod
+    def signals(self) -> List[Signal]:
+        """Get a list of all connected signals."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def signal(self, i: int = 0) -> Signal:
+        """Get the connected signal at index i."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def signal_count(self) -> int:
+        """Get the number of connected signals."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def connect(self, signal: Signal) -> None:
+        """Connect a signal."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def disconnect(self, i: int = 0) -> None:
+        """Disconnect a signal."""
+        raise NotImplementedError
 
 
 class InputPort(Port):
-	"""Input port.
-	TODO: More info.
-	"""
-	_source_signal: Optional[Signal]
+    """Input port.
+    TODO: More info.
+    """
+    _source_signal: Optional[Signal]
 
-	def __init__(self, port_id: PortId, operation: Operation):
-		super().__init__(port_id, operation)
-		self._source_signal = None
+    def __init__(self, port_id: PortId, operation: Operation):
+        super().__init__(port_id, operation)
+        self._source_signal = None
 
-	@property
-	def signals(self) -> List[Signal]:
-		return [] if self._source_signal is None else [self._source_signal]
+    @property
+    def signals(self) -> List[Signal]:
+        return [] if self._source_signal is None else [self._source_signal]
 
-	def signal(self, i: int = 0) -> Signal:
-		assert 0 <= i < self.signal_count() # TODO: Error message.
-		assert self._source_signal is not None # TODO: Error message.
-		return self._source_signal
+    def signal(self, i: int = 0) -> Signal:
+        assert 0 <= i < self.signal_count() # TODO: Error message.
+        assert self._source_signal is not None # TODO: Error message.
+        return self._source_signal
 
-	def signal_count(self) -> int:
-		return 0 if self._source_signal is None else 1
+    def signal_count(self) -> int:
+        return 0 if self._source_signal is None else 1
 
-	def connect(self, signal: Signal) -> None:
-		self._source_signal = signal
-		signal.destination = self
+    def connect(self, signal: Signal) -> None:
+        self._source_signal = signal
+        signal.destination = self
 
-	def disconnect(self, i: int = 0) -> None:
-		assert 0 <= i < self.signal_count() # TODO: Error message.
-		self._source_signal.disconnect_source()
-		self._source_signal = None
+    def disconnect(self, i: int = 0) -> None:
+        assert 0 <= i < self.signal_count() # TODO: Error message.
+        self._source_signal.disconnect_source()
+        self._source_signal = None
 
-	# TODO: More stuff.
+    # TODO: More stuff.
 
 
 class OutputPort(Port):
-	"""Output port.
-	TODO: More info.
-	"""
+    """Output port.
+    TODO: More info.
+    """
 
-	_destination_signals: List[Signal]
+    _destination_signals: List[Signal]
 
-	def __init__(self, port_id: PortId, operation: Operation):
-		super().__init__(port_id, operation)
-		self._destination_signals = []
+    def __init__(self, port_id: PortId, operation: Operation):
+        super().__init__(port_id, operation)
+        self._destination_signals = []
 
-	@property
-	def signals(self) -> List[Signal]:
-		return self._destination_signals.copy()
+    @property
+    def signals(self) -> List[Signal]:
+        return self._destination_signals.copy()
 
-	def signal(self, i: int = 0) -> Signal:
-		assert 0 <= i < self.signal_count() # TODO: Error message.
-		return self._destination_signals[i]
+    def signal(self, i: int = 0) -> Signal:
+        assert 0 <= i < self.signal_count() # TODO: Error message.
+        return self._destination_signals[i]
 
-	def signal_count(self) -> int:
-		return len(self._destination_signals)
+    def signal_count(self) -> int:
+        return len(self._destination_signals)
 
-	def connect(self, signal: Signal) -> None:
-		assert signal not in self._destination_signals # TODO: Error message.
-		self._destination_signals.append(signal)
-		signal.source = self
+    def connect(self, signal: Signal) -> None:
+        assert signal not in self._destination_signals # TODO: Error message.
+        self._destination_signals.append(signal)
+        signal.source = self
 
-	def disconnect(self, i: int = 0) -> None:
-		assert 0 <= i < self.signal_count() # TODO: Error message.
-		del self._destination_signals[i]
+    def disconnect(self, i: int = 0) -> None:
+        assert 0 <= i < self.signal_count() # TODO: Error message.
+        del self._destination_signals[i]
 
-	# TODO: More stuff.
+    # TODO: More stuff.
diff --git a/b_asic/precedence_chart.py b/b_asic/precedence_chart.py
index 93b86164..be55a123 100644
--- a/b_asic/precedence_chart.py
+++ b/b_asic/precedence_chart.py
@@ -7,15 +7,15 @@ from b_asic.signal_flow_graph import SFG
 
 
 class PrecedenceChart:
-	"""Precedence chart constructed from a signal flow graph.
-	TODO: More info.
-	"""
+    """Precedence chart constructed from a signal flow graph.
+    TODO: More info.
+    """
 
-	sfg: SFG
-	# TODO: More members.
+    sfg: SFG
+    # TODO: More members.
 
-	def __init__(self, sfg: SFG):
-		self.sfg = sfg
-		# TODO: Implement.
+    def __init__(self, sfg: SFG):
+        self.sfg = sfg
+        # TODO: Implement.
 
-	# TODO: More stuff.
+    # TODO: More stuff.
diff --git a/b_asic/schema.py b/b_asic/schema.py
index 41938263..e5068cdc 100644
--- a/b_asic/schema.py
+++ b/b_asic/schema.py
@@ -7,15 +7,15 @@ from b_asic.precedence_chart import PrecedenceChart
 
 
 class Schema:
-	"""Schema constructed from a precedence chart.
-	TODO: More info.
-	"""
+    """Schema constructed from a precedence chart.
+    TODO: More info.
+    """
 
-	pc: PrecedenceChart
-	# TODO: More members.
+    pc: PrecedenceChart
+    # TODO: More members.
 
-	def __init__(self, pc: PrecedenceChart):
-		self.pc = pc
-		# TODO: Implement.
+    def __init__(self, pc: PrecedenceChart):
+        self.pc = pc
+        # TODO: Implement.
 
-	# TODO: More stuff.
+    # TODO: More stuff.
diff --git a/b_asic/signal.py b/b_asic/signal.py
index 810c00dc..4d80530e 100644
--- a/b_asic/signal.py
+++ b/b_asic/signal.py
@@ -7,46 +7,46 @@ from b_asic.graph_component import TypeName
 from b_asic.abstract_graph_component import AbstractGraphComponent
 
 if TYPE_CHECKING:
-	from b_asic import OutputPort, InputPort
+    from b_asic import OutputPort, InputPort
 
 class Signal(AbstractGraphComponent):
-	"""A connection between two ports."""
-	_source: "OutputPort"
-	_destination: "InputPort"
-
-	def __init__(self, src: Optional["OutputPort"] = None, dest: Optional["InputPort"] = None, **kwds):
-		super().__init__(**kwds)
-		self._source = src
-		self._destination = dest
-
-	@property
-	def source(self) -> "OutputPort":
-		"""Returns the source OutputPort of the signal."""
-		return self._source
-
-	@property
-	def destination(self) -> "InputPort":
-		"""Returns the destination InputPort of the signal."""
-		return self._destination
-
-	@source.setter
-	def source(self, src: "OutputPort") -> None:
-		"""Sets the value of the source OutputPort of the signal."""
-		self._source = src
-
-	@destination.setter
-	def destination(self, dest: "InputPort") -> None:
-		"""Sets the value of the destination InputPort of the signal."""
-		self._destination = dest
-
-	@property
-	def type_name(self) -> TypeName:
-		return "s"
-
-	def disconnect_source(self) -> None:
-		"""Disconnects the source OutputPort of the signal."""
-		self._source = None
-
-	def disconnect_destination(self) -> None:
-		"""Disconnects the destination InputPort of the signal."""
-		self._destination = None
+    """A connection between two ports."""
+    _source: "OutputPort"
+    _destination: "InputPort"
+
+    def __init__(self, src: Optional["OutputPort"] = None, dest: Optional["InputPort"] = None, **kwds):
+        super().__init__(**kwds)
+        self._source = src
+        self._destination = dest
+
+    @property
+    def source(self) -> "OutputPort":
+        """Returns the source OutputPort of the signal."""
+        return self._source
+
+    @property
+    def destination(self) -> "InputPort":
+        """Returns the destination InputPort of the signal."""
+        return self._destination
+
+    @source.setter
+    def source(self, src: "OutputPort") -> None:
+        """Sets the value of the source OutputPort of the signal."""
+        self._source = src
+
+    @destination.setter
+    def destination(self, dest: "InputPort") -> None:
+        """Sets the value of the destination InputPort of the signal."""
+        self._destination = dest
+
+    @property
+    def type_name(self) -> TypeName:
+        return "s"
+
+    def disconnect_source(self) -> None:
+        """Disconnects the source OutputPort of the signal."""
+        self._source = None
+
+    def disconnect_destination(self) -> None:
+        """Disconnects the destination InputPort of the signal."""
+        self._destination = None
diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py
index 38c46697..6a91521a 100644
--- a/b_asic/signal_flow_graph.py
+++ b/b_asic/signal_flow_graph.py
@@ -14,78 +14,78 @@ from b_asic.graph_component import GraphComponent, Name, TypeName
 
 
 class SFG(AbstractOperation):
-	"""Signal flow graph.
-	TODO: More info.
-	"""
-
-	_graph_components_by_id: Dict[GraphID, GraphComponent]
-	_graph_components_by_name: DefaultDict[Name, List[GraphComponent]]
-	_graph_id_generator: GraphIDGenerator
-
-	def __init__(self, input_signals: List[Signal] = None, output_signals: List[Signal] = None, \
-				ops: List[Operation] = None, **kwds):
-		super().__init__(**kwds)
-		if input_signals is None:
-			input_signals = []
-		if output_signals is None:
-			output_signals = []
-		if ops is None:
-			ops = []
-
-		self._graph_components_by_id = dict() # Maps Graph ID to objects
-		self._graph_components_by_name = defaultdict(list) # Maps Name to objects
-		self._graph_id_generator = GraphIDGenerator()
-
-		for operation in ops:
-			self._add_graph_component(operation)
-
-		for input_signal in input_signals:
-			self._add_graph_component(input_signal)
-
-		# TODO: Construct SFG based on what inputs that were given
-		# TODO: Traverse the graph between the inputs/outputs and add to self._operations.
-		# TODO: Connect ports with signals with appropriate IDs.
-
-	def evaluate(self, inputs: list) -> list:
-		return [] # TODO: Implement
-
-	def _add_graph_component(self, graph_component: GraphComponent) -> GraphID:
-		"""Adds the entered graph component to the SFG's dictionary of graph objects and
-	 	returns a generated GraphID for it.
-
-		Keyword arguments:
-		graph_component: Graph component to add to the graph.
-		"""
-		# Add to name dict
-		self._graph_components_by_name[graph_component.name].append(graph_component)
-
-		# Add to ID dict
-		graph_id: GraphID = self._graph_id_generator.get_next_id(graph_component.type_name)
-		self._graph_components_by_id[graph_id] = graph_component
-		return graph_id
-
-	def find_by_id(self, graph_id: GraphID) -> Optional[GraphComponent]:
-		"""Finds a graph object based on the entered Graph ID and returns it. If no graph
-		object with the entered ID was found then returns None.
-
-		Keyword arguments:
-		graph_id: Graph ID of the wanted object.
-		"""
-		if graph_id in self._graph_components_by_id:
-			return self._graph_components_by_id[graph_id]
-
-		return None
-
-	def find_by_name(self, name: Name) -> List[GraphComponent]:
-		"""Finds all graph objects that have the entered name and returns them
-		in a list. If no graph object with the entered name was found then returns an
-		empty list.
-
-		Keyword arguments:
-		name: Name of the wanted object.
-		"""
-		return self._graph_components_by_name[name]
-
-	@property
-	def type_name(self) -> TypeName:
-		return "sfg"
+    """Signal flow graph.
+    TODO: More info.
+    """
+
+    _graph_components_by_id: Dict[GraphID, GraphComponent]
+    _graph_components_by_name: DefaultDict[Name, List[GraphComponent]]
+    _graph_id_generator: GraphIDGenerator
+
+    def __init__(self, input_signals: List[Signal] = None, output_signals: List[Signal] = None, \
+                ops: List[Operation] = None, **kwds):
+        super().__init__(**kwds)
+        if input_signals is None:
+            input_signals = []
+        if output_signals is None:
+            output_signals = []
+        if ops is None:
+            ops = []
+
+        self._graph_components_by_id = dict() # Maps Graph ID to objects
+        self._graph_components_by_name = defaultdict(list) # Maps Name to objects
+        self._graph_id_generator = GraphIDGenerator()
+
+        for operation in ops:
+            self._add_graph_component(operation)
+
+        for input_signal in input_signals:
+            self._add_graph_component(input_signal)
+
+        # TODO: Construct SFG based on what inputs that were given
+        # TODO: Traverse the graph between the inputs/outputs and add to self._operations.
+        # TODO: Connect ports with signals with appropriate IDs.
+
+    def evaluate(self, inputs: list) -> list:
+        return [] # TODO: Implement
+
+    def _add_graph_component(self, graph_component: GraphComponent) -> GraphID:
+        """Adds the entered graph component to the SFG's dictionary of graph objects and
+         returns a generated GraphID for it.
+
+        Keyword arguments:
+        graph_component: Graph component to add to the graph.
+        """
+        # Add to name dict
+        self._graph_components_by_name[graph_component.name].append(graph_component)
+
+        # Add to ID dict
+        graph_id: GraphID = self._graph_id_generator.get_next_id(graph_component.type_name)
+        self._graph_components_by_id[graph_id] = graph_component
+        return graph_id
+
+    def find_by_id(self, graph_id: GraphID) -> Optional[GraphComponent]:
+        """Finds a graph object based on the entered Graph ID and returns it. If no graph
+        object with the entered ID was found then returns None.
+
+        Keyword arguments:
+        graph_id: Graph ID of the wanted object.
+        """
+        if graph_id in self._graph_components_by_id:
+            return self._graph_components_by_id[graph_id]
+
+        return None
+
+    def find_by_name(self, name: Name) -> List[GraphComponent]:
+        """Finds all graph objects that have the entered name and returns them
+        in a list. If no graph object with the entered name was found then returns an
+        empty list.
+
+        Keyword arguments:
+        name: Name of the wanted object.
+        """
+        return self._graph_components_by_name[name]
+
+    @property
+    def type_name(self) -> TypeName:
+        return "sfg"
diff --git a/b_asic/simulation.py b/b_asic/simulation.py
index c4f7f8f3..50adaa52 100644
--- a/b_asic/simulation.py
+++ b/b_asic/simulation.py
@@ -8,28 +8,28 @@ from typing import List
 
 
 class OperationState:
-	"""Simulation state of an operation.
-	TODO: More info.
-	"""
+    """Simulation state of an operation.
+    TODO: More info.
+    """
 
-	output_values: List[Number]
-	iteration: int
+    output_values: List[Number]
+    iteration: int
 
-	def __init__(self):
-		self.output_values = []
-		self.iteration = 0
+    def __init__(self):
+        self.output_values = []
+        self.iteration = 0
 
 
 class SimulationState:
-	"""Simulation state.
-	TODO: More info.
-	"""
+    """Simulation state.
+    TODO: More info.
+    """
 
-	# operation_states: Dict[OperationId, OperationState]
-	iteration: int
+    # operation_states: Dict[OperationId, OperationState]
+    iteration: int
 
-	def __init__(self):
-		self.operation_states = {}
-		self.iteration = 0
+    def __init__(self):
+        self.operation_states = {}
+        self.iteration = 0
 
-	# TODO: More stuff.
+    # TODO: More stuff.
diff --git a/test/graph_id/test_graph_id_generator.py b/test/graph_id/test_graph_id_generator.py
index 7aeb6cad..8af36e8c 100644
--- a/test/graph_id/test_graph_id_generator.py
+++ b/test/graph_id/test_graph_id_generator.py
@@ -26,4 +26,3 @@ def test_different_strings_generator():
     assert graph_id_generator.get_next_id("mul") == "mul1"
     assert graph_id_generator.get_next_id("sub") == "sub2"
     assert graph_id_generator.get_next_id("mul") == "mul2"
-    
\ No newline at end of file
diff --git a/test/port/test_outputport.py b/test/port/test_outputport.py
index 5c76bb48..6ca126d5 100644
--- a/test/port/test_outputport.py
+++ b/test/port/test_outputport.py
@@ -15,4 +15,4 @@ def test_connect_multiple_signals(signals):
         outp_port.connect(s)
 
     assert outp_port.signal_count() == 3
-    assert outp_port.signals == signals
\ No newline at end of file
+    assert outp_port.signals == signals
-- 
GitLab


From 3d178c98e99d871c21be810e3d661af37d6a31eb Mon Sep 17 00:00:00 2001
From: Kevin Scott <kevsc634@student.liu.se>
Date: Fri, 6 Mar 2020 11:48:42 +0100
Subject: [PATCH 44/50] Added comments for fixtures

---
 test/fixtures/operation_tree.py          | 16 ++++++++++++++++
 test/fixtures/signal.py                  |  2 ++
 test/graph_id/test_graph_id_generator.py | 13 +++++++------
 3 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/test/fixtures/operation_tree.py b/test/fixtures/operation_tree.py
index bcc0bb27..7cc9e422 100644
--- a/test/fixtures/operation_tree.py
+++ b/test/fixtures/operation_tree.py
@@ -17,6 +17,12 @@ def create_operation(_type, dest_oper, index, **kwargs):
 
 @pytest.fixture
 def operation_tree():
+    """
+    Return a addition operation connected with 2 constants.
+    >---C---+
+            ---A
+    >---C---+
+    """
     add_oper = Addition()
     create_operation(Constant, add_oper, 0, value=2)
     create_operation(Constant, add_oper, 1, value=3)
@@ -24,6 +30,16 @@ def operation_tree():
 
 @pytest.fixture
 def large_operation_tree():
+    """
+    Return a constant operation connected with a large operation tree with 3 other constants and 3 additions.
+    >---C---+
+            ---A---+
+    >---C---+      |
+                   +---A
+    >---C---+      |
+            ---A---+
+    >---C---+
+    """
     add_oper = Addition()
     add_oper_2 = Addition()
 
diff --git a/test/fixtures/signal.py b/test/fixtures/signal.py
index 9139e93a..7b13c978 100644
--- a/test/fixtures/signal.py
+++ b/test/fixtures/signal.py
@@ -3,8 +3,10 @@ from b_asic import Signal
 
 @pytest.fixture
 def signal():
+    """Return a signal with no connections."""
     return Signal()
 
 @pytest.fixture
 def signals():
+    """Return 3 signals with no connections."""
     return [Signal() for _ in range(0,3)]
diff --git a/test/graph_id/test_graph_id_generator.py b/test/graph_id/test_graph_id_generator.py
index 8af36e8c..85fb088d 100644
--- a/test/graph_id/test_graph_id_generator.py
+++ b/test/graph_id/test_graph_id_generator.py
@@ -6,22 +6,23 @@ from b_asic.graph_id import GraphIDGenerator, GraphID
 
 import pytest
 
-def test_empty_string_generator():
+@pytest.fixture
+def graph_id_generator():
+    return GraphIDGenerator()
+
+def test_empty_string_generator(graph_id_generator):
     """Test the graph id generator for an empty string type."""
-    graph_id_generator = GraphIDGenerator()
     assert graph_id_generator.get_next_id("") == "1"
     assert graph_id_generator.get_next_id("") == "2"
 
 
-def test_normal_string_generator():
+def test_normal_string_generator(graph_id_generator):
     """"Test the graph id generator for a normal string type."""
-    graph_id_generator = GraphIDGenerator()
     assert graph_id_generator.get_next_id("add") == "add1"
     assert graph_id_generator.get_next_id("add") == "add2"
 
-def test_different_strings_generator():
+def test_different_strings_generator(graph_id_generator):
     """Test the graph id generator for different strings."""
-    graph_id_generator = GraphIDGenerator()
     assert graph_id_generator.get_next_id("sub") == "sub1"
     assert graph_id_generator.get_next_id("mul") == "mul1"
     assert graph_id_generator.get_next_id("sub") == "sub2"
-- 
GitLab


From c4b4fdaeba80f1f15ff80bf76adbb8ef00861d89 Mon Sep 17 00:00:00 2001
From: Angus Lothian <anglo547@student.liu.se>
Date: Thu, 12 Mar 2020 12:12:08 +0100
Subject: [PATCH 45/50] Add updated port and signal interface where connecting
 / disconnecting is done reflectibly between both signal and port, also add
 some more helper methods.

---
 b_asic/abstract_graph_component.py  |   1 +
 b_asic/abstract_operation.py        |  11 +-
 b_asic/core_operations.py           |  35 ++++---
 b_asic/graph_component.py           |   6 +-
 b_asic/graph_id.py                  |   2 +-
 b_asic/operation.py                 |   2 -
 b_asic/port.py                      | 156 ++++++++++++++++++++--------
 b_asic/signal.py                    |  81 +++++++++++----
 b_asic/signal_flow_graph.py         |  12 +--
 test/fixtures/operation_tree.py     |  16 +--
 test/graph_id/conftest.py           |   1 -
 test/port/test_inputport.py         |  84 +++++++++++++--
 test/port/test_outputport.py        |  35 +++++--
 test/signal/test_signal.py          |  62 +++++++++++
 test/signal_flow_graph/conftest.py  |   1 -
 test/traverse/test_traverse_tree.py |   5 +-
 16 files changed, 385 insertions(+), 125 deletions(-)
 delete mode 100644 test/graph_id/conftest.py
 create mode 100644 test/signal/test_signal.py
 delete mode 100644 test/signal_flow_graph/conftest.py

diff --git a/b_asic/abstract_graph_component.py b/b_asic/abstract_graph_component.py
index 6efb94e3..a0b71b41 100644
--- a/b_asic/abstract_graph_component.py
+++ b/b_asic/abstract_graph_component.py
@@ -5,6 +5,7 @@ TODO: More info.
 
 from b_asic.graph_component import GraphComponent, Name
 
+
 class AbstractGraphComponent(GraphComponent):
     """Abstract Graph Component class which is a component of a signal flow graph.
 
diff --git a/b_asic/abstract_operation.py b/b_asic/abstract_operation.py
index 7cee1790..1403f7a9 100644
--- a/b_asic/abstract_operation.py
+++ b/b_asic/abstract_operation.py
@@ -4,7 +4,7 @@ TODO: More info.
 """
 
 from abc import abstractmethod
-from typing import List, Set, Dict, Optional, Any
+from typing import List, Dict, Optional, Any
 from numbers import Number
 
 from b_asic.port import InputPort, OutputPort
@@ -13,6 +13,8 @@ from b_asic.operation import Operation
 from b_asic.simulation import SimulationState, OperationState
 from b_asic.utilities import breadth_first_search
 from b_asic.abstract_graph_component import AbstractGraphComponent
+from b_asic.graph_component import Name
+
 
 class AbstractOperation(Operation, AbstractGraphComponent):
     """Generic abstract operation class which most implementations will derive from.
@@ -23,15 +25,16 @@ class AbstractOperation(Operation, AbstractGraphComponent):
     _output_ports: List[OutputPort]
     _parameters: Dict[str, Optional[Any]]
 
-    def __init__(self, **kwds):
-        super().__init__(**kwds)
+    def __init__(self, name: Name = ""):
+        super().__init__(name)
         self._input_ports = []
         self._output_ports = []
         self._parameters = {}
 
     @abstractmethod
     def evaluate(self, inputs: list) -> list:
-        """Evaluate the operation and generate a list of output values given a list of input values."""
+        """Evaluate the operation and generate a list of output values given a 
+        list of input values."""
         raise NotImplementedError
 
     def inputs(self) -> List[InputPort]:
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index f023e1a5..42867aa5 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -29,10 +29,10 @@ class Constant(AbstractOperation):
     TODO: More info.
     """
 
-    def __init__(self, value: Number, **kwds):
-        """Construct a Constant."""
-        super().__init__(**kwds)
-        self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
+    def __init__(self, value: Number = 0, name: Name = ""):
+        super().__init__(name)
+
+        self._output_ports = [OutputPort(0, self)] # TODO: Generate appropriate ID for ports.
         self._parameters["value"] = value
 
     def evaluate(self, inputs: list) -> list:
@@ -48,11 +48,16 @@ class Addition(AbstractOperation):
     TODO: More info.
     """
 
-    def __init__(self, **kwds):
-        """Construct an Addition."""
-        super().__init__(**kwds)
-        self._input_ports = [InputPort(1, self), InputPort(1, self)] # TODO: Generate appropriate ID for ports.
-        self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
+    def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+
+        self._input_ports = [InputPort(0, self), InputPort(1, self)] # TODO: Generate appropriate ID for ports.
+        self._output_ports = [OutputPort(0, self)] # TODO: Generate appropriate ID for ports.
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+        if source2 is not None:
+            self._input_ports[1].connect_to_port(source2)
 
     def evaluate(self, inputs: list) -> list:
         return [inputs[0] + inputs[1]]
@@ -67,13 +72,15 @@ class ConstantMultiplication(AbstractOperation):
     TODO: More info.
     """
 
-    def __init__(self, coefficient: Number, **kwds):
-        """Construct a ConstantMultiplication."""
-        super().__init__(**kwds)
-        self._input_ports = [InputPort(1), self] # TODO: Generate appropriate ID for ports.
-        self._output_ports = [OutputPort(1, self)] # TODO: Generate appropriate ID for ports.
+    def __init__(self, coefficient: Number, source1: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self)] # TODO: Generate appropriate ID for ports.
+        self._output_ports = [OutputPort(0, self)] # TODO: Generate appropriate ID for ports.
         self._parameters["coefficient"] = coefficient
 
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+
     def evaluate(self, inputs: list) -> list:
         return [inputs[0] * self.param("coefficient")]
 
diff --git a/b_asic/graph_component.py b/b_asic/graph_component.py
index 4bce63f2..ec39a28d 100644
--- a/b_asic/graph_component.py
+++ b/b_asic/graph_component.py
@@ -18,17 +18,17 @@ class GraphComponent(ABC):
     @property
     @abstractmethod
     def type_name(self) -> TypeName:
-        """Returns the type name of the graph component"""
+        """Return the type name of the graph component"""
         raise NotImplementedError
 
     @property
     @abstractmethod
     def name(self) -> Name:
-        """Returns the name of the graph component."""
+        """Return the name of the graph component."""
         raise NotImplementedError
 
     @name.setter
     @abstractmethod
     def name(self, name: Name) -> None:
-        """Sets the name of the graph component to the entered name."""
+        """Set the name of the graph component to the entered name."""
         raise NotImplementedError
diff --git a/b_asic/graph_id.py b/b_asic/graph_id.py
index 0fd1855b..8da6a9d4 100644
--- a/b_asic/graph_id.py
+++ b/b_asic/graph_id.py
@@ -20,7 +20,7 @@ class GraphIDGenerator:
         self._next_id_number = defaultdict(lambda: 1)       # Initalises every key element to 1
 
     def get_next_id(self, graph_id_type: GraphIDType) -> GraphID:
-        """Returns the next graph id for a certain graph id type."""
+        """Return the next graph id for a certain graph id type."""
         graph_id = graph_id_type + str(self._next_id_number[graph_id_type])
         self._next_id_number[graph_id_type] += 1            # Increase the current id number
         return graph_id
diff --git a/b_asic/operation.py b/b_asic/operation.py
index 4a716b79..acd26672 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -43,13 +43,11 @@ class Operation(GraphComponent):
         """Get the input port at index i."""
         raise NotImplementedError
 
-
     @abstractmethod
     def output(self, i: int) -> "OutputPort":
         """Get the output port at index i."""
         raise NotImplementedError
 
-
     @abstractmethod
     def params(self) -> Dict[str, Optional[Any]]:
         """Get a dictionary of all parameter values."""
diff --git a/b_asic/port.py b/b_asic/port.py
index 6cbd59ba..eff9db9d 100644
--- a/b_asic/port.py
+++ b/b_asic/port.py
@@ -6,15 +6,16 @@ TODO: More info.
 from abc import ABC, abstractmethod
 from typing import NewType, Optional, List
 
-from b_asic.signal import Signal
 from b_asic.operation import Operation
+from b_asic.signal import Signal
 
 PortId = NewType("PortId", int)
 
 
 class Port(ABC):
     """Abstract port class.
-    TODO: More info.
+
+    Handles functionality for port id and saves the connection to the parent operation.
     """
 
     _port_id: PortId
@@ -25,39 +26,62 @@ class Port(ABC):
         self._operation = operation
 
     @property
-    def identifier(self) -> PortId:
-        """Get the unique identifier."""
+    def id(self) -> PortId:
+        """Return the unique portid."""
         return self._port_id
 
     @property
     def operation(self) -> Operation:
-        """Get the connected operation."""
+        """Return the connected operation."""
         return self._operation
 
     @property
     @abstractmethod
     def signals(self) -> List[Signal]:
-        """Get a list of all connected signals."""
+        """Return a list of all connected signals."""
         raise NotImplementedError
 
     @abstractmethod
     def signal(self, i: int = 0) -> Signal:
-        """Get the connected signal at index i."""
+        """Return the connected signal at index i.
+
+        Keyword argumens:
+        i: integer index of the signal requsted.
+        """
+        raise NotImplementedError
+
+    @property
+    @abstractmethod
+    def connected_ports(self) -> List["Port"]:
+        """Return a list of all connected Ports."""
         raise NotImplementedError
 
     @abstractmethod
     def signal_count(self) -> int:
-        """Get the number of connected signals."""
+        """Return the number of connected signals."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def connect_port(self, port: "Port") -> Signal:
+        """Create and return a signal that is connected to this port and the entered
+        port and connect this port to the signal and the entered port to the signal."""
         raise NotImplementedError
 
     @abstractmethod
-    def connect(self, signal: Signal) -> None:
-        """Connect a signal."""
+    def connect_signal(self, signal: Signal) -> None:
+        """Connect this port to the entered signal. If the entered signal isn't connected to
+        this port then connect the entered signal to the port aswell."""
         raise NotImplementedError
 
     @abstractmethod
-    def disconnect(self, i: int = 0) -> None:
-        """Disconnect a signal."""
+    def disconnect_signal(self, i: int = 0) -> None:
+        """Disconnect a signal from the port. If the port is still connected to the entered signal
+        then the port is disconnected from the the entered signal aswell."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def is_connected_to_signal(self, signal: Signal) -> bool:
+        """Return true if the port is connected to the entered signal else false."""
         raise NotImplementedError
 
 
@@ -65,34 +89,51 @@ class InputPort(Port):
     """Input port.
     TODO: More info.
     """
-    _source_signal: Optional[Signal]
+
+    _signal: Optional[Signal]
 
     def __init__(self, port_id: PortId, operation: Operation):
         super().__init__(port_id, operation)
-        self._source_signal = None
+        self._signal = None
 
     @property
     def signals(self) -> List[Signal]:
-        return [] if self._source_signal is None else [self._source_signal]
+        return [] if self._signal is None else [self._signal]
 
     def signal(self, i: int = 0) -> Signal:
-        assert 0 <= i < self.signal_count() # TODO: Error message.
-        assert self._source_signal is not None # TODO: Error message.
-        return self._source_signal
+        assert 0 <= i < self.signal_count(), "Signal index out of bound."
+        assert self._signal is not None, "No Signal connect to InputPort."
+        return self._signal
+
+    @property
+    def connected_ports(self) -> List[Port]:
+        return [] if self._signal is None else [self._signal.source]
 
     def signal_count(self) -> int:
-        return 0 if self._source_signal is None else 1
+        return 0 if self._signal is None else 1
 
-    def connect(self, signal: Signal) -> None:
-        self._source_signal = signal
-        signal.destination = self
+    def connect_port(self, port: "OutputPort") -> Signal:
+        assert self._signal is None, "Connecting new port to already connected input port."
+        return Signal(port, self)        # self._signal is set by the signal constructor
 
-    def disconnect(self, i: int = 0) -> None:
-        assert 0 <= i < self.signal_count() # TODO: Error message.
-        self._source_signal.disconnect_source()
-        self._source_signal = None
+    def connect_signal(self, signal: Signal) -> None:
+        assert self._signal is None, "Connecting new port to already connected input port."
+        self._signal = signal
+        if self is not signal.destination:
+            # Connect this inputport as destination for this signal if it isn't already.
+            signal.connect_destination(self)
 
-    # TODO: More stuff.
+    def disconnect_signal(self, i: int = 0) -> None:
+        assert 0 <= i < self.signal_count(), "Signal Index out of range."
+        old_signal: Signal = self._signal
+        self._signal = None
+        if self is old_signal.destination:
+            # Disconnect the dest of the signal if this inputport currently is the dest
+            old_signal.disconnect_destination()
+        old_signal.disconnect_destination()
+
+    def is_connected_to_signal(self, signal: Signal) -> bool:
+        return self._signal is signal
 
 
 class OutputPort(Port):
@@ -100,30 +141,57 @@ class OutputPort(Port):
     TODO: More info.
     """
 
-    _destination_signals: List[Signal]
+    _signals: List[Signal]
 
     def __init__(self, port_id: PortId, operation: Operation):
         super().__init__(port_id, operation)
-        self._destination_signals = []
+        self._signals = []
 
     @property
     def signals(self) -> List[Signal]:
-        return self._destination_signals.copy()
+        return self._signals.copy()
 
     def signal(self, i: int = 0) -> Signal:
-        assert 0 <= i < self.signal_count() # TODO: Error message.
-        return self._destination_signals[i]
-
-    def signal_count(self) -> int:
-        return len(self._destination_signals)
+        assert 0 <= i < self.signal_count(), "Signal index out of bounds."
+        return self._signals[i]
 
-    def connect(self, signal: Signal) -> None:
-        assert signal not in self._destination_signals # TODO: Error message.
-        self._destination_signals.append(signal)
-        signal.source = self
-
-    def disconnect(self, i: int = 0) -> None:
-        assert 0 <= i < self.signal_count() # TODO: Error message.
-        del self._destination_signals[i]
+    @property
+    def connected_ports(self) -> List[Port]:
+        return [signal.destination for signal in self._signals \
+            if signal.destination is not None]
 
-    # TODO: More stuff.
+    def signal_count(self) -> int:
+        return len(self._signals)
+
+    def connect_port(self, port: InputPort) -> Signal:
+        return Signal(self, port)      # Signal is added to self._signals in signal constructor
+
+    def connect_signal(self, signal: Signal) -> None:
+        assert not self.is_connected_to_signal(signal), \
+                "Attempting to connect to Signal already connected."
+        self._signals.append(signal)
+        if self is not signal.source:
+            # Connect this outputport to the signal if it isn't already
+            signal.connect_source(self)
+
+    def disconnect_signal(self, i: int = 0) -> None:
+        assert 0 <= i < self.signal_count(), "Signal index out of bounds."
+        old_signal: Signal = self._signals[i]
+        del self._signals[i]
+        if self is old_signal.source:
+            # Disconnect the source of the signal if this outputport currently is the source
+            old_signal.disconnect_source()
+
+    def disconnect_signal_by_ref(self, signal: Signal) -> None:
+        """Remove the signal that was entered from the OutputPorts signals.
+        If the entered signal still is connected to this port then disconnect the
+        entered signal from the port aswell.
+
+        Keyword arguments:
+        - signal: Signal to remove.
+        """
+        i: int = self._signals.index(signal)
+        self.disconnect_signal(i)
+
+    def is_connected_to_signal(self, signal: Signal) -> bool:
+        return signal in self._signals                  # O(n) complexity
diff --git a/b_asic/signal.py b/b_asic/signal.py
index 4d80530e..917e4af3 100644
--- a/b_asic/signal.py
+++ b/b_asic/signal.py
@@ -1,52 +1,97 @@
 """@package docstring
 B-ASIC Signal Module.
 """
-from typing import TYPE_CHECKING, Optional
+from typing import Optional, TYPE_CHECKING
 
 from b_asic.graph_component import TypeName
 from b_asic.abstract_graph_component import AbstractGraphComponent
+from b_asic.graph_component import Name
 
 if TYPE_CHECKING:
-    from b_asic import OutputPort, InputPort
+    from b_asic.port import InputPort, OutputPort
 
 class Signal(AbstractGraphComponent):
     """A connection between two ports."""
+
     _source: "OutputPort"
     _destination: "InputPort"
 
-    def __init__(self, src: Optional["OutputPort"] = None, dest: Optional["InputPort"] = None, **kwds):
-        super().__init__(**kwds)
-        self._source = src
-        self._destination = dest
+    def __init__(self, source: Optional["OutputPort"] = None, \
+                destination: Optional["InputPort"] = None, name: Name = ""):
+
+        super().__init__(name)
+
+        self._source = source
+        self._destination = destination
+
+        if source is not None:
+            self.connect_source(source)
+
+        if destination is not None:
+            self.connect_destination(destination)
 
     @property
     def source(self) -> "OutputPort":
-        """Returns the source OutputPort of the signal."""
+        """Return the source OutputPort of the signal."""
         return self._source
 
     @property
     def destination(self) -> "InputPort":
-        """Returns the destination InputPort of the signal."""
+        """Return the destination "InputPort" of the signal."""
         return self._destination
 
-    @source.setter
-    def source(self, src: "OutputPort") -> None:
-        """Sets the value of the source OutputPort of the signal."""
+    def connect_source(self, src: "OutputPort") -> None:
+        """Disconnect the previous source OutputPort of the signal and
+        connect to the entered source OutputPort. Also connect the entered
+        source port to the signal if it hasn't already been connected.
+
+        Keyword arguments:
+        - src: OutputPort to connect as source to the signal.
+        """
+        self.disconnect_source()
         self._source = src
+        if not src.is_connected_to_signal(self):
+            # If the new source isn't connected to this signal then connect it.
+            src.connect_signal(self)
 
-    @destination.setter
-    def destination(self, dest: "InputPort") -> None:
-        """Sets the value of the destination InputPort of the signal."""
+    def connect_destination(self, dest: "InputPort") -> None:
+        """Disconnect the previous destination InputPort of the signal and
+        connect to the entered destination InputPort. Also connect the entered
+        destination port to the signal if it hasn't already been connected.
+
+        Keywords argments:
+        - dest: InputPort to connect as destination to the signal.
+        """
+        self.disconnect_destination()
         self._destination = dest
+        if not dest.is_connected_to_signal(self):
+            # If the new destination isn't connected to tis signal then connect it.
+            dest.connect_signal(self)
 
     @property
     def type_name(self) -> TypeName:
         return "s"
 
     def disconnect_source(self) -> None:
-        """Disconnects the source OutputPort of the signal."""
-        self._source = None
+        """Disconnect the source OutputPort of the signal. If the source port
+        still is connected to this signal then also disconnect the source port."""
+        if self._source is not None:
+            old_source: "OutputPort" = self._source
+            self._source = None
+            if old_source.is_connected_to_signal(self):
+                # If the old destination port still is connected to this signal, then disconnect it.
+                old_source.disconnect_signal_by_ref(self)
 
     def disconnect_destination(self) -> None:
-        """Disconnects the destination InputPort of the signal."""
-        self._destination = None
+        """Disconnect the destination InputPort of the signal."""
+        if self._destination is not None:
+            old_destination: "InputPort" = self._destination
+            self._destination = None
+            if old_destination.is_connected_to_signal(self):
+                # If the old destination port still is connected to this signal, then disconnect it.
+                old_destination.disconnect_signal()
+
+    def is_connected(self) -> bool:
+        """Returns true if the signal is connected to both a source and a destination,
+        else false."""
+        return self._source is not None and self._destination is not None
diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py
index 6a91521a..ab2c3e94 100644
--- a/b_asic/signal_flow_graph.py
+++ b/b_asic/signal_flow_graph.py
@@ -50,8 +50,8 @@ class SFG(AbstractOperation):
         return [] # TODO: Implement
 
     def _add_graph_component(self, graph_component: GraphComponent) -> GraphID:
-        """Adds the entered graph component to the SFG's dictionary of graph objects and
-         returns a generated GraphID for it.
+        """Add the entered graph component to the SFG's dictionary of graph objects and
+         return a generated GraphID for it.
 
         Keyword arguments:
         graph_component: Graph component to add to the graph.
@@ -65,8 +65,8 @@ class SFG(AbstractOperation):
         return graph_id
 
     def find_by_id(self, graph_id: GraphID) -> Optional[GraphComponent]:
-        """Finds a graph object based on the entered Graph ID and returns it. If no graph
-        object with the entered ID was found then returns None.
+        """Find a graph object based on the entered Graph ID and return it. If no graph
+        object with the entered ID was found then return None.
 
         Keyword arguments:
         graph_id: Graph ID of the wanted object.
@@ -77,8 +77,8 @@ class SFG(AbstractOperation):
         return None
 
     def find_by_name(self, name: Name) -> List[GraphComponent]:
-        """Finds all graph objects that have the entered name and returns them
-        in a list. If no graph object with the entered name was found then returns an
+        """Find all graph objects that have the entered name and return them
+        in a list. If no graph object with the entered name was found then return an
         empty list.
 
         Keyword arguments:
diff --git a/test/fixtures/operation_tree.py b/test/fixtures/operation_tree.py
index 7cc9e422..b97e89d7 100644
--- a/test/fixtures/operation_tree.py
+++ b/test/fixtures/operation_tree.py
@@ -10,9 +10,9 @@ def operation():
 def create_operation(_type, dest_oper, index, **kwargs):
     oper = _type(**kwargs)
     oper_signal = Signal()
-    oper._output_ports[0].connect(oper_signal)
+    oper._output_ports[0].connect_signal(oper_signal)
 
-    dest_oper._input_ports[index].connect(oper_signal)
+    dest_oper._input_ports[index].connect_signal(oper_signal)
     return oper
 
 @pytest.fixture
@@ -50,11 +50,11 @@ def large_operation_tree():
     create_operation(Constant, add_oper_2, 1, value=5)
 
     add_oper_3 = Addition()
-    add_oper_signal = Signal(add_oper, add_oper_3)
-    add_oper._output_ports[0].connect(add_oper_signal)
-    add_oper_3._input_ports[0].connect(add_oper_signal)
+    add_oper_signal = Signal(add_oper.output(0), add_oper_3.output(0))
+    add_oper._output_ports[0].connect_signal(add_oper_signal)
+    add_oper_3._input_ports[0].connect_signal(add_oper_signal)
 
-    add_oper_2_signal = Signal(add_oper_2, add_oper_3)
-    add_oper_2._output_ports[0].connect(add_oper_2_signal)
-    add_oper_3._input_ports[1].connect(add_oper_2_signal)
+    add_oper_2_signal = Signal(add_oper_2.output(0), add_oper_3.output(0))
+    add_oper_2._output_ports[0].connect_signal(add_oper_2_signal)
+    add_oper_3._input_ports[1].connect_signal(add_oper_2_signal)
     return const_oper
diff --git a/test/graph_id/conftest.py b/test/graph_id/conftest.py
deleted file mode 100644
index 5871ed8e..00000000
--- a/test/graph_id/conftest.py
+++ /dev/null
@@ -1 +0,0 @@
-import pytest
diff --git a/test/port/test_inputport.py b/test/port/test_inputport.py
index f0e70cb7..7a78d5f7 100644
--- a/test/port/test_inputport.py
+++ b/test/port/test_inputport.py
@@ -2,19 +2,83 @@
 B-ASIC test suite for Inputport
 """
 
-from b_asic import InputPort
-
 import pytest
 
-def test_connect_multiple_signals(signals):
-    """
-    test if only one signal can connect to an input port
-    """
+from b_asic import InputPort, OutputPort
+from b_asic import Signal
+
+@pytest.fixture
+def inp_port():
+    return InputPort(0, None)
+
+@pytest.fixture
+def out_port():
+    return OutputPort(0, None)
+
+@pytest.fixture
+def out_port2():
+    return OutputPort(1, None)
+
+@pytest.fixture
+def dangling_sig():
+    return Signal()
+
+@pytest.fixture
+def s_w_source():
+    out_port = OutputPort(0, None)
+    return Signal(source=out_port)
+
+@pytest.fixture
+def sig_with_dest():
+    inp_port = InputPort(0, None)
+    return Signal(destination=out_port)
+
+@pytest.fixture
+def connected_sig():
+    out_port = OutputPort(0, None)
     inp_port = InputPort(0, None)
+    return Signal(source=out_port, destination=inp_port)
+
+def test_connect_port_then_disconnect(inp_port, out_port):
+    """Test connect unused port to port."""
+    s1 = inp_port.connect_port(out_port)
+
+    assert inp_port.connected_ports == [out_port]
+    assert out_port.connected_ports == [inp_port]
+    assert inp_port.signals == [s1]
+    assert out_port.signals == [s1]
+    assert s1.source is out_port
+    assert s1.destination is inp_port
+
+    inp_port.disconnect_signal()
+
+    assert inp_port.connected_ports == []
+    assert out_port.connected_ports == []
+    assert inp_port.signals == []
+    assert out_port.signals == [s1]
+    assert s1.source is out_port
+    assert s1.destination is None
+
+def test_connect_used_port_to_new_port(inp_port, out_port, out_port2):
+    """Does connecting multiple ports to an inputport throw error?"""
+    inp_port.connect_port(out_port)
+    with pytest.raises(AssertionError):
+        inp_port.connect_port(out_port2)
+
+def test_connect_signal_then_disconnect(inp_port, s_w_source):
+    inp_port.connect_signal(s_w_source)
+
+    assert inp_port.connected_ports == [s_w_source.source]
+    assert s_w_source.source.connected_ports == [inp_port]
+    assert inp_port.signals == [s_w_source]
+    assert s_w_source.source.signals == [s_w_source]
+    assert s_w_source.destination is inp_port
 
-    for s in signals:
-        inp_port.connect(s)
+    inp_port.disconnect_signal()
 
-    assert inp_port.signal_count() == 1
-    assert inp_port.signals[0] == signals[-1]
+    assert inp_port.connected_ports == []
+    assert s_w_source.source.connected_ports == []
+    assert inp_port.signals == []
+    assert s_w_source.source.signals == [s_w_source]
+    assert s_w_source.destination is None
 
diff --git a/test/port/test_outputport.py b/test/port/test_outputport.py
index 6ca126d5..f48afbdb 100644
--- a/test/port/test_outputport.py
+++ b/test/port/test_outputport.py
@@ -1,18 +1,31 @@
 """
-B-ASIC test suite for InputPort
+B-ASIC test suite for OutputPort
 TODO: More info
 """
-from b_asic import OutputPort
+from b_asic import InputPort, OutputPort
 import pytest
 
-def test_connect_multiple_signals(signals):
-    """
-    test if multiple signals can connect to an output port
-    """
-    outp_port = OutputPort(0, None)
+@pytest.fixture
+def inp_ports():
+    return [InputPort(_, None) for _ in range(0,3)]
 
-    for s in signals:
-        outp_port.connect(s)
+def test_connect_multiple_signals(inp_ports):
+    """Can multiple ports connect to an output port?"""
+    out_port = OutputPort(0, None)
 
-    assert outp_port.signal_count() == 3
-    assert outp_port.signals == signals
+    for port in inp_ports:
+        out_port.connect_port(port)
+    
+    assert out_port.signal_count() == len(inp_ports)
+
+def test_disconnect_multiple_signals(inp_ports):
+    """Can multiple ports disconnect from an output port?"""
+    out_port = OutputPort(0, None)
+
+    for port in inp_ports:
+        out_port.connect_port(port)
+    
+    for _ in inp_ports:
+        out_port.disconnect_signal(0)
+
+    assert out_port.signal_count() == 0
\ No newline at end of file
diff --git a/test/signal/test_signal.py b/test/signal/test_signal.py
new file mode 100644
index 00000000..8c10d1e3
--- /dev/null
+++ b/test/signal/test_signal.py
@@ -0,0 +1,62 @@
+"""
+B-ASIC test suit for the signal module which consists of the Signal class.
+"""
+
+from b_asic.port import InputPort, OutputPort
+from b_asic.signal import Signal
+
+import pytest
+
+def test_signal_creation_and_disconnction_and_connection_changing():
+    in_port = InputPort(0, None)
+    out_port = OutputPort(1, None)
+    s = Signal(out_port, in_port)
+
+    assert in_port.signals == [s]
+    assert out_port.signals == [s]
+    assert s.source is out_port
+    assert s.destination is in_port
+
+    in_port1 = InputPort(0, None)
+    s.connect_destination(in_port1)
+
+    assert in_port.signals == []
+    assert in_port1.signals == [s]
+    assert out_port.signals == [s]
+    assert s.source is out_port
+    assert s.destination is in_port1
+
+    s.disconnect_source()
+
+    assert out_port.signals == []
+    assert in_port1.signals == [s]
+    assert s.source is None
+    assert s.destination is in_port1
+
+    s.disconnect_destination()
+
+    assert out_port.signals == []
+    assert in_port1.signals == []
+    assert s.source is None
+    assert s.destination is None
+
+    out_port1 = OutputPort(0, None)
+    s.connect_source(out_port1)
+
+    assert out_port1.signals == [s]
+    assert s.source is out_port1
+    assert s.destination is None
+
+    s.connect_source(out_port)
+
+    assert out_port.signals == [s]
+    assert out_port1.signals == []
+    assert s.source is out_port
+    assert s.destination is None
+
+    s.connect_destination(in_port)
+
+    assert out_port.signals == [s]
+    assert in_port.signals == [s]
+    assert s.source is out_port
+    assert s.destination is in_port
diff --git a/test/signal_flow_graph/conftest.py b/test/signal_flow_graph/conftest.py
deleted file mode 100644
index 5871ed8e..00000000
--- a/test/signal_flow_graph/conftest.py
+++ /dev/null
@@ -1 +0,0 @@
-import pytest
diff --git a/test/traverse/test_traverse_tree.py b/test/traverse/test_traverse_tree.py
index 2c1d08fe..9f509287 100644
--- a/test/traverse/test_traverse_tree.py
+++ b/test/traverse/test_traverse_tree.py
@@ -24,6 +24,7 @@ def test_traverse_type(large_operation_tree):
 
 def test_traverse_loop(operation_tree):
     add_oper_signal = Signal()
-    operation_tree._output_ports[0].connect(add_oper_signal)
-    operation_tree._input_ports[0].connect(add_oper_signal)
+    operation_tree._output_ports[0].connect_signal(add_oper_signal)
+    operation_tree._input_ports[0].disconnect_signal()
+    operation_tree._input_ports[0].connect_signal(add_oper_signal)
     assert len(list(operation_tree.traverse())) == 2
-- 
GitLab


From 9494fe85d9d7d1c0e49e9432036d90d1765b9ad1 Mon Sep 17 00:00:00 2001
From: Arvid Westerlund <arvwe160@student.liu.se>
Date: Sat, 14 Mar 2020 23:51:11 +0100
Subject: [PATCH 46/50] Resolve "Basic Operations"

---
 b_asic/abstract_operation.py                  |   2 +-
 b_asic/core_operations.py                     | 288 ++++++++++++++++--
 .../basic_operations/test_basic_operations.py | 229 ++++++++++++++
 3 files changed, 500 insertions(+), 19 deletions(-)
 create mode 100644 test/basic_operations/test_basic_operations.py

diff --git a/b_asic/abstract_operation.py b/b_asic/abstract_operation.py
index 1403f7a9..fc3a9205 100644
--- a/b_asic/abstract_operation.py
+++ b/b_asic/abstract_operation.py
@@ -32,7 +32,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
         self._parameters = {}
 
     @abstractmethod
-    def evaluate(self, inputs: list) -> list:
+    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
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index 42867aa5..f64c63db 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -4,15 +4,14 @@ TODO: More info.
 """
 
 from numbers import Number
-
+from typing import Any
+from numpy import conjugate, sqrt, abs as np_abs
 from b_asic.port import InputPort, OutputPort
-from b_asic.operation import Operation
+from b_asic.graph_id import GraphIDType
 from b_asic.abstract_operation import AbstractOperation
-from b_asic.abstract_graph_component import AbstractGraphComponent
 from b_asic.graph_component import Name, TypeName
 
-
-class Input(Operation, AbstractGraphComponent):
+class Input(AbstractOperation):
     """Input operation.
     TODO: More info.
     """
@@ -24,6 +23,7 @@ class Input(Operation, AbstractGraphComponent):
         return "in"
 
 
+
 class Constant(AbstractOperation):
     """Constant value operation.
     TODO: More info.
@@ -32,15 +32,16 @@ class Constant(AbstractOperation):
     def __init__(self, value: Number = 0, name: Name = ""):
         super().__init__(name)
 
-        self._output_ports = [OutputPort(0, self)] # TODO: Generate appropriate ID for ports.
+        self._output_ports = [OutputPort(0, self)]
         self._parameters["value"] = value
 
-    def evaluate(self, inputs: list) -> list:
-        return [self.param("value")]
+    def evaluate(self) -> Any:
+        return self.param("value")
 
     @property
     def type_name(self) -> TypeName:
-        return "const"
+        return "c"
+
 
 
 class Addition(AbstractOperation):
@@ -51,22 +52,207 @@ class Addition(AbstractOperation):
     def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
         super().__init__(name)
 
-        self._input_ports = [InputPort(0, self), InputPort(1, self)] # TODO: Generate appropriate ID for ports.
-        self._output_ports = [OutputPort(0, self)] # TODO: Generate appropriate ID for ports.
+        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
             self._input_ports[0].connect_to_port(source1)
         if source2 is not None:
             self._input_ports[1].connect_to_port(source2)
 
-    def evaluate(self, inputs: list) -> list:
-        return [inputs[0] + inputs[1]]
+    def evaluate(self, a, b) -> Any:
+        return a + b
 
     @property
     def type_name(self) -> TypeName:
         return "add"
 
 
+class Subtraction(AbstractOperation):
+    """Binary subtraction operation.
+    TODO: More info.
+    """
+
+    def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+        if source2 is not None:
+            self._input_ports[1].connect_to_port(source2)
+
+    def evaluate(self, a, b) -> Any:
+        return a - b
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "sub"
+
+
+class Multiplication(AbstractOperation):
+    """Binary multiplication operation.
+    TODO: More info.
+    """
+
+    def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+        if source2 is not None:
+            self._input_ports[1].connect_to_port(source2)
+
+    def evaluate(self, a, b) -> Any:
+        return a * b
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "mul"
+
+
+class Division(AbstractOperation):
+    """Binary division operation.
+    TODO: More info.
+    """
+
+    def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+        if source2 is not None:
+            self._input_ports[1].connect_to_port(source2)
+
+    def evaluate(self, a, b) -> Any:
+        return a / b
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "div"
+
+
+class SquareRoot(AbstractOperation):
+    """Unary square root operation.
+    TODO: More info.
+    """
+
+    def __init__(self, source1: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+
+
+    def evaluate(self, a) -> Any:
+        return sqrt((complex)(a))
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "sqrt"
+
+
+class ComplexConjugate(AbstractOperation):
+    """Unary complex conjugate operation.
+    TODO: More info.
+    """
+
+    def __init__(self, source1: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+
+
+    def evaluate(self, a) -> Any:
+        return conjugate(a)
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "conj"
+
+
+class Max(AbstractOperation):
+    """Binary max operation.
+    TODO: More info.
+    """
+
+    def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+        if source2 is not None:
+            self._input_ports[1].connect_to_port(source2)
+
+    def evaluate(self, a, b) -> Any:
+        assert not isinstance(a, complex) and not isinstance(b, complex), \
+            ("core_operation.Max does not support complex numbers.")
+        return a if a > b else b
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "max"
+
+
+class Min(AbstractOperation):
+    """Binary min operation.
+    TODO: More info.
+    """
+
+    def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+        if source2 is not None:
+            self._input_ports[1].connect_to_port(source2)
+
+    def evaluate(self, a, b) -> Any:
+        assert not isinstance(a, complex) and not isinstance(b, complex), \
+            ("core_operation.Min does not support complex numbers.")
+        return a if a < b else b
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "min"
+
+
+class Absolute(AbstractOperation):
+    """Unary absolute value operation.
+    TODO: More info.
+    """
+
+    def __init__(self, source1: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+
+
+    def evaluate(self, a) -> Any:
+        return np_abs(a)
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "abs"
+
+
 class ConstantMultiplication(AbstractOperation):
     """Unary constant multiplication operation.
     TODO: More info.
@@ -74,16 +260,82 @@ class ConstantMultiplication(AbstractOperation):
 
     def __init__(self, coefficient: Number, source1: OutputPort = None, name: Name = ""):
         super().__init__(name)
-        self._input_ports = [InputPort(0, self)] # TODO: Generate appropriate ID for ports.
-        self._output_ports = [OutputPort(0, self)] # TODO: Generate appropriate ID for ports.
+        self._input_ports = [InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
         self._parameters["coefficient"] = coefficient
 
         if source1 is not None:
             self._input_ports[0].connect_to_port(source1)
 
-    def evaluate(self, inputs: list) -> list:
-        return [inputs[0] * self.param("coefficient")]
+    def evaluate(self, a) -> Any:
+        return a * self.param("coefficient")
 
     @property
     def type_name(self) -> TypeName:
-        return "const_mul"
+        return "cmul"
+
+
+class ConstantAddition(AbstractOperation):
+    """Unary constant addition operation.
+    TODO: More info.
+    """
+
+    def __init__(self, coefficient: Number, source1: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+        self._parameters["coefficient"] = coefficient
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+
+    def evaluate(self, a) -> Any:
+        return a + self.param("coefficient")
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "cadd"
+
+
+class ConstantSubtraction(AbstractOperation):
+    """Unary constant subtraction operation.
+    TODO: More info.
+    """
+
+    def __init__(self, coefficient: Number, source1: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+        self._parameters["coefficient"] = coefficient
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+
+    def evaluate(self, a) -> Any:
+        return a - self.param("coefficient")
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "csub"
+
+
+class ConstantDivision(AbstractOperation):
+    """Unary constant division operation.
+    TODO: More info.
+    """
+
+    def __init__(self, coefficient: Number, source1: OutputPort = None, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = [InputPort(0, self)]
+        self._output_ports = [OutputPort(0, self)]
+        self._parameters["coefficient"] = coefficient
+
+        if source1 is not None:
+            self._input_ports[0].connect_to_port(source1)
+
+    def evaluate(self, a) -> Any:
+        return a / self.param("coefficient")
+
+    @property
+    def type_name(self) -> GraphIDType:
+        return "cdiv"
diff --git a/test/basic_operations/test_basic_operations.py b/test/basic_operations/test_basic_operations.py
new file mode 100644
index 00000000..21561074
--- /dev/null
+++ b/test/basic_operations/test_basic_operations.py
@@ -0,0 +1,229 @@
+"""
+B-ASIC test suite for the basic operations.
+"""
+
+from b_asic.core_operations import Constant, Addition, Subtraction, Multiplication, Division, SquareRoot, ComplexConjugate, Max, Min, Absolute, ConstantMultiplication, ConstantAddition, ConstantSubtraction, ConstantDivision
+from b_asic.signal import Signal
+import pytest
+
+""" Constant tests. """
+def test_constant():
+    constant_operation = Constant(3)
+    assert constant_operation.evaluate() == 3
+
+def test_constant_negative():
+    constant_operation = Constant(-3)
+    assert constant_operation.evaluate() == -3
+
+def test_constant_complex():
+    constant_operation = Constant(3+4j)
+    assert constant_operation.evaluate() == 3+4j
+
+""" Addition tests. """
+def test_addition():
+    test_operation = Addition()
+    constant_operation = Constant(3)
+    constant_operation_2 = Constant(5)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == 8
+
+def test_addition_negative():
+    test_operation = Addition()
+    constant_operation = Constant(-3)
+    constant_operation_2 = Constant(-5)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == -8
+
+def test_addition_complex():
+    test_operation = Addition()
+    constant_operation = Constant((3+5j))
+    constant_operation_2 = Constant((4+6j))
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == (7+11j)
+
+""" Subtraction tests. """
+def test_subtraction():
+    test_operation = Subtraction()
+    constant_operation = Constant(5)
+    constant_operation_2 = Constant(3)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == 2
+
+def test_subtraction_negative():
+    test_operation = Subtraction()
+    constant_operation = Constant(-5)
+    constant_operation_2 = Constant(-3)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == -2
+
+def test_subtraction_complex():
+    test_operation = Subtraction()
+    constant_operation = Constant((3+5j))
+    constant_operation_2 = Constant((4+6j))
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == (-1-1j)
+
+""" Multiplication tests. """
+def test_multiplication():
+    test_operation = Multiplication()
+    constant_operation = Constant(5)
+    constant_operation_2 = Constant(3)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == 15
+
+def test_multiplication_negative():
+    test_operation = Multiplication()
+    constant_operation = Constant(-5)
+    constant_operation_2 = Constant(-3)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == 15
+
+def test_multiplication_complex():
+    test_operation = Multiplication()
+    constant_operation = Constant((3+5j))
+    constant_operation_2 = Constant((4+6j))
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == (-18+38j)
+
+""" Division tests. """
+def test_division():
+    test_operation = Division()
+    constant_operation = Constant(30)
+    constant_operation_2 = Constant(5)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == 6
+
+def test_division_negative():
+    test_operation = Division()
+    constant_operation = Constant(-30)
+    constant_operation_2 = Constant(-5)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == 6
+
+def test_division_complex():
+    test_operation = Division()
+    constant_operation = Constant((60+40j))
+    constant_operation_2 = Constant((10+20j))
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == (2.8-1.6j)
+
+""" SquareRoot tests. """
+def test_squareroot():
+    test_operation = SquareRoot()
+    constant_operation = Constant(36)
+    assert test_operation.evaluate(constant_operation.evaluate()) == 6
+
+def test_squareroot_negative():
+    test_operation = SquareRoot()
+    constant_operation = Constant(-36)
+    assert test_operation.evaluate(constant_operation.evaluate()) == 6j
+
+def test_squareroot_complex():
+    test_operation = SquareRoot()
+    constant_operation = Constant((48+64j))
+    assert test_operation.evaluate(constant_operation.evaluate()) == (8+4j)
+
+""" ComplexConjugate tests. """
+def test_complexconjugate():
+    test_operation = ComplexConjugate()
+    constant_operation = Constant(3+4j)
+    assert test_operation.evaluate(constant_operation.evaluate()) == (3-4j)
+
+def test_test_complexconjugate_negative():
+    test_operation = ComplexConjugate()
+    constant_operation = Constant(-3-4j)
+    assert test_operation.evaluate(constant_operation.evaluate()) == (-3+4j)
+
+""" Max tests. """
+def test_max():
+    test_operation = Max()
+    constant_operation = Constant(30)
+    constant_operation_2 = Constant(5)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == 30
+
+def test_max_negative():
+    test_operation = Max()
+    constant_operation = Constant(-30)
+    constant_operation_2 = Constant(-5)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == -5
+
+""" Min tests. """
+def test_min():
+    test_operation = Min()
+    constant_operation = Constant(30)
+    constant_operation_2 = Constant(5)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == 5
+
+def test_min_negative():
+    test_operation = Min()
+    constant_operation = Constant(-30)
+    constant_operation_2 = Constant(-5)
+    assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == -30
+
+""" Absolute tests. """
+def test_absolute():
+    test_operation = Absolute()
+    constant_operation = Constant(30)
+    assert test_operation.evaluate(constant_operation.evaluate()) == 30
+
+def test_absolute_negative():
+    test_operation = Absolute()
+    constant_operation = Constant(-5)
+    assert test_operation.evaluate(constant_operation.evaluate()) == 5
+
+def test_absolute_complex():
+    test_operation = Absolute()
+    constant_operation = Constant((3+4j))
+    assert test_operation.evaluate(constant_operation.evaluate()) == 5.0
+
+""" ConstantMultiplication tests. """
+def test_constantmultiplication():
+    test_operation = ConstantMultiplication(5)
+    constant_operation = Constant(20)
+    assert test_operation.evaluate(constant_operation.evaluate()) == 100
+
+def test_constantmultiplication_negative():
+    test_operation = ConstantMultiplication(5)
+    constant_operation = Constant(-5)
+    assert test_operation.evaluate(constant_operation.evaluate()) == -25
+
+def test_constantmultiplication_complex():
+    test_operation = ConstantMultiplication(3+2j)
+    constant_operation = Constant((3+4j))
+    assert test_operation.evaluate(constant_operation.evaluate()) == (1+18j)
+
+""" ConstantAddition tests. """
+def test_constantaddition():
+    test_operation = ConstantAddition(5)
+    constant_operation = Constant(20)
+    assert test_operation.evaluate(constant_operation.evaluate()) == 25
+
+def test_constantaddition_negative():
+    test_operation = ConstantAddition(4)
+    constant_operation = Constant(-5)
+    assert test_operation.evaluate(constant_operation.evaluate()) == -1
+
+def test_constantaddition_complex():
+    test_operation = ConstantAddition(3+2j)
+    constant_operation = Constant((3+4j))
+    assert test_operation.evaluate(constant_operation.evaluate()) == (6+6j)
+
+""" ConstantSubtraction tests. """
+def test_constantsubtraction():
+    test_operation = ConstantSubtraction(5)
+    constant_operation = Constant(20)
+    assert test_operation.evaluate(constant_operation.evaluate()) == 15
+
+def test_constantsubtraction_negative():
+    test_operation = ConstantSubtraction(4)
+    constant_operation = Constant(-5)
+    assert test_operation.evaluate(constant_operation.evaluate()) == -9
+
+def test_constantsubtraction_complex():
+    test_operation = ConstantSubtraction(4+6j)
+    constant_operation = Constant((3+4j))
+    assert test_operation.evaluate(constant_operation.evaluate()) == (-1-2j)
+
+""" ConstantDivision tests. """
+def test_constantdivision():
+    test_operation = ConstantDivision(5)
+    constant_operation = Constant(20)
+    assert test_operation.evaluate(constant_operation.evaluate()) == 4
+
+def test_constantdivision_negative():
+    test_operation = ConstantDivision(4)
+    constant_operation = Constant(-20)
+    assert test_operation.evaluate(constant_operation.evaluate()) == -5
+
+def test_constantdivision_complex():
+    test_operation = ConstantDivision(2+2j)
+    constant_operation = Constant((10+10j))
+    assert test_operation.evaluate(constant_operation.evaluate()) == (5+0j)
\ No newline at end of file
-- 
GitLab


From 8b7975190baae9b496d4055c5227d314bddfe2f0 Mon Sep 17 00:00:00 2001
From: Angus Lothian <anglo547@student.liu.se>
Date: Wed, 18 Mar 2020 20:36:36 +0100
Subject: [PATCH 47/50] Solve pull request comments and change so evaluate
 function in SFG uses the same interface as the abstract evaluate function

---
 b_asic/__init__.py                            |   3 -
 b_asic/abstract_graph_component.py            |  26 ---
 b_asic/abstract_operation.py                  | 117 -----------
 b_asic/core_operations.py                     |  17 +-
 b_asic/graph_component.py                     |  20 ++
 b_asic/operation.py                           | 120 ++++++++++-
 b_asic/port.py                                | 192 ++++++++++--------
 b_asic/signal.py                              |  37 ++--
 b_asic/signal_flow_graph.py                   |   4 +-
 b_asic/utilities.py                           |  21 --
 .../test_core_operations.py}                  |  32 +--
 test/fixtures/operation_tree.py               |  12 +-
 test/port/test_inputport.py                   |  27 ++-
 test/port/test_outputport.py                  |  31 ++-
 test/signal/test_signal.py                    |  12 +-
 test/traverse/test_traverse_tree.py           |   6 +-
 16 files changed, 347 insertions(+), 330 deletions(-)
 delete mode 100644 b_asic/abstract_graph_component.py
 delete mode 100644 b_asic/abstract_operation.py
 delete mode 100644 b_asic/utilities.py
 rename test/{basic_operations/test_basic_operations.py => core_operations/test_core_operations.py} (94%)

diff --git a/b_asic/__init__.py b/b_asic/__init__.py
index fc787edf..7e40ad52 100644
--- a/b_asic/__init__.py
+++ b/b_asic/__init__.py
@@ -2,8 +2,6 @@
 Better ASIC Toolbox.
 TODO: More info.
 """
-from b_asic.abstract_graph_component import *
-from b_asic.abstract_operation import *
 from b_asic.core_operations import *
 from b_asic.graph_component import *
 from b_asic.graph_id import *
@@ -14,4 +12,3 @@ from b_asic.schema import *
 from b_asic.signal_flow_graph import *
 from b_asic.signal import *
 from b_asic.simulation import *
-from b_asic.utilities import *
diff --git a/b_asic/abstract_graph_component.py b/b_asic/abstract_graph_component.py
deleted file mode 100644
index a0b71b41..00000000
--- a/b_asic/abstract_graph_component.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""@package docstring
-B-ASIC module for Graph Components of a signal flow graph.
-TODO: More info.
-"""
-
-from b_asic.graph_component import GraphComponent, Name
-
-
-class AbstractGraphComponent(GraphComponent):
-    """Abstract Graph Component class which is a component of a signal flow graph.
-
-    TODO: More info.
-    """
-
-    _name: Name
-
-    def __init__(self, name: Name = ""):
-        self._name = name
-
-    @property
-    def name(self) -> Name:
-        return self._name
-
-    @name.setter
-    def name(self, name: Name) -> None:
-        self._name = name
diff --git a/b_asic/abstract_operation.py b/b_asic/abstract_operation.py
deleted file mode 100644
index fc3a9205..00000000
--- a/b_asic/abstract_operation.py
+++ /dev/null
@@ -1,117 +0,0 @@
-"""@package docstring
-B-ASIC Abstract Operation Module.
-TODO: More info.
-"""
-
-from abc import abstractmethod
-from typing import List, Dict, Optional, Any
-from numbers import Number
-
-from b_asic.port import InputPort, OutputPort
-from b_asic.signal import Signal
-from b_asic.operation import Operation
-from b_asic.simulation import SimulationState, OperationState
-from b_asic.utilities import breadth_first_search
-from b_asic.abstract_graph_component import AbstractGraphComponent
-from b_asic.graph_component import Name
-
-
-class AbstractOperation(Operation, AbstractGraphComponent):
-    """Generic abstract operation class which most implementations will derive from.
-    TODO: More info.
-    """
-
-    _input_ports: List[InputPort]
-    _output_ports: List[OutputPort]
-    _parameters: Dict[str, Optional[Any]]
-
-    def __init__(self, name: Name = ""):
-        super().__init__(name)
-        self._input_ports = []
-        self._output_ports = []
-        self._parameters = {}
-
-    @abstractmethod
-    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 inputs(self) -> List[InputPort]:
-        return self._input_ports.copy()
-
-    def outputs(self) -> List[OutputPort]:
-        return self._output_ports.copy()
-
-    def input_count(self) -> int:
-        return len(self._input_ports)
-
-    def output_count(self) -> int:
-        return len(self._output_ports)
-
-    def input(self, i: int) -> InputPort:
-        return self._input_ports[i]
-
-    def output(self, i: int) -> OutputPort:
-        return self._output_ports[i]
-
-    def params(self) -> Dict[str, Optional[Any]]:
-        return self._parameters.copy()
-
-    def param(self, name: str) -> Optional[Any]:
-        return self._parameters.get(name)
-
-    def set_param(self, name: str, value: Any) -> None:
-        assert name in self._parameters # TODO: Error message.
-        self._parameters[name] = value
-
-    def evaluate_outputs(self, state: SimulationState) -> List[Number]:
-        # TODO: Check implementation.
-        input_count: int = self.input_count()
-        output_count: int = self.output_count()
-        assert input_count == len(self._input_ports) # TODO: Error message.
-        assert output_count == len(self._output_ports) # TODO: Error message.
-
-        self_state: OperationState = state.operation_states[self]
-
-        while self_state.iteration < state.iteration:
-            input_values: List[Number] = [0] * input_count
-            for i in range(input_count):
-                source: Signal = self._input_ports[i].signal
-                input_values[i] = source.operation.evaluate_outputs(state)[source.port_index]
-
-            self_state.output_values = self.evaluate(input_values)
-            assert len(self_state.output_values) == output_count # TODO: Error message.
-            self_state.iteration += 1
-            for i in range(output_count):
-                for signal in self._output_ports[i].signals():
-                    destination: Signal = signal.destination
-                    destination.evaluate_outputs(state)
-
-        return self_state.output_values
-
-    def split(self) -> List[Operation]:
-        # TODO: Check implementation.
-        results = self.evaluate(self._input_ports)
-        if all(isinstance(e, Operation) for e in results):
-            return results
-        return [self]
-
-    @property
-    def neighbours(self) -> List[Operation]:
-        neighbours: List[Operation] = []
-        for port in self._input_ports:
-            for signal in port.signals:
-                neighbours.append(signal.source.operation)
-
-        for port in self._output_ports:
-            for signal in port.signals:
-                neighbours.append(signal.destination.operation)
-
-        return neighbours
-
-    def traverse(self) -> Operation:
-        """Traverse the operation tree and return a generator with start point in the operation."""
-        return breadth_first_search(self)
-
-    # TODO: More stuff.
diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index f64c63db..ce1019f3 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -8,9 +8,10 @@ from typing import Any
 from numpy import conjugate, sqrt, abs as np_abs
 from b_asic.port import InputPort, OutputPort
 from b_asic.graph_id import GraphIDType
-from b_asic.abstract_operation import AbstractOperation
+from b_asic.operation import AbstractOperation
 from b_asic.graph_component import Name, TypeName
 
+
 class Input(AbstractOperation):
     """Input operation.
     TODO: More info.
@@ -23,7 +24,6 @@ class Input(AbstractOperation):
         return "in"
 
 
-
 class Constant(AbstractOperation):
     """Constant value operation.
     TODO: More info.
@@ -43,7 +43,6 @@ class Constant(AbstractOperation):
         return "c"
 
 
-
 class Addition(AbstractOperation):
     """Binary addition operation.
     TODO: More info.
@@ -52,7 +51,7 @@ class Addition(AbstractOperation):
     def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
         super().__init__(name)
 
-        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._input_ports = [InputPort(0, self), InputPort(1, self)]
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
@@ -75,7 +74,7 @@ class Subtraction(AbstractOperation):
 
     def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
         super().__init__(name)
-        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._input_ports = [InputPort(0, self), InputPort(1, self)]
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
@@ -98,7 +97,7 @@ class Multiplication(AbstractOperation):
 
     def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
         super().__init__(name)
-        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._input_ports = [InputPort(0, self), InputPort(1, self)]
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
@@ -121,7 +120,7 @@ class Division(AbstractOperation):
 
     def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
         super().__init__(name)
-        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._input_ports = [InputPort(0, self), InputPort(1, self)]
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
@@ -188,7 +187,7 @@ class Max(AbstractOperation):
 
     def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
         super().__init__(name)
-        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._input_ports = [InputPort(0, self), InputPort(1, self)]
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
@@ -213,7 +212,7 @@ class Min(AbstractOperation):
 
     def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
         super().__init__(name)
-        self._input_ports = [InputPort(0, self), InputPort(0, self)]
+        self._input_ports = [InputPort(0, self), InputPort(1, self)]
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
diff --git a/b_asic/graph_component.py b/b_asic/graph_component.py
index ec39a28d..1987d449 100644
--- a/b_asic/graph_component.py
+++ b/b_asic/graph_component.py
@@ -32,3 +32,23 @@ class GraphComponent(ABC):
     def name(self, name: Name) -> None:
         """Set the name of the graph component to the entered name."""
         raise NotImplementedError
+
+
+class AbstractGraphComponent(GraphComponent):
+    """Abstract Graph Component class which is a component of a signal flow graph.
+
+    TODO: More info.
+    """
+
+    _name: Name
+
+    def __init__(self, name: Name = ""):
+        self._name = name
+
+    @property
+    def name(self) -> Name:
+        return self._name
+
+    @name.setter
+    def name(self, name: Name) -> None:
+        self._name = name
diff --git a/b_asic/operation.py b/b_asic/operation.py
index acd26672..75644b73 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -5,13 +5,16 @@ TODO: More info.
 
 from abc import abstractmethod
 from numbers import Number
-from typing import List, Dict, Optional, Any, TYPE_CHECKING
+from typing import List, Dict, Optional, Any, Set, TYPE_CHECKING
+from collections import deque
 
-from b_asic.graph_component import GraphComponent
+from b_asic.graph_component import GraphComponent, AbstractGraphComponent, Name
+from b_asic.simulation import SimulationState, OperationState
+from b_asic.signal import Signal
 
 if TYPE_CHECKING:
     from b_asic.port import InputPort, OutputPort
-    from b_asic.simulation import SimulationState
+
 
 class Operation(GraphComponent):
     """Operation interface.
@@ -88,3 +91,114 @@ class Operation(GraphComponent):
         If no neighbours are found this returns an empty list
         """
         raise NotImplementedError
+
+
+class AbstractOperation(Operation, AbstractGraphComponent):
+    """Generic abstract operation class which most implementations will derive from.
+    TODO: More info.
+    """
+
+    _input_ports: List["InputPort"]
+    _output_ports: List["OutputPort"]
+    _parameters: Dict[str, Optional[Any]]
+
+    def __init__(self, name: Name = ""):
+        super().__init__(name)
+        self._input_ports = []
+        self._output_ports = []
+        self._parameters = {}
+
+    @abstractmethod
+    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 inputs(self) -> List["InputPort"]:
+        return self._input_ports.copy()
+
+    def outputs(self) -> List["OutputPort"]:
+        return self._output_ports.copy()
+
+    def input_count(self) -> int:
+        return len(self._input_ports)
+
+    def output_count(self) -> int:
+        return len(self._output_ports)
+
+    def input(self, i: int) -> "InputPort":
+        return self._input_ports[i]
+
+    def output(self, i: int) -> "OutputPort":
+        return self._output_ports[i]
+
+    def params(self) -> Dict[str, Optional[Any]]:
+        return self._parameters.copy()
+
+    def param(self, name: str) -> Optional[Any]:
+        return self._parameters.get(name)
+
+    def set_param(self, name: str, value: Any) -> None:
+        assert name in self._parameters # TODO: Error message.
+        self._parameters[name] = value
+
+    def evaluate_outputs(self, state: SimulationState) -> List[Number]:
+        # TODO: Check implementation.
+        input_count: int = self.input_count()
+        output_count: int = self.output_count()
+        assert input_count == len(self._input_ports) # TODO: Error message.
+        assert output_count == len(self._output_ports) # TODO: Error message.
+
+        self_state: OperationState = state.operation_states[self]
+
+        while self_state.iteration < state.iteration:
+            input_values: List[Number] = [0] * input_count
+            for i in range(input_count):
+                source: Signal = self._input_ports[i].signal
+                input_values[i] = source.operation.evaluate_outputs(state)[source.port_index]
+
+            self_state.output_values = self.evaluate(input_values)
+            assert len(self_state.output_values) == output_count # TODO: Error message.
+            self_state.iteration += 1
+            for i in range(output_count):
+                for signal in self._output_ports[i].signals():
+                    destination: Signal = signal.destination
+                    destination.evaluate_outputs(state)
+
+        return self_state.output_values
+
+    def split(self) -> List[Operation]:
+        # TODO: Check implementation.
+        results = self.evaluate(self._input_ports)
+        if all(isinstance(e, Operation) for e in results):
+            return results
+        return [self]
+
+    @property
+    def neighbours(self) -> List[Operation]:
+        neighbours: List[Operation] = []
+        for port in self._input_ports:
+            for signal in port.signals:
+                neighbours.append(signal.source.operation)
+
+        for port in self._output_ports:
+            for signal in port.signals:
+                neighbours.append(signal.destination.operation)
+
+        return neighbours
+
+    def traverse(self) -> Operation:
+        """Traverse the operation tree and return a generator with start point in the operation."""
+        return self._breadth_first_search()
+
+    def _breadth_first_search(self) -> Operation:
+        """Use breadth first search to traverse the operation tree."""
+        visited: Set[Operation] = {self}
+        queue = deque([self])
+        while queue:
+            operation = queue.popleft()
+            yield operation
+            for n_operation in operation.neighbours:
+                if n_operation not in visited:
+                    visited.add(n_operation)
+                    queue.append(n_operation)
diff --git a/b_asic/port.py b/b_asic/port.py
index eff9db9d..c22053df 100644
--- a/b_asic/port.py
+++ b/b_asic/port.py
@@ -9,31 +9,25 @@ from typing import NewType, Optional, List
 from b_asic.operation import Operation
 from b_asic.signal import Signal
 
-PortId = NewType("PortId", int)
-
+PortIndex = NewType("PortIndex", int)
 
 class Port(ABC):
-    """Abstract port class.
+    """Port Interface.
 
-    Handles functionality for port id and saves the connection to the parent operation.
+    TODO: More documentaiton?
     """
 
-    _port_id: PortId
-    _operation: Operation
-
-    def __init__(self, port_id: PortId, operation: Operation):
-        self._port_id = port_id
-        self._operation = operation
-
-    @property
-    def id(self) -> PortId:
-        """Return the unique portid."""
-        return self._port_id
-
     @property
+    @abstractmethod
     def operation(self) -> Operation:
         """Return the connected operation."""
-        return self._operation
+        raise NotImplementedError
+
+    @property
+    @abstractmethod
+    def index(self) -> PortIndex:
+        """Return the unique PortIndex."""
+        raise NotImplementedError
 
     @property
     @abstractmethod
@@ -62,136 +56,168 @@ class Port(ABC):
         raise NotImplementedError
 
     @abstractmethod
-    def connect_port(self, port: "Port") -> Signal:
+    def connect(self, port: "Port") -> Signal:
         """Create and return a signal that is connected to this port and the entered
         port and connect this port to the signal and the entered port to the signal."""
         raise NotImplementedError
 
     @abstractmethod
-    def connect_signal(self, signal: Signal) -> None:
+    def add_signal(self, signal: Signal) -> None:
         """Connect this port to the entered signal. If the entered signal isn't connected to
         this port then connect the entered signal to the port aswell."""
         raise NotImplementedError
 
     @abstractmethod
-    def disconnect_signal(self, i: int = 0) -> None:
-        """Disconnect a signal from the port. If the port is still connected to the entered signal
-        then the port is disconnected from the the entered signal aswell."""
+    def disconnect(self, port: "Port") -> None:
+        """Disconnect the entered port from the port by removing it from the ports signal.
+        If the entered port is still connected to this ports signal then disconnect the entered
+        port from the signal aswell."""
         raise NotImplementedError
 
     @abstractmethod
-    def is_connected_to_signal(self, signal: Signal) -> bool:
-        """Return true if the port is connected to the entered signal else false."""
+    def remove_signal(self, signal: Signal) -> None:
+        """Remove the signal that was entered from the Ports signals.
+        If the entered signal still is connected to this port then disconnect the
+        entered signal from the port aswell.
+
+        Keyword arguments:
+        - signal: Signal to remove.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def clear(self) -> None:
+        """Removes all connected signals from the Port."""
         raise NotImplementedError
 
 
-class InputPort(Port):
+class AbstractPort(Port):
+    """Abstract port class.
+
+    Handles functionality for port id and saves the connection to the parent operation.
+    """
+
+    _index: int
+    _operation: Operation
+
+    def __init__(self, index: int, operation: Operation):
+        self._index = index
+        self._operation = operation
+
+    @property
+    def operation(self) -> Operation:
+        return self._operation
+
+    @property
+    def index(self) -> PortIndex:
+        return self._index
+
+
+class InputPort(AbstractPort):
     """Input port.
     TODO: More info.
     """
 
-    _signal: Optional[Signal]
+    _source_signal: Optional[Signal]
 
-    def __init__(self, port_id: PortId, operation: Operation):
+    def __init__(self, port_id: PortIndex, operation: Operation):
         super().__init__(port_id, operation)
-        self._signal = None
+        self._source_signal = None
 
     @property
     def signals(self) -> List[Signal]:
-        return [] if self._signal is None else [self._signal]
+        return [] if self._source_signal is None else [self._source_signal]
 
     def signal(self, i: int = 0) -> Signal:
         assert 0 <= i < self.signal_count(), "Signal index out of bound."
-        assert self._signal is not None, "No Signal connect to InputPort."
-        return self._signal
+        assert self._source_signal is not None, "No Signal connect to InputPort."
+        return self._source_signal
 
     @property
     def connected_ports(self) -> List[Port]:
-        return [] if self._signal is None else [self._signal.source]
+        return [] if self._source_signal is None or self._source_signal.source is None \
+            else [self._source_signal.source]
 
     def signal_count(self) -> int:
-        return 0 if self._signal is None else 1
+        return 0 if self._source_signal is None else 1
 
-    def connect_port(self, port: "OutputPort") -> Signal:
-        assert self._signal is None, "Connecting new port to already connected input port."
-        return Signal(port, self)        # self._signal is set by the signal constructor
+    def connect(self, port: "OutputPort") -> Signal:
+        assert self._source_signal is None, "Connecting new port to already connected input port."
+        return Signal(port, self)        # self._source_signal is set by the signal constructor
 
-    def connect_signal(self, signal: Signal) -> None:
-        assert self._signal is None, "Connecting new port to already connected input port."
-        self._signal = signal
+    def add_signal(self, signal: Signal) -> None:
+        assert self._source_signal is None, "Connecting new port to already connected input port."
+        self._source_signal: Signal = signal
         if self is not signal.destination:
             # Connect this inputport as destination for this signal if it isn't already.
-            signal.connect_destination(self)
+            signal.set_destination(self)
 
-    def disconnect_signal(self, i: int = 0) -> None:
-        assert 0 <= i < self.signal_count(), "Signal Index out of range."
-        old_signal: Signal = self._signal
-        self._signal = None
+    def disconnect(self, port: "OutputPort") -> None:
+        assert self._source_signal.source is port, "The entered port is not connected to this port."
+        self._source_signal.remove_source()
+
+    def remove_signal(self, signal: Signal) -> None:
+        old_signal: Signal = self._source_signal
+        self._source_signal = None
         if self is old_signal.destination:
             # Disconnect the dest of the signal if this inputport currently is the dest
-            old_signal.disconnect_destination()
-        old_signal.disconnect_destination()
-
-    def is_connected_to_signal(self, signal: Signal) -> bool:
-        return self._signal is signal
+            old_signal.remove_destination()
 
+    def clear(self) -> None:
+        self.remove_signal(self._source_signal)
 
-class OutputPort(Port):
+class OutputPort(AbstractPort):
     """Output port.
     TODO: More info.
     """
 
-    _signals: List[Signal]
+    _destination_signals: List[Signal]
 
-    def __init__(self, port_id: PortId, operation: Operation):
+    def __init__(self, port_id: PortIndex, operation: Operation):
         super().__init__(port_id, operation)
-        self._signals = []
+        self._destination_signals = []
 
     @property
     def signals(self) -> List[Signal]:
-        return self._signals.copy()
+        return self._destination_signals.copy()
 
     def signal(self, i: int = 0) -> Signal:
         assert 0 <= i < self.signal_count(), "Signal index out of bounds."
-        return self._signals[i]
+        return self._destination_signals[i]
 
     @property
     def connected_ports(self) -> List[Port]:
-        return [signal.destination for signal in self._signals \
+        return [signal.destination for signal in self._destination_signals \
             if signal.destination is not None]
 
     def signal_count(self) -> int:
-        return len(self._signals)
+        return len(self._destination_signals)
 
-    def connect_port(self, port: InputPort) -> Signal:
-        return Signal(self, port)      # Signal is added to self._signals in signal constructor
+    def connect(self, port: InputPort) -> Signal:
+        return Signal(self, port)      # Signal is added to self._destination_signals in signal constructor
 
-    def connect_signal(self, signal: Signal) -> None:
-        assert not self.is_connected_to_signal(signal), \
+    def add_signal(self, signal: Signal) -> None:
+        assert signal not in self.signals, \
                 "Attempting to connect to Signal already connected."
-        self._signals.append(signal)
+        self._destination_signals.append(signal)
         if self is not signal.source:
             # Connect this outputport to the signal if it isn't already
-            signal.connect_source(self)
-
-    def disconnect_signal(self, i: int = 0) -> None:
-        assert 0 <= i < self.signal_count(), "Signal index out of bounds."
-        old_signal: Signal = self._signals[i]
-        del self._signals[i]
+            signal.set_source(self)
+
+    def disconnect(self, port: InputPort) -> None:
+        assert port in self.connected_ports, "Attempting to disconnect port that isn't connected."
+        for sig in self._destination_signals:
+            if sig.destination is port:
+                sig.remove_destination()
+                break
+
+    def remove_signal(self, signal: Signal) -> None:
+        i: int = self._destination_signals.index(signal)
+        old_signal: Signal = self._destination_signals[i]
+        del self._destination_signals[i]
         if self is old_signal.source:
-            # Disconnect the source of the signal if this outputport currently is the source
-            old_signal.disconnect_source()
-
-    def disconnect_signal_by_ref(self, signal: Signal) -> None:
-        """Remove the signal that was entered from the OutputPorts signals.
-        If the entered signal still is connected to this port then disconnect the
-        entered signal from the port aswell.
-
-        Keyword arguments:
-        - signal: Signal to remove.
-        """
-        i: int = self._signals.index(signal)
-        self.disconnect_signal(i)
+            old_signal.remove_source()
 
-    def is_connected_to_signal(self, signal: Signal) -> bool:
-        return signal in self._signals                  # O(n) complexity
+    def clear(self) -> None:
+        for signal in self._destination_signals:
+            self.remove_signal(signal)
diff --git a/b_asic/signal.py b/b_asic/signal.py
index 917e4af3..64c25948 100644
--- a/b_asic/signal.py
+++ b/b_asic/signal.py
@@ -3,13 +3,12 @@ B-ASIC Signal Module.
 """
 from typing import Optional, TYPE_CHECKING
 
-from b_asic.graph_component import TypeName
-from b_asic.abstract_graph_component import AbstractGraphComponent
-from b_asic.graph_component import Name
+from b_asic.graph_component import AbstractGraphComponent, TypeName, Name
 
 if TYPE_CHECKING:
     from b_asic.port import InputPort, OutputPort
 
+
 class Signal(AbstractGraphComponent):
     """A connection between two ports."""
 
@@ -25,10 +24,10 @@ class Signal(AbstractGraphComponent):
         self._destination = destination
 
         if source is not None:
-            self.connect_source(source)
+            self.set_source(source)
 
         if destination is not None:
-            self.connect_destination(destination)
+            self.set_destination(destination)
 
     @property
     def source(self) -> "OutputPort":
@@ -40,7 +39,7 @@ class Signal(AbstractGraphComponent):
         """Return the destination "InputPort" of the signal."""
         return self._destination
 
-    def connect_source(self, src: "OutputPort") -> None:
+    def set_source(self, src: "OutputPort") -> None:
         """Disconnect the previous source OutputPort of the signal and
         connect to the entered source OutputPort. Also connect the entered
         source port to the signal if it hasn't already been connected.
@@ -48,13 +47,13 @@ class Signal(AbstractGraphComponent):
         Keyword arguments:
         - src: OutputPort to connect as source to the signal.
         """
-        self.disconnect_source()
+        self.remove_source()
         self._source = src
-        if not src.is_connected_to_signal(self):
+        if self not in src.signals:
             # If the new source isn't connected to this signal then connect it.
-            src.connect_signal(self)
+            src.add_signal(self)
 
-    def connect_destination(self, dest: "InputPort") -> None:
+    def set_destination(self, dest: "InputPort") -> None:
         """Disconnect the previous destination InputPort of the signal and
         connect to the entered destination InputPort. Also connect the entered
         destination port to the signal if it hasn't already been connected.
@@ -62,34 +61,34 @@ class Signal(AbstractGraphComponent):
         Keywords argments:
         - dest: InputPort to connect as destination to the signal.
         """
-        self.disconnect_destination()
+        self.remove_destination()
         self._destination = dest
-        if not dest.is_connected_to_signal(self):
+        if self not in dest.signals:
             # If the new destination isn't connected to tis signal then connect it.
-            dest.connect_signal(self)
+            dest.add_signal(self)
 
     @property
     def type_name(self) -> TypeName:
         return "s"
 
-    def disconnect_source(self) -> None:
+    def remove_source(self) -> None:
         """Disconnect the source OutputPort of the signal. If the source port
         still is connected to this signal then also disconnect the source port."""
         if self._source is not None:
             old_source: "OutputPort" = self._source
             self._source = None
-            if old_source.is_connected_to_signal(self):
+            if self in old_source.signals:
                 # If the old destination port still is connected to this signal, then disconnect it.
-                old_source.disconnect_signal_by_ref(self)
+                old_source.remove_signal(self)
 
-    def disconnect_destination(self) -> None:
+    def remove_destination(self) -> None:
         """Disconnect the destination InputPort of the signal."""
         if self._destination is not None:
             old_destination: "InputPort" = self._destination
             self._destination = None
-            if old_destination.is_connected_to_signal(self):
+            if self in old_destination.signals:
                 # If the old destination port still is connected to this signal, then disconnect it.
-                old_destination.disconnect_signal()
+                old_destination.remove_signal(self)
 
     def is_connected(self) -> bool:
         """Returns true if the signal is connected to both a source and a destination,
diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py
index ab2c3e94..9c08aecc 100644
--- a/b_asic/signal_flow_graph.py
+++ b/b_asic/signal_flow_graph.py
@@ -7,7 +7,7 @@ from typing import List, Dict, Optional, DefaultDict
 from collections import defaultdict
 
 from b_asic.operation import Operation
-from b_asic.abstract_operation import AbstractOperation
+from b_asic.operation import AbstractOperation
 from b_asic.signal import Signal
 from b_asic.graph_id import GraphIDGenerator, GraphID
 from b_asic.graph_component import GraphComponent, Name, TypeName
@@ -46,7 +46,7 @@ class SFG(AbstractOperation):
         # TODO: Traverse the graph between the inputs/outputs and add to self._operations.
         # TODO: Connect ports with signals with appropriate IDs.
 
-    def evaluate(self, inputs: list) -> list:
+    def evaluate(self, *inputs) -> list:
         return [] # TODO: Implement
 
     def _add_graph_component(self, graph_component: GraphComponent) -> GraphID:
diff --git a/b_asic/utilities.py b/b_asic/utilities.py
deleted file mode 100644
index 25707ff8..00000000
--- a/b_asic/utilities.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""@package docstring
-B-ASIC Operation Module.
-TODO: More info.
-"""
-
-from typing import Set
-from collections import deque
-
-from b_asic.operation import Operation
-
-def breadth_first_search(start: Operation) -> Operation:
-    """Use breadth first search to traverse the operation tree."""
-    visited: Set[Operation] = {start}
-    queue = deque([start])
-    while queue:
-        operation = queue.popleft()
-        yield operation
-        for n_operation in operation.neighbours:
-            if n_operation not in visited:
-                visited.add(n_operation)
-                queue.append(n_operation)
diff --git a/test/basic_operations/test_basic_operations.py b/test/core_operations/test_core_operations.py
similarity index 94%
rename from test/basic_operations/test_basic_operations.py
rename to test/core_operations/test_core_operations.py
index 21561074..1d33bfe1 100644
--- a/test/basic_operations/test_basic_operations.py
+++ b/test/core_operations/test_core_operations.py
@@ -1,12 +1,12 @@
 """
-B-ASIC test suite for the basic operations.
+B-ASIC test suite for the core operations.
 """
 
 from b_asic.core_operations import Constant, Addition, Subtraction, Multiplication, Division, SquareRoot, ComplexConjugate, Max, Min, Absolute, ConstantMultiplication, ConstantAddition, ConstantSubtraction, ConstantDivision
 from b_asic.signal import Signal
 import pytest
 
-""" Constant tests. """
+# Constant tests.
 def test_constant():
     constant_operation = Constant(3)
     assert constant_operation.evaluate() == 3
@@ -19,7 +19,7 @@ def test_constant_complex():
     constant_operation = Constant(3+4j)
     assert constant_operation.evaluate() == 3+4j
 
-""" Addition tests. """
+# Addition tests.
 def test_addition():
     test_operation = Addition()
     constant_operation = Constant(3)
@@ -38,7 +38,7 @@ def test_addition_complex():
     constant_operation_2 = Constant((4+6j))
     assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == (7+11j)
 
-""" Subtraction tests. """
+# Subtraction tests.
 def test_subtraction():
     test_operation = Subtraction()
     constant_operation = Constant(5)
@@ -57,7 +57,7 @@ def test_subtraction_complex():
     constant_operation_2 = Constant((4+6j))
     assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == (-1-1j)
 
-""" Multiplication tests. """
+# Multiplication tests.
 def test_multiplication():
     test_operation = Multiplication()
     constant_operation = Constant(5)
@@ -76,7 +76,7 @@ def test_multiplication_complex():
     constant_operation_2 = Constant((4+6j))
     assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == (-18+38j)
 
-""" Division tests. """
+# Division tests.
 def test_division():
     test_operation = Division()
     constant_operation = Constant(30)
@@ -95,7 +95,7 @@ def test_division_complex():
     constant_operation_2 = Constant((10+20j))
     assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == (2.8-1.6j)
 
-""" SquareRoot tests. """
+# SquareRoot tests.
 def test_squareroot():
     test_operation = SquareRoot()
     constant_operation = Constant(36)
@@ -111,7 +111,7 @@ def test_squareroot_complex():
     constant_operation = Constant((48+64j))
     assert test_operation.evaluate(constant_operation.evaluate()) == (8+4j)
 
-""" ComplexConjugate tests. """
+# ComplexConjugate tests.
 def test_complexconjugate():
     test_operation = ComplexConjugate()
     constant_operation = Constant(3+4j)
@@ -122,7 +122,7 @@ def test_test_complexconjugate_negative():
     constant_operation = Constant(-3-4j)
     assert test_operation.evaluate(constant_operation.evaluate()) == (-3+4j)
 
-""" Max tests. """
+# Max tests.
 def test_max():
     test_operation = Max()
     constant_operation = Constant(30)
@@ -135,7 +135,7 @@ def test_max_negative():
     constant_operation_2 = Constant(-5)
     assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == -5
 
-""" Min tests. """
+# Min tests.
 def test_min():
     test_operation = Min()
     constant_operation = Constant(30)
@@ -148,7 +148,7 @@ def test_min_negative():
     constant_operation_2 = Constant(-5)
     assert test_operation.evaluate(constant_operation.evaluate(), constant_operation_2.evaluate()) == -30
 
-""" Absolute tests. """
+# Absolute tests.
 def test_absolute():
     test_operation = Absolute()
     constant_operation = Constant(30)
@@ -164,7 +164,7 @@ def test_absolute_complex():
     constant_operation = Constant((3+4j))
     assert test_operation.evaluate(constant_operation.evaluate()) == 5.0
 
-""" ConstantMultiplication tests. """
+# ConstantMultiplication tests.
 def test_constantmultiplication():
     test_operation = ConstantMultiplication(5)
     constant_operation = Constant(20)
@@ -180,7 +180,7 @@ def test_constantmultiplication_complex():
     constant_operation = Constant((3+4j))
     assert test_operation.evaluate(constant_operation.evaluate()) == (1+18j)
 
-""" ConstantAddition tests. """
+# ConstantAddition tests.
 def test_constantaddition():
     test_operation = ConstantAddition(5)
     constant_operation = Constant(20)
@@ -196,7 +196,7 @@ def test_constantaddition_complex():
     constant_operation = Constant((3+4j))
     assert test_operation.evaluate(constant_operation.evaluate()) == (6+6j)
 
-""" ConstantSubtraction tests. """
+# ConstantSubtraction tests.
 def test_constantsubtraction():
     test_operation = ConstantSubtraction(5)
     constant_operation = Constant(20)
@@ -212,7 +212,7 @@ def test_constantsubtraction_complex():
     constant_operation = Constant((3+4j))
     assert test_operation.evaluate(constant_operation.evaluate()) == (-1-2j)
 
-""" ConstantDivision tests. """
+# ConstantDivision tests.
 def test_constantdivision():
     test_operation = ConstantDivision(5)
     constant_operation = Constant(20)
@@ -226,4 +226,4 @@ def test_constantdivision_negative():
 def test_constantdivision_complex():
     test_operation = ConstantDivision(2+2j)
     constant_operation = Constant((10+10j))
-    assert test_operation.evaluate(constant_operation.evaluate()) == (5+0j)
\ No newline at end of file
+    assert test_operation.evaluate(constant_operation.evaluate()) == (5+0j)
diff --git a/test/fixtures/operation_tree.py b/test/fixtures/operation_tree.py
index b97e89d7..74d3b8c6 100644
--- a/test/fixtures/operation_tree.py
+++ b/test/fixtures/operation_tree.py
@@ -10,9 +10,9 @@ def operation():
 def create_operation(_type, dest_oper, index, **kwargs):
     oper = _type(**kwargs)
     oper_signal = Signal()
-    oper._output_ports[0].connect_signal(oper_signal)
+    oper._output_ports[0].add_signal(oper_signal)
 
-    dest_oper._input_ports[index].connect_signal(oper_signal)
+    dest_oper._input_ports[index].add_signal(oper_signal)
     return oper
 
 @pytest.fixture
@@ -51,10 +51,10 @@ def large_operation_tree():
 
     add_oper_3 = Addition()
     add_oper_signal = Signal(add_oper.output(0), add_oper_3.output(0))
-    add_oper._output_ports[0].connect_signal(add_oper_signal)
-    add_oper_3._input_ports[0].connect_signal(add_oper_signal)
+    add_oper._output_ports[0].add_signal(add_oper_signal)
+    add_oper_3._input_ports[0].add_signal(add_oper_signal)
 
     add_oper_2_signal = Signal(add_oper_2.output(0), add_oper_3.output(0))
-    add_oper_2._output_ports[0].connect_signal(add_oper_2_signal)
-    add_oper_3._input_ports[1].connect_signal(add_oper_2_signal)
+    add_oper_2._output_ports[0].add_signal(add_oper_2_signal)
+    add_oper_3._input_ports[1].add_signal(add_oper_2_signal)
     return const_oper
diff --git a/test/port/test_inputport.py b/test/port/test_inputport.py
index 7a78d5f7..a4324069 100644
--- a/test/port/test_inputport.py
+++ b/test/port/test_inputport.py
@@ -39,9 +39,9 @@ def connected_sig():
     inp_port = InputPort(0, None)
     return Signal(source=out_port, destination=inp_port)
 
-def test_connect_port_then_disconnect(inp_port, out_port):
+def test_connect_then_disconnect(inp_port, out_port):
     """Test connect unused port to port."""
-    s1 = inp_port.connect_port(out_port)
+    s1 = inp_port.connect(out_port)
 
     assert inp_port.connected_ports == [out_port]
     assert out_port.connected_ports == [inp_port]
@@ -50,7 +50,7 @@ def test_connect_port_then_disconnect(inp_port, out_port):
     assert s1.source is out_port
     assert s1.destination is inp_port
 
-    inp_port.disconnect_signal()
+    inp_port.remove_signal(s1)
 
     assert inp_port.connected_ports == []
     assert out_port.connected_ports == []
@@ -61,12 +61,13 @@ def test_connect_port_then_disconnect(inp_port, out_port):
 
 def test_connect_used_port_to_new_port(inp_port, out_port, out_port2):
     """Does connecting multiple ports to an inputport throw error?"""
-    inp_port.connect_port(out_port)
+    inp_port.connect(out_port)
     with pytest.raises(AssertionError):
-        inp_port.connect_port(out_port2)
+        inp_port.connect(out_port2)
 
-def test_connect_signal_then_disconnect(inp_port, s_w_source):
-    inp_port.connect_signal(s_w_source)
+def test_add_signal_then_disconnect(inp_port, s_w_source):
+    """Can signal be connected then disconnected properly?"""
+    inp_port.add_signal(s_w_source)
 
     assert inp_port.connected_ports == [s_w_source.source]
     assert s_w_source.source.connected_ports == [inp_port]
@@ -74,7 +75,7 @@ def test_connect_signal_then_disconnect(inp_port, s_w_source):
     assert s_w_source.source.signals == [s_w_source]
     assert s_w_source.destination is inp_port
 
-    inp_port.disconnect_signal()
+    inp_port.remove_signal(s_w_source)
 
     assert inp_port.connected_ports == []
     assert s_w_source.source.connected_ports == []
@@ -82,3 +83,13 @@ def test_connect_signal_then_disconnect(inp_port, s_w_source):
     assert s_w_source.source.signals == [s_w_source]
     assert s_w_source.destination is None
 
+def test_connect_then_disconnect(inp_port, out_port):
+    """Can port be connected and then disconnected properly?"""
+    inp_port.connect(out_port)
+
+    inp_port.disconnect(out_port)
+
+    print("outport signals:", out_port.signals, "count:", out_port.signal_count())
+    assert inp_port.signal_count() == 1
+    assert len(inp_port.connected_ports) == 0
+    assert out_port.signal_count() == 0
diff --git a/test/port/test_outputport.py b/test/port/test_outputport.py
index f48afbdb..ac50818e 100644
--- a/test/port/test_outputport.py
+++ b/test/port/test_outputport.py
@@ -14,18 +14,33 @@ def test_connect_multiple_signals(inp_ports):
     out_port = OutputPort(0, None)
 
     for port in inp_ports:
-        out_port.connect_port(port)
-    
+        out_port.connect(port)
+
     assert out_port.signal_count() == len(inp_ports)
 
 def test_disconnect_multiple_signals(inp_ports):
-    """Can multiple ports disconnect from an output port?"""
+    """Can multiple signals disconnect from an output port?"""
+    out_port = OutputPort(0, None)
+
+    sigs = []
+
+    for port in inp_ports:
+        sigs.append(out_port.connect(port))
+
+    for sig in sigs:
+        out_port.remove_signal(sig)
+
+    assert out_port.signal_count() == 0
+
+def test_disconnect_mulitple_ports(inp_ports):
+    """Can multiple ports be disconnected from an output port?"""
     out_port = OutputPort(0, None)
 
     for port in inp_ports:
-        out_port.connect_port(port)
-    
-    for _ in inp_ports:
-        out_port.disconnect_signal(0)
+        out_port.connect(port)
+
+    for port in inp_ports:
+        out_port.disconnect(port)
 
-    assert out_port.signal_count() == 0
\ No newline at end of file
+    assert out_port.signal_count() == 3
+    assert len(out_port.connected_ports) == 0
\ No newline at end of file
diff --git a/test/signal/test_signal.py b/test/signal/test_signal.py
index 8c10d1e3..ab07eb77 100644
--- a/test/signal/test_signal.py
+++ b/test/signal/test_signal.py
@@ -18,7 +18,7 @@ def test_signal_creation_and_disconnction_and_connection_changing():
     assert s.destination is in_port
 
     in_port1 = InputPort(0, None)
-    s.connect_destination(in_port1)
+    s.set_destination(in_port1)
 
     assert in_port.signals == []
     assert in_port1.signals == [s]
@@ -26,14 +26,14 @@ def test_signal_creation_and_disconnction_and_connection_changing():
     assert s.source is out_port
     assert s.destination is in_port1
 
-    s.disconnect_source()
+    s.remove_source()
 
     assert out_port.signals == []
     assert in_port1.signals == [s]
     assert s.source is None
     assert s.destination is in_port1
 
-    s.disconnect_destination()
+    s.remove_destination()
 
     assert out_port.signals == []
     assert in_port1.signals == []
@@ -41,20 +41,20 @@ def test_signal_creation_and_disconnction_and_connection_changing():
     assert s.destination is None
 
     out_port1 = OutputPort(0, None)
-    s.connect_source(out_port1)
+    s.set_source(out_port1)
 
     assert out_port1.signals == [s]
     assert s.source is out_port1
     assert s.destination is None
 
-    s.connect_source(out_port)
+    s.set_source(out_port)
 
     assert out_port.signals == [s]
     assert out_port1.signals == []
     assert s.source is out_port
     assert s.destination is None
 
-    s.connect_destination(in_port)
+    s.set_destination(in_port)
 
     assert out_port.signals == [s]
     assert in_port.signals == [s]
diff --git a/test/traverse/test_traverse_tree.py b/test/traverse/test_traverse_tree.py
index 9f509287..031aeec7 100644
--- a/test/traverse/test_traverse_tree.py
+++ b/test/traverse/test_traverse_tree.py
@@ -24,7 +24,7 @@ def test_traverse_type(large_operation_tree):
 
 def test_traverse_loop(operation_tree):
     add_oper_signal = Signal()
-    operation_tree._output_ports[0].connect_signal(add_oper_signal)
-    operation_tree._input_ports[0].disconnect_signal()
-    operation_tree._input_ports[0].connect_signal(add_oper_signal)
+    operation_tree._output_ports[0].add_signal(add_oper_signal)
+    operation_tree._input_ports[0].remove_signal(add_oper_signal)
+    operation_tree._input_ports[0].add_signal(add_oper_signal)
     assert len(list(operation_tree.traverse())) == 2
-- 
GitLab


From 3dbb4ab92c58d11d205813ef6c5b0397352904ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20H=C3=A4rnqvist?= <ivaha717@student.liu.se>
Date: Fri, 20 Mar 2020 18:33:15 +0100
Subject: [PATCH 48/50] Misc. fixes

---
 b_asic/core_operations.py | 54 +++++++++++++++++++--------------------
 b_asic/operation.py       | 16 ++++++------
 2 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index ce1019f3..d06cbab3 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -35,7 +35,7 @@ class Constant(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
         self._parameters["value"] = value
 
-    def evaluate(self) -> Any:
+    def evaluate(self):
         return self.param("value")
 
     @property
@@ -59,7 +59,7 @@ class Addition(AbstractOperation):
         if source2 is not None:
             self._input_ports[1].connect_to_port(source2)
 
-    def evaluate(self, a, b) -> Any:
+    def evaluate(self, a, b):
         return a + b
 
     @property
@@ -82,11 +82,11 @@ class Subtraction(AbstractOperation):
         if source2 is not None:
             self._input_ports[1].connect_to_port(source2)
 
-    def evaluate(self, a, b) -> Any:
+    def evaluate(self, a, b):
         return a - b
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "sub"
 
 
@@ -105,11 +105,11 @@ class Multiplication(AbstractOperation):
         if source2 is not None:
             self._input_ports[1].connect_to_port(source2)
 
-    def evaluate(self, a, b) -> Any:
+    def evaluate(self, a, b):
         return a * b
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "mul"
 
 
@@ -128,11 +128,11 @@ class Division(AbstractOperation):
         if source2 is not None:
             self._input_ports[1].connect_to_port(source2)
 
-    def evaluate(self, a, b) -> Any:
+    def evaluate(self, a, b):
         return a / b
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "div"
 
 
@@ -150,11 +150,11 @@ class SquareRoot(AbstractOperation):
             self._input_ports[0].connect_to_port(source1)
 
 
-    def evaluate(self, a) -> Any:
+    def evaluate(self, a):
         return sqrt((complex)(a))
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "sqrt"
 
 
@@ -172,11 +172,11 @@ class ComplexConjugate(AbstractOperation):
             self._input_ports[0].connect_to_port(source1)
 
 
-    def evaluate(self, a) -> Any:
+    def evaluate(self, a):
         return conjugate(a)
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "conj"
 
 
@@ -195,13 +195,13 @@ class Max(AbstractOperation):
         if source2 is not None:
             self._input_ports[1].connect_to_port(source2)
 
-    def evaluate(self, a, b) -> Any:
+    def evaluate(self, a, b):
         assert not isinstance(a, complex) and not isinstance(b, complex), \
-            ("core_operation.Max does not support complex numbers.")
+            ("core_operations.Max does not support complex numbers.")
         return a if a > b else b
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "max"
 
 
@@ -220,13 +220,13 @@ class Min(AbstractOperation):
         if source2 is not None:
             self._input_ports[1].connect_to_port(source2)
 
-    def evaluate(self, a, b) -> Any:
+    def evaluate(self, a, b):
         assert not isinstance(a, complex) and not isinstance(b, complex), \
-            ("core_operation.Min does not support complex numbers.")
+            ("core_operations.Min does not support complex numbers.")
         return a if a < b else b
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "min"
 
 
@@ -244,11 +244,11 @@ class Absolute(AbstractOperation):
             self._input_ports[0].connect_to_port(source1)
 
 
-    def evaluate(self, a) -> Any:
+    def evaluate(self, a):
         return np_abs(a)
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "abs"
 
 
@@ -266,7 +266,7 @@ class ConstantMultiplication(AbstractOperation):
         if source1 is not None:
             self._input_ports[0].connect_to_port(source1)
 
-    def evaluate(self, a) -> Any:
+    def evaluate(self, a):
         return a * self.param("coefficient")
 
     @property
@@ -288,11 +288,11 @@ class ConstantAddition(AbstractOperation):
         if source1 is not None:
             self._input_ports[0].connect_to_port(source1)
 
-    def evaluate(self, a) -> Any:
+    def evaluate(self, a):
         return a + self.param("coefficient")
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "cadd"
 
 
@@ -310,11 +310,11 @@ class ConstantSubtraction(AbstractOperation):
         if source1 is not None:
             self._input_ports[0].connect_to_port(source1)
 
-    def evaluate(self, a) -> Any:
+    def evaluate(self, a):
         return a - self.param("coefficient")
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "csub"
 
 
@@ -332,9 +332,9 @@ class ConstantDivision(AbstractOperation):
         if source1 is not None:
             self._input_ports[0].connect_to_port(source1)
 
-    def evaluate(self, a) -> Any:
+    def evaluate(self, a):
         return a / self.param("coefficient")
 
     @property
-    def type_name(self) -> GraphIDType:
+    def type_name(self) -> TypeName:
         return "cdiv"
diff --git a/b_asic/operation.py b/b_asic/operation.py
index 75644b73..fc007ffd 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -86,9 +86,9 @@ class Operation(GraphComponent):
 
     @property
     @abstractmethod
-    def neighbours(self) -> "List[Operation]":
+    def neighbors(self) -> "List[Operation]":
         """Return all operations that are connected by signals to this operation.
-        If no neighbours are found this returns an empty list
+        If no neighbors are found, this returns an empty list.
         """
         raise NotImplementedError
 
@@ -175,17 +175,17 @@ class AbstractOperation(Operation, AbstractGraphComponent):
         return [self]
 
     @property
-    def neighbours(self) -> List[Operation]:
-        neighbours: List[Operation] = []
+    def neighbors(self) -> List[Operation]:
+        neighbors: List[Operation] = []
         for port in self._input_ports:
             for signal in port.signals:
-                neighbours.append(signal.source.operation)
+                neighbors.append(signal.source.operation)
 
         for port in self._output_ports:
             for signal in port.signals:
-                neighbours.append(signal.destination.operation)
+                neighbors.append(signal.destination.operation)
 
-        return neighbours
+        return neighbors
 
     def traverse(self) -> Operation:
         """Traverse the operation tree and return a generator with start point in the operation."""
@@ -198,7 +198,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
         while queue:
             operation = queue.popleft()
             yield operation
-            for n_operation in operation.neighbours:
+            for n_operation in operation.neighbors:
                 if n_operation not in visited:
                     visited.add(n_operation)
                     queue.append(n_operation)
-- 
GitLab


From 1b995d1924e726e0a30fedc8516609f629243c06 Mon Sep 17 00:00:00 2001
From: Angus Lothian <anglo547@student.liu.se>
Date: Tue, 31 Mar 2020 14:28:05 +0200
Subject: [PATCH 49/50] Change from import to import as import for Signal to
 see if that works since no circular dependencies are involved there.

---
 b_asic/core_operations.py                 | 41 ++++++------
 b_asic/operation.py                       | 78 +++++++++++++++++++++--
 test/operation/test_abstract_operation.py | 77 ++++++++++++++++++++++
 3 files changed, 167 insertions(+), 29 deletions(-)
 create mode 100644 test/operation/test_abstract_operation.py

diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py
index d06cbab3..8902b169 100644
--- a/b_asic/core_operations.py
+++ b/b_asic/core_operations.py
@@ -55,9 +55,9 @@ class Addition(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
         if source2 is not None:
-            self._input_ports[1].connect_to_port(source2)
+            self._input_ports[1].connect(source2)
 
     def evaluate(self, a, b):
         return a + b
@@ -78,9 +78,9 @@ class Subtraction(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
         if source2 is not None:
-            self._input_ports[1].connect_to_port(source2)
+            self._input_ports[1].connect(source2)
 
     def evaluate(self, a, b):
         return a - b
@@ -101,9 +101,9 @@ class Multiplication(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
         if source2 is not None:
-            self._input_ports[1].connect_to_port(source2)
+            self._input_ports[1].connect(source2)
 
     def evaluate(self, a, b):
         return a * b
@@ -124,9 +124,9 @@ class Division(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
         if source2 is not None:
-            self._input_ports[1].connect_to_port(source2)
+            self._input_ports[1].connect(source2)
 
     def evaluate(self, a, b):
         return a / b
@@ -147,8 +147,7 @@ class SquareRoot(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
-
+            self._input_ports[0].connect(source1)
 
     def evaluate(self, a):
         return sqrt((complex)(a))
@@ -169,8 +168,7 @@ class ComplexConjugate(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
-
+            self._input_ports[0].connect(source1)
 
     def evaluate(self, a):
         return conjugate(a)
@@ -191,9 +189,9 @@ class Max(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
         if source2 is not None:
-            self._input_ports[1].connect_to_port(source2)
+            self._input_ports[1].connect(source2)
 
     def evaluate(self, a, b):
         assert not isinstance(a, complex) and not isinstance(b, complex), \
@@ -216,9 +214,9 @@ class Min(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
         if source2 is not None:
-            self._input_ports[1].connect_to_port(source2)
+            self._input_ports[1].connect(source2)
 
     def evaluate(self, a, b):
         assert not isinstance(a, complex) and not isinstance(b, complex), \
@@ -241,8 +239,7 @@ class Absolute(AbstractOperation):
         self._output_ports = [OutputPort(0, self)]
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
-
+            self._input_ports[0].connect(source1)
 
     def evaluate(self, a):
         return np_abs(a)
@@ -264,7 +261,7 @@ class ConstantMultiplication(AbstractOperation):
         self._parameters["coefficient"] = coefficient
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
 
     def evaluate(self, a):
         return a * self.param("coefficient")
@@ -286,7 +283,7 @@ class ConstantAddition(AbstractOperation):
         self._parameters["coefficient"] = coefficient
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
 
     def evaluate(self, a):
         return a + self.param("coefficient")
@@ -308,7 +305,7 @@ class ConstantSubtraction(AbstractOperation):
         self._parameters["coefficient"] = coefficient
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
 
     def evaluate(self, a):
         return a - self.param("coefficient")
@@ -330,7 +327,7 @@ class ConstantDivision(AbstractOperation):
         self._parameters["coefficient"] = coefficient
 
         if source1 is not None:
-            self._input_ports[0].connect_to_port(source1)
+            self._input_ports[0].connect(source1)
 
     def evaluate(self, a):
         return a / self.param("coefficient")
diff --git a/b_asic/operation.py b/b_asic/operation.py
index fc007ffd..5578e3c4 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -109,9 +109,10 @@ class AbstractOperation(Operation, AbstractGraphComponent):
         self._parameters = {}
 
     @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."""
+        list of input values.
+        """
         raise NotImplementedError
 
     def inputs(self) -> List["InputPort"]:
@@ -139,15 +140,15 @@ class AbstractOperation(Operation, AbstractGraphComponent):
         return self._parameters.get(name)
 
     def set_param(self, name: str, value: Any) -> None:
-        assert name in self._parameters # TODO: Error message.
+        assert name in self._parameters  # TODO: Error message.
         self._parameters[name] = value
 
     def evaluate_outputs(self, state: SimulationState) -> List[Number]:
         # TODO: Check implementation.
         input_count: int = self.input_count()
         output_count: int = self.output_count()
-        assert input_count == len(self._input_ports) # TODO: Error message.
-        assert output_count == len(self._output_ports) # TODO: Error message.
+        assert input_count == len(self._input_ports)  # TODO: Error message.
+        assert output_count == len(self._output_ports)  # TODO: Error message.
 
         self_state: OperationState = state.operation_states[self]
 
@@ -155,10 +156,12 @@ class AbstractOperation(Operation, AbstractGraphComponent):
             input_values: List[Number] = [0] * input_count
             for i in range(input_count):
                 source: Signal = self._input_ports[i].signal
-                input_values[i] = source.operation.evaluate_outputs(state)[source.port_index]
+                input_values[i] = source.operation.evaluate_outputs(state)[
+                    source.port_index]
 
             self_state.output_values = self.evaluate(input_values)
-            assert len(self_state.output_values) == output_count # TODO: Error message.
+            # TODO: Error message.
+            assert len(self_state.output_values) == output_count
             self_state.iteration += 1
             for i in range(output_count):
                 for signal in self._output_ports[i].signals():
@@ -202,3 +205,64 @@ class AbstractOperation(Operation, AbstractGraphComponent):
                 if n_operation not in visited:
                     visited.add(n_operation)
                     queue.append(n_operation)
+
+    def __add__(self, other):
+        """Overloads the addition operator to make it return a new Addition operation
+        object that is connected to the self and other objects. If other is a number then
+        returns a ConstantAddition operation object instead.
+        """
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Addition, ConstantAddition
+
+        if isinstance(other, Operation):
+            return Addition(self.output(0), other.output(0))
+        elif isinstance(other, Number):
+            return ConstantAddition(other, self.output(0))
+        else:
+            raise TypeError("Other type is not an Operation or a Number.")
+
+    def __sub__(self, other):
+        """Overloads the subtraction operator to make it return a new Subtraction operation
+        object that is connected to the self and other objects. If other is a number then
+        returns a ConstantSubtraction operation object instead.
+        """
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Subtraction, ConstantSubtraction
+
+        if isinstance(other, Operation):
+            return Subtraction(self.output(0), other.output(0))
+        elif isinstance(other, Number):
+            return ConstantSubtraction(other, self.output(0))
+        else:
+            raise TypeError("Other type is not an Operation or a Number.")
+
+    def __mul__(self, other):
+        """Overloads the multiplication operator to make it return a new Multiplication operation
+        object that is connected to the self and other objects. If other is a number then
+        returns a ConstantMultiplication operation object instead.
+        """
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Multiplication, ConstantMultiplication
+
+        if isinstance(other, Operation):
+            return Multiplication(self.output(0), other.output(0))
+        elif isinstance(other, Number):
+            return ConstantMultiplication(other, self.output(0))
+        else:
+            raise TypeError("Other type is not an Operation or a Number.")
+
+    def __truediv__(self, other):
+        """Overloads the division operator to make it return a new Division operation
+        object that is connected to the self and other objects. If other is a number then
+        returns a ConstantDivision operation object instead.
+        """
+        # Import here to avoid circular imports.
+        from b_asic.core_operations import Division, ConstantDivision
+
+        if isinstance(other, Operation):
+            return Division(self.output(0), other.output(0))
+        elif isinstance(other, Number):
+            return ConstantDivision(other, self.output(0))
+        else:
+            raise TypeError("Other type is not an Operation or a Number.")
+
diff --git a/test/operation/test_abstract_operation.py b/test/operation/test_abstract_operation.py
new file mode 100644
index 00000000..626a2dc3
--- /dev/null
+++ b/test/operation/test_abstract_operation.py
@@ -0,0 +1,77 @@
+"""
+B-ASIC test suite for the AbstractOperation class.
+"""
+
+from b_asic.core_operations import Addition, ConstantAddition, Subtraction, ConstantSubtraction, \
+    Multiplication, ConstantMultiplication, Division, ConstantDivision
+
+import pytest
+
+
+def test_addition_overload():
+    """Tests addition overloading for both operation and number argument."""
+    add1 = Addition(None, None, "add1")
+    add2 = Addition(None, None, "add2")
+
+    add3 = add1 + add2
+
+    assert isinstance(add3, Addition)
+    assert add3.input(0).signals == add1.output(0).signals
+    assert add3.input(1).signals == add2.output(0).signals
+
+    add4 = add3 + 5
+
+    assert isinstance(add4, ConstantAddition)
+    assert add4.input(0).signals == add3.output(0).signals
+
+
+def test_subtraction_overload():
+    """Tests subtraction overloading for both operation and number argument."""
+    add1 = Addition(None, None, "add1")
+    add2 = Addition(None, None, "add2")
+
+    sub1 = add1 - add2
+
+    assert isinstance(sub1, Subtraction)
+    assert sub1.input(0).signals == add1.output(0).signals
+    assert sub1.input(1).signals == add2.output(0).signals
+
+    sub2 = sub1 - 5
+
+    assert isinstance(sub2, ConstantSubtraction)
+    assert sub2.input(0).signals == sub1.output(0).signals
+
+
+def test_multiplication_overload():
+    """Tests multiplication overloading for both operation and number argument."""
+    add1 = Addition(None, None, "add1")
+    add2 = Addition(None, None, "add2")
+
+    mul1 = add1 * add2
+
+    assert isinstance(mul1, Multiplication)
+    assert mul1.input(0).signals == add1.output(0).signals
+    assert mul1.input(1).signals == add2.output(0).signals
+
+    mul2 = mul1 * 5
+
+    assert isinstance(mul2, ConstantMultiplication)
+    assert mul2.input(0).signals == mul1.output(0).signals
+
+
+def test_division_overload():
+    """Tests division overloading for both operation and number argument."""
+    add1 = Addition(None, None, "add1")
+    add2 = Addition(None, None, "add2")
+
+    div1 = add1 / add2
+
+    assert isinstance(div1, Division)
+    assert div1.input(0).signals == add1.output(0).signals
+    assert div1.input(1).signals == add2.output(0).signals
+
+    div2 = div1 / 5
+
+    assert isinstance(div2, ConstantDivision)
+    assert div2.input(0).signals == div1.output(0).signals
+
-- 
GitLab


From 7c87314374ef9cb3df6af0bfd6cc9a4abd5356ff Mon Sep 17 00:00:00 2001
From: Kevin Scott <kevsc634@student.liu.se>
Date: Wed, 1 Apr 2020 09:51:29 +0200
Subject: [PATCH 50/50] Changed test layout and refactorized some tests for
 OutputPort

---
 test/conftest.py                              |  5 +-
 test/fixtures/operation_tree.py               | 26 +++---
 test/fixtures/port.py                         | 10 +++
 test/graph_id/test_graph_id_generator.py      | 29 -------
 test/port/test_outputport.py                  | 46 -----------
 .../test_signal_flow_graph.py                 | 10 ---
 .../test_core_operations.py                   |  2 -
 test/test_graph_id_generator.py               | 28 +++++++
 test/{port => }/test_inputport.py             |  0
 test/test_operation.py                        | 31 +++++++
 test/test_outputport.py                       | 80 +++++++++++++++++++
 test/{signal => }/test_signal.py              |  0
 test/traverse/test_traverse_tree.py           | 30 -------
 13 files changed, 164 insertions(+), 133 deletions(-)
 create mode 100644 test/fixtures/port.py
 delete mode 100644 test/graph_id/test_graph_id_generator.py
 delete mode 100644 test/port/test_outputport.py
 delete mode 100644 test/signal_flow_graph/test_signal_flow_graph.py
 rename test/{core_operations => }/test_core_operations.py (99%)
 create mode 100644 test/test_graph_id_generator.py
 rename test/{port => }/test_inputport.py (100%)
 create mode 100644 test/test_operation.py
 create mode 100644 test/test_outputport.py
 rename test/{signal => }/test_signal.py (100%)
 delete mode 100644 test/traverse/test_traverse_tree.py

diff --git a/test/conftest.py b/test/conftest.py
index 66ee9630..64f39843 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -1,3 +1,4 @@
+from test.fixtures.signal import signal, signals
+from test.fixtures.operation_tree import *
+from test.fixtures.port import *
 import pytest
-from test.fixtures.signal import *
-from test.fixtures.operation_tree import *
\ No newline at end of file
diff --git a/test/fixtures/operation_tree.py b/test/fixtures/operation_tree.py
index 74d3b8c6..df3fcac3 100644
--- a/test/fixtures/operation_tree.py
+++ b/test/fixtures/operation_tree.py
@@ -17,11 +17,10 @@ def create_operation(_type, dest_oper, index, **kwargs):
 
 @pytest.fixture
 def operation_tree():
-    """
-    Return a addition operation connected with 2 constants.
-    >---C---+
-            ---A
-    >---C---+
+    """Return a addition operation connected with 2 constants.
+    ---C---+
+           ---A
+    ---C---+
     """
     add_oper = Addition()
     create_operation(Constant, add_oper, 0, value=2)
@@ -30,15 +29,14 @@ def operation_tree():
 
 @pytest.fixture
 def large_operation_tree():
-    """
-    Return a constant operation connected with a large operation tree with 3 other constants and 3 additions.
-    >---C---+
-            ---A---+
-    >---C---+      |
-                   +---A
-    >---C---+      |
-            ---A---+
-    >---C---+
+    """Return a constant operation connected with a large operation tree with 3 other constants and 3 additions.
+    ---C---+
+           ---A---+
+    ---C---+      |
+                  +---A
+    ---C---+      |
+           ---A---+
+    ---C---+
     """
     add_oper = Addition()
     add_oper_2 = Addition()
diff --git a/test/fixtures/port.py b/test/fixtures/port.py
new file mode 100644
index 00000000..4019b3a2
--- /dev/null
+++ b/test/fixtures/port.py
@@ -0,0 +1,10 @@
+import pytest
+from b_asic.port import InputPort, OutputPort
+
+@pytest.fixture
+def input_port():
+    return InputPort(0, None)
+
+@pytest.fixture
+def output_port():
+    return OutputPort(0, None)
diff --git a/test/graph_id/test_graph_id_generator.py b/test/graph_id/test_graph_id_generator.py
deleted file mode 100644
index 85fb088d..00000000
--- a/test/graph_id/test_graph_id_generator.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""
-B-ASIC test suite for graph id generator.
-"""
-
-from b_asic.graph_id import GraphIDGenerator, GraphID
-
-import pytest
-
-@pytest.fixture
-def graph_id_generator():
-    return GraphIDGenerator()
-
-def test_empty_string_generator(graph_id_generator):
-    """Test the graph id generator for an empty string type."""
-    assert graph_id_generator.get_next_id("") == "1"
-    assert graph_id_generator.get_next_id("") == "2"
-
-
-def test_normal_string_generator(graph_id_generator):
-    """"Test the graph id generator for a normal string type."""
-    assert graph_id_generator.get_next_id("add") == "add1"
-    assert graph_id_generator.get_next_id("add") == "add2"
-
-def test_different_strings_generator(graph_id_generator):
-    """Test the graph id generator for different strings."""
-    assert graph_id_generator.get_next_id("sub") == "sub1"
-    assert graph_id_generator.get_next_id("mul") == "mul1"
-    assert graph_id_generator.get_next_id("sub") == "sub2"
-    assert graph_id_generator.get_next_id("mul") == "mul2"
diff --git a/test/port/test_outputport.py b/test/port/test_outputport.py
deleted file mode 100644
index ac50818e..00000000
--- a/test/port/test_outputport.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""
-B-ASIC test suite for OutputPort
-TODO: More info
-"""
-from b_asic import InputPort, OutputPort
-import pytest
-
-@pytest.fixture
-def inp_ports():
-    return [InputPort(_, None) for _ in range(0,3)]
-
-def test_connect_multiple_signals(inp_ports):
-    """Can multiple ports connect to an output port?"""
-    out_port = OutputPort(0, None)
-
-    for port in inp_ports:
-        out_port.connect(port)
-
-    assert out_port.signal_count() == len(inp_ports)
-
-def test_disconnect_multiple_signals(inp_ports):
-    """Can multiple signals disconnect from an output port?"""
-    out_port = OutputPort(0, None)
-
-    sigs = []
-
-    for port in inp_ports:
-        sigs.append(out_port.connect(port))
-
-    for sig in sigs:
-        out_port.remove_signal(sig)
-
-    assert out_port.signal_count() == 0
-
-def test_disconnect_mulitple_ports(inp_ports):
-    """Can multiple ports be disconnected from an output port?"""
-    out_port = OutputPort(0, None)
-
-    for port in inp_ports:
-        out_port.connect(port)
-
-    for port in inp_ports:
-        out_port.disconnect(port)
-
-    assert out_port.signal_count() == 3
-    assert len(out_port.connected_ports) == 0
\ No newline at end of file
diff --git a/test/signal_flow_graph/test_signal_flow_graph.py b/test/signal_flow_graph/test_signal_flow_graph.py
deleted file mode 100644
index d18d2da5..00000000
--- a/test/signal_flow_graph/test_signal_flow_graph.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from b_asic.signal_flow_graph import SFG
-from b_asic.core_operations import Addition, Constant
-from b_asic.signal import Signal
-from b_asic.signal_flow_graph import SFG
-
-import pytest
-
-def test_adding_to_sfg():
-    pass
-
diff --git a/test/core_operations/test_core_operations.py b/test/test_core_operations.py
similarity index 99%
rename from test/core_operations/test_core_operations.py
rename to test/test_core_operations.py
index 1d33bfe1..b176b2a6 100644
--- a/test/core_operations/test_core_operations.py
+++ b/test/test_core_operations.py
@@ -3,8 +3,6 @@ B-ASIC test suite for the core operations.
 """
 
 from b_asic.core_operations import Constant, Addition, Subtraction, Multiplication, Division, SquareRoot, ComplexConjugate, Max, Min, Absolute, ConstantMultiplication, ConstantAddition, ConstantSubtraction, ConstantDivision
-from b_asic.signal import Signal
-import pytest
 
 # Constant tests.
 def test_constant():
diff --git a/test/test_graph_id_generator.py b/test/test_graph_id_generator.py
new file mode 100644
index 00000000..b14597ea
--- /dev/null
+++ b/test/test_graph_id_generator.py
@@ -0,0 +1,28 @@
+"""
+B-ASIC test suite for graph id generator.
+"""
+
+from b_asic.graph_id import GraphIDGenerator, GraphID
+import pytest
+
+@pytest.fixture
+def graph_id_generator():
+    return GraphIDGenerator()
+
+class TestGetNextId:
+    def test_empty_string_generator(self, graph_id_generator):
+        """Test the graph id generator for an empty string type."""
+        assert graph_id_generator.get_next_id("") == "1"
+        assert graph_id_generator.get_next_id("") == "2"
+
+    def test_normal_string_generator(self, graph_id_generator):
+        """"Test the graph id generator for a normal string type."""
+        assert graph_id_generator.get_next_id("add") == "add1"
+        assert graph_id_generator.get_next_id("add") == "add2"
+
+    def test_different_strings_generator(self, graph_id_generator):
+        """Test the graph id generator for different strings."""
+        assert graph_id_generator.get_next_id("sub") == "sub1"
+        assert graph_id_generator.get_next_id("mul") == "mul1"
+        assert graph_id_generator.get_next_id("sub") == "sub2"
+        assert graph_id_generator.get_next_id("mul") == "mul2"
diff --git a/test/port/test_inputport.py b/test/test_inputport.py
similarity index 100%
rename from test/port/test_inputport.py
rename to test/test_inputport.py
diff --git a/test/test_operation.py b/test/test_operation.py
new file mode 100644
index 00000000..6c37e30b
--- /dev/null
+++ b/test/test_operation.py
@@ -0,0 +1,31 @@
+from b_asic.core_operations import Constant, Addition
+from b_asic.signal import Signal
+from b_asic.port import InputPort, OutputPort
+
+import pytest
+
+class TestTraverse:
+    def test_traverse_single_tree(self, operation):
+        """Traverse a tree consisting of one operation."""
+        constant = Constant(None)
+        assert list(constant.traverse()) == [constant]
+
+    def test_traverse_tree(self, operation_tree):
+        """Traverse a basic addition tree with two constants."""
+        assert len(list(operation_tree.traverse())) == 3
+
+    def test_traverse_large_tree(self, large_operation_tree):
+        """Traverse a larger tree."""
+        assert len(list(large_operation_tree.traverse())) == 7
+
+    def test_traverse_type(self, large_operation_tree):
+        traverse = list(large_operation_tree.traverse())
+        assert len(list(filter(lambda type_: isinstance(type_, Addition), traverse))) == 3
+        assert len(list(filter(lambda type_: isinstance(type_, Constant), traverse))) == 4
+
+    def test_traverse_loop(self, operation_tree):
+        add_oper_signal = Signal()
+        operation_tree._output_ports[0].add_signal(add_oper_signal)
+        operation_tree._input_ports[0].remove_signal(add_oper_signal)
+        operation_tree._input_ports[0].add_signal(add_oper_signal)
+        assert len(list(operation_tree.traverse())) == 2
diff --git a/test/test_outputport.py b/test/test_outputport.py
new file mode 100644
index 00000000..deed7a1e
--- /dev/null
+++ b/test/test_outputport.py
@@ -0,0 +1,80 @@
+"""
+B-ASIC test suite for OutputPort.
+"""
+from b_asic import OutputPort, InputPort, Signal
+import pytest
+
+@pytest.fixture
+def output_port():
+    return OutputPort(0, None)
+
+@pytest.fixture
+def input_port():
+    return InputPort(0, None)
+
+@pytest.fixture
+def list_of_input_ports():
+    return [InputPort(_, None) for _ in range(0,3)]
+
+class TestConnect:
+    def test_multiple_ports(self, output_port, list_of_input_ports):
+        """Can multiple ports connect to an output port?"""
+        for port in list_of_input_ports:
+            output_port.connect(port)
+
+        assert output_port.signal_count() == len(list_of_input_ports)
+
+    def test_same_port(self, output_port, list_of_input_ports):
+        """Check error handing."""
+        output_port.connect(list_of_input_ports[0])
+        with pytest.raises(AssertionError):
+            output_port.connect(list_of_input_ports[0])
+
+        assert output_port.signal_count() == 2
+
+class TestAddSignal:
+    def test_dangling(self, output_port):
+        s = Signal()
+        output_port.add_signal(s)
+
+        assert output_port.signal_count() == 1
+
+    def test_with_destination(self, output_port, input_port):
+        s = Signal(destination=input_port)
+        output_port.add_signal(s)
+
+        assert output_port.connected_ports == [s.destination]
+
+class TestDisconnect:
+    def test_multiple_ports(self, output_port, list_of_input_ports):
+        """Can multiple ports disconnect from OutputPort?"""
+        for port in list_of_input_ports:
+            output_port.connect(port)
+
+        for port in list_of_input_ports:
+            output_port.disconnect(port)
+
+        assert output_port.signal_count() == 3
+        assert output_port.connected_ports == []
+
+class TestRemoveSignal:
+    def test_one_signal(self, output_port, input_port):
+        s = output_port.connect(input_port)
+        output_port.remove_signal(s)
+
+        assert output_port.signal_count() == 0
+        assert output_port.signals == []
+        assert output_port.connected_ports == []
+
+    def test_multiple_signals(self, output_port, list_of_input_ports):
+        """Can multiple signals disconnect from OutputPort?"""
+        sigs = []
+
+        for port in list_of_input_ports:
+            sigs.append(output_port.connect(port))
+
+        for sig in sigs:
+            output_port.remove_signal(sig)
+
+        assert output_port.signal_count() == 0
+        assert output_port.signals == []
diff --git a/test/signal/test_signal.py b/test/test_signal.py
similarity index 100%
rename from test/signal/test_signal.py
rename to test/test_signal.py
diff --git a/test/traverse/test_traverse_tree.py b/test/traverse/test_traverse_tree.py
deleted file mode 100644
index 031aeec7..00000000
--- a/test/traverse/test_traverse_tree.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from b_asic.core_operations import Constant, Addition
-from b_asic.signal import Signal
-from b_asic.port import InputPort, OutputPort
-
-import pytest
-
-def test_traverse_single_tree(operation):
-    """Traverse a tree consisting of one operation."""
-    constant = Constant(None)
-    assert list(constant.traverse()) == [constant]
-
-def test_traverse_tree(operation_tree):
-    """Traverse a basic addition tree with two constants."""
-    assert len(list(operation_tree.traverse())) == 3
-
-def test_traverse_large_tree(large_operation_tree):
-    """Traverse a larger tree."""
-    assert len(list(large_operation_tree.traverse())) == 7
-
-def test_traverse_type(large_operation_tree):
-    traverse = list(large_operation_tree.traverse())
-    assert len(list(filter(lambda type_: isinstance(type_, Addition), traverse))) == 3
-    assert len(list(filter(lambda type_: isinstance(type_, Constant), traverse))) == 4
-
-def test_traverse_loop(operation_tree):
-    add_oper_signal = Signal()
-    operation_tree._output_ports[0].add_signal(add_oper_signal)
-    operation_tree._input_ports[0].remove_signal(add_oper_signal)
-    operation_tree._input_ports[0].add_signal(add_oper_signal)
-    assert len(list(operation_tree.traverse())) == 2
-- 
GitLab