Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions scripts/builtin/residencyMatchMain.dml
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
#-------------------------------------------------------------
## 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 UnORDERED matrix.
#
# C Matrix --- Capacity of Hospitals matrix C.
# It must be a [c,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 acceptor 2 the most, followed by acceptor 1 and acceptor 3.
# If it was UNORDERED, this would mean that proposer 1 (row 1) likes acceptor 3 the most (since the value at [1,3] is the row max),
# followed by acceptor 1 (2.0 preference value) and acceptor 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
# 0.0,0.0,3.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 3.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_residencyMatchMain = function(Matrix[Double] R, Matrix[Double] H,Matrix[Double] C )
return (Matrix[Double] residencyMatch)
{

# 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 termiates after maximum iterations.

print("\n")
print("STARTING RESIDENCY MATCH ALGORITHM");
print("READING R as residents AND H as Hospitals and also C as capacity...");

m = nrow(R)
n = ncol(R)
capacityRows = nrow(C)
#######################################################################################################
Capacity = matrix(0.0, rows=capacityRows , cols=1)
Capacity = C;
max_position = colMaxs(Capacity)
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: Wrong Input !Capacity indexes is not match with the Number of hospitals ")
#it means that some hospitals' capacity is not defined.
}# end of if

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)

while(sum(startM) > 0) {

for(i in 1:m) {

while (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
}

}#end of if (as.scalar(HIndex[i]) != 0 )#

else if (as.scalar(Capacity[as.scalar(HIndex[i]),1]) < 1) {

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
}
}# end of if(prevResIndex > 0)
}# end of checking secondIndex
}## end of else if
} #end of if X

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
}
}###end else if hosvalue
}# end of if if (as.scalar(HIndex[i])
# new_best_proposer_index = i
}
} ## end of for (i 1:m)
} # end of while

