This is an automated email from the ASF dual-hosted git repository.

ssiddiqi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/systemds.git


The following commit(s) were added to refs/heads/master by this push:
     new 9d5b898  [SYSTEMDS-2591] Builtin hospitalResidencyMatch for Stable 
matching Closes #1312.
9d5b898 is described below

commit 9d5b898b4e4b94a685f239d513c55c23eaa0a4ef
Author: Atefeh Asayesh <[email protected]>
AuthorDate: Sat Jun 19 23:17:52 2021 +0200

    [SYSTEMDS-2591] Builtin hospitalResidencyMatch for Stable matching
    Closes #1312.
---
 scripts/builtin/hospitalResidencyMatch.dml         | 181 +++++++++++++++++++++
 .../java/org/apache/sysds/common/Builtins.java     |   1 +
 .../builtin/BuiltinHospitalResidencyMatchTest.java | 133 +++++++++++++++
 .../scripts/functions/builtin/residencymatch.dml   |  28 ++++
 4 files changed, 343 insertions(+)

diff --git a/scripts/builtin/hospitalResidencyMatch.dml 
b/scripts/builtin/hospitalResidencyMatch.dml
new file mode 100644
index 0000000..94eb931
--- /dev/null
+++ b/scripts/builtin/hospitalResidencyMatch.dml
@@ -0,0 +1,181 @@
+#-------------------------------------------------------------
+## 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.
+##-------------------------------------------------------------
+# THIS SCRIPT COMPUTES A SOLUTION FOR THE HOSPITAL RESIDENCY MATCH PROBLEM
+#
+# INPUT PARAMETERS:
+# 
--------------------------------------------------------------------------------------------
+# NAME                  TYPE       DEFAULT      MEANING
+# 
--------------------------------------------------------------------------------------------
+# R                     Matrix     ---          Residents matrix R.
+#                                               It must be an ORDERED  matrix.
+#
+# H                     Matrix     ---          Hospitals matrix H.
+#                                               It must be an UNORDRED matrix.
+#
+# capacity              Matrix     ---          capacity of Hospitals matrix C.
+#                                               It must be a [n*1] matrix with 
non zero values.
+#                                               i.e. the leftmost value in a 
row is the most preferred partner's index.
+#                                               i.e. the leftmost value in a 
row in P is the preference value for the acceptor
+#                                               with index 1 and vice-versa 
(higher is better).
+# OUTPUT PARAMETERS:
+# 
--------------------------------------------------------------------------------------------
+# NAME                  TYPE       DEFAULT      MEANING
+# 
--------------------------------------------------------------------------------------------
+# residencyMatch        Matrix     ---          Result Matrix
+#                                               If cell [i,j] is non-zero, it 
means that Resident i has matched with Hospital j.
+#                                               Further, if cell [i,j] is 
non-zero, it holds the preference value that led to the match.
+#
+#
+# hospitalMatch         Matrix     ---          Result Matrix
+#                                               If cell [i,j] is non-zero, it 
means that Resident i has matched with Hospital j.
+#                                               Further, if cell [i,j] is 
non-zero, it holds the preference value that led to the match.
+#
+#
+# Residents.mtx:
+# 2.0,1.0,3.0
+# 1.0,2.0,3.0
+# 1.0,2.0,0.0
+#
+# Since it is an ORDERED  matrix, this means that Resident 1 (row 1) likes 
hospital 2 the most, followed by hospital 1 and hospital 3.
+# If it was UNORDERED, this would mean that resident 1 (row 1) likes hospital 
3 the most (since the value at [1,3] is the row max),
+# followed by hospital 1 (2.0 preference value) and hospital 2 (1.0 preference 
value).
+#
+# Hospitals.mtx:
+# 2.0,1.0,0.0
+# 0.0,1.0,2.0
+# 1.0,2.0,0.0
+#
+# Since it is an UNORDERED matrix this means that Hospital 1 (row 1) likes 
Resident 1 the most (since the value at [1,1] is the row max).
+#
+# capacity.mtx
+# 1.0
+# 1.0
+# 1.0
+#
+# residencyMatch.mtx
+# 2.0,0.0,0.0
+# 1.0,0.0,0.0
+# 0.0,2.0,0.0
+#
+
+
+# hospitalMatch.mtx
+# 0.0,1.0,0.0
+# 0.0,0.0,2.0
+# 1.0,0.0,0.0
+#
+# Resident 1 has matched with Hospital 3 (since [1,3] is non-zero) at a 
preference level of 2.0.
+# Resident 2 has matched with Hospital 1 (since [2,1] is non-zero) at a 
preference level of 1.0.
+# Resident 3 has matched with Hospital 2 (since [3,2] is non-zero) at a 
preference level of 2.0.
+# 
--------------------------------------------------------------------------------------------
+
+m_hospitalResidencyMatch = function(Matrix[Double] R, Matrix[Double] H, 
Matrix[Double] capacity, Boolean verbose = FALSE)
+  return (Matrix[Double] residencyMatch, Matrix[Double] hospitalMatch)
+{
+
+  # in this step we consider that  Residents Matrix is ORDERED.
+  # in this step we consider that  Hospital  Matrix is UNORDERED.
+  # in the next implementation can consider number of choices for every 
resident.
+  
+  #TODO set a finite number of maximum iterations so that the execution 
terminates after maximum iterations.
+
+  print("STARTING RESIDENCY MATCH ALGORITHM");
+  print("READING R  as residents AND H as Hospitals and capacity...");
+
+  m = nrow(R)
+  n = ncol(R)
+
+  residencyMatch = matrix(0.0, rows=m, cols=n)
+  hospitalMatch = matrix(0.0, rows=n, cols=m)
+  resultmMatrix = matrix(0.0, rows=nrow(R), cols=ncol(R))
+
+  if(nrow(capacity) != nrow(H)) 
+    print("ERROR: Missing capacity info for some hospitals")
+  
+
+  startM = matrix(1.0, rows=m, cols=1)  ### for checking while
+
+  hIndex =matrix(1.0, rows=m, cols=1)
+  proposer_pointers = matrix(1.0, rows=m, cols=1)
+  prev_Residents_vector = matrix(1.0, rows=n, cols=1)
+  prevIndex_Residents_vector = matrix(1.0, rows=n, cols=1)
+
+  prev_Residents_vector = rowMins(hospitalMatch)
+  prevIndex_Residents_vector =  rowIndexMin(hospitalMatch)
+  # TODO remove the nested looping by vectorizing 
+  while(sum(startM) > 0) { 
+    for(i in 1:m) {
+      if(as.scalar(startM[i]) == 1) { 
+        secondIndex = as.scalar (proposer_pointers[i])   
+        hIndex[i] = as.scalar (R[i,secondIndex]) 
+        #the minimum value means most preference.
+        prev_Residents_vector = rowMaxs(hospitalMatch)  
+        prevIndex_Residents_vector =  rowIndexMax(hospitalMatch)
+        if (as.scalar(hIndex[i]) != 0) { 
+          hosValue = as.scalar (H[as.scalar(hIndex[i]),i])
+          if (hosValue > 0) {
+            # if this hospital likes this resident and has the capacity ...
+            if(as.scalar(capacity[as.scalar (hIndex[i]),1]) >= 1) { 
+              capacity[as.scalar(hIndex[i]),1] = as.scalar(capacity[as.scalar 
(hIndex[i]),1]) - 1
+              residencyMatch [i,as.scalar(hIndex[i])] = 
as.scalar(proposer_pointers[i]) 
+              hospitalMatch [as.scalar(hIndex[i]), i] = hosValue 
+              #Disable freshly Matched resident to search for a new Hospital 
in the next round
+              startM[i] = 0 
+              proposer_pointers[i] = as.scalar(proposer_pointers[i]) + 1  
+              if (as.scalar(proposer_pointers[i]) > n)
+                proposer_pointers[i] = n
+            }
+            else if(as.scalar(prev_Residents_vector[as.scalar(hIndex[i])]) >= 
secondIndex) {
+              #in this step we check that if the hospital capacity is 0
+              # but the preference value of prev residents is lower than
+              #the preference value of current resident.
+              # we should replace the prev resident with current resident.
+              resPrev= as.scalar(prevIndex_Residents_vector[as.scalar 
(hIndex[i]),1])
+              hospitalMatch [as.scalar(hIndex[i]) ,resPrev] = 0
+              residencyMatch[resPrev,as.scalar(hIndex[i])] = 0
+              hospitalMatch [as.scalar(hIndex[i]),i ] = 
as.scalar(proposer_pointers[i])
+              residencyMatch [i,as.scalar(hIndex[i])] = 
as.scalar(proposer_pointers[i])
+              startM[i] = 0
+              prevResIndex 
=as.scalar(prevIndex_Residents_vector[as.scalar(hIndex[i]),1])
+              if(prevResIndex  > 0){
+                startM[prevResIndex ] =1
+                proposer_pointers[i] = as.scalar(proposer_pointers[i]) + 1
+                if (as.scalar(proposer_pointers[i]) > n)
+                  proposer_pointers[i] = n 
+              }
+            } 
+          } 
+          if ( as.scalar (startM[i]) == 1 ) {
+            proposer_pointers[i] = as.scalar(proposer_pointers[i]) + 1 
+            if (as.scalar(proposer_pointers[i]) > n)
+              proposer_pointers[i] = n  
+          }
+        }
+      }
+    } 
+  }
+  if(verbose) {
+    print("residencyMatch")
+    print(toString(residencyMatch))
+    print("hospitalMatch")
+    print(toString(hospitalMatch))
+  }
+}
+
+
diff --git a/src/main/java/org/apache/sysds/common/Builtins.java 
b/src/main/java/org/apache/sysds/common/Builtins.java
index be3647b..99acfaf 100644
--- a/src/main/java/org/apache/sysds/common/Builtins.java
+++ b/src/main/java/org/apache/sysds/common/Builtins.java
@@ -129,6 +129,7 @@ public enum Builtins {
        GMM_PREDICT("gmmPredict", true),
        GNMF("gnmf", true),
        GRID_SEARCH("gridSearch", true),
+       HOSPITAL_RESIDENCY_MATCH("hospitalResidencyMatch", true),
        HYPERBAND("hyperband", true),
        IFELSE("ifelse", false),
        IMG_MIRROR("img_mirror", true),
diff --git 
a/src/test/java/org/apache/sysds/test/functions/builtin/BuiltinHospitalResidencyMatchTest.java
 
b/src/test/java/org/apache/sysds/test/functions/builtin/BuiltinHospitalResidencyMatchTest.java
new file mode 100644
index 0000000..91cc38f
--- /dev/null
+++ 
b/src/test/java/org/apache/sysds/test/functions/builtin/BuiltinHospitalResidencyMatchTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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 org.apache.sysds.common.Types;
+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;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class BuiltinHospitalResidencyMatchTest extends AutomatedTestBase {
+
+
+       private final static String TEST_NAME = "residencymatch";
+       private final static String TEST_DIR = "functions/builtin/";
+       private static final String TEST_CLASS_DIR = TEST_DIR + 
BuiltinHospitalResidencyMatchTest.class.getSimpleName() + "/";
+
+       private final static double eps = 0.0001;
+
+       @Override
+       public void setUp() {
+               addTestConfiguration(TEST_NAME,new 
TestConfiguration(TEST_CLASS_DIR, TEST_NAME,new String[]{"RM"}));
+       }
+
+       @Test
+       public void testResidencyMatch1() {
+               double[][] R = {
+                               {2,3,1},{1,3,2},{3,1,3}};
+               double[][] H = {
+                               {1,2,0},{3,1,2},{0,1,2}};
+               double[][] C = {
+                               {2},{3},{2}};
+               double[][]EM = { // this is an expected matrix
+                               {0,1,0},{1,0,0},{0,0,1}};
+               runtestResidencyMatchTest(R, H, C, EM, Types.ExecType.CP);
+       }
+
+       @Test
+       public void testResidencyMatch2() {
+               double[][] R = {
+                               {2,1,3},{1,2,3},{1,3,2}};
+               double[][] H = {
+                               {3,1,2},{2,1,3},{3,2,1}};
+               double[][] C = {
+                               {1},{1},{1}};
+               double[][]EM = { // this is an expected matrix
+                               {0,0,3},{0,2,0},{1,0,0}};
+               runtestResidencyMatchTest(R, H, C, EM, Types.ExecType.CP);
+       }
+
+       @Test
+       public void testResidencyMatch3() {
+               double[][] R = {
+                               {1,2},{2,1},{1,2},{1,2}};
+               double[][] H = {
+                               {3,2,1,4},{2,1,3,0}};
+               double[][] C = {
+                               {4},{3}};
+               double[][]EM = { // this is an expected matrix
+                               {1,0},{0,1},{1,0},{1,0}};
+               runtestResidencyMatchTest(R, H, C, EM, Types.ExecType.CP);
+       }
+       @Test
+       public void testResidencyMatch4() {
+               double[][] R = {
+                               {1,2},{2,1},{1,2},{1,2}};
+               double[][] H = {
+                               {3,2,1,4},{2,1,3,0}};
+               double[][] C = {
+                               {4},{3}};
+               double[][]EM = { // this is an expected matrix
+                               {1,0},{0,1},{1,0},{1,0}};
+               runtestResidencyMatchTest(R, H, C, EM, Types.ExecType.SPARK);
+       }
+
+       private void runtestResidencyMatchTest(double[][] R, double[][] H, 
double[][] C, double[][] EM,
+               Types.ExecType instType) {
+
+               Types.ExecMode platformOld = setExecMode(instType);
+               try {
+                       loadTestConfiguration(getTestConfiguration(TEST_NAME));
+                       String HOME = SCRIPT_DIR + TEST_DIR;
+                       fullDMLScriptName = HOME + TEST_NAME + ".dml";
+                       List<String> proArgs = new ArrayList<>();
+                       proArgs.add("-args");
+                       proArgs.add(input("R"));
+                       proArgs.add(input("H"));
+                       proArgs.add(input("C"));
+                       proArgs.add(output("RM"));
+
+                       programArgs = proArgs.toArray(new 
String[proArgs.size()]);
+                       // defining Residents Matrix
+
+                       writeInputMatrixWithMTD("R", R, true);
+                       writeInputMatrixWithMTD("H", H, true);
+                       writeInputMatrixWithMTD("C", C, true);
+
+
+                       runTest(true, EXCEPTION_NOT_EXPECTED, null, -1);
+
+                       //compare expected results
+                       HashMap<MatrixValue.CellIndex, Double> matrixU = 
readDMLMatrixFromOutputDir("RM");
+                       double[][] OUT = 
TestUtils.convertHashMapToDoubleArray(matrixU);
+                       TestUtils.compareMatrices(EM, OUT, eps);
+               }
+               finally {
+                       rtplatform = platformOld;
+               }
+       }
+}
+
diff --git a/src/test/scripts/functions/builtin/residencymatch.dml 
b/src/test/scripts/functions/builtin/residencymatch.dml
new file mode 100644
index 0000000..d5a2090
--- /dev/null
+++ b/src/test/scripts/functions/builtin/residencymatch.dml
@@ -0,0 +1,28 @@
+#-------------------------------------------------------------
+#
+# 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.
+#
+#-------------------------------------------------------------
+
+R = read($1)
+H = read($2)
+C = read($3)
+[R, H] = hospitalResidencyMatch(R=R,H=H,capacity=C, verbose = TRUE)
+write(R, $4)
+
+

Reply via email to