All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.salesforce.jgrapht.alg.spanning.GreedyMultiplicativeSpanner Maven / Gradle / Ivy

Go to download

This project contains the apt processor that implements all the checks enumerated in @Verify. It is a self contained, and shaded jar.

There is a newer version: 2.0.7
Show newest version
/*
 * (C) Copyright 2016-2017, by Dimitrios Michail and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * This program and the accompanying materials are dual-licensed under
 * either
 *
 * (a) the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation, or (at your option) any
 * later version.
 *
 * or (per the licensee's choosing)
 *
 * (b) the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation.
 */
package com.salesforce.jgrapht.alg.spanning;

import java.util.*;

import com.salesforce.jgrapht.*;
import com.salesforce.jgrapht.alg.interfaces.*;
import com.salesforce.jgrapht.graph.*;
import com.salesforce.jgrapht.util.*;

/**
 * Greedy algorithm for (2k-1)-multiplicative spanner construction (for any integer
 * {@literal k >= 1}).
 *
 * 

* The spanner is guaranteed to contain O(n^{1+1/k}) edges and the shortest path distance between * any two vertices in the spanner is at most 2k-1 times the corresponding shortest path distance in * the original graph. Here n denotes the number of vertices of the graph. * *

* The algorithm is described in: Althoefer, Das, Dobkin, Joseph, Soares. * On Sparse Spanners of Weighted Graphs. Discrete * Computational Geometry 9(1):81-100, 1993. * *

* If the graph is unweighted the algorithm runs in O(m n^{1+1/k}) time. Setting k to infinity will * result in a slow version of Kruskal's algorithm where cycle detection is performed by a BFS * computation. In such a case use the implementation of Kruskal with union-find. Here n and m are * the number of vertices and edges of the graph respectively. * *

* If the graph is weighted the algorithm runs in O(m (n^{1+1/k} + nlogn)) time by using Dijkstra's * algorithm. Edge weights must be non-negative. * * @param the graph vertex type * @param the graph edge type * * @author Dimitrios Michail * @since July 15, 2016 */ public class GreedyMultiplicativeSpanner implements SpannerAlgorithm { private final UndirectedGraph graph; private final int k; private static final int MAX_K = 1 << 29; /** * Constructs instance to compute a (2k-1)-spanner of an undirected graph. * * @param graph an undirected graph * @param k positive integer. */ public GreedyMultiplicativeSpanner(UndirectedGraph graph, int k) { this.graph = Objects.requireNonNull(graph, "Graph cannot be null"); if (k <= 0) { throw new IllegalArgumentException( "k should be positive in (2k-1)-spanner construction"); } this.k = Math.min(k, MAX_K); } @Override public Spanner getSpanner() { if (graph instanceof WeightedGraph) { return new WeightedSpannerAlgorithm().run(); } else { return new UnweightedSpannerAlgorithm().run(); } } // base algorithm implementation private abstract class SpannerAlgorithmBase { public abstract boolean isSpannerReachable(V s, V t, double distance); public abstract void addSpannerEdge(V s, V t, double weight); public Spanner run() { // sort edges ArrayList allEdges = new ArrayList<>(graph.edgeSet()); Collections.sort( allEdges, (e1, e2) -> Double .valueOf(graph.getEdgeWeight(e1)).compareTo(graph.getEdgeWeight(e2))); // check precondition double minWeight = graph.getEdgeWeight(allEdges.get(0)); if (minWeight < 0.0) { throw new IllegalArgumentException("Illegal edge weight: negative"); } // run main loop Set edgeList = new LinkedHashSet<>(); double edgeListWeight = 0d; for (E e : allEdges) { V s = graph.getEdgeSource(e); V t = graph.getEdgeTarget(e); if (!s.equals(t)) { // self-loop? double eWeight = graph.getEdgeWeight(e); if (!isSpannerReachable(s, t, (2 * k - 1) * eWeight)) { edgeList.add(e); edgeListWeight += eWeight; addSpannerEdge(s, t, eWeight); } } } return new SpannerImpl<>(edgeList, edgeListWeight); } } private class UnweightedSpannerAlgorithm extends SpannerAlgorithmBase { protected UndirectedGraph spanner; protected Map vertexDistance; protected Deque queue; protected Deque touchedVertices; public UnweightedSpannerAlgorithm() { spanner = new SimpleGraph(graph.getEdgeFactory()); touchedVertices = new ArrayDeque(graph.vertexSet().size()); for (V v : graph.vertexSet()) { spanner.addVertex(v); touchedVertices.push(v); } vertexDistance = new HashMap(graph.vertexSet().size()); queue = new ArrayDeque<>(); } /** * Check if two vertices are reachable by a BFS in the spanner graph using only a certain * number of hops. * * We execute this procedure repeatedly, therefore we need to keep track of what it touches * and only clean those before the next execution. */ @Override public boolean isSpannerReachable(V s, V t, double hops) { // initialize distances and queue while (!touchedVertices.isEmpty()) { V u = touchedVertices.pop(); vertexDistance.put(u, Integer.MAX_VALUE); } while (!queue.isEmpty()) { queue.pop(); } // do BFS touchedVertices.push(s); queue.push(s); vertexDistance.put(s, 0); while (!queue.isEmpty()) { V u = queue.pop(); Integer uDistance = vertexDistance.get(u); if (u.equals(t)) { // found return uDistance <= hops; } for (E e : spanner.edgesOf(u)) { V v = Graphs.getOppositeVertex(spanner, e, u); Integer vDistance = vertexDistance.get(v); if (vDistance == Integer.MAX_VALUE) { touchedVertices.push(v); vertexDistance.put(v, uDistance + 1); queue.push(v); } } } return false; } @Override public void addSpannerEdge(V s, V t, double weight) { spanner.addEdge(s, t); } } private class WeightedSpannerAlgorithm extends SpannerAlgorithmBase { protected WeightedGraph spanner; protected FibonacciHeap heap; protected Map> nodes; public WeightedSpannerAlgorithm() { this.spanner = new SimpleWeightedGraph(graph.getEdgeFactory()); for (V v : graph.vertexSet()) { spanner.addVertex(v); } this.heap = new FibonacciHeap(); this.nodes = new LinkedHashMap>(); } @Override public boolean isSpannerReachable(V s, V t, double distance) { // init heap.clear(); nodes.clear(); FibonacciHeapNode sNode = new FibonacciHeapNode(s); nodes.put(s, sNode); heap.insert(sNode, 0d); while (!heap.isEmpty()) { FibonacciHeapNode uNode = heap.removeMin(); double uDistance = uNode.getKey(); V u = uNode.getData(); if (uDistance > distance) { return false; } if (u.equals(t)) { // found return true; } for (E e : spanner.edgesOf(u)) { V v = Graphs.getOppositeVertex(spanner, e, u); FibonacciHeapNode vNode = nodes.get(v); double vDistance = uDistance + spanner.getEdgeWeight(e); if (vNode == null) { // no distance vNode = new FibonacciHeapNode(v); nodes.put(v, vNode); heap.insert(vNode, vDistance); } else if (vDistance < vNode.getKey()) { heap.decreaseKey(vNode, vDistance); } } } return false; } @Override public void addSpannerEdge(V s, V t, double weight) { Graphs.addEdge(spanner, s, t, weight); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy