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

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