blob: ea74a10537a12c6dd4974fc2ca696aeb8b784a37 [file] [log] [blame]
#-------------------------------------------------------------
#
# 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 stable marriage problem.
#
# result description:
#
# If cell [i,j] is non-zero, it means that acceptor i has matched with
# proposer j. Further, if cell [i,j] is non-zero, it holds the preference
# value that led to the match.
# Proposers.mtx:
# 2.0,1.0,3.0
# 1.0,2.0,3.0
# 1.0,3.0,2.0
#
# Since ordered=TRUE, this means that proposer 1 (row 1) likes acceptor 2
# the most, followed by acceptor 1 and acceptor 3.
# If ordered=FALSE, 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).
#
# Acceptors.mtx:
# 3.0,1.0,2.0
# 2.0,1.0,3.0
# 3.0,2.0,1.0
#
# Since ordered=TRUE, this means that acceptor 1 (row 1) likes proposer 3
# the most, followed by proposer 1 and proposer 2.
# If ordered=FALSE, this would mean that acceptor 1 (row 1) likes proposer 1
# the most (since the value at [1,1] is the row max),
# followed by proposer 3 (2.0 preference value) and proposer 2
# (1.0 preference value).
#
# Output.mtx (assuming ordered=TRUE):
# 0.0,0.0,3.0
# 0.0,3.0,0.0
# 1.0,0.0,0.0
#
# Acceptor 1 has matched with proposer 3 (since [1,3] is non-zero) at a
# preference level of 3.0.
# Acceptor 2 has matched with proposer 2 (since [2,2] is non-zero) at a
# preference level of 3.0.
# Acceptor 3 has matched with proposer 1 (since [3,1] is non-zero) at a
# preference level of 1.0.
#
# INPUT:
# ----------------------------------------------------------------------------------
# P proposer matrix P.
# It must be a square matrix with no zeros.
# A acceptor matrix A.
# It must be a square matrix with no zeros.
# ordered If true, P and A are assumed to be ordered,
# 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).
# verbose if the algorithm should print verbosely
# ----------------------------------------------------------------------------------
#
# OUTPUT:
# ----------------------------------------------------------------------------------------
# result_matrix Result Matrix
# ----------------------------------------------------------------------------------------
m_stableMarriage = function(Matrix[Double] P, Matrix[Double] A, Boolean ordered = TRUE, Boolean verbose = FALSE)
return (Matrix[Double] result_matrix)
{
# variable names follow publication
print("STARTING STABLE MARRIAGE");
assert(nrow(A) == nrow(P));
assert(ncol(A) == ncol(P));
if(nrow(P) != ncol(P) | nrow(A) != ncol(A))
stop("StableMarriage error: Wrong Input! Both P and A must be square.")
n = nrow(P)
# Let S be the identity matrix
S = diag(matrix(1.0, rows=n, cols=1))
result_matrix = matrix(0.0, rows=n, cols=n)
# Pre-processing
if(!ordered) {
# If unordered, we need to order P
ordered_P = matrix(0.0, rows=n, cols=n)
transposed_P = t(P)
parfor(i in 1:n)
ordered_P[,i] = order(target=transposed_P, by=i, decreasing=TRUE, index.return=TRUE)
P = t(ordered_P)
}
else {
# If ordered, we need to unorder A
unordered_A = matrix(0.0, rows=n, cols=n)
# Since cells can be converted to unordered indices independently, we can nest parfor loops.
parfor(i in 1:n) {
parfor(j in 1:n, check=0)
unordered_A[i, as.scalar(A[i, j])] = n - j + 1
}
A = unordered_A
}
proposer_pointers = matrix(1.0, rows=n, cols=1)
while(sum(S) > 0) {
stripped_preferences = S %*% P
mask_matrix = matrix(0.0, rows=n, cols=n)
for(i in 1:n) {
max_proposal = as.scalar(stripped_preferences[i, as.scalar(proposer_pointers[i])])
if(max_proposal != 0) {
proposer_pointers[i] = as.scalar(proposer_pointers[i]) + 1
mask_matrix[max_proposal, i] = 1
}
}
# make Hadamard Product
Propose_round_results = mask_matrix * A
best_proposers_vector = rowIndexMax(Propose_round_results)
prev_best_proposers = rowIndexMax(result_matrix)
for(i in 1:n, check=0) {
new_best_proposer_index = as.scalar(best_proposers_vector[i])
new_best = as.scalar(Propose_round_results[i, new_best_proposer_index])
if(new_best > 0) {
prev_best_proposer_index = as.scalar(prev_best_proposers[i])
prev_best = as.scalar(result_matrix[i, prev_best_proposer_index])
if (new_best > prev_best)
{
# Proposal is better than current fiance
result_matrix[i, prev_best_proposer_index] = 0
result_matrix[i, new_best_proposer_index] = new_best
#Disable freshly married man to search for a new woman in the next round
S[new_best_proposer_index, new_best_proposer_index] = 0
# If a fiance existed, dump him/her
if(prev_best > 0)
S[prev_best_proposer_index, prev_best_proposer_index] = 1
}
}
}
}
if(verbose)
print("Result: \n"+toString(result_matrix))
}