From 273021dcef9c23610790e47a109fcbf476b829ec Mon Sep 17 00:00:00 2001
From: Hong-Phuc Bui <hong-phuc.bui@htwsaar.de>
Date: Sun, 25 Aug 2024 19:11:08 +0200
Subject: [PATCH] example about graph

---
 .gitignore                                      |    3 -
 num-int/src/numint/mainwindow.ui                |    2 
 stundenplan/src/pygraph/graphdemo.py            |   30 ++++++++++++---
 stundenplan/src/pygraph/shortestpath.py         |   28 ++++++++++++++
 stundenplan/tests/pygraph/shortestpath_tests.py |   26 +++++++++++++
 5 files changed, 79 insertions(+), 10 deletions(-)

diff --git a/.gitignore b/.gitignore
index 6f12ec8..e019710 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,4 @@
-sandbox*
-queens*
 *.venv*
 __pycache__/
 .idea/
-htw-python-kurs-2024-mmb/
 *.egg-info/
\ No newline at end of file
diff --git a/num-int/src/numint/mainwindow.ui b/num-int/src/numint/mainwindow.ui
index 8f1f47f..e8f8a18 100644
--- a/num-int/src/numint/mainwindow.ui
+++ b/num-int/src/numint/mainwindow.ui
@@ -11,7 +11,7 @@
    </rect>
   </property>
   <property name="windowTitle">
-   <string>Riemann Sum</string>
+   <string>Riemann Sums</string>
   </property>
   <widget class="QWidget" name="centralwidget">
    <layout class="QHBoxLayout" name="horizontalLayout_2">
diff --git a/stundenplan/src/pygraph/graphdemo.py b/stundenplan/src/pygraph/graphdemo.py
index c0e3df9..2a6cf7d 100644
--- a/stundenplan/src/pygraph/graphdemo.py
+++ b/stundenplan/src/pygraph/graphdemo.py
@@ -6,7 +6,7 @@
         self._adjacent: dict[int, set] = {}
         self._vertex_attribute: dict[int, dict] = {}
 
-    def add_vertex(self, vertex):
+    def add_vertex(self, vertex:int):
         if vertex not in self._adjacent:
             if not isinstance(vertex, int):
                 raise TypeError(f"Argument {vertex} is not a valid vertex")
@@ -14,7 +14,7 @@
             self._vertex_attribute[vertex] = {}
         pass
 
-    def add_edge(self, u, v):
+    def add_edge(self, u: int, v: int):
         self.add_vertex(u)
         self.add_vertex(v)
         v_neighbor = self._adjacent[v]
@@ -25,6 +25,11 @@
             u_neighbor.add(v)
         pass
 
+    def add_edges(self, u: int, adjacent:[int]):
+        for v in adjacent:
+            self.add_edge(u, v)
+        pass
+
     def merge_attribute(self, vertex: int, properties: dict):
         """
         merge a dict of attribute to a vertex. Usage example:
@@ -39,7 +44,7 @@
         """
         if vertex not in self._adjacent:
             raise ValueError(f"Graph does not include vertex {vertex}")
-        old_attributes = self._vertex_attribute.get[vertex]
+        old_attributes = self._vertex_attribute.get(vertex)
         self._vertex_attribute[vertex] = old_attributes | properties
         return self
         pass
@@ -52,20 +57,33 @@
         pass
 
     def vertices(self):
+        """
+        returns the set of vertices in not defined order.
+        :return:
+        """
         return self._adjacent.keys()
 
     def for_each_vertices(self, action: Callable[[int], Any]):
-        for v in self.vertices():
+        """
+        iterates over all vertices in this graph in ascending order.
+        :param action:
+        :return:
+        """
+        for v in sorted( self.vertices() ):
             action(v)
 
     def adjacent_of(self, vertex: int):
         if vertex not in self._adjacent:
             raise ValueError(f"Graph does not include vertex {vertex}")
-        return self._adjacent.get(vertex)
+        return sorted( self._adjacent.get(vertex) )
 
     def for_each_edges(self, action: Callable[[int, int], Any]):
+        """
+        iterates over all edges of the graph, executes the action on each edge.
+        :param action: a binary function, its parameters are start and end vertices of the visited edges
+        """
         visited_edges: dict[int, set] = {}
-        for start in self.vertices():
+        for start in sorted( self.vertices() ):
             for end in self.adjacent_of(start):
                 (first, second) = (start, end) if (start >= end) else (end, start)
             if first in visited_edges:
diff --git a/stundenplan/src/pygraph/shortestpath.py b/stundenplan/src/pygraph/shortestpath.py
new file mode 100644
index 0000000..4426f95
--- /dev/null
+++ b/stundenplan/src/pygraph/shortestpath.py
@@ -0,0 +1,28 @@
+import collections
+
+from pygraph.graphdemo import Graph
+
+
+def bfs(g: Graph, start: int) -> {int:int|None}:
+    """
+
+    :param g:
+    :param start:
+    :return:
+    """
+    tree = Graph()
+    queue = collections.deque()
+    VISITED = "visited"
+    not_visited = {VISITED: False}
+    visited = {VISITED: True}
+    g.for_each_vertices(lambda v : g.merge_attribute(v, not_visited))
+    queue.append(start)  # append() is the enqueue() operation of a Queue
+    g.merge_attribute(start, visited)
+    while queue:
+        u = queue.popleft()  # popleft() is the dequeue() operation of a Queue
+        for v in g.adjacent_of(u):
+            if not g.get_attribute(v)[VISITED]:
+                tree.add_edge(u, v)
+                g.merge_attribute(v, visited)
+                queue.append(v)
+    return tree
diff --git a/stundenplan/tests/pygraph/shortestpath_tests.py b/stundenplan/tests/pygraph/shortestpath_tests.py
new file mode 100644
index 0000000..6b9b2de
--- /dev/null
+++ b/stundenplan/tests/pygraph/shortestpath_tests.py
@@ -0,0 +1,26 @@
+import unittest
+
+from pygraph.graphdemo import Graph
+from pygraph.shortestpath import bfs
+
+
+class ShortestPathTestCase(unittest.TestCase):
+    def test_bfs(self):
+        g = Graph()
+        g.add_edges(0, [1, 2])
+        g.add_edges(1, [0, 3, 4])
+        g.add_edges(2, [0, 4, 6, 7])
+        g.add_edges(3, [1])
+        g.add_edges(4, [1, 2, 5])
+        g.add_edges(5, [4])
+        g.add_edges(6, [2])
+        g.add_edges(7, [2])
+        #print(g)
+        # self.assertEqual(True, False)  # add assertion here
+
+        tree = bfs(g, 0)
+        print(tree)
+
+
+if __name__ == '__main__':
+    unittest.main()

--
Gitblit v1.10.0-SNAPSHOT