001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.algorithm; 033 034import java.util.Comparator; 035import java.util.HashSet; 036import java.util.Iterator; 037import java.util.LinkedList; 038import java.util.PriorityQueue; 039import java.util.Set; 040 041import org.graphstream.graph.Edge; 042import org.graphstream.graph.Element; 043import org.graphstream.graph.Graph; 044import org.graphstream.graph.Node; 045 046/** 047 * Compute the "betweenness" centrality of each vertex of a given graph. 048 * 049 * <p> 050 * The betweenness centrality counts how many shortest paths between each 051 * pair of nodes of the graph pass by a node. It does it for all nodes of 052 * the graph. 053 * </p> 054 * 055 * <h2>Usage</h2> 056 * 057 * <p> 058 * This algorithm, by default, stores the centrality values for each node inside 059 * the "Cb" attribute. You can change this attribute name at construction time. 060 * </p> 061 * 062 * <p> 063 * <b>This algorithm does not accept multi-graphs (p-graphs with p>1) yet.</b> 064 * </p> 065 * 066 * <p> 067 * This algorithm does not take into account edge direction yet. 068 * </p> 069 * 070 * <p> 071 * By default the 072 * weight attribute name is "weight", you can activate the weights using 073 * {@link #setWeighted()}. You can change the weight attribute name using the 074 * dedicated constructor or the {@link #setWeightAttributeName(String)} method. 075 * This method implicitly enable weights in the computation. Use 076 * {@link #setUnweighted()} to disable weights. 077 * </p> 078 * 079 * <p> 080 * The result of the computation is stored on each node inside the "Cb" 081 * attribute. You can change the name of this attribute using the dedicated 082 * constructor or the {@link #setCentralityAttributeName(String)} method. 083 * </p> 084 * 085 * <p> 086 * As the computing of centrality can take a lot of time, you can provide a 087 * progress 'callback' to get notified each time the algorithm finished 088 * processing a node (however the centrality values are usable only when the 089 * algorithm finished). See the {@link #registerProgressIndicator(Progress)} 090 * method. 091 * </p> 092 * 093 * <h2>Complexity</h2> 094 * 095 * <p> 096 * By default the algorithm performs on a graph considered as not weighted with 097 * complexity O(nm). You can specify that the graph edges contain weights in 098 * which case the algorithm complexity is O(nm + n^2 log n). 099 * </p> 100 * 101 * <h2>Example</h2> 102 * 103 * <pre> 104 * Graph graph = new SingleGraph("Betweenness Test"); 105 * 106 * // E----D AB=1, BC=5, CD=3, DE=2, BE=6, EA=4 107 * // /| | Cb(A)=4 108 * // / | | Cb(B)=2 109 * // A | | Cb(C)=0 110 * // \ | | Cb(D)=2 111 * // \| | Cb(E)=4 112 * // B----C 113 * 114 * Node A = graph.addNode("A"); 115 * Node B = graph.addNode("B"); 116 * Node E = graph.addNode("E"); 117 * Node C = graph.addNode("C"); 118 * Node D = graph.addNode("D"); 119 * 120 * graph.addEdge("AB", "A", "B"); 121 * graph.addEdge("BE", "B", "E"); 122 * graph.addEdge("BC", "B", "C"); 123 * graph.addEdge("ED", "E", "D"); 124 * graph.addEdge("CD", "C", "D"); 125 * graph.addEdge("AE", "A", "E"); 126 * 127 * bcb.setWeight(A, B, 1); 128 * bcb.setWeight(B, E, 6); 129 * bcb.setWeight(B, C, 5); 130 * bcb.setWeight(E, D, 2); 131 * bcb.setWeight(C, D, 3); 132 * bcb.setWeight(A, E, 4); 133 * 134 * BetweennessCentrality bcb = new BetweennessCentrality(); 135 * bcb.setWeightAttributeName("weight"); 136 * bcb.init(graph); 137 * bcb.compute(); 138 * 139 * System.out.println("A="+ graph.getNode("A").getAttribute("Cb")); 140 * System.out.println("B="+ graph.getNode("B").getAttribute("Cb")); 141 * System.out.println("C="+ graph.getNode("C").getAttribute("Cb")); 142 * System.out.println("D="+ graph.getNode("D").getAttribute("Cb")); 143 * System.out.println("E="+ graph.getNode("E").getAttribute("Cb")); 144 * </pre> 145 * 146 * <h2>Reference</h2> 147 * 148 * <p> 149 * This is based on the algorithm described in "A Faster Algorithm for 150 * Betweenness Centrality", Ulrik Brandes, Journal of Mathematical Sociology, 151 * 2001, and in 152 * "On variants of shortest-path betweenness centrality and their generic computation", 153 * of the same author, 2008. 154 * </p> 155 * 156 * @reference A Faster Algorithm for Betweenness Centrality, Ulrik Brandes, 157 * Journal of Mathematical Sociology, 2001, 25:2, pp. 163 - 177", 158 * "DOI: 10.1080/0022250X.2001.9990249" 159 * 160 * @reference On variants of shortest-path betweenness centrality and their generic computation, 161 * Ulrik Brandes, Social Networks, vol 30:2", pp. 136 - 145, 2008, 162 * issn 0378-8733, "DOI: 10.1016/j.socnet.2007.11.001". 163 */ 164public class BetweennessCentrality implements Algorithm { 165 166 protected static double INFINITY = 1000000.0; 167 168 /** Store the centrality value in this attribute on nodes and edges. */ 169 protected String centralityAttributeName = "Cb"; 170 171 /** The predecessors. */ 172 protected String predAttributeName = "brandes.P"; 173 174 /** The sigma value. */ 175 protected String sigmaAttributeName = "brandes.sigma"; 176 177 /** The distance value. */ 178 protected String distAttributeName = "brandes.d"; 179 180 /** The delta value. */ 181 protected String deltaAttributeName = "brandes.delta"; 182 183 /** Name of the attribute used to retrieve weights on edges. */ 184 protected String weightAttributeName = "weight"; 185 186 /** Do not use weights ? */ 187 protected boolean unweighted = true; 188 189 /** The graph to modify. */ 190 protected Graph graph; 191 192 /** The progress call-back method. */ 193 protected Progress progress = null; 194 195 /** Compute the centrality of edges. */ 196 protected boolean doEdges = true; 197 198 /** 199 * New centrality algorithm that will perform as if the graph was 200 * unweighted. By default the centrality will be stored in a "Cb" attribute 201 * on each node. 202 */ 203 public BetweennessCentrality() { 204 unweighted = true; 205 } 206 207 /** 208 * New centrality algorithm that will perform as if the graph was 209 * unweighted. The centrality for each node will be stored in an attribute 210 * whose name is specified by the <code>centralityAttributeName</code> 211 * argument. 212 * 213 * @param centralityAttributeName 214 * The name of the attribute used to store the result of the 215 * algorithm on each node. 216 */ 217 public BetweennessCentrality(String centralityAttributeName) { 218 this.centralityAttributeName = centralityAttributeName; 219 this.unweighted = true; 220 } 221 222 /** 223 * New centrality algorithm that will perform on a weighted graph, taking 224 * the weight of each edge in the given <code>weightAttributeName</code>. 225 * The result of the algorithm is stored for each node using the given 226 * <code>centralityAttributeName</code>. If an edge has no weight attribute, 227 * it is considered as having a weight of one. 228 * 229 * @param centralityAttributeName 230 * Name to use to store the centrality results on each node. 231 * @param weightAttributeName 232 * Name to use to retrieve the edge weights. 233 */ 234 public BetweennessCentrality(String centralityAttributeName, 235 String weightAttributeName) { 236 this.centralityAttributeName = centralityAttributeName; 237 this.weightAttributeName = weightAttributeName; 238 this.unweighted = false; 239 } 240 241 /** 242 * Name of the attribute used to retrieve weights on edges. 243 */ 244 public String getWeightAttributeName() { 245 return weightAttributeName; 246 } 247 248 /** 249 * Name of the attribute used to store centrality values on nodes. 250 */ 251 public String getCentralityAttributeName() { 252 return centralityAttributeName; 253 } 254 255 /** 256 * Specify the name of the weight attribute to retrieve edge attributes. 257 * This automatically set the algorithm to perform on the graph as if it was 258 * weighted. 259 */ 260 public void setWeightAttributeName(String weightAttributeName) { 261 unweighted = false; 262 this.weightAttributeName = weightAttributeName; 263 } 264 265 /** 266 * Consider the edges to have a weight. By default the weight is stored in a 267 * "weight" attribute. You can change this attribute using 268 * {@link #setWeightAttributeName(String)}. If an edge has no weight the 269 * value 1.0 is used. 270 */ 271 public void setWeighted() { 272 unweighted = false; 273 } 274 275 /** 276 * Consider all the edges to have the weight. 277 */ 278 public void setUnweighted() { 279 unweighted = true; 280 } 281 282 /** 283 * Activate or deactivate the centrality computation on edges. By default it is 284 * activated. Notice that this does not change the complexity of the algorithm. 285 * Only one more access on the edges is done to store the centrality in addition 286 * to the node access. 287 * @param on If true, the edges centrality is also computed. 288 */ 289 public void computeEdgeCentrality(boolean on) { 290 doEdges = on; 291 } 292 293 /** 294 * Specify the name of the attribute used to store the computed centrality 295 * values for each node. 296 */ 297 public void setCentralityAttributeName(String centralityAttributeName) { 298 this.centralityAttributeName = centralityAttributeName; 299 } 300 301 /** 302 * Specify an interface to call in order to indicate the algorithm progress. 303 * Pass null to remove the progress indicator. The progress indicator will 304 * be called regularly to indicate the computation progress. 305 */ 306 public void registerProgressIndicator(Progress progress) { 307 this.progress = progress; 308 } 309 310 /** 311 * Setup the algorithm to work on the given graph. 312 */ 313 public void init(Graph graph) { 314 this.graph = graph; 315 } 316 317 /** 318 * Compute the betweenness centrality on the given graph for each node. The 319 * result is by default stored in the "Cb" attribute on each node. 320 */ 321 public void compute() { 322 if (graph != null) { 323 betweennessCentrality(graph); 324 } 325 } 326 327 /** 328 * Compute the betweenness centrality on the given graph for each node and 329 * eventually edges. This method is equivalent to a call in sequence to the 330 * two methods {@link #init(Graph)} then {@link #compute()}. 331 */ 332 public void betweennessCentrality(Graph graph) { 333 init(graph); 334 initAllNodes(graph); 335 initAllEdges(graph); 336 337 float n = graph.getNodeCount(); 338 float i = 0; 339 340 for (Node s : graph) { 341 PriorityQueue<Node> S = null; 342 343 if (unweighted) 344 S = simpleExplore(s, graph); 345 else 346 S = dijkstraExplore2(s, graph); 347 348 // The really new things in the Brandes algorithm are here: 349 // Accumulation phase: 350 351 while (!S.isEmpty()) { 352 Node w = S.poll(); 353 354 for (Node v : predecessorsOf(w)) { 355 double c = ((sigma(v) / sigma(w)) * (1.0 + delta(w))); 356 if(doEdges) { 357 Edge e = w.getEdgeBetween(v); 358 setCentrality(e, centrality(e) + c); 359 } 360 setDelta(v, delta(v) + c); 361 } 362 if (w != s) { 363 setCentrality(w, centrality(w) + delta(w)); 364 } 365 } 366 367 if (progress != null) 368 progress.progress(i / n); 369 370 i++; 371 } 372 } 373 374 /** 375 * Compute single-source multiple-targets shortest paths on an unweighted 376 * graph. 377 * 378 * @param source 379 * The source node. 380 * @param graph 381 * The graph. 382 * @return A priority queue of explored nodes with sigma values usable to 383 * compute the centrality. 384 */ 385 protected PriorityQueue<Node> simpleExplore(Node source, Graph graph) { 386 LinkedList<Node> Q = new LinkedList<Node>(); 387 PriorityQueue<Node> S = new PriorityQueue<Node>(graph.getNodeCount(), 388 new BrandesNodeComparatorLargerFirst()); 389 390 setupAllNodes(graph); 391 Q.add(source); 392 setSigma(source, 1.0); 393 setDistance(source, 0.0); 394 395 while (!Q.isEmpty()) { 396 Node v = Q.removeFirst(); 397 398 S.add(v); 399 Iterator<? extends Edge> ww = v.getLeavingEdgeIterator(); 400 401 while (ww.hasNext()) { 402 Edge l = ww.next(); 403 Node w = l.getOpposite(v);//ww.next(); 404 405 if (distance(w) == INFINITY) { 406 setDistance(w, distance(v) + 1); 407 Q.add(w); 408 } 409 410 if (distance(w) == (distance(v) + 1.0)) { 411 setSigma(w, sigma(w) + sigma(v)); 412 addToPredecessorsOf(w, v); 413 } 414 } 415 } 416 417 return S; 418 } 419 420 /** 421 * Compute single-source multiple-targets paths on a weighted graph. 422 * 423 * @param source 424 * The source node. 425 * @param graph 426 * The graph. 427 * @return A priority queue of explored nodes with sigma values usable to 428 * compute the centrality. 429 */ 430 protected PriorityQueue<Node> dijkstraExplore(Node source, Graph graph) { 431 PriorityQueue<Node> S = new PriorityQueue<Node>(graph.getNodeCount(), 432 new BrandesNodeComparatorLargerFirst()); 433 PriorityQueue<Node> Q = new PriorityQueue<Node>(graph.getNodeCount(), 434 new BrandesNodeComparatorSmallerFirst()); 435 436 setupAllNodes(graph); 437 setDistance(source, 0.0); 438 setSigma(source, 1.0); 439 440 Q.add(source); 441 442 while (!Q.isEmpty()) { 443 Node u = Q.poll(); 444 445 if (distance(u) < 0.0) { // XXX Can happen ??? XXX 446 Q.clear(); 447 throw new RuntimeException("negative distance ??"); 448 } else { 449 S.add(u); 450 451// Iterator<? extends Node> k = u.getNeighborNodeIterator(); 452 Iterator<? extends Edge> k = u.getLeavingEdgeIterator(); 453 454 while (k.hasNext()) { 455// Node v = k.next(); 456 Edge l = k.next(); 457 Node v = l.getOpposite(u); 458 459 double alt = distance(u) + weight(u, v); 460 461 if (alt < distance(v)) { 462 if (distance(v) == INFINITY) { 463 setDistance(v, alt); 464 updatePriority(S, v); 465 updatePriority(Q, v); 466 Q.add(v); 467 setSigma(v, sigma(v) + sigma(u)); // XXX 468 // sigma(v)==0, 469 // always ?? XXX 470 } else { 471 setDistance(v, alt); 472 updatePriority(S, v); 473 updatePriority(Q, v); 474 setSigma(v, sigma(u)); 475 } 476 replacePredecessorsOf(v, u); 477 } else if (alt == distance(v)) { 478 setSigma(v, sigma(v) + sigma(u)); 479 addToPredecessorsOf(v, u); 480 } 481 } 482 } 483 } 484 485 return S; 486 } 487 488 /** 489 * The implementation of the Brandes paper. 490 * 491 * <ul> 492 * <li>title = 493 * "On variants of shortest-path betweenness centrality and their generic computation" 494 * ,</li> 495 * <li>author = "Ulrik Brandes",</li> 496 * <li>journal = "Social Networks",</li> 497 * <li>volume = "30",</li> 498 * <li>number = "2",</li> 499 * <li>pages = "136 - 145",</li> 500 * <li>year = "2008",</li> 501 * <li>note = "",</li> 502 * <li>issn = "0378-8733",</li> 503 * <li>doi = "DOI: 10.1016/j.socnet.2007.11.001",</li> </li> 504 */ 505 protected PriorityQueue<Node> dijkstraExplore2(Node source, Graph graph) { 506 PriorityQueue<Node> S = new PriorityQueue<Node>(graph.getNodeCount(), 507 new BrandesNodeComparatorLargerFirst()); 508 PriorityQueue<Node> Q = new PriorityQueue<Node>(graph.getNodeCount(), 509 new BrandesNodeComparatorSmallerFirst()); 510 511 setupAllNodes(graph); 512 setDistance(source, 0.0); 513 setSigma(source, 1.0); 514 Q.add(source); 515 516 while (!Q.isEmpty()) { 517 Node v = Q.poll(); 518 519 S.add(v); 520 521 //Iterator<? extends Node> k = v.getNeighborNodeIterator(); 522 Iterator<? extends Edge> k = v.getLeavingEdgeIterator(); 523 524 while (k.hasNext()) { 525 //Node w = k.next(); 526 Edge l = k.next(); 527 Node w = l.getOpposite(v); 528 529 double alt = distance(v) + weight(v, w); 530 double dw = distance(w); 531 532 if (alt < dw) { 533 setDistance(w, alt); 534 updatePriority(S, w); 535 updatePriority(Q, w); 536 if (dw == INFINITY) { 537 Q.add(w); 538 } 539 setSigma(w, 0.0); 540 clearPredecessorsOf(w); 541 } 542 if (distance(w) == alt) { 543 setSigma(w, sigma(w) + sigma(v)); 544 addToPredecessorsOf(w, v); 545 } 546 } 547 } 548 549 return S; 550 } 551 552 /** 553 * Update the given priority queue if the given node changed its priority 554 * (here distance) and if the node is already part of the queue. 555 * 556 * @param Q 557 * The queue. 558 * @param node 559 * The node. 560 */ 561 protected void updatePriority(PriorityQueue<Node> Q, Node node) { 562 if (Q.contains(node)) { 563 Q.remove(node); 564 Q.add(node); 565 } 566 } 567 568 /** 569 * The sigma value of the given node. 570 * 571 * @param node 572 * Extract the sigma value of this node. 573 * @return The sigma value. 574 */ 575 protected double sigma(Node node) { 576 return node.getNumber(sigmaAttributeName); 577 } 578 579 /** 580 * The distance value of the given node. 581 * 582 * @param node 583 * Extract the distance value of this node. 584 * @return The distance value. 585 */ 586 protected double distance(Node node) { 587 return node.getNumber(distAttributeName); 588 } 589 590 /** 591 * The delta value of the given node. 592 * 593 * @param node 594 * Extract the delta value of this node. 595 * @return The delta value. 596 */ 597 protected double delta(Node node) { 598 return node.getNumber(deltaAttributeName); 599 } 600 601 /** 602 * The centrality value of the given node or edge. 603 * 604 * @param elt 605 * Extract the centrality of this node or edge. 606 * @return The centrality value. 607 */ 608 public double centrality(Element elt) { 609 return elt.getNumber(centralityAttributeName); 610 } 611 612 /** 613 * List of predecessors of the given node. 614 * 615 * @param node 616 * Extract the predecessors of this node. 617 * @return The list of predecessors. 618 */ 619 @SuppressWarnings("all") 620 protected Set<Node> predecessorsOf(Node node) { 621 return (HashSet<Node>) node.getAttribute(predAttributeName); 622 } 623 624 /** 625 * Set the sigma value of the given node. 626 * 627 * @param node 628 * The node to modify. 629 * @param sigma 630 * The sigma value to store on the node. 631 */ 632 protected void setSigma(Node node, double sigma) { 633 node.setAttribute(sigmaAttributeName, sigma); 634 } 635 636 /** 637 * Set the distance value of the given node. 638 * 639 * @param node 640 * The node to modify. 641 * @param distance 642 * The delta value to store on the node. 643 */ 644 protected void setDistance(Node node, double distance) { 645 node.setAttribute(distAttributeName, distance); 646 } 647 648 /** 649 * Set the delta value of the given node. 650 * 651 * @param node 652 * The node to modify. 653 * @param delta 654 * The delta value to store on the node. 655 */ 656 protected void setDelta(Node node, double delta) { 657 node.setAttribute(deltaAttributeName, delta); 658 } 659 660 /** 661 * Set the centrality of the given node or edge. 662 * 663 * @param elt 664 * The node or edge to modify. 665 * @param centrality 666 * The centrality to store on the node. 667 */ 668 public void setCentrality(Element elt, double centrality) { 669 elt.setAttribute(centralityAttributeName, centrality); 670 } 671 672 /** 673 * Set the weight of the edge between 'from' and 'to'. 674 * 675 * @param from 676 * The source node. 677 * @param to 678 * The target node. 679 * @param weight 680 * The weight to store on the edge between 'from' and 'to'. 681 */ 682 public void setWeight(Node from, Node to, double weight) { 683 if (from.hasEdgeBetween(to.getId())) 684 from.getEdgeBetween(to.getId()).setAttribute(weightAttributeName, 685 weight); 686 } 687 688 /** 689 * The weight of the edge between 'from' and 'to'. 690 * 691 * @param from 692 * The origin node. 693 * @param to 694 * The target node. 695 * @return The weight on the edge between 'form' and 'to'. 696 */ 697 public double weight(Node from, Node to) { 698 Edge edge = from.getEdgeBetween(to.getId()); 699 700 if (edge != null) { 701 if (edge.hasAttribute(weightAttributeName)) 702 return edge.getNumber(weightAttributeName); 703 else 704 return 1.0; 705 } else { 706 return 0.0; 707 } 708 } 709 710 /** 711 * Remove all predecessors of the given node and then add it a first 712 * predecessor. 713 * 714 * @param node 715 * The node to modify. 716 * @param predecessor 717 * The predecessor to add. 718 */ 719 protected void replacePredecessorsOf(Node node, Node predecessor) { 720 HashSet<Node> set = new HashSet<Node>(); 721 722 set.add(predecessor); 723 node.setAttribute(predAttributeName, set); 724 } 725 726 /** 727 * Add a node to the predecessors of another. 728 * 729 * @param node 730 * Modify the predecessors of this node. 731 * @param predecessor 732 * The predecessor to add. 733 */ 734 @SuppressWarnings("all") 735 protected void addToPredecessorsOf(Node node, Node predecessor) { 736 HashSet<Node> preds = (HashSet<Node>) node 737 .getAttribute(predAttributeName); 738 739 preds.add(predecessor); 740 } 741 742 /** 743 * Remove all predecessors of the given node. 744 * 745 * @param node 746 * Remove all predecessors of this node. 747 */ 748 protected void clearPredecessorsOf(Node node) { 749 HashSet<Node> set = new HashSet<Node>(); 750 node.setAttribute(predAttributeName, set); 751 } 752 753 /** 754 * Set a default centrality of 0 to all nodes. 755 * 756 * @param graph 757 * The graph to modify. 758 */ 759 protected void initAllNodes(Graph graph) { 760 for (Node node : graph) { 761 setCentrality(node, 0.0); 762 } 763 } 764 765 /** 766 * Set a default centrality of 0 to all edges. 767 * 768 * @param graph 769 * The graph to modify. 770 */ 771 protected void initAllEdges(Graph graph) { 772 if(doEdges) { 773 for(Edge edge : graph.getEachEdge()) { 774 setCentrality(edge, 0.0); 775 } 776 } 777 } 778 779 /** 780 * Add a default value for attributes used during computation. 781 * 782 * @param graph 783 * The graph to modify. 784 */ 785 protected void setupAllNodes(Graph graph) { 786 for (Node node : graph) { 787 clearPredecessorsOf(node); 788 setSigma(node, 0.0); 789 setDistance(node, INFINITY); 790 setDelta(node, 0.0); 791 } 792 } 793 794 /** 795 * Delete attributes used by this algorithm in nodes and edges of the graph 796 */ 797 public void cleanGraph(){ 798 cleanElement(graph.getEachEdge()); 799 cleanElement(graph.getEachNode()); 800 } 801 802 /** 803 * Delete attributes used by this algorithm in nodes of the graph 804 */ 805 public void cleanNodes(){ 806 cleanElement(graph.getEachNode()); 807 } 808 809 /** 810 * Delete attributes used by this algorithm in edges of the graph 811 */ 812 public void cleanEdges(){ 813 cleanElement(graph.getEachEdge()); 814 } 815 816 /** 817 * Delete attributes used by this algorithm in elements of a graph 818 * @param it the list of elements 819 */ 820 private void cleanElement(Iterable<? extends Element> it){ 821 for(Element e : it){ 822 if(e.hasAttribute(predAttributeName)) 823 e.removeAttribute(predAttributeName); 824 if(e.hasAttribute(sigmaAttributeName)) 825 e.removeAttribute(sigmaAttributeName); 826 if(e.hasAttribute(distAttributeName)) 827 e.removeAttribute(distAttributeName); 828 if(e.hasAttribute(deltaAttributeName)) 829 e.removeAttribute(deltaAttributeName); 830 } 831 } 832 833 /** 834 * Increasing comparator used for priority queues. 835 */ 836 protected class BrandesNodeComparatorLargerFirst implements 837 Comparator<Node> { 838 public int compare(Node x, Node y) { 839 // return (int) ( (distance(y)*1000.0) - (distance(x)*1000.0) ); 840 double yy = distance(y); 841 double xx = distance(x); 842 843 if (xx > yy) 844 return -1; 845 else if (xx < yy) 846 return 1; 847 848 return 0; 849 } 850 } 851 852 /** 853 * Decreasing comparator used for priority queues. 854 */ 855 protected class BrandesNodeComparatorSmallerFirst implements 856 Comparator<Node> { 857 public int compare(Node x, Node y) { 858 // return (int) ( (distance(x)*1000.0) - (distance(y)*1000.0) ); 859 double yy = distance(y); 860 double xx = distance(x); 861 862 if (xx > yy) 863 return 1; 864 else if (xx < yy) 865 return -1; 866 867 return 0; 868 } 869 } 870 871 /** 872 * Interface allowing to be notified of the algorithm progress. 873 */ 874 public interface Progress { 875 /** 876 * Progress of the algorithm. 877 * 878 * @param percent 879 * a value between 0 and 1, 0 meaning 0% and 1 meaning 100%. 880 */ 881 void progress(float percent); 882 } 883}