wolffd@0
|
1
|
wolffd@0
|
2 % GENERAL GRAPH THEORY CLASS: DiGraph
|
wolffd@0
|
3 %
|
wolffd@0
|
4 % CAVE: Any special application oriented stuff
|
wolffd@0
|
5 % should be outside here
|
wolffd@0
|
6
|
wolffd@0
|
7 classdef DiGraph < handle
|
wolffd@0
|
8
|
wolffd@0
|
9 properties
|
wolffd@0
|
10
|
wolffd@0
|
11 V = sparse(0,1); % boolean for nodes
|
wolffd@0
|
12 Vlbl = {};
|
wolffd@0
|
13
|
wolffd@0
|
14 E = sparse(0,0); % sparse weighted edge graph
|
wolffd@0
|
15 end
|
wolffd@0
|
16
|
wolffd@0
|
17 methods
|
wolffd@0
|
18
|
wolffd@0
|
19 % ---
|
wolffd@0
|
20 % Constructor
|
wolffd@0
|
21 % ---
|
wolffd@0
|
22 function G = DiGraph(V, E, Vlbl)
|
wolffd@0
|
23
|
wolffd@0
|
24 if nargin == 1
|
wolffd@0
|
25 if ~isnumeric(V)
|
wolffd@0
|
26
|
wolffd@0
|
27 G = DiGraph.copy(V);
|
wolffd@0
|
28 else
|
wolffd@0
|
29 % build graph from Edge Adjacency Matrix
|
wolffd@0
|
30 G.V = sparse(find(sum(V + V')) > 0);
|
wolffd@0
|
31 G.E = V;
|
wolffd@0
|
32 end
|
wolffd@0
|
33 end
|
wolffd@0
|
34
|
wolffd@0
|
35 if nargin >= 2
|
wolffd@0
|
36 G = DiGraph();
|
wolffd@0
|
37
|
wolffd@0
|
38 if numel(V) ~= max(size(E))
|
wolffd@0
|
39 error 'wrong dimensions';
|
wolffd@0
|
40 end
|
wolffd@0
|
41
|
wolffd@0
|
42 % ---
|
wolffd@0
|
43 % We copy existent nodes and edges
|
wolffd@0
|
44 % ---
|
wolffd@0
|
45 if numel(V) == size(V,2)
|
wolffd@0
|
46 G.V = V;
|
wolffd@0
|
47 else
|
wolffd@0
|
48 G.V = V';
|
wolffd@0
|
49 end
|
wolffd@0
|
50 G.E = E;
|
wolffd@0
|
51
|
wolffd@0
|
52 % ---
|
wolffd@0
|
53 % We copy the labels for existent nodes,
|
wolffd@0
|
54 % if supplied.
|
wolffd@0
|
55 % NOTE: the FIND call is neccessary, as logical indexing
|
wolffd@0
|
56 % does not work in this case
|
wolffd@0
|
57 % TODO: WHY NOT?
|
wolffd@0
|
58 % ---
|
wolffd@0
|
59 if (nargin == 3) && ~isempty(Vlbl)
|
wolffd@0
|
60 G.Vlbl = cell(numel(G.V),1);
|
wolffd@0
|
61 G.Vlbl(find(G.V)) = Vlbl(find(G.V));
|
wolffd@0
|
62 end
|
wolffd@0
|
63
|
wolffd@0
|
64 end
|
wolffd@0
|
65 end
|
wolffd@0
|
66
|
wolffd@0
|
67 % get list of node ids
|
wolffd@0
|
68 function out = nodes(G)
|
wolffd@0
|
69 out = find(G.V);
|
wolffd@0
|
70 end
|
wolffd@0
|
71
|
wolffd@0
|
72 % get list of node ids
|
wolffd@0
|
73 function out = last_node(G)
|
wolffd@0
|
74 out = find(G.V,1,'last');
|
wolffd@0
|
75 end
|
wolffd@0
|
76
|
wolffd@0
|
77 function w = edge(G, V1, V2)
|
wolffd@0
|
78
|
wolffd@0
|
79 w = G.E(V1, V2);
|
wolffd@0
|
80 end
|
wolffd@0
|
81
|
wolffd@0
|
82 % get edge list representation
|
wolffd@0
|
83 function [val, V1, V2] = edges(G, V1)
|
wolffd@0
|
84
|
wolffd@0
|
85 if nargin == 1
|
wolffd@0
|
86
|
wolffd@0
|
87 % get all edges of the graph
|
wolffd@0
|
88 [V1, V2] = find(G.E);
|
wolffd@0
|
89
|
wolffd@0
|
90 val = zeros(numel(V1), 1);
|
wolffd@0
|
91
|
wolffd@0
|
92 for i = 1:numel(V1)
|
wolffd@0
|
93 val(i) = G.E(V1(i), V2(i));
|
wolffd@0
|
94 end
|
wolffd@0
|
95
|
wolffd@0
|
96 elseif nargin == 2
|
wolffd@0
|
97
|
wolffd@0
|
98 % get all edges coming from or entering this node
|
wolffd@0
|
99 V2 = find(G.E(V1, :));
|
wolffd@0
|
100 val = G.E(V1, V2);
|
wolffd@0
|
101
|
wolffd@0
|
102 V2 = [ V2 find(G.E(:,V1))'];
|
wolffd@0
|
103 val = [ val G.E(V2, V1)'];
|
wolffd@0
|
104 end
|
wolffd@0
|
105 end
|
wolffd@0
|
106
|
wolffd@0
|
107 % compare two graphs
|
wolffd@0
|
108 function out = eq(a,b)
|
wolffd@0
|
109
|
wolffd@0
|
110 % ---
|
wolffd@0
|
111 % compare graph stats
|
wolffd@0
|
112 % necessary
|
wolffd@0
|
113 % ---
|
wolffd@0
|
114 if a.order ~= b.order
|
wolffd@0
|
115 out = false;
|
wolffd@0
|
116 cprint(3, 'eq: graph node numbers do not match');
|
wolffd@0
|
117 return
|
wolffd@0
|
118 end
|
wolffd@0
|
119
|
wolffd@0
|
120 if a.num_edges ~= b.num_edges
|
wolffd@0
|
121 out = false;
|
wolffd@0
|
122 cprint(3, 'eq: graph edge numbers do not match');
|
wolffd@0
|
123 return
|
wolffd@0
|
124 end
|
wolffd@0
|
125
|
wolffd@0
|
126
|
wolffd@0
|
127 % ---
|
wolffd@0
|
128 % compare labels and reindex graph b if
|
wolffd@0
|
129 % necessary
|
wolffd@0
|
130 % ---
|
wolffd@0
|
131 lbla = a.label();
|
wolffd@0
|
132 lblb = b.label();
|
wolffd@0
|
133
|
wolffd@0
|
134 for i = 1:numel(lbla)
|
wolffd@0
|
135 if ~isempty(lbla{i})
|
wolffd@0
|
136 tmpidx = strcellfind(lblb, lbla{i});
|
wolffd@0
|
137
|
wolffd@0
|
138 % check for substring problems
|
wolffd@0
|
139 for j = 1:numel(tmpidx)
|
wolffd@0
|
140 if strcmp(lblb{tmpidx(j)}, lbla{i})
|
wolffd@0
|
141 idx(i) = tmpidx(j);
|
wolffd@0
|
142 break;
|
wolffd@0
|
143 end
|
wolffd@0
|
144 end
|
wolffd@0
|
145 end
|
wolffd@0
|
146 end
|
wolffd@0
|
147
|
wolffd@0
|
148 % test on found labels
|
wolffd@0
|
149 num_matching_lbl = sum(idx > 0);
|
wolffd@0
|
150 if ~isempty(lbla) && (num_matching_lbl < a.order)
|
wolffd@0
|
151 out = false;
|
wolffd@0
|
152 cprint(3, 'eq: graph labels do not match');
|
wolffd@0
|
153 return
|
wolffd@0
|
154 end
|
wolffd@0
|
155
|
wolffd@0
|
156 % ---
|
wolffd@0
|
157 % reindex edges and nodes: only replace valid indexes
|
wolffd@0
|
158 % ---
|
wolffd@0
|
159 val_idx = idx > 0;
|
wolffd@0
|
160 idx = idx(val_idx);
|
wolffd@0
|
161
|
wolffd@0
|
162 bE(val_idx,val_idx) = b.E(idx,idx);
|
wolffd@0
|
163 bV(val_idx) = b.V(idx);
|
wolffd@0
|
164
|
wolffd@0
|
165 if sum(a.V ~= bV) > 0
|
wolffd@0
|
166 out = false;
|
wolffd@0
|
167 cprint(3, 'eq: nodes do not match');
|
wolffd@0
|
168 return
|
wolffd@0
|
169 end
|
wolffd@0
|
170
|
wolffd@0
|
171 if sum(sum(a.E ~= bE)) > 0
|
wolffd@0
|
172 out = false;
|
wolffd@0
|
173 cprint(3, 'eq: edges do not match');
|
wolffd@0
|
174 return
|
wolffd@0
|
175 end
|
wolffd@0
|
176
|
wolffd@0
|
177 % ---
|
wolffd@0
|
178 % OK, seems these Graphs are the same!!!
|
wolffd@0
|
179 % ---
|
wolffd@0
|
180 out = true;
|
wolffd@0
|
181
|
wolffd@0
|
182 end
|
wolffd@0
|
183
|
wolffd@0
|
184 % find node via its label
|
wolffd@0
|
185 function Vid = node(G, label)
|
wolffd@0
|
186 lbl = G.label();
|
wolffd@0
|
187
|
wolffd@0
|
188 Vid = strcellfind(lbl, label,1);
|
wolffd@0
|
189 end
|
wolffd@0
|
190
|
wolffd@0
|
191 % add a node
|
wolffd@0
|
192 function add_node(G,V)
|
wolffd@0
|
193
|
wolffd@0
|
194 % set node indicator
|
wolffd@0
|
195 G.V(V) = 1;
|
wolffd@0
|
196
|
wolffd@0
|
197 G.E(V,V) = 0;
|
wolffd@0
|
198 end
|
wolffd@0
|
199
|
wolffd@0
|
200 % remove a node
|
wolffd@0
|
201 function remove_node(G,V)
|
wolffd@0
|
202
|
wolffd@0
|
203 % remove node
|
wolffd@0
|
204 G.V(V) = 0;
|
wolffd@0
|
205
|
wolffd@0
|
206 % remove all edges
|
wolffd@0
|
207 G.E(V,:) = 0;
|
wolffd@0
|
208 G.E(:,V) = 0;
|
wolffd@0
|
209 end
|
wolffd@0
|
210
|
wolffd@0
|
211 % remove an edge
|
wolffd@0
|
212 function remove_edge(G, V1, V2)
|
wolffd@0
|
213
|
wolffd@0
|
214 G.E(V1, V2) = 0;
|
wolffd@0
|
215 end
|
wolffd@0
|
216
|
wolffd@0
|
217 % add an edge
|
wolffd@0
|
218 function add_edge(G, V1, V2, weight)
|
wolffd@0
|
219
|
wolffd@0
|
220 G.add_node(V1);
|
wolffd@0
|
221 G.add_node(V2);
|
wolffd@0
|
222
|
wolffd@0
|
223 if G.E(V1,V2) == 0
|
wolffd@0
|
224
|
wolffd@0
|
225 % save weight
|
wolffd@0
|
226 set_edge(G, V1, V2, weight);
|
wolffd@0
|
227 else
|
wolffd@0
|
228
|
wolffd@0
|
229 join_edge(G, V1, V2, weight)
|
wolffd@0
|
230 end
|
wolffd@0
|
231 end
|
wolffd@0
|
232
|
wolffd@0
|
233 % ---
|
wolffd@0
|
234 % implementation of edge joining,
|
wolffd@0
|
235 % to be overwritten for several
|
wolffd@0
|
236 % purposes
|
wolffd@0
|
237 % ---
|
wolffd@0
|
238 function join_edge(G, V1, V2, weight)
|
wolffd@0
|
239
|
wolffd@0
|
240 set_edge(G, V1, V2, G.edge(V1, V2) + weight);
|
wolffd@0
|
241 end
|
wolffd@0
|
242
|
wolffd@0
|
243 % ---
|
wolffd@0
|
244 % sets the edge without considering earlier weights
|
wolffd@0
|
245 % ---
|
wolffd@0
|
246 function set_edge(G, V1, V2, weight)
|
wolffd@0
|
247
|
wolffd@0
|
248 G.E(V1, V2) = weight;
|
wolffd@0
|
249 end
|
wolffd@0
|
250
|
wolffd@0
|
251 % ---
|
wolffd@0
|
252 % Graph-specific functions
|
wolffd@0
|
253 % ---
|
wolffd@0
|
254 function c = children(G, Vid)
|
wolffd@0
|
255
|
wolffd@0
|
256 c = find(G.E(Vid,:) > 0);
|
wolffd@0
|
257 end
|
wolffd@0
|
258
|
wolffd@0
|
259 function p = parents(G, Vid)
|
wolffd@0
|
260
|
wolffd@0
|
261 p = find(G.E(:,Vid) > 0)';
|
wolffd@0
|
262 end
|
wolffd@0
|
263
|
wolffd@0
|
264 % ---
|
wolffd@0
|
265 % Vertex Degree
|
wolffd@0
|
266 % ---
|
wolffd@0
|
267 function out = degree(G, V)
|
wolffd@0
|
268
|
wolffd@0
|
269 out = sum(G.E(V,:) > 0) + sum(G.E(:,V) > 0);
|
wolffd@0
|
270 end
|
wolffd@0
|
271
|
wolffd@0
|
272 % ---
|
wolffd@0
|
273 % Vertex Degree
|
wolffd@0
|
274 % ---
|
wolffd@0
|
275 function out = degree_in(G, V)
|
wolffd@0
|
276
|
wolffd@0
|
277 out = sum(G.E(V,:) > 0);
|
wolffd@0
|
278 end
|
wolffd@0
|
279
|
wolffd@0
|
280 % ---
|
wolffd@0
|
281 % Max Degree In
|
wolffd@0
|
282 % ---
|
wolffd@0
|
283 function out = max_degree_in(G)
|
wolffd@0
|
284
|
wolffd@0
|
285 out = max(sum(G.E > 0, 2));
|
wolffd@0
|
286 end
|
wolffd@0
|
287
|
wolffd@0
|
288 % ---
|
wolffd@0
|
289 % Vertex Degree
|
wolffd@0
|
290 % ---
|
wolffd@0
|
291 function out = degree_out(G, V)
|
wolffd@0
|
292
|
wolffd@0
|
293 out = sum(G.E(:,V) > 0);
|
wolffd@0
|
294 end
|
wolffd@0
|
295
|
wolffd@0
|
296 % ---
|
wolffd@0
|
297 % Max Degree In
|
wolffd@0
|
298 % ---
|
wolffd@0
|
299 function out = max_degree_out(G)
|
wolffd@0
|
300
|
wolffd@0
|
301 out = max(sum(G.E > 0, 1));
|
wolffd@0
|
302 end
|
wolffd@0
|
303
|
wolffd@0
|
304
|
wolffd@0
|
305 % ---
|
wolffd@0
|
306 % Max Degree
|
wolffd@0
|
307 % ---
|
wolffd@0
|
308 function out = max_degree(G)
|
wolffd@0
|
309
|
wolffd@0
|
310 out = max(sum(G.E > 0, 1) + sum(G.E > 0, 2)');
|
wolffd@0
|
311 end
|
wolffd@0
|
312
|
wolffd@0
|
313 % ---
|
wolffd@0
|
314 % Max weight
|
wolffd@0
|
315 % ---
|
wolffd@0
|
316 function out = max_weight(G)
|
wolffd@0
|
317
|
wolffd@0
|
318 out = max(max(G.E));
|
wolffd@0
|
319 end
|
wolffd@0
|
320
|
wolffd@0
|
321 % ---
|
wolffd@0
|
322 % Number of Vertices in Graph
|
wolffd@0
|
323 % ---
|
wolffd@0
|
324 function out = order(G)
|
wolffd@0
|
325 out = sum(G.V);
|
wolffd@0
|
326 end
|
wolffd@0
|
327
|
wolffd@0
|
328 % ---
|
wolffd@0
|
329 % Number of Edges in Graph
|
wolffd@0
|
330 % ---
|
wolffd@0
|
331 function out = num_edges(G)
|
wolffd@0
|
332 out = sum(sum(G.E ~= 0));
|
wolffd@0
|
333 end
|
wolffd@0
|
334
|
wolffd@0
|
335 % ---
|
wolffd@0
|
336 % Number of Vertices in Graph
|
wolffd@0
|
337 % ---
|
wolffd@0
|
338 function out = cardinality(G)
|
wolffd@0
|
339 out = order(G);
|
wolffd@0
|
340 end
|
wolffd@0
|
341
|
wolffd@0
|
342 function Gs = unconnected_subgraph(G)
|
wolffd@0
|
343 Vtmp = (sum(G.E + G.E') == 0);
|
wolffd@0
|
344 Etmp = sparse(size(G.E, 1), size(G.E, 1));
|
wolffd@0
|
345
|
wolffd@0
|
346 Gs = DiGraph(Vtmp, Etmp, G.label());
|
wolffd@0
|
347 end
|
wolffd@0
|
348
|
wolffd@0
|
349 % ---
|
wolffd@0
|
350 % return string labels for a (set of) node(S)
|
wolffd@0
|
351 % ---
|
wolffd@0
|
352 function out = label(G, Vid)
|
wolffd@0
|
353
|
wolffd@0
|
354 out = {};
|
wolffd@0
|
355 if nargin == 1
|
wolffd@0
|
356 % maybe much faster for whole graph
|
wolffd@0
|
357 if (numel(G.Vlbl) < G.order() || isempty(G.Vlbl))
|
wolffd@0
|
358
|
wolffd@0
|
359 out = cell(numel(G.V), 1);
|
wolffd@0
|
360 for i = 1:numel(Vid)
|
wolffd@0
|
361 out{i} = sprintf('%d', Vid(i));
|
wolffd@0
|
362 end
|
wolffd@0
|
363 else
|
wolffd@0
|
364 out = G.Vlbl;
|
wolffd@0
|
365 end
|
wolffd@0
|
366
|
wolffd@0
|
367 elseif nargin == 2
|
wolffd@0
|
368 for i = 1:numel(Vid)
|
wolffd@0
|
369
|
wolffd@0
|
370 if (numel(G.Vlbl) < Vid(i)) || isempty(G.Vlbl{Vid(i)})
|
wolffd@0
|
371
|
wolffd@0
|
372 if numel(Vid) > 1
|
wolffd@0
|
373 out{i} = sprintf('%d', Vid(i));
|
wolffd@0
|
374 else
|
wolffd@0
|
375 out = sprintf('%d', Vid(i));
|
wolffd@0
|
376 end
|
wolffd@0
|
377 else
|
wolffd@0
|
378 if numel(Vid) > 1
|
wolffd@0
|
379 out{i} = G.Vlbl{Vid(i)};
|
wolffd@0
|
380 else
|
wolffd@0
|
381 out = G.Vlbl{Vid(i)};
|
wolffd@0
|
382 end
|
wolffd@0
|
383 end
|
wolffd@0
|
384 end
|
wolffd@0
|
385 end
|
wolffd@0
|
386 end
|
wolffd@0
|
387
|
wolffd@0
|
388
|
wolffd@0
|
389 % -----------------------------------------------------------------
|
wolffd@0
|
390 % Graph theory algorithms
|
wolffd@0
|
391 % ---
|
wolffd@0
|
392
|
wolffd@0
|
393
|
wolffd@0
|
394 % ---
|
wolffd@0
|
395 % sets all the main diagonal edges to zero
|
wolffd@0
|
396 % ---
|
wolffd@0
|
397 function remove_cycles_length1(G)
|
wolffd@0
|
398
|
wolffd@0
|
399 for i = G.nodes();
|
wolffd@0
|
400 G.E(i,i) = 0;
|
wolffd@0
|
401 end
|
wolffd@0
|
402 end
|
wolffd@0
|
403
|
wolffd@0
|
404
|
wolffd@0
|
405 % ---
|
wolffd@0
|
406 % Returns whether a given graph has cycles or not,
|
wolffd@0
|
407 % using the SCC search
|
wolffd@0
|
408 % ---
|
wolffd@0
|
409 function out = isAcyclic(G)
|
wolffd@0
|
410 % ---
|
wolffd@0
|
411 % We get all the sccs in the DiGraph, and
|
wolffd@0
|
412 % return true if none of the sccs has more than
|
wolffd@0
|
413 % one node
|
wolffd@0
|
414 % ---
|
wolffd@0
|
415
|
wolffd@0
|
416 % check for self-loops
|
wolffd@0
|
417 if sum(abs(diag(G.E))) > 0
|
wolffd@0
|
418 out = 0;
|
wolffd@0
|
419 return;
|
wolffd@0
|
420 end
|
wolffd@0
|
421
|
wolffd@0
|
422 [~, s, ~] = strongly_connected_components(G);
|
wolffd@0
|
423
|
wolffd@0
|
424 if max(s) < 2
|
wolffd@0
|
425 out = 1;
|
wolffd@0
|
426 else
|
wolffd@0
|
427 out = 0;
|
wolffd@0
|
428 end
|
wolffd@0
|
429 end
|
wolffd@0
|
430
|
wolffd@0
|
431 % ---
|
wolffd@0
|
432 % All SCC's ordered by number of nodes in graph
|
wolffd@0
|
433 %
|
wolffd@0
|
434 % this should also be able to return
|
wolffd@0
|
435 % the SCC of just one node
|
wolffd@0
|
436 % ---
|
wolffd@0
|
437 function [Gs, s, id] = strongly_connected_components(G, Vin)
|
wolffd@0
|
438
|
wolffd@0
|
439 % marking
|
wolffd@0
|
440 marked = zeros(size(G.V));
|
wolffd@0
|
441
|
wolffd@0
|
442 % ---
|
wolffd@0
|
443 % two stacks,
|
wolffd@0
|
444 % one: has not been assigned to scc
|
wolffd@0
|
445 % two: unclear if in same scc
|
wolffd@0
|
446 % ---
|
wolffd@0
|
447 stack1 = CStack();
|
wolffd@0
|
448 stack2 = CStack();
|
wolffd@0
|
449
|
wolffd@0
|
450 % initialise scc ids
|
wolffd@0
|
451 id = zeros(size(G.V)) - 1; % assigned scc?
|
wolffd@0
|
452 idctr = 1;
|
wolffd@0
|
453
|
wolffd@0
|
454 % initialise graph ordering
|
wolffd@0
|
455 preorder = zeros(G.order, 1);
|
wolffd@0
|
456 prectr = 0;
|
wolffd@0
|
457
|
wolffd@0
|
458 for v = G.nodes();
|
wolffd@0
|
459 if ~marked(v)
|
wolffd@0
|
460 dfs(G, v);
|
wolffd@0
|
461 end
|
wolffd@0
|
462 end
|
wolffd@0
|
463
|
wolffd@0
|
464 % ---
|
wolffd@0
|
465 % create subgraphs (DiGraph here) for sccs
|
wolffd@0
|
466 % ---
|
wolffd@0
|
467 if nargin == 1
|
wolffd@0
|
468
|
wolffd@0
|
469 s = zeros(idctr-1,1);
|
wolffd@0
|
470 for idctr2 = 1:idctr-1
|
wolffd@0
|
471 Vtmp = (id == idctr2);
|
wolffd@0
|
472 Emask = sparse(size(G.E, 1), size(G.E, 1));
|
wolffd@0
|
473 Emask(Vtmp,Vtmp) = 1;
|
wolffd@0
|
474 Etmp = G.E .* Emask;
|
wolffd@0
|
475
|
wolffd@0
|
476 Gs(idctr2) = DiGraph(Vtmp, Etmp, G.Vlbl);
|
wolffd@0
|
477 s(idctr2) = Gs(idctr2).order();
|
wolffd@0
|
478 end
|
wolffd@0
|
479
|
wolffd@0
|
480 % ---
|
wolffd@0
|
481 % order by number of nodes
|
wolffd@0
|
482 % ---
|
wolffd@0
|
483 [s, idx] = sort(s,'descend');
|
wolffd@0
|
484 Gs = Gs(idx);
|
wolffd@0
|
485 Gmax = Gs(1);
|
wolffd@0
|
486
|
wolffd@0
|
487 else
|
wolffd@0
|
488 % ---
|
wolffd@0
|
489 % get just the scc for the questioned node
|
wolffd@0
|
490 % ---
|
wolffd@0
|
491 Vtmp = (id == id(Vin));
|
wolffd@0
|
492 Emask = sparse(size(G.E, 1), size(G.E, 1));
|
wolffd@0
|
493 Emask(Vtmp,Vtmp) = 1;
|
wolffd@0
|
494 Etmp = G.E .* Emask;
|
wolffd@0
|
495
|
wolffd@0
|
496 Gs = DiGraph(Vtmp, Etmp);
|
wolffd@0
|
497 s = Gs.order();
|
wolffd@0
|
498 end
|
wolffd@0
|
499
|
wolffd@0
|
500 % ---
|
wolffd@0
|
501 % NOTE: THIS IS A NESTED DFS BASED GRAPH ORDERING
|
wolffd@0
|
502 % ---
|
wolffd@0
|
503 function dfs(G, v)
|
wolffd@0
|
504
|
wolffd@0
|
505 % mark this node as visited
|
wolffd@0
|
506 marked(v) = 1;
|
wolffd@0
|
507
|
wolffd@0
|
508 preorder(v) = prectr;
|
wolffd@0
|
509 prectr = prectr + 1;
|
wolffd@0
|
510
|
wolffd@0
|
511 % push into both stacks
|
wolffd@0
|
512 stack1.push(v);
|
wolffd@0
|
513 stack2.push(v);
|
wolffd@0
|
514
|
wolffd@0
|
515 % ---
|
wolffd@0
|
516 % dfs
|
wolffd@0
|
517 % ---
|
wolffd@0
|
518 for w = G.children(v)
|
wolffd@0
|
519 if ~marked(w)
|
wolffd@0
|
520 % ---
|
wolffd@0
|
521 % traverse into dfs if not yet visited
|
wolffd@0
|
522 % ---
|
wolffd@0
|
523 dfs(G, w);
|
wolffd@0
|
524
|
wolffd@0
|
525 elseif id(w) == -1
|
wolffd@0
|
526
|
wolffd@0
|
527 % ---
|
wolffd@0
|
528 % w has not yet been assigned to a strongly connected
|
wolffd@0
|
529 % component
|
wolffd@0
|
530 % ---
|
wolffd@0
|
531 while ((preorder(stack2.top()) > preorder(w)))
|
wolffd@0
|
532 stack2.pop();
|
wolffd@0
|
533 end
|
wolffd@0
|
534 end
|
wolffd@0
|
535 end
|
wolffd@0
|
536
|
wolffd@0
|
537 % ---
|
wolffd@0
|
538 % found scc containing v
|
wolffd@0
|
539 % ---
|
wolffd@0
|
540 if (stack2.top() == v)
|
wolffd@0
|
541 stack2.pop();
|
wolffd@0
|
542
|
wolffd@0
|
543 w = -1;
|
wolffd@0
|
544 while (w ~= v)
|
wolffd@0
|
545
|
wolffd@0
|
546 % ---
|
wolffd@0
|
547 % collect all the nodes of this scc
|
wolffd@0
|
548 % ---
|
wolffd@0
|
549 w = stack1.pop();
|
wolffd@0
|
550 id(w) = idctr;
|
wolffd@0
|
551 end
|
wolffd@0
|
552 idctr = idctr + 1;
|
wolffd@0
|
553 end
|
wolffd@0
|
554
|
wolffd@0
|
555 end % function dfs
|
wolffd@0
|
556 end
|
wolffd@0
|
557
|
wolffd@0
|
558 function [Gs, s, id] = connected_components(G, varargin)
|
wolffd@0
|
559 % ---
|
wolffd@0
|
560 % get all connected subgraphs:
|
wolffd@0
|
561 % ---
|
wolffd@0
|
562
|
wolffd@0
|
563 % make edge matrix undirected
|
wolffd@0
|
564 G2 = DiGraph(Graph(G));
|
wolffd@0
|
565
|
wolffd@0
|
566 [GsGraph, s, id] = strongly_connected_components(G2, varargin{:});
|
wolffd@0
|
567
|
wolffd@0
|
568 % get the actual subgraps
|
wolffd@0
|
569
|
wolffd@0
|
570 for i =1:numel(GsGraph)
|
wolffd@0
|
571 Gs(i) = G.subgraph(GsGraph(i).nodes);
|
wolffd@0
|
572 end
|
wolffd@0
|
573 end
|
wolffd@0
|
574
|
wolffd@0
|
575 % ---
|
wolffd@0
|
576 % creates new graph just containing the
|
wolffd@0
|
577 % specified nodes and optionally specified edges
|
wolffd@0
|
578 % nodes can be specified using
|
wolffd@0
|
579 % a. the binary G.V structure
|
wolffd@0
|
580 % b. or node indices
|
wolffd@0
|
581 % ---
|
wolffd@0
|
582 function G2 = subgraph(G, V, E)
|
wolffd@0
|
583 if nargin == 2
|
wolffd@0
|
584 E = [];
|
wolffd@0
|
585 end
|
wolffd@0
|
586
|
wolffd@0
|
587 % ---
|
wolffd@0
|
588 % create new graph as copy ofthe old
|
wolffd@0
|
589 % ---
|
wolffd@0
|
590
|
wolffd@0
|
591 G2 = feval(class(G), G);
|
wolffd@0
|
592
|
wolffd@0
|
593 % ---
|
wolffd@0
|
594 % reset nodes and edges
|
wolffd@0
|
595 % NOTE: we determine the input format first
|
wolffd@0
|
596 % ---
|
wolffd@0
|
597 if (max(V) == 1 && numel(V) > 1) || max(V) == 0
|
wolffd@0
|
598
|
wolffd@0
|
599 G2.remove_node(find(~V));
|
wolffd@0
|
600 else
|
wolffd@0
|
601
|
wolffd@0
|
602 G2.remove_node(setdiff(1:numel(G.V), V));
|
wolffd@0
|
603 end
|
wolffd@0
|
604 if ~isempty(E)
|
wolffd@0
|
605 G2.E = E;
|
wolffd@0
|
606 end
|
wolffd@0
|
607 end
|
wolffd@0
|
608
|
wolffd@0
|
609 % ---
|
wolffd@0
|
610 % joins the information of graph G2 with
|
wolffd@0
|
611 % this GRaph, not duplicating nodes.
|
wolffd@0
|
612 % ---
|
wolffd@0
|
613 function add_graph(G, G2)
|
wolffd@0
|
614
|
wolffd@0
|
615 % determine if symmetric edges have to be added
|
wolffd@0
|
616 clas = cat(1, class(G2), superclasses(G2));
|
wolffd@0
|
617 if strcellfind(clas, 'Graph')
|
wolffd@0
|
618 add_symm = 1;
|
wolffd@0
|
619 else
|
wolffd@0
|
620 add_symm = 0;
|
wolffd@0
|
621 end
|
wolffd@0
|
622
|
wolffd@0
|
623 % ---
|
wolffd@0
|
624 % add all nodes and edges in G2
|
wolffd@0
|
625 % ---
|
wolffd@0
|
626 for V = G2.nodes();
|
wolffd@0
|
627
|
wolffd@0
|
628 G.add_node(V);
|
wolffd@0
|
629 end
|
wolffd@0
|
630
|
wolffd@0
|
631 % ---
|
wolffd@0
|
632 % NOTE / TODO:
|
wolffd@0
|
633 % this LABEL inheritance is far too expensive when
|
wolffd@0
|
634 % creating many copiesof the same graph
|
wolffd@0
|
635 % Its also unnessecary for lots of them
|
wolffd@0
|
636 % except for debugging :(.
|
wolffd@0
|
637 % ---
|
wolffd@0
|
638 G.Vlbl = cell(1,numel(G2.V));
|
wolffd@0
|
639 G.Vlbl(G2.nodes()) = G2.label(G2.nodes());
|
wolffd@0
|
640
|
wolffd@0
|
641 % ---
|
wolffd@0
|
642 % get all edges in G2
|
wolffd@0
|
643 % ---
|
wolffd@0
|
644 [val, V1, V2] = edges(G2);
|
wolffd@0
|
645
|
wolffd@0
|
646 for i = 1:numel(val)
|
wolffd@0
|
647
|
wolffd@0
|
648 % ---
|
wolffd@0
|
649 % add edges to graph
|
wolffd@0
|
650 % ---
|
wolffd@0
|
651 G.add_edge(V1(i), V2(i), val(i));
|
wolffd@0
|
652
|
wolffd@0
|
653 if add_symm
|
wolffd@0
|
654 % ---
|
wolffd@0
|
655 % add symmetric edges to graph
|
wolffd@0
|
656 % ---
|
wolffd@0
|
657 G.add_edge(V2(i), V1(i), val(i));
|
wolffd@0
|
658 end
|
wolffd@0
|
659
|
wolffd@0
|
660 end
|
wolffd@0
|
661 end
|
wolffd@0
|
662
|
wolffd@0
|
663 % ---
|
wolffd@0
|
664 % substracts the edges in G2 from
|
wolffd@0
|
665 % this GRaph, removing not connected nodes.
|
wolffd@0
|
666 % ---
|
wolffd@0
|
667 function Gout = minus(a, b)
|
wolffd@0
|
668 Gout = feval(class(a),a);
|
wolffd@0
|
669 Gout.remove_graph(b);
|
wolffd@0
|
670 end
|
wolffd@0
|
671
|
wolffd@0
|
672 function remove_graph(G, G2)
|
wolffd@0
|
673
|
wolffd@0
|
674 % determine if symmetric edges have to be added
|
wolffd@0
|
675 clas = cat(1, class(G2), superclasses(G2));
|
wolffd@0
|
676 if strcellfind(clas, 'Graph')
|
wolffd@0
|
677 symm = 1;
|
wolffd@0
|
678 else
|
wolffd@0
|
679 symm = 0;
|
wolffd@0
|
680 end
|
wolffd@0
|
681
|
wolffd@0
|
682 % remove edges
|
wolffd@0
|
683 [val, V1, V2] = G2.edges();
|
wolffd@0
|
684 for j = 1:numel(val)
|
wolffd@0
|
685
|
wolffd@0
|
686 % remove specified edges
|
wolffd@0
|
687 G.remove_edge(V1(j), V2(j));
|
wolffd@0
|
688
|
wolffd@0
|
689 % remove symmetric edges if subtracting symm graph
|
wolffd@0
|
690 if symm
|
wolffd@0
|
691
|
wolffd@0
|
692 G.remove_edge(V2(j), V1(j));
|
wolffd@0
|
693 end
|
wolffd@0
|
694 end
|
wolffd@0
|
695
|
wolffd@0
|
696 % ---
|
wolffd@0
|
697 % Note : we only remove nodes with no
|
wolffd@0
|
698 % remaining incoming edges
|
wolffd@0
|
699 % ---
|
wolffd@0
|
700 V = G2.nodes();
|
wolffd@0
|
701 for j = 1:numel(V)
|
wolffd@0
|
702
|
wolffd@0
|
703 if G.degree(V(j)) == 0
|
wolffd@0
|
704
|
wolffd@0
|
705 G.remove_node(V(j));
|
wolffd@0
|
706 end
|
wolffd@0
|
707 end
|
wolffd@0
|
708
|
wolffd@0
|
709 end
|
wolffd@0
|
710
|
wolffd@0
|
711 % ---
|
wolffd@0
|
712 % compact graph representation
|
wolffd@0
|
713 % ---
|
wolffd@0
|
714 function [E, labels] = compact(G)
|
wolffd@0
|
715
|
wolffd@0
|
716 % ---
|
wolffd@0
|
717 % get nodes and create a reverse index
|
wolffd@0
|
718 % ---
|
wolffd@0
|
719 Vidx = sparse(G.nodes,1,1:G.order());
|
wolffd@0
|
720 [w, V1, V2] = G.edges();
|
wolffd@0
|
721
|
wolffd@0
|
722 % create compact adjacency matrix
|
wolffd@0
|
723 E = sparse(Vidx(V1), Vidx(V2),w, G.order(), G.order());
|
wolffd@0
|
724
|
wolffd@0
|
725 labels = G.label(G.nodes());
|
wolffd@0
|
726 end
|
wolffd@0
|
727
|
wolffd@0
|
728 % ---
|
wolffd@0
|
729 % determines if Edges in G2 are the same as in G
|
wolffd@0
|
730 % ---
|
wolffd@0
|
731 function [out] = isSubgraph(G, G2)
|
wolffd@0
|
732
|
wolffd@0
|
733 [val, V1, V2] = G2.edges();
|
wolffd@0
|
734 validE = false(numel(V1), 1);
|
wolffd@0
|
735
|
wolffd@0
|
736 i = 1;
|
wolffd@0
|
737 while i <= numel(V1)
|
wolffd@0
|
738
|
wolffd@0
|
739 % ---
|
wolffd@0
|
740 % Test if edge exists in other graph
|
wolffd@0
|
741 % ---
|
wolffd@0
|
742 if G.edge(V1(i),V2(i)) == val(i)
|
wolffd@0
|
743 out = 0;
|
wolffd@0
|
744 return;
|
wolffd@0
|
745 end
|
wolffd@0
|
746 i = i +1 ;
|
wolffd@0
|
747 end
|
wolffd@0
|
748
|
wolffd@0
|
749 % ---
|
wolffd@0
|
750 % Test labels
|
wolffd@0
|
751 % ---
|
wolffd@0
|
752 if ~isempty(G.Vlbl) && ~isempty(G2.Vlbl)
|
wolffd@0
|
753
|
wolffd@0
|
754 V = G2.nodes();
|
wolffd@0
|
755 i = 1;
|
wolffd@0
|
756 while i <= numel(V)
|
wolffd@0
|
757 if strcmp(G.label(V(i)), G2.label(V(i))) ~= 0
|
wolffd@0
|
758 out = 0;
|
wolffd@0
|
759 return;
|
wolffd@0
|
760 end
|
wolffd@0
|
761 i = i + 1;
|
wolffd@0
|
762 end
|
wolffd@0
|
763 end
|
wolffd@0
|
764
|
wolffd@0
|
765 out = 1;
|
wolffd@0
|
766 end
|
wolffd@0
|
767
|
wolffd@0
|
768 % ---
|
wolffd@0
|
769 % Visualise the Similarity Graph
|
wolffd@0
|
770 % ---
|
wolffd@0
|
771 function visualise(G)
|
wolffd@0
|
772
|
wolffd@0
|
773 % ---
|
wolffd@0
|
774 % get colormap for edge weights
|
wolffd@0
|
775 % ---
|
wolffd@0
|
776 cmap = jet(100);
|
wolffd@0
|
777
|
wolffd@0
|
778 % ---
|
wolffd@0
|
779 % NOTE: we now get the weight colors for all edges
|
wolffd@0
|
780 % get maximum weight and all edges
|
wolffd@0
|
781 % ---
|
wolffd@0
|
782 [colors, V1, V2] = G.edges();
|
wolffd@0
|
783 w = G.max_weight();
|
wolffd@0
|
784
|
wolffd@0
|
785 % normalise colors
|
wolffd@0
|
786 colors = max(1,round((colors / w) * 100));
|
wolffd@0
|
787
|
wolffd@0
|
788 % get node labels
|
wolffd@0
|
789 V1lbl = G.label(V1);
|
wolffd@0
|
790 V2lbl = G.label(V2);
|
wolffd@0
|
791
|
wolffd@0
|
792 % ---
|
wolffd@0
|
793 % compose edgecolor matrix
|
wolffd@0
|
794 % ---
|
wolffd@0
|
795 ec = cell(numel(V1), 3);
|
wolffd@0
|
796 for i = 1:numel(V1)
|
wolffd@0
|
797 ec(i,:) = {V1lbl{i}, V2lbl{i}, cmap(colors(i),:)};
|
wolffd@0
|
798 end
|
wolffd@0
|
799
|
wolffd@0
|
800 % ---
|
wolffd@0
|
801 % For Performance Issues
|
wolffd@0
|
802 % We get the compact Graph and
|
wolffd@0
|
803 % !hope! for the labels to correspond to those above
|
wolffd@0
|
804 % ---
|
wolffd@0
|
805 [E, labels] = compact(G);
|
wolffd@0
|
806
|
wolffd@0
|
807 % determine if symmetric Graph
|
wolffd@0
|
808 clas = cat(1, class(G), superclasses(G));
|
wolffd@0
|
809 if strcellfind(clas, 'Graph')
|
wolffd@0
|
810 symm = 1;
|
wolffd@0
|
811 else
|
wolffd@0
|
812 symm = 0;
|
wolffd@0
|
813 end
|
wolffd@0
|
814
|
wolffd@0
|
815 graphViz4Matlab('-adjMat',E, ...
|
wolffd@0
|
816 '-nodeLabels',labels, ...
|
wolffd@0
|
817 '-edgeColors', ec, ...
|
wolffd@0
|
818 '-undirected', symm);
|
wolffd@0
|
819 end
|
wolffd@0
|
820 end
|
wolffd@0
|
821
|
wolffd@0
|
822
|
wolffd@0
|
823 methods (Static)
|
wolffd@0
|
824 function G = copy(Gin)
|
wolffd@0
|
825
|
wolffd@0
|
826 if strcmp(class(Gin), 'DiGraph')
|
wolffd@0
|
827
|
wolffd@0
|
828 G = DiGraph(Gin.V, Gin.E);
|
wolffd@0
|
829 G.Vlbl = Gin.Vlbl;
|
wolffd@0
|
830
|
wolffd@0
|
831 else
|
wolffd@0
|
832 warning ('cannot copy graph, casting instead')
|
wolffd@0
|
833 G = DiGraph.cast(Gin);
|
wolffd@0
|
834 end
|
wolffd@0
|
835
|
wolffd@0
|
836 end
|
wolffd@0
|
837
|
wolffd@0
|
838 function G = cast(Gin)
|
wolffd@0
|
839
|
wolffd@0
|
840 % ---
|
wolffd@0
|
841 % this uses an imput grpaph
|
wolffd@0
|
842 % to create a new digraph
|
wolffd@0
|
843 % ---
|
wolffd@0
|
844 G = DiGraph();
|
wolffd@0
|
845
|
wolffd@0
|
846 % ---
|
wolffd@0
|
847 % Add the input graph to the empty one
|
wolffd@0
|
848 % ---
|
wolffd@0
|
849 G.add_graph(Gin);
|
wolffd@0
|
850
|
wolffd@0
|
851 end
|
wolffd@0
|
852 end
|
wolffd@0
|
853 end |