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

org.neo4j.gds.paths.yens.Yens Maven / Gradle / Ivy

/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j 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 .
 */
package org.neo4j.gds.paths.yens;

import org.neo4j.gds.Algorithm;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.core.concurrency.Concurrency;
import org.neo4j.gds.core.concurrency.DefaultPool;
import org.neo4j.gds.core.concurrency.RunWithConcurrency;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.paths.PathResult;
import org.neo4j.gds.paths.dijkstra.Dijkstra;
import org.neo4j.gds.paths.dijkstra.PathFindingResult;
import org.neo4j.gds.paths.dijkstra.SingleTarget;
import org.neo4j.gds.paths.yens.config.ShortestPathYensBaseConfig;
import org.neo4j.gds.termination.TerminationFlag;

import java.util.ArrayList;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

public final class Yens extends Algorithm {

    private final Graph graph;
    private final ShortestPathYensBaseConfig config;
    private final Concurrency concurrency;
    private final boolean trackRelationships;

    /**
     * Configure Yens to compute at most one source-target shortest path.
     */
    public static Yens sourceTarget(
        Graph graph,
        ShortestPathYensBaseConfig config,
        Concurrency concurrency,
        ProgressTracker progressTracker,
        TerminationFlag terminationFlag
    ) {
        // If the input graph is a multi-graph, we need to track
        // parallel relationships ids. This is necessary since shortest
        // paths can visit the same nodes via different relationships.
        //If not, we need to track which is the next neighbor.
        boolean shouldTrackRelationships = graph.isMultiGraph();

        return new Yens(graph, config, concurrency, shouldTrackRelationships, progressTracker, terminationFlag);
    }

    private Yens(
        Graph graph,
        ShortestPathYensBaseConfig config,
        Concurrency concurrency,
        boolean trackRelationships,
        ProgressTracker progressTracker,
        TerminationFlag terminationFlag
    ) {
        super(progressTracker);
        this.graph = graph;
        this.config = config;
        this.concurrency = concurrency;
        this.terminationFlag = terminationFlag;
        this.trackRelationships = trackRelationships;
    }

    @Override
    public PathFindingResult compute() {
        progressTracker.beginSubTask("Yens");
        var kShortestPaths = new ArrayList();
        // compute top 1 shortest path

        var shortestPath = findFirstPath();

        // no shortest path has been found
        if (shortestPath.isEmpty()) {
            return new PathFindingResult(Stream.empty(), progressTracker::endSubTask);
        }

        kShortestPaths.add(MutablePathResult.of(shortestPath.get()));

        var candidatePathsQueue = new CandidatePathsPriorityQueue();

        AtomicInteger currentSpurIndexId = new AtomicInteger(0);

        var tasks = createTasks(kShortestPaths, candidatePathsQueue, currentSpurIndexId);

        progressTracker.beginSubTask("Path growing");

        for (int i = 1; i < config.k(); i++) {
            var prevPath = kShortestPaths.get(i - 1);
            for (var task : tasks) {
                task.withPreviousPath(prevPath);
            }

            RunWithConcurrency.builder()
                .concurrency(concurrency)
                .tasks(tasks)
                .executor(DefaultPool.INSTANCE)
                .run();
            progressTracker.logProgress();

            if (candidatePathsQueue.isEmpty()) {
                break;
            }
            addPathToSolution(i, kShortestPaths, candidatePathsQueue, currentSpurIndexId);
        }
        progressTracker.endSubTask();

        return new PathFindingResult(
            kShortestPaths.stream().map(MutablePathResult::toPathResult),
            progressTracker::endSubTask
        );
    }

    private void addPathToSolution(
        int index,
        ArrayList kShortestPaths,
        CandidatePathsPriorityQueue candidatePathsQueue,
        AtomicInteger currentSpurIndexId
    ) {
        var pathToAdd = candidatePathsQueue.pop();
        int newIndex = (int) pathToAdd.index();
        currentSpurIndexId.set(newIndex);   //Apply lawler's modification
        pathToAdd.withIndex(index); //set the correct index to this path
        kShortestPaths.add(pathToAdd);

    }


    private ArrayList createTasks(
        ArrayList kShortestPaths,
        CandidatePathsPriorityQueue candidatePathsQueue,
        AtomicInteger currentSpurIndexId
    ) {
        var tasks = new ArrayList();
        for (int concurrentId = 0; concurrentId < concurrency.value(); ++concurrentId) {
            tasks.add(new YensTask(
                graph.concurrentCopy(),
                config.targetNode(),
                kShortestPaths,
                candidatePathsQueue,
                currentSpurIndexId,
                trackRelationships,
                config.k(),
                terminationFlag
            ));
        }
        return tasks;
    }

    private Optional findFirstPath() {
        var dijkstra = new Dijkstra(
            graph,
            graph.toMappedNodeId(config.sourceNode()),
            new SingleTarget(graph.toMappedNodeId(config.targetNode())),
            trackRelationships,
            Optional.empty(),
            progressTracker,
            terminationFlag
        );

        return dijkstra.compute().findFirst();
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy