/******************************************************************************* * Copyright 2012 University of Southern California * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This code was developed by the Information Integration Group as part * of the Karma project at the Information Sciences Institute of the * University of Southern California. For more information, publications, * and related projects, please see: http://www.isi.edu/integration ******************************************************************************/ package edu.isi.karma.modeling.alignment; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; import org.jgrapht.UndirectedGraph; import org.jgrapht.alg.BellmanFordShortestPath; import org.jgrapht.alg.DijkstraShortestPath; import org.jgrapht.alg.KruskalMinimumSpanningTree; import org.jgrapht.graph.Pseudograph; import org.jgrapht.graph.WeightedMultigraph; import edu.isi.karma.rep.alignment.Link; import edu.isi.karma.rep.alignment.Node; import edu.isi.karma.rep.alignment.SimpleLink; /** * The approach is taken from the paper "A fast algorithm for steiner trees" by L. Kou et. al. * @author mohsen * */ public class SteinerTree { static Logger logger = Logger.getLogger(SteinerTree.class); UndirectedGraph<Node, Link> graph; WeightedMultigraph<Node, Link> tree; List<Node> steinerNodes; public SteinerTree(UndirectedGraph<Node, Link> graph, List<Node> steinerNodes) { this.graph = graph; this.steinerNodes = steinerNodes; runAlgorithm(); } /** * Construct the complete undirected distance graph G1=(V1,EI,d1) from G and S. * @return */ private Pseudograph<Node, Link> step1() { logger.debug("<enter"); Pseudograph<Node, Link> g = new Pseudograph<Node, Link>(Link.class); for (Node n : this.steinerNodes) { g.addVertex(n); } BellmanFordShortestPath<Node, Link> path; for (Node n1 : this.steinerNodes) { path = new BellmanFordShortestPath<Node, Link>(this.graph, n1); for (Node n2 : this.steinerNodes) { if (n1.equals(n2)) continue; if (g.containsEdge(n1, n2)) continue; Link e = new SimpleLink(null, null); g.addEdge(n1, n2, e); g.setEdgeWeight(e, path.getCost(n2)); } } logger.debug("exit>"); return g; } /** * Find the minimal spanning tree, T1, of G1. (If there are several minimal spanning trees, pick an arbitrary one.) * @param g1 * @return */ private WeightedMultigraph<Node, Link> step2(Pseudograph<Node, Link> g1) { logger.debug("<enter"); KruskalMinimumSpanningTree<Node, Link> mst = new KruskalMinimumSpanningTree<Node, Link>(g1); // System.out.println("Total MST Cost: " + mst.getSpanningTreeCost()); Set<Link> edges = mst.getEdgeSet(); WeightedMultigraph<Node, Link> g2 = new WeightedMultigraph<Node, Link>(Link.class); List<Link> edgesSortedById = new ArrayList<Link>(); for (Link e : edges) edgesSortedById.add(e); Collections.sort(edgesSortedById); for (Link edge : edgesSortedById) { g2.addVertex(edge.getSource()); g2.addVertex(edge.getTarget()); g2.addEdge( edge.getSource(), edge.getTarget(), edge); } logger.debug("exit>"); return g2; } /** * Construct the subgraph, Gs, of G by replacing each edge in T1 by its corresponding shortest path in G. * (If there are several shortest paths, pick an arbitrary one.) * @param g2 * @return */ private WeightedMultigraph<Node, Link> step3(WeightedMultigraph<Node, Link> g2) { logger.debug("<enter"); WeightedMultigraph<Node, Link> g3 = new WeightedMultigraph<Node, Link>(Link.class); Set<Link> edges = g2.edgeSet(); DijkstraShortestPath<Node, Link> path; Node source, target; for (Link edge : edges) { source = edge.getSource(); target = edge.getTarget(); path = new DijkstraShortestPath<Node, Link>(this.graph, source, target); List<Link> pathEdges = path.getPathEdgeList(); if (pathEdges == null) continue; for (int i = 0; i < pathEdges.size(); i++) { if (g3.edgeSet().contains(pathEdges.get(i))) continue; source = pathEdges.get(i).getSource(); target = pathEdges.get(i).getTarget(); if (!g3.vertexSet().contains(source) ) g3.addVertex(source); if (!g3.vertexSet().contains(target) ) g3.addVertex(target); g3.addEdge(source, target, pathEdges.get(i)); } } logger.debug("exit>"); return g3; } /** * Find the minimal spanning tree, Ts, of Gs. (If there are several minimal spanning trees, pick an arbitrary one.) * @param g3 * @return */ private WeightedMultigraph<Node, Link> step4(WeightedMultigraph<Node, Link> g3) { logger.debug("<enter"); KruskalMinimumSpanningTree<Node, Link> mst = new KruskalMinimumSpanningTree<Node, Link>(g3); // System.out.println("Total MST Cost: " + mst.getSpanningTreeCost()); Set<Link> edges = mst.getEdgeSet(); WeightedMultigraph<Node, Link> g4 = new WeightedMultigraph<Node, Link>(Link.class); List<Link> edgesSortedById = new ArrayList<Link>(); for (Link e : edges) edgesSortedById.add(e); Collections.sort(edgesSortedById); for (Link edge : edgesSortedById) { g4.addVertex(edge.getSource()); g4.addVertex(edge.getTarget()); g4.addEdge( edge.getSource(), edge.getTarget(), edge); } logger.debug("exit>"); return g4; } /** * Construct a Steiner tree, Th, from Ts by deleting edges in Ts,if necessary, * so that all the leaves in Th are Steiner points. * @param g4 * @return */ private WeightedMultigraph<Node, Link> step5(WeightedMultigraph<Node, Link> g4) { logger.debug("<enter"); WeightedMultigraph<Node, Link> g5 = g4; List<Node> nonSteinerLeaves = new ArrayList<Node>(); Set<Node> vertexSet = g4.vertexSet(); for (Node vertex : vertexSet) { if (g5.degreeOf(vertex) == 1 && steinerNodes.indexOf(vertex) == -1) { nonSteinerLeaves.add(vertex); } } Node source, target; for (int i = 0; i < nonSteinerLeaves.size(); i++) { source = nonSteinerLeaves.get(i); do { Link e = g5.edgesOf(source).toArray(new Link[0])[0]; target = this.graph.getEdgeTarget(e); // this should not happen, but just in case of ... if (target.equals(source)) target = e.getSource(); g5.removeVertex(source); source = target; } while(g5.degreeOf(source) == 1 && steinerNodes.indexOf(source) == -1); } logger.debug("exit>"); return g5; } private void runAlgorithm() { logger.debug("<enter"); logger.debug("step1 ..."); Pseudograph<Node, Link> g1 = step1(); // logger.info("after doing step 1 ...................................................................."); // GraphUtil.printGraphSimple(g1); // GraphUtil.printGraph(g1); if (g1.vertexSet().size() < 2) { this.tree = new WeightedMultigraph<Node, Link>(Link.class); for (Node n : g1.vertexSet()) this.tree.addVertex(n); return; } logger.debug("step2 ..."); WeightedMultigraph<Node, Link> g2 = step2(g1); // logger.info("after doing step 2 ...................................................................."); // GraphUtil.printGraphSimple(g2); // GraphUtil.printGraph(g2); logger.debug("step3 ..."); WeightedMultigraph<Node, Link> g3 = step3(g2); // logger.info("after doing step 3 ...................................................................."); // GraphUtil.printGraphSimple(g3); // GraphUtil.printGraph(g3); logger.debug("step4 ..."); WeightedMultigraph<Node, Link> g4 = step4(g3); // logger.info("after doing step 4 ...................................................................."); // GraphUtil.printGraphSimple(g4); // GraphUtil.printGraph(g4); logger.debug("step5 ..."); WeightedMultigraph<Node, Link> g5 = step5(g4); // logger.info("after doing step 5 ...................................................................."); // GraphUtil.printGraphSimple(g5); // GraphUtil.printGraph(g5); this.tree = g5; logger.debug("exit>"); } public WeightedMultigraph<Node, Link> getSteinerTree() { return this.tree; } // public static void main(String[] args) { // // WeightedMultigraph<Vertex, LabeledWeightedEdge> g = // new WeightedMultigraph<Vertex, LabeledWeightedEdge>(LabeledWeightedEdge.class); // // LabeledWeightedEdge e1 = new LabeledWeightedEdge("e1"); // LabeledWeightedEdge e2 = new LabeledWeightedEdge("e2"); // LabeledWeightedEdge e3 = new LabeledWeightedEdge("e3"); // LabeledWeightedEdge e4 = new LabeledWeightedEdge("e4"); // LabeledWeightedEdge e5 = new LabeledWeightedEdge("e5"); // LabeledWeightedEdge e6 = new LabeledWeightedEdge("e6"); // LabeledWeightedEdge e7 = new LabeledWeightedEdge("e7"); // LabeledWeightedEdge e8 = new LabeledWeightedEdge("e8"); // LabeledWeightedEdge e9 = new LabeledWeightedEdge("e9"); // LabeledWeightedEdge e10 = new LabeledWeightedEdge("e10"); // LabeledWeightedEdge e11 = new LabeledWeightedEdge("e11"); // LabeledWeightedEdge e12 = new LabeledWeightedEdge("e12"); // // Vertex v1 = new Vertex("v1"); // Vertex v2 = new Vertex("v2"); // Vertex v3 = new Vertex("v3"); // Vertex v4 = new Vertex("v4"); // Vertex v5 = new Vertex("v5"); // Vertex v6 = new Vertex("v6"); // Vertex v7 = new Vertex("v7"); // Vertex v8 = new Vertex("v8"); // Vertex v9 = new Vertex("v9"); // // g.addVertex(v1); // g.addVertex(v2); // g.addVertex(v3); // g.addVertex(v4); // g.addVertex(v5); // g.addVertex(v6); // g.addVertex(v7); // g.addVertex(v8); // g.addVertex(v9); // // List<Vertex> steinerNodes = new ArrayList<Vertex>(); // steinerNodes.add(v1); // steinerNodes.add(v2); // steinerNodes.add(v3); // steinerNodes.add(v4); // // g.addEdge(v1, v2, e1); // g.addEdge(v2, v3, e2); // g.addEdge(v3, v4, e3); // g.addEdge(v4, v5, e4); // g.addEdge(v5, v6, e5); // g.addEdge(v6, v7, e6); // g.addEdge(v7, v8, e7); // g.addEdge(v8, v9, e8); // g.addEdge(v9, v1, e9); // g.addEdge(v5, v9, e10); // g.addEdge(v2, v6, e11); // g.addEdge(v3, v5, e12); // // g.setEdgeWeight(e1, 10.0); // g.setEdgeWeight(e2, 8.0); // g.setEdgeWeight(e3, 9.0); // g.setEdgeWeight(e4, 2.0); // g.setEdgeWeight(e5, 1.0); // g.setEdgeWeight(e6, 1.1); // g.setEdgeWeight(e7, 0.3); // g.setEdgeWeight(e8, 0.5); // g.setEdgeWeight(e9, 1.0); // g.setEdgeWeight(e10, 1.0); // g.setEdgeWeight(e11, 1.0); // g.setEdgeWeight(e12, 2.0); // // SteinerTree st = new SteinerTree(g, steinerNodes); // WeightedMultigraph<Vertex, LabeledWeightedEdge> steiner = st.getSteinerTree(); // double sum = 0.0; // for (LabeledWeightedEdge edge : steiner.edgeSet()) { // sum += steiner.getEdgeWeight(edge); // } // // System.out.println("Steiner Cost: " + sum); // // } }