|
1 | | -## Graph Representation@cha:repreSo far the graphs were represented as drawings. But, to program them we need a data structure.The most used data structures to represent graphs are:- An adjacency matrix where connection between nodes is basically a cross within a matrix whose entries are nodes. The cross can contain information when we want to manipulate weighted graphs.- An adjacency list where connection between nodes are given as a list of pairs of nodes or triples in case of weighted edges.Note that you can use an adjancency list to _define_ a graph and use internally a matrix to perform the computation.Basically the internal representation should be seen more as a design choice and it should not impactthe way we express algorithms. Providing a good API to manipulate graph will make the algorithm independent from the internal representationand let the developer implement optimizations when needed.### Graph descriptionIn this library we use the adjacency list data structure to specify a graph.For example, the following graph is created using the messages `nodes:` and `edges:from:to:`.It also defines that the edges are coming from the first element to the second one.```| graph | |
| 1 | +## Graph Representation |
| 2 | + |
| 3 | +@cha:repre |
| 4 | + |
| 5 | +So far the graphs were represented as drawings. But, to program them we need a data structure. |
| 6 | +The most used data structures to represent graphs are: |
| 7 | + |
| 8 | +- An adjacency matrix where connection between nodes is basically a cross within a matrix whose entries are nodes. The cross can contain information when we want to manipulate weighted graphs. |
| 9 | +- An adjacency list where connection between nodes are given as a list of pairs of nodes or triples in case of weighted edges. |
| 10 | + |
| 11 | +Note that you can use an adjancency list to _define_ a graph and use internally a matrix to perform the computation. |
| 12 | +Basically the internal representation should be seen more as a design choice and it should not impact |
| 13 | +the way we express algorithms. Providing a good API to manipulate graph will make the algorithm independent from the internal representation |
| 14 | +and let the developer implement optimizations when needed. |
| 15 | + |
| 16 | +### Graph description |
| 17 | + |
| 18 | +In this library we use the adjacency list data structure to specify a graph. |
| 19 | +For example, the following graph is created using the messages `nodes:` and `edges:from:to:`. |
| 20 | +It also defines that the edges are coming from the first element to the second one. |
| 21 | + |
| 22 | +``` |
| 23 | +| graph | |
2 | 24 | graph := AIGraphAlgorithm new. |
3 | 25 | graph nodes: #( $A $B $C ). |
4 | 26 | graph edges: #( |
5 | | - #( $A $B ) |
6 | | - #( $A $C ) |
7 | | - #( $B $C ) ) |
| 27 | + #( $A $B ) |
| 28 | + #( $A $C ) |
| 29 | + #( $B $C ) ) |
8 | 30 | from: [:each | each first] |
9 | 31 | to: [:each | each second]. |
10 | | -graph run.```The previous snippet is a template in the sense that:- First, we instantiate the graph algorithm \(in this case an abstract one\).- Then, we instantiate the nodes. And finally, we set the edges. Note that for the edges we need to pass a list. The elements inside a list can be any kind of object. In the above example the objects are also a list.- And then, we need to specific a block that is needed to obtain the `from:` and `to:` relationships. In the example, the _in node_ is the first element of the list and the second one is the _out node_. So, we need only to send the messages `first` and `second`.We will often define our graphs this way.#### Blocks and symbols.Since we are a bit lazy to type, we pass directly the method names as symbol that we want to apply on the edge to extract information.```| graph | |
| 32 | +graph run. |
| 33 | +``` |
| 34 | + |
| 35 | +The previous snippet is a template in the sense that: |
| 36 | + |
| 37 | +- First, we instantiate the graph algorithm \(in this case an abstract one\). |
| 38 | +- Then, we instantiate the nodes. And finally, we set the edges. Note that for the edges we need to pass a list. The elements inside a list can be any kind of object. In the above example the objects are also a list. |
| 39 | +- And then, we need to specific a block that is needed to obtain the `from:` and `to:` relationships. In the example, the _in node_ is the first element of the list and the second one is the _out node_. So, we need only to send the messages `first` and `second`. |
| 40 | + |
| 41 | +We will often define our graphs this way. |
| 42 | + |
| 43 | +#### Blocks and symbols |
| 44 | + |
| 45 | +Since we are a bit lazy to type, we pass directly the method names as symbol that we want to apply on the edge to extract information. |
| 46 | + |
| 47 | +``` |
| 48 | +| graph | |
11 | 49 | graph := AIGraphAlgorithm new. |
12 | 50 | graph nodes: #( $A $B $C ). |
13 | 51 | graph edges: #( |
14 | | - #( $A $B ) |
15 | | - #( $A $C ) |
16 | | - #( $B $C ) ) |
| 52 | + #( $A $B ) |
| 53 | + #( $A $C ) |
| 54 | + #( $B $C ) ) |
17 | 55 | from: #first |
18 | 56 | to: #second. |
19 | | -graph run.```#### Weighted graphsTo represent weighted graphs we use the `edges:from:to:weight:` method.```| graph | |
| 57 | +graph run. |
| 58 | +``` |
| 59 | + |
| 60 | +#### Weighted graphs |
| 61 | + |
| 62 | +To represent weighted graphs we use the `edges:from:to:weight:` method. |
| 63 | + |
| 64 | +``` |
| 65 | +| graph | |
20 | 66 | graph := AIGraphAlgorithm new. |
21 | 67 | graph nodes: #( $A $B $C ). |
22 | 68 | graph edges: #( |
23 | | - #( $A $B 2 ) |
24 | | - #( $A $C 4 ) |
25 | | - #( $B $C 7 ) ) |
| 69 | + #( $A $B 2 ) |
| 70 | + #( $A $C 4 ) |
| 71 | + #( $B $C 7 ) ) |
26 | 72 | from: [:each | each first] |
27 | 73 | to: [:each | each second] |
28 | 74 | weight: [:each | each third]. |
29 | | -graph run.```So in this case the library is going to take the third element as the weight for each of the edges.### Basic graph elementsAs shown by Fig. *@architecture@*, a graph is basically a collection of edges and a collection of nodes.The nodes are entities that can contain some specific value from the domain but also for the algorithm execution.This is why we get a rich hierarchy of nodes as shown below.### About nodesThe graph algorithms of this library use different nodes. All the nodes inherit from the same abstract class called `AIGraphNode`and show below where indentation represents inheritance \(Fig. *@NodeHierarchy@*\).% ClassHierarchyPrinter new% forClass: AIGraphNode;% doNotShowState;% doNotShowSuperclasses;% print```AIGraphNode |
| 75 | +graph run. |
| 76 | +``` |
| 77 | + |
| 78 | +So in this case the library is going to take the third element as the weight for each of the edges. |
| 79 | + |
| 80 | +### Basic graph elements |
| 81 | + |
| 82 | +As shown by Figure *@architecture@*, a graph is basically a collection of edges and a collection of nodes. |
| 83 | +The nodes are entities that can contain some specific value from the domain but also for the algorithm execution. |
| 84 | +This is why we get a rich hierarchy of nodes as shown below. |
| 85 | + |
| 86 | + |
| 87 | + |
| 88 | +### About nodes |
| 89 | + |
| 90 | +The graph algorithms of this library use different nodes. All the nodes inherit from the same abstract class called `AIGraphNode` |
| 91 | +and show below where indentation represents inheritance \(Figure *@NodeHierarchy@*\). |
| 92 | + |
| 93 | +% ClassHierarchyPrinter new |
| 94 | +% forClass: AIGraphNode; |
| 95 | +% doNotShowState; |
| 96 | +% doNotShowSuperclasses; |
| 97 | +% print |
| 98 | + |
| 99 | +``` |
| 100 | +AIGraphNode |
30 | 101 | AIBFSNode |
31 | | - AIDisjointSetNode |
32 | | - AINodeWithPrevious |
33 | | - AiHitsNode |
34 | | - AIWeightedHitsNode |
| 102 | + AIDisjointSetNode |
| 103 | + AINodeWithPrevious |
| 104 | + AiHitsNode |
| 105 | + AIWeightedHitsNode |
35 | 106 | AIPathDistanceNode |
36 | | - AIReducedGraphNode |
37 | | - AITarjanNode```We have different subclasses because a specific algorithm may need some store some special state.But all the nodes share a common API.The most important methods of the API are:- `node from:`- `node from:edge:`- `node adjacentNodes`- `node model`- `node model:`- `node to:`- `node to:edge:`### Graph algorithm inheritance treeAll of the algorithms are subclasses of `AIGraphAlgorithm` as shown below where indentation represents inheritance and shown in Fig. *@AlgoHierarchy@*.% ClassHierarchyPrinter new% forClass: AIGraphAlgorithm;% doNotShowState;% doNotShowSuperclasses;% print```AIGraphAlgorithm |
38 | | - AIBFS |
39 | | - AIBellmanFord |
40 | | - AIDijkstra |
41 | | - AIGraphReducer |
42 | | - AIHits |
43 | | - AIWeightedHits |
44 | | - AIKruskal |
45 | | - AIShortestPathInDAG |
46 | | - AITarjan |
47 | | - AITopologicalSorting```As the nodes, all the graph algorithms of this library share a common API also.The class `AIGraphAlgorithm` provides the common API to add nodes, edges, searching the nodes, etc.Some of the methods of the API are:- `algorithm nodes:`- `algorithm nodes`- `algorithm edges`- `algorithm edges:from:to:`- `algorithm edges:from:to:weight:`- `algorithm findNode:`- `algorithm run`% ClassHierarchyPrinter new% forClass: AIGraphAlgorithm;% doNotShowState;% doNotShowSuperclasses;% print### ConclusionAfter this basic introduction we are ready to present and implement the algorithms. |
| 107 | + AIReducedGraphNode |
| 108 | + AITarjanNode |
| 109 | +``` |
| 110 | + |
| 111 | + |
| 112 | + |
| 113 | +We have different subclasses because a specific algorithm may need some store some special state. |
| 114 | +But all the nodes share a common API. |
| 115 | + |
| 116 | +The most important methods of the API are: |
| 117 | + |
| 118 | +- `node from:` |
| 119 | +- `node from:edge:` |
| 120 | +- `node adjacentNodes` |
| 121 | +- `node model` |
| 122 | +- `node model:` |
| 123 | +- `node to:` |
| 124 | +- `node to:edge:` |
| 125 | + |
| 126 | +### Graph algorithm inheritance tree |
| 127 | + |
| 128 | +All of the algorithms are subclasses of `AIGraphAlgorithm` as shown below where indentation represents inheritance and shown in Figure *@AlgoHierarchy@*. |
| 129 | + |
| 130 | +% ClassHierarchyPrinter new |
| 131 | +% forClass: AIGraphAlgorithm; |
| 132 | +% doNotShowState; |
| 133 | +% doNotShowSuperclasses; |
| 134 | +% print |
| 135 | + |
| 136 | +``` |
| 137 | +AIGraphAlgorithm |
| 138 | + AIBFS |
| 139 | + AIBellmanFord |
| 140 | + AIDijkstra |
| 141 | + AIGraphReducer |
| 142 | + AIHits |
| 143 | + AIWeightedHits |
| 144 | + AIKruskal |
| 145 | + AIShortestPathInDAG |
| 146 | + AITarjan |
| 147 | + AITopologicalSorting |
| 148 | +``` |
| 149 | + |
| 150 | + |
| 151 | + |
| 152 | +As the nodes, all the graph algorithms of this library share a common API also. |
| 153 | +The class `AIGraphAlgorithm` provides the common API to add nodes, edges, searching the nodes, etc. |
| 154 | + |
| 155 | +Some of the methods of the API are: |
| 156 | + |
| 157 | +- `algorithm nodes:` |
| 158 | +- `algorithm nodes` |
| 159 | +- `algorithm edges` |
| 160 | +- `algorithm edges:from:to:` |
| 161 | +- `algorithm edges:from:to:weight:` |
| 162 | +- `algorithm findNode:` |
| 163 | +- `algorithm run` |
| 164 | + |
| 165 | +% ClassHierarchyPrinter new |
| 166 | +% forClass: AIGraphAlgorithm; |
| 167 | +% doNotShowState; |
| 168 | +% doNotShowSuperclasses; |
| 169 | +% print |
| 170 | + |
| 171 | +### Visualizing a graph |
| 172 | + |
| 173 | +There is a feature to visualize a graph and by the way inspect each node or edge model. It is based on *Roassal*, Pharo's visualization engine. |
| 174 | + |
| 175 | +For example, to visualize the graph fixture structure answered by `AINonWeightedDAGFixture new moduleGraph2`, we inspect the answer to see Figure *@moduleGraph2@*. |
| 176 | + |
| 177 | + |
| 178 | + |
| 179 | +The method `moduleGraph2` answers an instance of `AIGraphNonWeightedFixtureStructure` as we see here: |
| 180 | + |
| 181 | +``` |
| 182 | +AINonWeightedDAGFixture >> moduleGraph2 |
| 183 | +| nodes edges graph | |
| 184 | +nodes := #( $u $w $v $z $a $b $c $d ). |
| 185 | +edges := #( #( $u $w ) #( $w $a ) #( $w $b ) #( $w $c ) #( $w $d ) |
| 186 | + #( $w $v ) #( $v $b ) #( $v $d ) #( $v $z ) #( $z $b ) |
| 187 | + #( $a $d ) #( $c $v ) #( $c $z ) #( $d $b ) #( $d $z ) ). |
| 188 | +graph:= AIGraphNonWeightedFixtureStructure new. |
| 189 | +
|
| 190 | +graph nodes: nodes. |
| 191 | +graph edges: edges. |
| 192 | +^graph |
| 193 | +``` |
| 194 | + |
| 195 | +`AIGraphNonWeightedFixtureStructure` and `AIGraphWeightedFixtureStructure` are both subclasses of `AIGraphTestFixtureStructure`. |
| 196 | + |
| 197 | +Besides viewing the graph, we can interact with it. Thanks to Roassal's canvas controller, we can among other things: |
| 198 | + |
| 199 | +- inspect the model of a selected node or edge |
| 200 | + |
| 201 | +- rearrange the position of the nodes |
| 202 | + |
| 203 | +- zoom in and out. |
| 204 | + |
| 205 | +By the *Help* option at the top right, we can popup the shortcuts list. |
| 206 | + |
| 207 | +Of course, we can also use a subclass of `AIGraphTestFixtureStructure` to visualize the graph contained in a `AIGraphAlgorithm`. For example, doing the following code shows the inspector visualizing the graph defined in the Kruskal's algorithm instance: |
| 208 | + |
| 209 | +``` |
| 210 | +| nodes edges structure kruskal | |
| 211 | + nodes := #( $s $a $b $t ). |
| 212 | + edges := #( #( $s $a 10 ) #( $s $b 8 ) #( $a $b 5 ) #( $a $t 10 ) #( $b $t 10 ) ). |
| 213 | +
|
| 214 | +kruskal := AIKruskal new. |
| 215 | +kruskal nodes: nodes. |
| 216 | +kruskal |
| 217 | + edges: edges |
| 218 | + from: [ :each | each first ] |
| 219 | + to: [ :each | each second ] |
| 220 | + weight: [ :each | each third ]. |
| 221 | +
|
| 222 | +structure:= AIGraphWeightedFixtureStructure new. |
| 223 | +structure edges: (kruskal edges collect: [:edge | edge asTuple ]). |
| 224 | +structure nodes: (kruskal nodes collect: [:node | node model ]). |
| 225 | +structure inspect |
| 226 | +``` |
| 227 | + |
| 228 | +Note that we can also use `AIGraphNonWeightedFixtureStructure` for a weighted graph in case we do not want the edge weights to be drawn. |
| 229 | + |
| 230 | +### Conclusion |
| 231 | + |
| 232 | +After this basic introduction we are ready to present and implement the algorithms. |
0 commit comments