print("residencyMatch")
print(toString(residencyMatch))
print("hospitalMatch")
print(toString(hospitalMatch))
}
1 change: 1 addition & 0 deletions src/main/java/org/apache/sysds/common/Builtins.java
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ public enum Builtins {
RANGE("range", false),
RBIND("rbind", false),
REMOVE("remove", false, ReturnType.MULTI_RETURN),
RESIDENCYMATCH("residencyMatchMain", true),
REV("rev", false),
ROUND("round", false),
ROWINDEXMAX("rowIndexMax", false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,11 @@ public void updateInstructionThreadID(String pattern, String replace) {
* @param ec execution context
* @return instruction
*/
public Instruction preprocessInstruction(ExecutionContext ec){
public Instruction preprocessInstruction(ExecutionContext ec) {
// Lineage tracing
if (DMLScript.LINEAGE)
ec.traceLineage(this);
//return instruction ifself
//return the instruction itself
return this;
}
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.apache.sysds.api.DMLScript;
import org.apache.sysds.common.Builtins;
import org.apache.sysds.common.Types.DataType;
import org.apache.sysds.conf.ConfigurationManager;
Expand All @@ -46,6 +47,7 @@
import org.apache.sysds.runtime.controlprogram.caching.FrameObject;
import org.apache.sysds.runtime.controlprogram.caching.MatrixObject;
import org.apache.sysds.runtime.controlprogram.context.ExecutionContext;
import org.apache.sysds.runtime.lineage.LineageItem;
import org.apache.sysds.runtime.matrix.data.MatrixBlock;
import org.apache.sysds.runtime.matrix.operators.Operator;
import org.apache.sysds.runtime.util.DataConverter;
Expand Down Expand Up @@ -120,6 +122,7 @@ public void processInstruction(ExecutionContext ec) {

//4. expand list arguments if needed
CPOperand[] boundInputs2 = null;
LineageItem[] lineageInputs = null;
if( boundInputs.length == 1 && boundInputs[0].getDataType().isList()
&& !(fpb.getInputParams().size() == 1 && fpb.getInputParams().get(0).getDataType().isList()))
{
Expand All @@ -135,11 +138,13 @@ public void processInstruction(ExecutionContext ec) {
boundInputs2[i] = new CPOperand(varName, in);
}
boundInputs = boundInputs2;
lineageInputs = DMLScript.LINEAGE
? lo.getLineageItems().toArray(new LineageItem[lo.getLength()]) : null;
}

//5. call the function (to unoptimized function)
FunctionCallCPInstruction fcpi = new FunctionCallCPInstruction(nsName, funcName,
false, boundInputs, fpb.getInputParamNames(), boundOutputNames, "eval func");
false, boundInputs, lineageInputs, fpb.getInputParamNames(), boundOutputNames, "eval func");
fcpi.processInstruction(ec);

//6. convert the result to matrix
Expand Down Expand Up @@ -251,8 +256,12 @@ private static void checkValidArguments(List<Data> loData, List<String> loNames,

private static ListObject reorderNamedListForFunctionCall(ListObject in, List<String> fArgNames) {
List<Data> sortedData = new ArrayList<>();
for( String name : fArgNames )
List<LineageItem> sortedLI = DMLScript.LINEAGE ? new ArrayList<>() : null;
for( String name : fArgNames ) {
sortedData.add(in.getData(name));
return new ListObject(sortedData, new ArrayList<>(fArgNames));
if (DMLScript.LINEAGE)
sortedLI.add(in.getLineageItem(name));
}
return new ListObject(sortedData, new ArrayList<>(fArgNames), sortedLI);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,31 @@ public class FunctionCallCPInstruction extends CPInstruction {
private final String _namespace;
private final boolean _opt;
private final CPOperand[] _boundInputs;
private final LineageItem[] _lineageInputs;
private final List<String> _boundInputNames;
private final List<String> _funArgNames;
private final List<String> _boundOutputNames;

public FunctionCallCPInstruction(String namespace, String functName, boolean opt,
CPOperand[] boundInputs, List<String> funArgNames, List<String> boundOutputNames, String istr) {
CPOperand[] boundInputs, LineageItem[] lineageInputs, List<String> funArgNames,
List<String> boundOutputNames, String istr) {
super(CPType.FCall, null, functName, istr);
_functionName = functName;
_namespace = namespace;
_opt = opt;
_boundInputs = boundInputs;
_lineageInputs = lineageInputs;
_boundInputNames = Arrays.stream(boundInputs).map(i -> i.getName())
.collect(Collectors.toCollection(ArrayList::new));
_funArgNames = funArgNames;
_boundOutputNames = boundOutputNames;
}

public FunctionCallCPInstruction(String namespace, String functName, boolean opt,
CPOperand[] boundInputs, List<String> funArgNames, List<String> boundOutputNames, String istr) {
this(namespace, functName, opt, boundInputs, null, funArgNames, boundOutputNames, istr);
}

public String getFunctionName() {
return _functionName;
}
Expand Down Expand Up @@ -125,8 +133,10 @@ public void processInstruction(ExecutionContext ec) {
}

// check if function outputs can be reused from cache
LineageItem[] liInputs = LineageCacheConfig.isMultiLevelReuse() || DMLScript.LINEAGE_ESTIMATE ?
LineageItemUtils.getLineage(ec, _boundInputs) : null;
LineageItem[] liInputs = _lineageInputs;
if (_lineageInputs == null)
liInputs = (LineageCacheConfig.isMultiLevelReuse() || DMLScript.LINEAGE_ESTIMATE)
? LineageItemUtils.getLineage(ec, _boundInputs) : null;
if (!fpb.isNondeterministic() && reuseFunctionOutputs(liInputs, fpb, ec))
return; //only if all the outputs are found in cache

Expand Down Expand Up @@ -164,7 +174,7 @@ public void processInstruction(ExecutionContext ec) {

//map lineage to function arguments
if( lineage != null ) {
LineageItem litem = ec.getLineageItem(input);
LineageItem litem = _lineageInputs == null ? ec.getLineageItem(input) : _lineageInputs[i];
lineage.set(currFormalParam.getName(), (litem!=null) ?
litem : ec.getLineage().getOrCreate(input));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.sysds.lops.LeftIndex;
import org.apache.sysds.lops.RightIndex;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.sysds.api.DMLScript;
import org.apache.sysds.common.Types.ValueType;
import org.apache.sysds.runtime.DMLRuntimeException;
import org.apache.sysds.runtime.controlprogram.caching.CacheableData;
Expand Down Expand Up @@ -67,6 +68,7 @@ else if ( opcode.equalsIgnoreCase(LeftIndex.OPCODE)) {

//execute right indexing operation and set output
if( input2.getDataType().isList() ) { //LIST <- LIST
//TODO: copy the lineage trace of input2 list to input1 list
ListObject rin = (ListObject) ec.getVariable(input2.getName());
if( rl.getValueType()==ValueType.STRING || ru.getValueType()==ValueType.STRING )
ec.setVariable(output.getName(), lin.copy().set(rl.getStringValue(), ru.getStringValue(), rin));
Expand All @@ -75,18 +77,21 @@ else if ( opcode.equalsIgnoreCase(LeftIndex.OPCODE)) {
}
else if( input2.getDataType().isScalar() ) { //LIST <- SCALAR
ScalarObject scalar = ec.getScalarInput(input2);
//LineageItem li = DMLScript.LINEAGE ? LineageItemUtils.getLineage(ec, input2)[0] : null;
LineageItem li = DMLScript.LINEAGE ? ec.getLineage().getOrCreate(input2) : null;
if( rl.getValueType()==ValueType.STRING )
ec.setVariable(output.getName(), lin.copy().set(rl.getStringValue(), scalar));
ec.setVariable(output.getName(), lin.copy().set(rl.getStringValue(), scalar, li));
else
ec.setVariable(output.getName(), lin.copy().set((int)rl.getLongValue()-1, scalar));
ec.setVariable(output.getName(), lin.copy().set((int)rl.getLongValue()-1, scalar, li));
}
else if( input2.getDataType().isMatrix() ) { //LIST <- MATRIX/FRAME
CacheableData<?> dat = ec.getCacheableData(input2);
dat.enableCleanup(false);
LineageItem li = DMLScript.LINEAGE ? ec.getLineage().get(input2) : null;
if( rl.getValueType()==ValueType.STRING )
ec.setVariable(output.getName(), lin.copy().set(rl.getStringValue(), dat));
ec.setVariable(output.getName(), lin.copy().set(rl.getStringValue(), dat, li));
else
ec.setVariable(output.getName(), lin.copy().set((int)rl.getLongValue()-1, dat));
ec.setVariable(output.getName(), lin.copy().set((int)rl.getLongValue()-1, dat, li));
}
else {
throw new DMLRuntimeException("Unsupported list "
Expand Down
Loading