diff --git a/scripts/builtin/shortestPath.dml b/scripts/builtin/shortestPath.dml new file mode 100644 index 00000000000..c433845347e --- /dev/null +++ b/scripts/builtin/shortestPath.dml @@ -0,0 +1,98 @@ +#------------------------------------------------------------- +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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. +# +#------------------------------------------------------------- +# +# Computes the minimum distances (shortest-path) between a single +# source vertex and every other vertex in the graph. +# +# Grzegorz Malewicz, Matthew H. Austern, Aart J. C. Bilk, +# James C. Dehnert, Ikkan Horn, Naty Leiser and Grzegorz Czajkowski: +# Pregel: A System for Large-Scale Graph Processing +# +#------------------------------------------------------------------------------ +# +#G Input matrix (double), adjacency matrix of the labeled graph: +# - Such graph can be directed (G is symmetric) or undirected +# (G is not symmetric). +# - The values of G can be 0/1 (just specifying whether the nodes are +# connected or not) or integer values (representing the weight of the edjes +# or the distances between nodes, 0 if not connected). +# +#sourceNode node index to calculate the shortest paths to all other nodes. +# +#maxi max number of iterations accepted (0 for FALSE, i.e. max number of iterations +# not defined) +# +#verbose flag for verbose debug output +#------------------------------------------------------------------------------ +# +#C Output matrix (double) of minimum distances (shortest-path) between vertices: +# - The value of the ith row and the jth column of the output matrix is +# the minimum distance shortest-path from vertex i to vertex j. +# - When the value of the minimum distance is infinity, the two nodes are not +# connected. +#------------------------------------------------------------------------------ + + +m_shortestPath = function(Matrix[Double] G, Integer maxi = 0, Integer sourceNode, Boolean verbose = FALSE) + return (Matrix[Double] C) +{ + if(verbose) { + print("SHORTEST PATH CALCULATION"); + } + + if(min(G) < 0){ + stop("All values in G must be positive") + } + + if(ncol(G) != nrow(G)){ + stop("Not correct matrix dimensions") + } + + matrixSize = nrow(G) + + G = replace(target=G, pattern=0, replacement=Inf) + + # initialize the matrix of minimum distances with "infinity" values: + minDistVector = matrix(Inf,rows=matrixSize,cols=1) + + # update minimum distance from the sourceNode to itself to 0: + minDistVector[sourceNode,1] = 0 + + iter = 1 + diff = Inf; + while( diff > 0 & (maxi==0 | iter<=maxi) ) { + w = t(colMins(G + minDistVector)) + u = min(w, minDistVector); + diff = sum(u != minDistVector) + minDistVector = u; # update assignment + if( verbose ){ + print("Shortest Path: iter = "+iter+", #diff = "+diff); + } + iter = iter + 1; + } + + C=minDistVector + if(verbose) { + print("SHORTEST PATH CALCULATION FINISHED, CHECK OUTPUT MATRIX OF MINIMUM DISTANCES:"); + print(toString(C)) + } +} + diff --git a/src/main/java/org/apache/sysds/common/Builtins.java b/src/main/java/org/apache/sysds/common/Builtins.java index 27a0d73a356..d60ad82fdf3 100644 --- a/src/main/java/org/apache/sysds/common/Builtins.java +++ b/src/main/java/org/apache/sysds/common/Builtins.java @@ -217,6 +217,7 @@ public enum Builtins { SEQ("seq", false), SHERLOCK("sherlock", true), SHERLOCKPREDICT("sherlockPredict", true), + SHORTESTPATH("shortestPath", true), SIGMOID("sigmoid", true), // 1 / (1 + exp(-X)) SIGN("sign", false), SIN("sin", false), diff --git a/src/test/java/org/apache/sysds/test/functions/builtin/BuiltinShortestPathTest.java b/src/test/java/org/apache/sysds/test/functions/builtin/BuiltinShortestPathTest.java new file mode 100644 index 00000000000..825c5ca1225 --- /dev/null +++ b/src/test/java/org/apache/sysds/test/functions/builtin/BuiltinShortestPathTest.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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 org.apache.sysds.test.functions.builtin; + +import java.util.HashMap; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.sysds.common.Types; +import org.apache.sysds.lops.LopProperties; +import org.apache.sysds.runtime.matrix.data.MatrixValue; +import org.apache.sysds.test.AutomatedTestBase; +import org.apache.sysds.test.TestConfiguration; +import org.apache.sysds.test.TestUtils; + +public class BuiltinShortestPathTest extends AutomatedTestBase { + private final static String TEST_NAME = "shortestPathTest"; + private final static String TEST_DIR = "functions/builtin/"; + private static final String TEST_CLASS_DIR = TEST_DIR + BuiltinShortestPathTest.class.getSimpleName() + "/"; + private final static double eps = 1e-10; + + @Override + public void setUp() { + TestUtils.clearAssertionInformation(); + addTestConfiguration(TEST_NAME,new TestConfiguration(TEST_CLASS_DIR, TEST_NAME,new String[]{"r"})); + } + + @Test + public void testShortestPathNode1CP() { + runShortestPathNodeTest(1, new double[][] {{0}, {2}, {5}, {5}}); + } + + @Test + public void testShortestPathNode2CP() { + runShortestPathNodeTest(2, new double[][] {{1}, {0}, {4}, {5}}); + } + + @Test + public void testShortestPathNode3CP() { + runShortestPathNodeTest(3, new double[][] {{4}, {3}, {0}, {1}}); + } + + + + private void runShortestPathNodeTest(int node, double [][] Res) + { + loadTestConfiguration(getTestConfiguration(TEST_NAME)); + + String HOME = SCRIPT_DIR + TEST_DIR; + fullDMLScriptName = HOME + TEST_NAME + ".dml"; + programArgs = new String[]{ "-args", + input("X"), String.valueOf(node), output("R")}; + + double[][] X = {{0, 2, 5, 5 }, + {1, 0, 4, 10}, + {0, 3, 0, 1 }, + {3, 2, 0, 0 }}; + + writeInputMatrixWithMTD("X", X, true); + + runTest(true, false, null, -1); + + HashMap dmlfile = readDMLMatrixFromOutputDir("R"); + double[][] Y = TestUtils.convertHashMapToDoubleArray(dmlfile); + TestUtils.compareMatrices(Res, Y, eps); + + } +} diff --git a/src/test/scripts/functions/builtin/shortestPathTest.dml b/src/test/scripts/functions/builtin/shortestPathTest.dml new file mode 100644 index 00000000000..3e5454b1081 --- /dev/null +++ b/src/test/scripts/functions/builtin/shortestPathTest.dml @@ -0,0 +1,26 @@ + +#------------------------------------------------------------- +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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. +# +#------------------------------------------------------------- + +X = read($1) +C = shortestPath(G=X,sourceNode = $2) + +write(C, $3)