0

I'm trying to find out the best techniques or resources to help me solve an optimization problem. The problem is to optimally match meeting attendees with predefined roles. Prior to the meeting, an attendee with decide for each role whether he or she:

  • actively seeks to fill the role, if possible ("A")
  • is willing to fill the role but does not feel strongly about it ("W")
  • is unwilling to fill the role ("U").

For example, a possible input matrix would be:

The objective is to maximum the number of matches for attendees with roles for which they have marked "A", under the following constraints:

  • All roles are filled by exactly one attendee
  • Matches for which the attendee marked "U" are forbidden

For this example, an optimal solution would be as follows, where 4/5 roles are filled using "A" matches.

Does anyone know if there a better way to solve this kind of problem that is better than brute force (which can very quickly become infeasible for larger matrices)? I'm open to general suggestions for optimization algorithms that I would implement myself (i.e., tabu search, genetic algorithms, branch-and-bound, etc.), or even to pointers to functionality within an existing package such as OptaPlanner.

Thanks!

1 Answers1

0

Approach

Here is a simple Integer-Programming approach, implemented with cvxpy in python.

Code

import numpy as np
from cvxpy import *

""" INPUT """
N_ATTENDEES = 6
N_ROLES = 5
ATTENDEE_CAN_TAKE_MULTIPLE_ROLES = False  # Modelling-decision

A_ROLES = np.array([[1,1,0,0,0],
                    [0,0,0,0,1],
                    [0,1,0,0,0],
                    [1,1,0,0,0],
                    [1,0,0,0,0],
                    [0,1,1,1,0]], dtype=bool)

W_ROLES = np.array([[0,0,0,1,0],
                    [0,0,1,1,0],
                    [1,0,0,1,1],
                    [0,0,0,0,0],
                    [0,0,1,0,1],
                    [0,0,0,0,1]], dtype=bool)

U_ROLES = np.ones((N_ATTENDEES, N_ROLES), dtype=bool) - A_ROLES - W_ROLES

""" Variables """
X = Bool(N_ATTENDEES, N_ROLES)  # 1 -> attendee takes role

""" Constraints """
constraints = []

# (1) All roles assigned to exactly one attendee
for r in range(N_ROLES):
    constraints.append(sum_entries(X[:, r]) == 1)

# (2) Forbidden roles
constraints.append(X <= (1-U_ROLES))

# (3) optional: max one role per attendee
if not ATTENDEE_CAN_TAKE_MULTIPLE_ROLES:
    for a in range(N_ATTENDEES):
        constraints.append(sum_entries(X[a, :]) <= 1)

""" Objective """
objective = Maximize(sum_entries(mul_elemwise(A_ROLES, X)))

""" Solve """
problem = Problem(objective, constraints)
problem.solve()

""" Output """
print(problem.status, problem.value)
print('assignments: \n' + str(np.array(np.round(X.value), dtype=int)))  # pretty-printing of ASSIGNMENTS
print('a-roles taken: \n' + str(np.array(np.round(mul_elemwise(A_ROLES, X).value), dtype=int)))  # pretty-printing of hitted A-roles

Output

('optimal', 4.000000000087978)
assignments: 
[[1 0 0 0 0]
 [0 0 0 0 1]
 [0 0 0 0 0]
 [0 1 0 0 0]
 [0 0 1 0 0]
 [0 0 0 1 0]]
a-roles taken: 
[[1 0 0 0 0]
 [0 0 0 0 1]
 [0 0 0 0 0]
 [0 1 0 0 0]
 [0 0 0 0 0]
 [0 0 0 1 0]]

It might be possible to implement an approach while sticking to Linear-Programming (which has lower computational-complexity in practice and theory), taking some ideas mentioned by @user29970, although i'm a bit sceptical about some of these descriptions.

I'm also lazy and a MIP-approach is at least better regarding numerical stability.

sascha
  • 32,238
  • 6
  • 68
  • 110