annotate core/tools/DiGraph.m @ 0:e9a9cd732c1e tip

first hg version after svn
author wolffd
date Tue, 10 Feb 2015 15:05:51 +0000
parents
children
rev   line source
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