/** * Finds the set of clusters which have the strongest "community structure". The more edges * removed the smaller and more cohesive the clusters. * * @param graph the graph */ public Set<Set<V>> transform(Graph<V, E> graph) { if (mNumEdgesToRemove < 0 || mNumEdgesToRemove > graph.getEdgeCount()) { throw new IllegalArgumentException("Invalid number of edges passed in."); } edges_removed.clear(); for (int k = 0; k < mNumEdgesToRemove; k++) { BetweennessCentrality<V, E> bc = new BetweennessCentrality<V, E>(graph); E to_remove = null; double score = 0; for (E e : graph.getEdges()) { Integer weight = mWeight.get(e); if (bc.getEdgeScore(e) / weight > score) { to_remove = e; score = bc.getEdgeScore(e) / weight; } } edges_removed.put(to_remove, graph.getEndpoints(to_remove)); graph.removeEdge(to_remove); } WeakComponentClusterer<V, E> wcSearch = new WeakComponentClusterer<V, E>(); Set<Set<V>> clusterSet = wcSearch.transform(graph); for (Map.Entry<E, Pair<V>> entry : edges_removed.entrySet()) { Pair<V> endpoints = entry.getValue(); graph.addEdge(entry.getKey(), endpoints.getFirst(), endpoints.getSecond()); } return clusterSet; }
protected void initialize(Graph<V, E> graph) { this.graph = graph; this.vertex_scores = new HashMap<V, Double>(); this.edge_scores = new HashMap<E, Double>(); this.vertex_data = new HashMap<V, BetweennessData>(); for (V v : graph.getVertices()) this.vertex_scores.put(v, 0.0); for (E e : graph.getEdges()) this.edge_scores.put(e, 0.0); }
/** * Calculates betweenness scores based on the all-pairs weighted shortest paths in the graph. * * <p>NOTE: This version of the algorithm may not work correctly on all graphs; we're still * working out the bugs. Use at your own risk. * * @param graph the graph for which the scores are to be calculated * @param edge_weights the edge weights to be used in the path length calculations */ public BetweennessCentrality(Graph<V, E> graph, Transformer<E, ? extends Number> edge_weights) { // reject negative-weight edges up front for (E e : graph.getEdges()) { double e_weight = edge_weights.transform(e).doubleValue(); if (e_weight < 0) throw new IllegalArgumentException("Weight for edge '" + e + "' is < 0: " + e_weight); } initialize(graph); computeBetweenness(new MapBinaryHeap<V>(new BetweennessComparator()), edge_weights); }
/** * Searches the graph and searches for the shortest path from a given start node to the * destination. Returns {@code null} if there are no vertices, edges, or path to the goal, * otherwise a list with the order of vertices on the shortest path. * * @param graph The graph to search * @param source The vertex to start the search from * @param destination The goal vertex * @return A list with the order of vertices on the shortest path, null if no path exists in the * graph. */ public List<V> search(Graph<V, E> graph, V source, V destination) { // Check if it is even possible to find a path, return null // if the graph has no vertices or edges if (graph.getVertexCount() == 0) { System.out.println("No nodes in the graph, " + "no shortest path can be found"); return null; } else if (graph.getEdgeCount() == 0) { System.out.println("No edges in graph, no path " + "can be found"); return null; } // Keep record of distance to each vertex, map each vertex // in the graph to it's distance HashMap<V, Number> distanceTable = new HashMap<>(); // Unvisited node queue, uses a pair <Vertex, Double> and ordered // by the distance to the vertex PriorityQueue<Pair<V, Number>> queue = new PriorityQueue<>(new QueueComparator()); // Map of nodes on the path, parents is value, key is child HashMap<V, V> parent = new HashMap<>(); Number maxValue; E edgeTest = graph.getEdges().iterator().next(); // This is so ugly, I hate Java Numbers int numberType = 0; if (edgeTest.getWeight() instanceof Integer) { numberType = 1; } else if (edgeTest.getWeight() instanceof Double) { numberType = 2; } // Place each vertex in the map, initialize distances and put // the pairings into the queue. for (V vertex : graph.getVertices()) { if (numberType == 1) { maxValue = Integer.MAX_VALUE; if (vertex.equals(source)) { distanceTable.put(source, 0); queue.add(new Pair<>(vertex, 0)); } else { distanceTable.put(vertex, Integer.MAX_VALUE); queue.add(new Pair<>(vertex, maxValue)); } } else if (numberType == 2) { maxValue = Double.MAX_VALUE; if (vertex.equals(source)) { distanceTable.put(source, 0.0); queue.add(new Pair<>(vertex, 0.0)); } else { distanceTable.put(vertex, Double.MAX_VALUE); queue.add(new Pair<>(vertex, maxValue)); } } } parent.put(source, null); while (!queue.isEmpty()) { Pair<V, Number> topPair = queue.remove(); V vertex = topPair.getLeft(); // Goal test, return the list of nodes on the path // if we reach the destination if (vertex.equals(destination)) { return tracePath(parent, destination); } Collection<V> neighbours = graph.getNeighbors(vertex); for (V neighbour : neighbours) { E edge = graph.findEdge(vertex, neighbour); assert (edge != null); // Test for type of number used for weight, work accordingly // Did I mention I hate the Java Number class. if (numberType == 1) { Integer alternateDistance = (Integer) edge.getWeight(); if (alternateDistance < (Integer) distanceTable.get(neighbour)) { distanceTable.put(neighbour, alternateDistance); parent.put(neighbour, vertex); queue.add(new Pair<>(neighbour, alternateDistance)); } } else if (numberType == 2) { Double alternateDistance = (Double) edge.getWeight(); if (alternateDistance < (Double) distanceTable.get(neighbour)) { distanceTable.put(neighbour, alternateDistance); parent.put(neighbour, vertex); queue.add(new Pair<>(neighbour, alternateDistance)); } } } } // Exhausted all possible paths from source, could not find a path // to the goal. return null; }
public void testMixedSaveLoadSave() throws IOException { Graph<Number, Number> graph1 = new SparseMultigraph<Number, Number>(); for (int i = 0; i < 5; i++) { graph1.addVertex(i); } int j = 0; List<Number> id = new ArrayList<Number>(graph1.getVertices()); GreekLabels<Number> gl = new GreekLabels<Number>(id); Number[] edges = {0, 1, 2, 3, 4, 5}; graph1.addEdge(j++, 0, 1, EdgeType.DIRECTED); graph1.addEdge(j++, 0, 2, EdgeType.DIRECTED); graph1.addEdge(j++, 1, 2, EdgeType.DIRECTED); graph1.addEdge(j++, 1, 3); graph1.addEdge(j++, 1, 4); graph1.addEdge(j++, 4, 3); Map<Number, Number> nr = new HashMap<Number, Number>(); for (int i = 0; i < edges.length; i++) { nr.put(edges[i], new Float(Math.random())); } assertEquals(graph1.getEdgeCount(), 6); // System.err.println(" mixed graph1 = "+graph1); // for(Number edge : graph1.getEdges()) { // System.err.println("edge "+edge+" is directed? "+graph1.getEdgeType(edge)); // } // for(Number v : graph1.getVertices()) { // System.err.println(v+" outedges are "+graph1.getOutEdges(v)); // System.err.println(v+" inedges are "+graph1.getInEdges(v)); // System.err.println(v+" incidentedges are "+graph1.getIncidentEdges(v)); // } String testFilename = "mtest.net"; String testFilename2 = testFilename + "2"; // assign arbitrary locations to each vertex Map<Number, Point2D> locations = new HashMap<Number, Point2D>(); for (Number v : graph1.getVertices()) { locations.put(v, new Point2D.Double(v.intValue() * v.intValue(), 1 << v.intValue())); } Function<Number, Point2D> vld = Functions.forMap(locations); PajekNetWriter<Number, Number> pnw = new PajekNetWriter<Number, Number>(); pnw.save(graph1, testFilename, gl, Functions.forMap(nr), vld); Graph<Number, Number> graph2 = pnr.load(testFilename, graphFactory); Function<Number, String> pl = pnr.getVertexLabeller(); List<Number> id2 = new ArrayList<Number>(graph2.getVertices()); Function<Number, Point2D> vld2 = pnr.getVertexLocationTransformer(); assertEquals(graph1.getVertexCount(), graph2.getVertexCount()); assertEquals(graph1.getEdgeCount(), graph2.getEdgeCount()); // test vertex labels and locations for (int i = 0; i < graph1.getVertexCount(); i++) { Number v1 = id.get(i); Number v2 = id2.get(i); assertEquals(gl.apply(v1), pl.apply(v2)); assertEquals(vld.apply(v1), vld2.apply(v2)); } // test edge weights Function<Number, Number> nr2 = pnr.getEdgeWeightTransformer(); for (Number e2 : graph2.getEdges()) { Pair<Number> endpoints = graph2.getEndpoints(e2); Number v1_2 = endpoints.getFirst(); Number v2_2 = endpoints.getSecond(); Number v1_1 = id.get(id2.indexOf(v1_2)); Number v2_1 = id.get(id2.indexOf(v2_2)); Number e1 = graph1.findEdge(v1_1, v2_1); assertNotNull(e1); assertEquals(nr.get(e1).floatValue(), nr2.apply(e2).floatValue(), 0.0001); } pnw.save(graph2, testFilename2, pl, nr2, vld2); compareIndexedGraphs(graph1, graph2); pnr.setVertexLabeller(null); Graph<Number, Number> graph3 = pnr.load(testFilename2, graphFactory); compareIndexedGraphs(graph2, graph3); File file1 = new File(testFilename); File file2 = new File(testFilename2); Assert.assertTrue(file1.length() == file2.length()); file1.delete(); file2.delete(); }
protected void computeBetweenness(Queue<V> queue, Transformer<E, ? extends Number> edge_weights) { for (V v : graph.getVertices()) { // initialize the betweenness data for this new vertex for (V s : graph.getVertices()) this.vertex_data.put(s, new BetweennessData()); // if (v.equals(new Integer(0))) // System.out.println("pause"); vertex_data.get(v).numSPs = 1; vertex_data.get(v).distance = 0; Stack<V> stack = new Stack<V>(); // Buffer<V> queue = new UnboundedFifoBuffer<V>(); // queue.add(v); queue.offer(v); while (!queue.isEmpty()) { // V w = queue.remove(); V w = queue.poll(); stack.push(w); BetweennessData w_data = vertex_data.get(w); for (E e : graph.getOutEdges(w)) { // TODO (jrtom): change this to getOtherVertices(w, e) V x = graph.getOpposite(w, e); if (x.equals(w)) continue; double wx_weight = edge_weights.transform(e).doubleValue(); // for(V x : graph.getSuccessors(w)) // { // if (x.equals(w)) // continue; // FIXME: the other problem is that I need to // keep putting the neighbors of things we've just // discovered in the queue, if they're undiscovered or // at greater distance. // FIXME: this is the problem, right here, I think: // need to update position in queue if distance changes // (which can only happen with weighted edges). // for each outgoing edge e from w, get other end x // if x not already visited (dist x < 0) // set x's distance to w's dist + edge weight // add x to queue; pri in queue is x's dist // if w's dist + edge weight < x's dist // update x's dist // update x in queue (MapBinaryHeap) // clear x's incoming edge list // if w's dist + edge weight = x's dist // add e to x's incoming edge list BetweennessData x_data = vertex_data.get(x); double x_potential_dist = w_data.distance + wx_weight; if (x_data.distance < 0) { // queue.add(x); // vertex_data.get(x).distance = // vertex_data.get(w).distance + 1; x_data.distance = x_potential_dist; queue.offer(x); } // note: // (1) this can only happen with weighted edges // (2) x's SP count and incoming edges are updated below if (x_data.distance > x_potential_dist) { x_data.distance = x_potential_dist; // invalidate previously identified incoming edges // (we have a new shortest path distance to x) x_data.incomingEdges.clear(); // update x's position in queue ((MapBinaryHeap<V>) queue).update(x); } // if (vertex_data.get(x).distance == // vertex_data.get(w).distance + 1) // // if (x_data.distance == x_potential_dist) // { // x_data.numSPs += w_data.numSPs; //// vertex_data.get(x).predecessors.add(w); // x_data.incomingEdges.add(e); // } } for (E e : graph.getOutEdges(w)) { V x = graph.getOpposite(w, e); if (x.equals(w)) continue; double e_weight = edge_weights.transform(e).doubleValue(); BetweennessData x_data = vertex_data.get(x); double x_potential_dist = w_data.distance + e_weight; if (x_data.distance == x_potential_dist) { x_data.numSPs += w_data.numSPs; // vertex_data.get(x).predecessors.add(w); x_data.incomingEdges.add(e); } } } while (!stack.isEmpty()) { V x = stack.pop(); // for (V w : vertex_data.get(x).predecessors) for (E e : vertex_data.get(x).incomingEdges) { V w = graph.getOpposite(x, e); double partialDependency = vertex_data.get(w).numSPs / vertex_data.get(x).numSPs * (1.0 + vertex_data.get(x).dependency); vertex_data.get(w).dependency += partialDependency; // E w_x = graph.findEdge(w, x); // double w_x_score = edge_scores.get(w_x).doubleValue(); // w_x_score += partialDependency; // edge_scores.put(w_x, w_x_score); double e_score = edge_scores.get(e).doubleValue(); edge_scores.put(e, e_score + partialDependency); } if (!x.equals(v)) { double x_score = vertex_scores.get(x).doubleValue(); x_score += vertex_data.get(x).dependency; vertex_scores.put(x, x_score); } } } if (graph instanceof UndirectedGraph) { for (V v : graph.getVertices()) { double v_score = vertex_scores.get(v).doubleValue(); v_score /= 2.0; vertex_scores.put(v, v_score); } for (E e : graph.getEdges()) { double e_score = edge_scores.get(e).doubleValue(); e_score /= 2.0; edge_scores.put(e, e_score); } } vertex_data.clear(); }