Skip to content

Commit 8aca1c9

Browse files
authored
Merge pull request #8 from Driolar/master
New section 3.5: Visualizing a graph
2 parents 5ca8e24 + 4151fdb commit 8aca1c9

File tree

2 files changed

+214
-29
lines changed

2 files changed

+214
-29
lines changed

Chapters/Chapter3/chapter3.md

Lines changed: 214 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,232 @@
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 |
224
graph := AIGraphAlgorithm new.
325
graph nodes: #( $A $B $C ).
426
graph edges: #(
5-
#( $A $B )
6-
#( $A $C )
7-
#( $B $C ) )
27+
#( $A $B )
28+
#( $A $C )
29+
#( $B $C ) )
830
from: [:each | each first]
931
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 |
1149
graph := AIGraphAlgorithm new.
1250
graph nodes: #( $A $B $C ).
1351
graph edges: #(
14-
#( $A $B )
15-
#( $A $C )
16-
#( $B $C ) )
52+
#( $A $B )
53+
#( $A $C )
54+
#( $B $C ) )
1755
from: #first
1856
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 |
2066
graph := AIGraphAlgorithm new.
2167
graph nodes: #( $A $B $C ).
2268
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 ) )
2672
from: [:each | each first]
2773
to: [:each | each second]
2874
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.![Basic graph object-oriented representation: two collections of elements.](figures/architecture.pdf width=55&label=architecture)### 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+
![Basic graph object-oriented representation: two collections of elements.](figures/architecture.pdf width=55&label=architecture)
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
30101
AIBFSNode
31-
AIDisjointSetNode
32-
AINodeWithPrevious
33-
AiHitsNode
34-
AIWeightedHitsNode
102+
AIDisjointSetNode
103+
AINodeWithPrevious
104+
AiHitsNode
105+
AIWeightedHitsNode
35106
AIPathDistanceNode
36-
AIReducedGraphNode
37-
AITarjanNode```![Node hierarchy.](figures/hierarchy.pdf width=55&label=NodeHierarchy)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```![Algorithm hierarchy.](figures/hierarchyAlgo.pdf width=55&label=AlgoHierarchy)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+
![Node hierarchy.](figures/hierarchy.pdf width=55&label=NodeHierarchy)
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+
![Algorithm hierarchy.](figures/hierarchyAlgo.pdf width=55&label=AlgoHierarchy)
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+
![Visualized graph.](figures/modulegraph2.pdf width=55&label=moduleGraph2)
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.
35.9 KB
Binary file not shown.

0 commit comments

Comments
 (0)