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

org.onlab.graph.SuurballeGraphSearch Maven / Gradle / Ivy

There is a newer version: 2.7.0
Show newest version
/*
 * Copyright 2015-present Open Networking Foundation
 *
 * 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 com.google.common.collect.Sets;

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
    protected Result internalSearch(Graph graph, V src, V dst,
                               EdgeWeigher weigher, int maxPaths) {
        // FIXME: This method needs to be refactored as it is difficult to follow and debug.

        // FIXME: There is a defect here triggered by 3+ edges between the same vertices,
        // which makes an attempt to produce looping paths. Protection against
        // this was added to AbstractGraphPathSearch, but the root issue remains here.

        // FIXME: There is a defect here where not all paths are truly disjoint.
        // This class needs to filter its own results to make sure that the
        // paths are indeed disjoint. Temporary fix for this is provided, but
        // the issue needs to be addressed through refactoring.

        EdgeWeigher weightf = weigher;
        DefaultResult firstDijkstraS = (DefaultResult) super.internalSearch(
                graph, src, dst, weigher, ALL_PATHS);
        DefaultResult firstDijkstra = (DefaultResult) super.internalSearch(
                graph, src, null, weigher, ALL_PATHS);

        //choose an arbitrary shortest path to run Suurballe on
        Path shortPath = null;
        if (firstDijkstraS.paths().isEmpty()) {
            return firstDijkstraS;
        }

        DisjointPathResult result = new DisjointPathResult(firstDijkstra, src, dst, maxPaths);

        for (Path p: firstDijkstraS.paths()) {
            shortPath = p;
            //transforms the graph so tree edges have 0 weight
            EdgeWeigher modified = new EdgeWeigher() {
                @Override
                public Weight weight(E edge) {
                    return edge instanceof ReverseEdge ?
                            weightf.getInitialWeight() :
                            (weightf.weight(edge).isNegative() ?
                                    new ScalarWeight(-1.0) :
                                    weightf.weight(edge).merge(firstDijkstra.cost(edge.src()))
                                            .subtract(firstDijkstra.cost(edge.dst())));
                }

                @Override
                public Weight getInitialWeight() {
                    return weightf.getInitialWeight();
                }

                @Override
                public Weight getNonViableWeight() {
                    return weightf.getNonViableWeight();
                }
            };

            EdgeWeigher modified2 = new EdgeWeigher() {
                @Override
                public Weight weight(E edge) {
                    return weightf.weight(edge).merge(firstDijkstra.cost(edge.src()))
                            .subtract(firstDijkstra.cost(edge.dst()));
                }

                @Override
                public Weight getInitialWeight() {
                    return weightf.getInitialWeight();
                }

                @Override
                public Weight getNonViableWeight() {
                    return weightf.getNonViableWeight();
                }
            };

            //create a residual graph g' by removing all src vertices and reversing 0 length path edges
            MutableGraph gt = mutableCopy(graph);

            Map revToEdge = new HashMap<>();
            graph.getEdgesTo(src).forEach(gt::removeEdge);
            for (E edge: shortPath.edges()) {
                gt.removeEdge(edge);
                Edge reverse = new ReverseEdge(edge);
                revToEdge.put((E) reverse, edge);
                gt.addEdge((E) reverse);
            }

            //rerun dijkstra on the temporary graph to get a second path
            Result secondDijkstra = new DijkstraGraphSearch()
                    .search(gt, src, dst, modified, ALL_PATHS);

            Path residualShortPath = null;
            if (secondDijkstra.paths().isEmpty()) {
                result.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 (edge instanceof ReverseEdge) {
                            roundTrip.removeEdge(revToEdge.get(edge));
                        } else {
                            roundTrip.addEdge((E) edge);
                        }
                    }
                }
                //Actually build the final result
                DefaultResult lastSearch = (DefaultResult)
                        super.internalSearch(roundTrip, src, dst, weigher, ALL_PATHS);
                Path primary = lastSearch.paths().iterator().next();
                primary.edges().forEach(roundTrip::removeEdge);

                Set> backups = super.internalSearch(roundTrip, src, dst,
                        weigher, ALL_PATHS).paths();

                // Find first backup path that does not share any nodes with the primary
                for (Path backup : backups) {
                    if (isDisjoint(primary, backup)) {
                        result.dpps.add(new DisjointPathPair<>(primary, backup));
                        break;
                    }
                }
            }
        }

        for (int i = result.dpps.size() - 1; i > 0; i--) {
            if (result.dpps.get(i).size() <= 1) {
                result.dpps.remove(i);
            }
        }

        result.buildPaths();
        return result;
    }

    private boolean isDisjoint(Path a, Path b) {
        return Sets.intersection(vertices(a), vertices(b)).isEmpty();
    }

    private Set vertices(Path p) {
        Set set = new HashSet<>();
        p.edges().forEach(e -> set.add(e.src()));
        set.remove(p.src());
        return set;
    }

    /**
     * Creates a mutable copy of an immutable graph.
     *
     * @param graph   immutable graph
     * @return mutable copy
     */
    private MutableGraph mutableCopy(Graph graph) {
        return new MutableAdjacencyListsGraph<>(graph.getVertexes(), graph.getEdges());
    }

    private static final class ReverseEdge extends AbstractEdge {
        private ReverseEdge(Edge edge) {
            super(edge.dst(), edge.src());
        }

        @Override
        public String toString() {
            return "ReversedEdge " + "src=" + src() + " dst=" + dst();
        }
    }

    // Auxiliary result for disjoint path search
    private final class DisjointPathResult implements AbstractGraphPathSearch.Result {

        private final Result searchResult;
        private final V src, dst;
        private final int maxPaths;
        private final List> dpps = new ArrayList<>();
        private final Set> disjointPaths = new HashSet<>();

        private DisjointPathResult(Result searchResult, V src, V dst, int maxPaths) {
            this.searchResult = searchResult;
            this.src = src;
            this.dst = dst;
            this.maxPaths = maxPaths;
        }

        @Override
        public V src() {
            return src;
        }

        @Override
        public V dst() {
            return dst;
        }

        @Override
        public Set> paths() {
            return disjointPaths;
        }

        private void buildPaths() {
            int paths = 0;
            for (DisjointPathPair path: dpps) {
                disjointPaths.add(path);
                paths++;
                if (paths == maxPaths) {
                    break;
                }
            }
        }

        @Override
        public Map> parents() {
            return searchResult.parents();
        }

        @Override
        public Map costs() {
            return searchResult.costs();
        }
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy