I'm taking a course on Discrete Optimization, and we're working through constraint programming. In a topic about reification, we're working through the Stable Marriages Problem (SPM).
The model formulation is
enum Men = [George, Hugh, Will, Clive];
enum Women = [Julia, Halle, Angelina, Keira];
# mensRanking[Hugh, Julia] is Hugh's ranking of Julia
# lower the number, the higher the preference
int mensRanking[Men, Women];
int womensRanking[Women, Men];
# Decision variables below
# Array of decision variables called wives that is indexed on men stores the wife
# of man
var{Women} wives[Men]
# Array of decision variables called husbands that is indexed on women stores the
# husband of woman
var{Men} husbands[Women]
# Constraints below
solve {
# The husband of wife of man must equal man
forall(m in Men)
husband[wife[m]] = m;
# The wife of husband of woman must equal woman
forall(w in Women)
wife[husband[w]] = w;
# NEED HELP with the constraints below
forall(m in Man)
forall(w in Women)
# If man m prefers woman w over his wife, then
# woman w must prefer her husband over m
if (mensRanking[m,w] < mensRanking[m,wife[m]])
womensRanking[w,husband[w]] < womensRanking[w, m]
# If woman w prefers man m over her husband, then
# man m must prefer his wife over w
if (womensRanking[w,m] < womensRanking[w, husband[w]])
mensRanking[m,wife[m]] < mensRanking[m, w]
}
I can't figure out how to do the ranking comparison. Here's my attempt via or-tools in Python:
def main():
n = 4
men = range(n)
women = range(n)
# mensRanking[man][woman] is man's ranking of woman.
# lower the number, the higher the preference
mensRanking = [random.sample(range(n),n) for _ in men]
womensRanking = [random.sample(range(n),n) for _ in women]
model = cp_model.CpModel()
# For wife 'Julia', who is her husband?
husbands = [model.NewIntVar(0, n-1, f'woman{i}') for i in women]
# For husband 'George', who is his wife?
wives = [model.NewIntVar(0, n-1, f'man{i}') for i in men]
for man in men:
# The husband of wife of man must equal man
# I.e., husbands[wife] = man
wife = wives[man]
model.AddElement(wife, husbands, man)
for woman in women:
# The wife of husband of woman must equal woman
# I.e., wives[husband] = woman
husband = husbands[woman]
model.AddElement(husband, wives, woman)
for m in men:
for w in women:
womans_rank_of_husband = model.NewIntVar(0, n-1, '')
model.AddElement(husbands[w], womensRanking[w], womans_rank_of_husband)
mans_rank_of_wife = model.NewIntVar(0, n-1, '')
model.AddElement(wives[m], mensRanking[m], mans_rank_of_wife)
# If man m prefers woman w over his wife, then
# woman w must prefer her husband over m
# TypeError: 'BoundedLinearExpression' object is not iterable
model.Add(womans_rank_of_husband < womensRanking[w][m]).OnlyEnforceIf(
mensRanking[m][w] < mans_rank_of_wife
)
# If woman w prefers man m over her husband, then
# man m must prefer his wife over w
# TypeError: 'BoundedLinearExpression' object is not iterable
model.Add(mans_rank_of_wife < mensRanking[m][w]).OnlyEnforceIf(
womensRanking[w][m] < womans_rank_of_husband
)
solver = cp_model.CpSolver()
solution_printer = SolutionPrinter(x)
status = solver.SearchForAllSolutions(model, solution_printer)
print(solver.ResponseStats())
print(status)
Basically, I need to do an inequality check while using a decision variable as an index. I'm familiar with doing an EQUALITY check via model.AddElement(index, array, target)
for array[index] == target
, but can't figure out how to do array[index] < target
when index
is a decision variable.
EDIT: I used a temp_var as suggested by @Laurent-Perron, but now I'm getting the error:
# TypeError: 'BoundedLinearExpression' object is not iterable
Any idea why? I also tried AddImplication
:
womans_rank_of_husband = model.NewIntVar(0, n-1, '')
model.AddElement(husbands[w], womensRanking[w], womans_rank_of_husband)
mans_rank_of_wife = model.NewIntVar(0, n-1, '')
model.AddElement(wives[m], mensRanking[m], mans_rank_of_wife)
# If man m prefers woman w over his wife, then
# woman w must prefer her husband over m
model.AddImplication(mensRanking[m][w] < mans_rank_of_wife,
womans_rank_of_husband < womensRanking[w][m])
# If woman w prefers man m over her husband, then
# man m must prefer his wife over w
model.AddImplication(womensRanking[w][m] < womans_rank_of_husband,
mans_rank_of_wife < mensRanking[m][w])
But that gave me the error
TypeError: NotSupported: model.GetOrMakeBooleanIndex(unnamed_var_12 <= 1)
at model.AddImplication()
.