/* * Copyright 2015 Open Networking Laboratory * * 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. */ package org.onlab.graph; import java.util.ArrayList; import java.util.Set; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.HashSet; import java.util.stream.Collectors; /** * Suurballe shortest-path graph search algorithm capable of finding both * a shortest path, as well as a backup shortest path, between a source and a destination * such that the sum of the path lengths is minimized. */ public class SuurballeGraphSearch> extends DijkstraGraphSearch { @Override public Result search(Graph graph, V src, V dst, EdgeWeight weight, int maxPaths) { if (weight == null) { weight = edge -> 1; } List> dpps = new ArrayList<>(); final EdgeWeight weightf = weight; DefaultResult firstDijkstraS = (DefaultResult) super.search(graph, src, dst, weight, ALL_PATHS); DefaultResult firstDijkstra = (DefaultResult) super.search(graph, src, null, weight, ALL_PATHS); //choose an arbitrary shortest path to run Suurballe on Path shortPath = null; if (firstDijkstraS.paths().size() == 0) { return firstDijkstraS; } for (Path p: firstDijkstraS.paths()) { shortPath = p; //transforms the graph so tree edges have 0 weight EdgeWeight> modified = edge -> { if (classE().isInstance(edge)) { return weightf.weight((E) (edge)) + firstDijkstra.cost(edge.src()) - firstDijkstra.cost(edge.dst()); } return 0; }; EdgeWeight modified2 = edge -> weightf.weight(edge) + firstDijkstra.cost(edge.src()) - firstDijkstra.cost(edge.dst()); //create a residual graph g' by removing all src vertices and reversing 0 length path edges MutableGraph> gt = mutableCopy(graph); Map, E> revToEdge = new HashMap<>(); graph.getEdgesTo(src).forEach(gt::removeEdge); for (E edge: shortPath.edges()) { gt.removeEdge(edge); Edge reverse = new Edge() { final Edge orig = edge; public V src() { return orig.dst(); } public V dst() { return orig.src(); } public String toString() { return "ReversedEdge " + "src=" + src() + " dst=" + dst(); } }; revToEdge.put(reverse, edge); gt.addEdge(reverse); } //rerun dijkstra on the temporary graph to get a second path Result> secondDijkstra; secondDijkstra = new DijkstraGraphSearch>().search(gt, src, dst, modified, ALL_PATHS); Path> residualShortPath = null; if (secondDijkstra.paths().size() == 0) { dpps.add(new DisjointPathPair(shortPath, null)); continue; } for (Path p2: secondDijkstra.paths()) { residualShortPath = p2; MutableGraph roundTrip = mutableCopy(graph); List tmp = roundTrip.getEdges().stream().collect(Collectors.toList()); tmp.forEach(roundTrip::removeEdge); shortPath.edges().forEach(roundTrip::addEdge); if (residualShortPath != null) { for (Edge edge: residualShortPath.edges()) { if (classE().isInstance(edge)) { roundTrip.addEdge((E) edge); } else { roundTrip.removeEdge(revToEdge.get(edge)); } } } //Actually build the final result DefaultResult lastSearch = (DefaultResult) super.search(roundTrip, src, dst, weight, ALL_PATHS); Path path1 = lastSearch.paths().iterator().next(); path1.edges().forEach(roundTrip::removeEdge); Set> bckpaths = super.search(roundTrip, src, dst, weight, ALL_PATHS).paths(); Path backup = null; if (bckpaths.size() != 0) { backup = bckpaths.iterator().next(); } dpps.add(new DisjointPathPair<>(path1, backup)); } } for (int i = dpps.size() - 1; i > 0; i--) { if (dpps.get(i).size() <= 1) { dpps.remove(i); } } return new Result() { final DefaultResult search = firstDijkstra; public V src() { return src; } public V dst() { return dst; } public Set> paths() { Set> pathsD = new HashSet<>(); int paths = 0; for (DisjointPathPair path: dpps) { pathsD.add((Path) path); paths++; if (paths == maxPaths) { break; } } return pathsD; } public Map costs() { return search.costs(); } public Map> parents() { return search.parents(); } }; } private Class clazzV; public Class classV() { return clazzV; } private Class clazzE; public Class classE() { return clazzE; } /** * Creates a mutable copy of an immutable graph. * * @param graph immutable graph * @return mutable copy */ public MutableGraph mutableCopy(Graph graph) { clazzV = graph.getVertexes().iterator().next().getClass(); clazzE = graph.getEdges().iterator().next().getClass(); return new MutableAdjacencyListsGraph(graph.getVertexes(), graph.getEdges()); } }