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

com.graphhopper.routing.AbstractBidirAlgo Maven / Gradle / Ivy

Go to download

GraphHopper is a fast and memory efficient Java road routing engine working seamlessly with OpenStreetMap data.

There is a newer version: 10.0
Show newest version
/*
 *  Licensed to GraphHopper GmbH under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for
 *  additional information regarding copyright ownership.
 *
 *  GraphHopper GmbH licenses this file to you 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 com.graphhopper.routing;

import com.carrotsearch.hppc.IntObjectMap;
import com.graphhopper.coll.GHIntObjectHashMap;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.util.EdgeIterator;

import java.util.Collections;
import java.util.List;
import java.util.PriorityQueue;

import static com.graphhopper.util.EdgeIterator.ANY_EDGE;

public abstract class AbstractBidirAlgo implements EdgeToEdgeRoutingAlgorithm {
    protected final TraversalMode traversalMode;
    protected int from;
    protected int to;
    protected int fromOutEdge;
    protected int toInEdge;
    protected IntObjectMap bestWeightMapFrom;
    protected IntObjectMap bestWeightMapTo;
    protected IntObjectMap bestWeightMapOther;
    protected SPTEntry currFrom;
    protected SPTEntry currTo;
    protected SPTEntry bestFwdEntry;
    protected SPTEntry bestBwdEntry;
    protected double bestWeight = Double.MAX_VALUE;
    protected int maxVisitedNodes = Integer.MAX_VALUE;
    protected long timeoutMillis = Long.MAX_VALUE;
    private long finishTimeMillis = Long.MAX_VALUE;
    PriorityQueue pqOpenSetFrom;
    PriorityQueue pqOpenSetTo;
    protected boolean updateBestPath = true;
    protected boolean finishedFrom;
    protected boolean finishedTo;
    int visitedCountFrom;
    int visitedCountTo;
    private boolean alreadyRun;

    public AbstractBidirAlgo(TraversalMode traversalMode) {
        this.traversalMode = traversalMode;
        fromOutEdge = ANY_EDGE;
        toInEdge = ANY_EDGE;
    }

    protected void initCollections(int size) {
        pqOpenSetFrom = new PriorityQueue<>(size);
        bestWeightMapFrom = new GHIntObjectHashMap<>(size);

        pqOpenSetTo = new PriorityQueue<>(size);
        bestWeightMapTo = new GHIntObjectHashMap<>(size);
    }

    /**
     * Creates the root shortest path tree entry for the forward or backward search.
     */
    protected abstract SPTEntry createStartEntry(int node, double weight, boolean reverse);

    @Override
    public List calcPaths(int from, int to) {
        return Collections.singletonList(calcPath(from, to));
    }

    @Override
    public Path calcPath(int from, int to) {
        return calcPath(from, to, ANY_EDGE, ANY_EDGE);
    }

    @Override
    public Path calcPath(int from, int to, int fromOutEdge, int toInEdge) {
        if ((fromOutEdge != ANY_EDGE || toInEdge != ANY_EDGE) && !traversalMode.isEdgeBased()) {
            throw new IllegalArgumentException("Restricting the start/target edges is only possible for edge-based graph traversal");
        }
        this.fromOutEdge = fromOutEdge;
        this.toInEdge = toInEdge;
        checkAlreadyRun();
        setupFinishTime();
        init(from, 0, to, 0);
        runAlgo();
        return extractPath();
    }

    void init(int from, double fromWeight, int to, double toWeight) {
        initFrom(from, fromWeight);
        initTo(to, toWeight);
        postInit(from, to);
    }

    protected void initFrom(int from, double weight) {
        this.from = from;
        currFrom = createStartEntry(from, weight, false);
        pqOpenSetFrom.add(currFrom);
        if (!traversalMode.isEdgeBased()) {
            bestWeightMapFrom.put(from, currFrom);
        }
    }

    protected void initTo(int to, double weight) {
        this.to = to;
        currTo = createStartEntry(to, weight, true);
        pqOpenSetTo.add(currTo);
        if (!traversalMode.isEdgeBased()) {
            bestWeightMapTo.put(to, currTo);
        }
    }

    protected void postInit(int from, int to) {
        if (!traversalMode.isEdgeBased()) {
            if (updateBestPath) {
                bestWeightMapOther = bestWeightMapFrom;
                updateBestPath(Double.POSITIVE_INFINITY, currFrom, EdgeIterator.NO_EDGE, to, true);
            }
        } else if (from == to && fromOutEdge == ANY_EDGE && toInEdge == ANY_EDGE) {
            // special handling if start and end are the same and no directions are restricted
            // the resulting weight should be zero
            if (currFrom.weight != 0 || currTo.weight != 0) {
                throw new IllegalStateException("If from=to, the starting weight must be zero for from and to");
            }
            bestFwdEntry = currFrom;
            bestBwdEntry = currTo;
            bestWeight = 0;
            finishedFrom = true;
            finishedTo = true;
            return;
        }
        postInitFrom();
        postInitTo();
    }

    protected abstract void postInitFrom();

    protected abstract void postInitTo();

    protected void runAlgo() {
        while (!finished() && !isMaxVisitedNodesExceeded() && !isTimeoutExceeded()) {
            if (!finishedFrom)
                finishedFrom = !fillEdgesFrom();

            if (!finishedTo)
                finishedTo = !fillEdgesTo();
        }
    }

    // http://www.cs.princeton.edu/courses/archive/spr06/cos423/Handouts/EPP%20shortest%20path%20algorithms.pdf
    // a node from overlap may not be on the best path!
    // => when scanning an arc (v, w) in the forward search and w is scanned in the reverseOrder
    //    search, update extractPath = μ if df (v) + (v, w) + dr (w) < μ
    protected boolean finished() {
        if (finishedFrom || finishedTo)
            return true;

        return currFrom.weight + currTo.weight >= bestWeight;
    }

    abstract boolean fillEdgesFrom();

    abstract boolean fillEdgesTo();

    protected void updateBestPath(double edgeWeight, SPTEntry entry, int origEdgeIdForCH, int traversalId, boolean reverse) {
        assert traversalMode.isEdgeBased() != Double.isInfinite(edgeWeight);
        SPTEntry entryOther = bestWeightMapOther.get(traversalId);
        if (entryOther == null)
            return;

        // update μ
        double weight = entry.getWeightOfVisitedPath() + entryOther.getWeightOfVisitedPath();
        if (traversalMode.isEdgeBased()) {
            if (getIncomingEdge(entryOther) != getIncomingEdge(entry))
                throw new IllegalStateException("cannot happen for edge based execution of " + getName());

            // prevents the path to contain the edge at the meeting point twice and subtracts the weight (excluding turn weight => no previous edge)
            entry = entry.getParent();
            weight -= edgeWeight;
        }

        if (weight < bestWeight) {
            bestFwdEntry = reverse ? entryOther : entry;
            bestBwdEntry = reverse ? entry : entryOther;
            bestWeight = weight;
        }
    }

    protected abstract double getInEdgeWeight(SPTEntry entry);

    protected int getIncomingEdge(SPTEntry entry) {
        return entry.edge;
    }

    abstract protected Path extractPath();

    protected boolean fromEntryCanBeSkipped() {
        return false;
    }

    protected boolean fwdSearchCanBeStopped() {
        return false;
    }

    protected boolean toEntryCanBeSkipped() {
        return false;
    }

    protected boolean bwdSearchCanBeStopped() {
        return false;
    }

    protected double getCurrentFromWeight() {
        return currFrom.weight;
    }

    protected double getCurrentToWeight() {
        return currTo.weight;
    }

    IntObjectMap getBestFromMap() {
        return bestWeightMapFrom;
    }

    IntObjectMap getBestToMap() {
        return bestWeightMapTo;
    }

    void setBestOtherMap(IntObjectMap other) {
        bestWeightMapOther = other;
    }

    protected void setUpdateBestPath(boolean b) {
        updateBestPath = b;
    }

    @Override
    public int getVisitedNodes() {
        return visitedCountFrom + visitedCountTo;
    }

    void setToDataStructures(AbstractBidirAlgo other) {
        to = other.to;
        toInEdge = other.toInEdge;
        pqOpenSetTo = other.pqOpenSetTo;
        bestWeightMapTo = other.bestWeightMapTo;
        finishedTo = other.finishedTo;
        currTo = other.currTo;
        visitedCountTo = other.visitedCountTo;
        // inEdgeExplorer
    }

    @Override
    public void setMaxVisitedNodes(int numberOfNodes) {
        this.maxVisitedNodes = numberOfNodes;
    }

    @Override
    public void setTimeoutMillis(long timeoutMillis) {
        this.timeoutMillis = timeoutMillis;
    }

    protected void checkAlreadyRun() {
        if (alreadyRun)
            throw new IllegalStateException("Create a new instance per call");

        alreadyRun = true;
    }

    protected void setupFinishTime() {
        try {
            this.finishTimeMillis = Math.addExact(System.currentTimeMillis(), timeoutMillis);
        } catch (ArithmeticException e) {
            this.finishTimeMillis = Long.MAX_VALUE;
        }
    }

    @Override
    public String getName() {
        return getClass().getSimpleName();
    }

    protected boolean isMaxVisitedNodesExceeded() {
        return maxVisitedNodes < getVisitedNodes();
    }

    protected boolean isTimeoutExceeded() {
        return finishTimeMillis < Long.MAX_VALUE && System.currentTimeMillis() > finishTimeMillis;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy