Java tutorial
/* * This file is part of the GeMTC software for MTC model generation and * analysis. GeMTC is distributed from http://drugis.org/gemtc. * Copyright (C) 2009-2012 Gert van Valkenhoef. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.drugis.mtc.graph; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections15.Transformer; import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath; import edu.uci.ics.jung.algorithms.shortestpath.Distance; import edu.uci.ics.jung.graph.UndirectedGraph; import edu.uci.ics.jung.graph.util.Pair; /** * Find the absolute 1-center of an undirected graph. * * Let e = (u,v) be an edge of the graph, and l(e) the length of e. * Let x(e) be a point along the edge, with t = t(x(e)) \in [0, l(e)] the distance of x(e) from u. * Then, for any point x on G, d(v, x) is the length of the shortest path in G between the vertex v and point x. * Define F(x) = \max_{v \in V} d(v, x), and x* = \arg\min_{x on G} F(x). * x* is the absolute 1-center of G and F(x*) is the absolute 1-radius of G. * * Algorithm from <a href="http://www.jstor.org/pss/2100910">Kariv and Hakimi (1979), SIAM J Appl Math 37 (3): 513-538</a>. * * (Note: in the paper, the weight of an edge is referred to as its length l(e), and vertices can also have weights w(v). * This class only implements the vertex-unweighted algorithm. Edge-weighted graphs are supported, however.) */ public class AbsoluteOneCenter<V, E> { public static class UnitLength<E> implements Transformer<E, Number> { public Number transform(E input) { return 1.0; } } private final UndirectedGraph<V, E> d_graph; private final Distance<V> d_distance; private final Transformer<E, Number> d_edgeLength; private Comparator<V> d_comparator; /** * Absolute 1-center of an unweighted graph. */ public AbsoluteOneCenter(final UndirectedGraph<V, E> graph) { this(graph, new UnitLength<E>(), new DijkstraShortestPath<V, E>(graph)); } /** * Absolute 1-center of an unweighted graph. */ public AbsoluteOneCenter(final UndirectedGraph<V, E> graph, final Comparator<V> comparator) { this(graph, new UnitLength<E>(), new DijkstraShortestPath<V, E>(graph), comparator); } /** * Absolute 1-center of a weighted graph. */ public AbsoluteOneCenter(final UndirectedGraph<V, E> graph, final Transformer<E, Number> edgeLength) { this(graph, edgeLength, new DijkstraShortestPath<V, E>(graph)); } /** * Absolute 1-center of a weighted graph. */ public AbsoluteOneCenter(final UndirectedGraph<V, E> graph, final Transformer<E, Number> edgeLength, final Comparator<V> comparator) { this(graph, edgeLength, new DijkstraShortestPath<V, E>(graph), comparator); } /** * Absolute 1-center of an weighted graph. */ public AbsoluteOneCenter(final UndirectedGraph<V, E> graph, final Transformer<E, Number> edgeLength, final Distance<V> distance) { this(graph, edgeLength, distance, null); } /** * Absolute 1-center of an weighted graph. */ public AbsoluteOneCenter(final UndirectedGraph<V, E> graph, final Transformer<E, Number> edgeLength, final Distance<V> distance, final Comparator<V> comparator) { d_graph = graph; d_edgeLength = edgeLength; d_distance = distance; d_comparator = comparator; } public PointOnEdge<V, E> getCenter() { LocalCenter<V, E> localCenter = new LocalCenter<V, E>(d_graph, d_edgeLength, d_distance, d_comparator); Center<V, E> c = null; for (E e : d_graph.getEdges()) { Center<V, E> lc = localCenter.transform(e); if (c == null || lc.getRadius() < c.getRadius() || (lc.getRadius() == c.getRadius() && compareEdge(lc.getEdge(), c.getEdge()) < 0)) { c = lc; } } return c; } private int compareEdge(E e1, E e2) { if (d_comparator == null) { return 0; } Pair<V> x = new Pair<V>(d_graph.getIncidentVertices(e1)); Pair<V> y = new Pair<V>(d_graph.getIncidentVertices(e2)); final int compareFirst = d_comparator.compare(x.getFirst(), y.getFirst()); if (compareFirst != 0) { return compareFirst; } return d_comparator.compare(x.getSecond(), y.getSecond()); } /** * Given an edge e = (e0, e1) and vertices u and v, find the distance t* \in (0, l) from e0 where D_e(u, t*) = D_e(v, t*), * and D_e(u, t*) and D_e(v, t*) have opposite signs (if it exists). */ static <V> Double intersect(final Distance<V> distance, final V e0, final V e1, final double l, final V u, final V v) { final double lu = distance.getDistance(e0, u).doubleValue(); final double ru = distance.getDistance(e1, u).doubleValue(); final double lv = distance.getDistance(e0, v).doubleValue(); final double rv = distance.getDistance(e1, v).doubleValue(); if (lu == lv || ru == rv) { // they coincide or intersect only at the edge return null; } else if (lu > lv && ru > rv) { // u dominates v return null; } else if (lu < lv && ru < rv) { // v dominates u return null; } else { final double t1 = 0.5 * (rv - lu + l); final double t2 = 0.5 * (ru - lv + l); if (t1 + lu <= l - t1 + ru) { return t1; } else { return t2; } } } /** * Sort the vertices of the graph according to non-increasing distance from a vertex v. * @param distance Distance function for the graph. * @param v The vertex. * @param comparator Comparator to break ties between vertices of equal distance. * Used to make the results fully deterministic, can be null if this is not required. */ static <V> List<V> distanceOrderedVertices(final Distance<V> distance, final V v, final Comparator<V> comparator) { final Map<V, Number> map = distance.getDistanceMap(v); List<V> list = new ArrayList<V>(map.keySet()); Collections.sort(list, new Comparator<V>() { public int compare(V a, V b) { if (map.get(a).equals(map.get(b))) { return comparator == null ? 0 : comparator.compare(a, b); } else { return ((Double) map.get(b).doubleValue()).compareTo(map.get(a).doubleValue()); } } }); return list; } static class Center<V, E> extends PointOnEdge<V, E> { private final double d_r; public Center(E e, V v0, V v1, double t, double r) { super(e, v0, v1, t); d_r = r; } public Center(PointOnEdge<V, E> x, double r) { this(x.getEdge(), x.getVertex0(), x.getVertex1(), x.getDistance(), r); } public double getRadius() { return d_r; } } /** * Find the local center along an edge e = (u, v) of an undirected graph G. * Algorithm 2.3 (pages 521-522) of <a href="http://www.jstor.org/pss/2100910">Kariv and Hakimi (1979)</a>. * * A local center of G on edge e is a point x*(e) on e, such that F(x*(e)) = min_{x(e) on e} F(x(e)). */ static class LocalCenter<V, E> implements Transformer<E, Center<V, E>> { private final UndirectedGraph<V, E> d_graph; private final Distance<V> d_distance; private final Transformer<E, Number> d_edgeLength; /** * The lists L(v) of vertices sorted according to non-increasing distance from v. */ private final Map<V, List<V>> d_orderedVertices = new HashMap<V, List<V>>(); /** * @param graph The graph to calculate local centers for. * @param edgeLength Edge length function l(e). * @param distance Distance function defined on the given graph. */ public LocalCenter(final UndirectedGraph<V, E> graph, final Transformer<E, Number> edgeLength, final Distance<V> distance) { this(graph, edgeLength, distance, null); } /** * @param graph The graph to calculate local centers for. * @param edgeLength Edge length function l(e). * @param distance Distance function defined on the given graph. * @param vertexComparator Used to break ties in distance. */ public LocalCenter(final UndirectedGraph<V, E> graph, final Transformer<E, Number> edgeLength, final Distance<V> distance, Comparator<V> vertexComparator) { d_graph = graph; d_edgeLength = edgeLength; d_distance = distance; // Pre-processing: calculate the lists L(v) for (V v : d_graph.getVertices()) { d_orderedVertices.put(v, distanceOrderedVertices(distance, v, vertexComparator)); } } /** * Distance between u and v. */ private double d(V u, V v) { return d_distance.getDistance(u, v).doubleValue(); } /** * distance between vertex v and point x */ private double de(final PointOnEdge<V, E> x, final V v) { return Math.min(x.getDistance() + d(x.getVertex0(), v), l(x) - x.getDistance() + d(x.getVertex1(), v)); } /** * Length of the edge x is on. */ private double l(PointOnEdge<V, E> x) { return d_edgeLength.transform(x.getEdge()).doubleValue(); } /** * Point that coincides with the left-hand side vertex. */ private PointOnEdge<V, E> xr(PointOnEdge<V, E> x) { return new PointOnEdge<V, E>(x.getEdge(), x.getVertex0(), x.getVertex1(), 0.0); } /** * Point that coincides with the right-hand side vertex. */ private PointOnEdge<V, E> xs(PointOnEdge<V, E> x) { return new PointOnEdge<V, E>(x.getEdge(), x.getVertex0(), x.getVertex1(), l(x)); } /** * Point that coincides with the right-hand side vertex. */ private PointOnEdge<V, E> xt(PointOnEdge<V, E> x, double t) { return new PointOnEdge<V, E>(x.getEdge(), x.getVertex0(), x.getVertex1(), t); } /** * Calculate the local center of an edge. * @param edge The edge to calculate the local center of. * @return The local center (point on the given edge). */ public Center<V, E> transform(final E edge) { // System.out.println("Edge " + edge + " start"); final V vr = d_graph.getEndpoints(edge).getFirst(); final V vs = d_graph.getEndpoints(edge).getSecond(); final PointOnEdge<V, E> xr = new PointOnEdge<V, E>(edge, vr, vs, 0.0); final PointOnEdge<V, E> xs = xs(xr); // Step 1: treatment of t = 0 and t = l(e) Center<V, E> c = null; double dr = de(xr, d_orderedVertices.get(vr).get(0)); double ds = de(xs, d_orderedVertices.get(vs).get(0)); if (dr <= ds) { c = new Center<V, E>(xr, dr); } else { c = new Center<V, E>(xs, ds); } if (d_orderedVertices.get(vr).get(0) == d_orderedVertices.get(vs).get(0)) { return c; } else { return step3(c, d_orderedVertices.get(vr).get(0), 0); } } /** * Step 3: treatment of vertices v s.t. D_e(v, 0) = D_e(v_1, 0) * @param c The suspected center * @param l List of vertices to consider * @param i Index of the last-treated vertex * @return The local center */ private Center<V, E> step3(Center<V, E> c, V vm, int i) { // System.out.println("step3: " + (i + 1)); V v = d_orderedVertices.get(c.getVertex0()).get(i + 1); // guaranteed to succeed if (de(xs(c), v) != de(xs(c), vm)) { return step4(c, vm, i + 1); } else if (de(xr(c), v) > de(xs(c), vm)) { return step3(c, v, i + 1); // v_m <- v* } else { return step3(c, vm, i + 1); } } private Center<V, E> step4(Center<V, E> c, V vm, int i) { V vbar = vm; if (isLastVertex(i)) { return step8(c, vbar); } else { vm = d_orderedVertices.get(c.getVertex0()).get(i); return step5(c, vm, vbar, i); } } private boolean isLastVertex(int i) { return i == d_graph.getVertexCount() - 1; } /** * Step 5: find all vertices v s.t. D_e(v, 0) = D_e(v_i, 0) and find the corresponding v_m. */ private Center<V, E> step5(Center<V, E> c, V vm, V vbar, int i) { // System.out.println("step5: " + (i + 1)); V v = d_orderedVertices.get(c.getVertex0()).get(i + 1); // guaranteed to succeed if (de(xr(c), v) != de(xr(c), vm)) { return step6(c, vm, vbar, i + 1); } else if (de(xs(c), v) > de(xs(c), vm)) { return step5(c, v, vbar, i + 1); // v_m <- v* } else { return step5(c, vm, vbar, i + 1); } } /** * Step 6: treatment of the point t_m. */ private Center<V, E> step6(Center<V, E> c, V vm, V vbar, int i) { Double tm = intersect(d_distance, c.getVertex0(), c.getVertex1(), l(c), vm, vbar); // System.out.println("Proposed center: " + c + ", intersect " + vm + " and " + vbar + " --> " + tm); if (tm == null) { return step7(c, vm, vbar, i); } else { PointOnEdge<V, E> xt = xt(c, tm); Center<V, E> ct = new Center<V, E>(xt, de(xt, vm)); if (ct.getRadius() < c.getRadius()) { return step7(ct, vm, vbar, i); } else { return step7(c, vm, vbar, i); } } } /** * Step 7: proceed to the next vertex. */ private Center<V, E> step7(Center<V, E> c, V vm, V vbar, int i) { if (de(xs(c), vm) > de(xs(c), vbar)) { vbar = vm; } if (isLastVertex(i)) { return step8(c, vbar); } else { return step5(c, d_orderedVertices.get(c.getVertex0()).get(i), vbar, i); } } private Center<V, E> step8(Center<V, E> c, V vbar) { V vn = d_orderedVertices.get(c.getVertex0()).get(d_graph.getVertexCount() - 1); // == v_r ? Double tm = intersect(d_distance, c.getVertex0(), c.getVertex1(), l(c), vn, vbar); // System.out.println("Proposed center: " + c + ", intersect " + vn + " and " + vbar + " --> " + tm); if (tm == null) { return c; } else { PointOnEdge<V, E> xt = xt(c, tm); Center<V, E> ct = new Center<V, E>(xt, de(xt, vn)); if (ct.getRadius() < c.getRadius()) { return ct; } else { return c; } } } } }