MATE: Asignación de estudiantes a grupos de trabajo en segundos

Contexto

Cada semestre, la Pontificia Universidad Católica de Chile tiene que armar los grupos de proyecto de dos cursos masivos: Desafíos de la Ingeniería y Sustentabilidad. Cada curso tiene cientos de alumnos y cada alumno tiene sus propias preferencias y restricciones: temas que le gustaría trabajar, horarios disponibles y atributos (género, universidad de origen, etc.) que conviene repartir equilibradamente entre los grupos.

Hacerlo a mano le tomaba varios días al equipo académico y, aun así, no se podía asegurar que la asignación fuera buena, en el sentido de que cada alumno quede asignado a un grupo con un tema de su preferencia. Por eso construimos MATE (Make A Team Efficiently): una aplicación en la que subes una planilla con los alumnos, dices cuántos grupos quieres y qué reglas se tienen que cumplir, y obtienes una asignación completa.

El desafío

Armar grupos parece fácil hasta que juntas todas las reglas: tamaño mínimo y máximo, cupos por horario, balance de atributos, topes por tema, y encima que a cada alumno le toque uno de los temas que pidió. Este tipo de problemas son resueltos generalmente a través de un modelo de optimización entero mixto.

El problema es que esta familia de modelos de optimización suelen requerir de extensos tiempos de ejecución cuando se considera un conjunto relativamente grande de variables. Así que gran parte del trabajo fue hacer que el problema fuera más pequeño y más fácil de resolver, sin cambiar la respuesta. Trabajamos en tres frentes: un preprocesamiento que elimina simetrías, un warm start que le da al solver un punto de partida razonable y un postprocesamiento que construye la asignación entregada al equipo académico, acompañado de insights de la solución.

Detalles del caso
Cliente:

Pontificia Universidad Católica de Chile

Sector:

Educación superior

Categoría:

Optimización, Asignación, Schedulling, Crew planning

Herramienta:

CP-SAT (Google OR-Tools)

MATE de punta a punta

Cada vez que alguien ejecuta una optimización en MATE ocurren cinco etapas. Las dos de los extremos (pre y postprocesamiento) son las que permiten que el modelo del centro sea resoluble y que el resultado sea útil para el equipo académico.

1
Preprocesamiento

Chequeamos que los parámetros ingresados sean factibles antes de formular el modelo. De ser factibles, agrupamos a los alumnos idénticos en "tipos" para reducir el tamaño del modelo.

2
Formulación del modelo MIP

Variables, restricciones y una función objetivo que premia dar a cada alumno sus primeras opciones.

3
Warm start

Una heurística rápida arma una asignación de partida para que el solver no empiece desde cero.

4
Resolución del modelo

El solver de Google OR-Tools busca el óptimo, o demuestra que con esas reglas no se puede y diagnostica qué reglas chocan.

5
Postprocesamiento

Mapeamos la solución del modelo a una asignación de estudiantes, generamos un Excel con los resultados y proveemos insights para hacer análisis de sensibilidad de la solución.

Conjuntos y parámetros

Antes de preprocesar y de escribir el modelo, estos son los conjuntos y los datos que usamos en todas las fórmulas de más abajo.

Conjuntos

\(S\)alumnos de la nómina, uno por estudiante
\(I\)tipos de alumno, tras agrupar a los estudiantes idénticos
\(NA \subseteq I\)tipos de alumno que no respondieron sus preferencias
\(I_r \subseteq I\)tipos de alumno con la característica \(r\)
\(R\)características o atributos (por ejemplo "Femenino", "PUC")
\(T\)temas
\(M\)módulos (secciones horarias)
\(G\)grupos candidatos
\(G_t,\ G_m,\ G_{tm} \subseteq G\)grupos con el tema \(t\), del módulo \(m\), o de ambos

Parámetros

\(R_i\)cantidad de alumnos del tipo \(i \in I\)
\(N_i\)flexibilidad horaria del tipo \(i \in I\), que pondera su preferencia en el objetivo
\(N_{\text{grupos}}\)cantidad de grupos que se quiere formar
\(Q_{\min},\ Q_{\max}\)tamaño mínimo y máximo de un grupo
\(D_{im}\)1 si el tipo \(i \in I\) tiene disponibilidad para el módulo \(m \in M\), y 0 si no
\(C_m\)cantidad de cupos del módulo \(m \in M\)
\(LT_t,\ UT_t\)cantidad mínima y máxima de grupos para el tema \(t \in T\)
\(LR_r,\ UR_r\)mínimo y máximo de alumnos con la característica \(r \in R\) por grupo
\(UP\)cantidad máxima de temas que se pueden usar
\(FD_{tm}\)1 si el tema \(t \in T\) puede ir en el módulo \(m \in M\), y 0 si está excluido
\(P_{ig}\)prioridad del grupo \(g \in G\) para el tipo \(i \in I\): 1 si es su primera opción, 2 la segunda, y así sucesivamente, o un valor muy alto si su tema no está entre sus preferencias

1. Preprocesamiento

Antes de resolver el modelo de optimización, MATE hace dos cosas. Primero, comprueba que las reglas ingresadas por el usuario se puedan cumplir a la vez, con el propósito de no intentar resolver un modelo que sabemos que será infactible. Después, agrupa a los alumnos por tipos de alumnos, con el propósito de eliminar simetrías y reducir la cantidad de variables del modelo.

a. Chequeo de parámetros

Hay combinaciones de parámetros que no admiten ninguna solución, y se pueden detectar sin necesidad de resolver nada. MATE las detecta en la propia pantalla de configuración, en tiempo real, mientras se ajustan los controles. Si alguna se cumple, no tiene sentido ejecutar el solver. Son condiciones suficientes de infactibilidad, deducidas directamente de las restricciones:

Alguien marcó que no puede en ningún horario.

\[\sum_{m \in M} D_{im} \ge 1 \quad \forall i \in I\]

Pides más grupos que los candidatos que se pueden construir.

\[N_{\text{grupos}} \le |G|\]

Con todos los grupos en el tamaño máximo no cabe el curso, o con todos en el mínimo sobran grupos.

\[N_{\text{grupos}}\, Q_{\min} \le \sum_{i \in I} R_i \le N_{\text{grupos}}\, Q_{\max}\]

El total de alumnos con una característica no es compatible con el rango por grupo.

\[N_{\text{grupos}}\, LR_r \le \sum_{i \in I_r} R_i \le N_{\text{grupos}}\, UR_r\]

Los cupos de todos los horarios juntos no alcanzan para alojar a todo el curso.

\[\sum_{m \in M} \min\Bigl(C_m,\ \sum_{i:\, D_{im} = 1} R_i\Bigr) \ge \sum_{i \in I} R_i\]

Los topes por tema no alcanzan para formar la cantidad de grupos pedida (o los mínimos se pasan).

\[\sum_{t \in T} LT_t \le N_{\text{grupos}} \le \sum_{t \in T} UT_t\]

Así se ve en la app. Si pides, por ejemplo, 8 grupos de entre 7 y 9 alumnos para una nómina de 48, el panel en vivo marca el problema en rojo y el botón de ejecutar queda bloqueado.

MATE: validación en vivo con parámetros que hacen el modelo infactible

b. Mapeo de alumnos a tipos de alumnos

El problema: Simetría de la asignación óptima

La formulación más directa del problema usaría una variable binaria \(x_{sg}\) por cada par (alumno, grupo), con \(s \in S\) y \(g \in G\): vale 1 si el alumno \(s\) queda en el grupo \(g\), y 0 si no. El problema es que esta formulación contiene muchas soluciones idénticas, es decir, tiene simetrías. Por ejemplo, imagina que tenemos 3 alumnas con los mismos atributos (todas mujeres, por ejemplo), la misma disponibilidad horaria y las mismas preferencias de tema. Para la asignación que hace el modelo es indiferente cuál de las 3 va a un grupo: son intercambiables. Si las 3 van a 3 grupos distintos, hay \(3! = 6\) maneras de repartir a las alumnas en los 3 grupos que obtienen la misma función objetivo.

La presencia de simetrías en la formulación afecta negativamente al branch and bound, el algoritmo con el que se resuelve el modelo MIP. El branch and bound va partiendo el problema en ramas y descartando las que no pueden mejorar la mejor solución conocida. Las copias equivalentes caen en ramas distintas y, como todas tienen el mismo valor y la misma cota, ninguna se puede descartar por ser peor que otra: el solver las recorre una por una. La relajación lineal también se resiente, porque tiende a repartir de forma fraccionaria a los alumnos idénticos y eso debilita la cota.

La solución: Asignar tipos de alumnos en lugar de alumnos

Para eliminar la simetría, agrupamos a los alumnos en tipos: dos alumnos son del mismo tipo si comparten atributos, orden de preferencias de tema y disponibilidad horaria. En vez de una variable binaria por alumno, usamos una variable entera \(y_{ig} \in \mathbb{N}\) que cuenta cuántos alumnos del tipo \(i \in I\) van al grupo \(g \in G\). En el ejemplo, las 3 alumnas forman un solo tipo, y hay una sola manera de decir "3 alumnas de este tipo van a este grupo". Esas ramas repetidas desaparecen y el solver gasta su tiempo en soluciones realmente distintas. Todo el modelo de la sección siguiente se escribe en términos de \(y_{ig}\).

Alumnos Tipos de alumnos Ana Atributos: Fem Disponibilidad: Módulo 1 Tema preferido: X Bea Atributos: Fem Disponibilidad: Módulo 1 Tema preferido: X Carla Atributos: Fem Disponibilidad: Módulo 1 Tema preferido: X Diego Atributos: Masc Disponibilidad: Módulo 1 Tema preferido: X Eli Atributos: Fem Disponibilidad: Módulo 2 Tema preferido: Y Fran Atributos: Fem Disponibilidad: Módulo 2 Tema preferido: Y Tipo 1 · 3 alumnos Atributos: Fem Disponibilidad: Módulo 1 Tema preferido: X 3! = 6 maneras de repartirlos, ahora 1 sola Tipo 2 · 1 alumno Atributos: Masc Disponibilidad: Módulo 1 Tema preferido: X Ningún otro alumno es igual Tipo 3 · 2 alumnos Atributos: Fem Disponibilidad: Módulo 2 Tema preferido: Y 2! = 2 maneras de repartirlos, ahora 1 sola idénticos otro género otro módulo
Seis alumnos de la nómina caen en tres tipos. Ana, Bea y Carla comparten atributos, disponibilidad y tema preferido, así que son intercambiables y pasan a ser un solo tipo de 3 alumnos. En este ejemplo las variables de asignación bajan de 6 × |G| a 3 × |G|. En una nómina real la reducción depende de cuántos alumnos se parezcan entre sí.

Además, esta formulación puede reducir drásticamente el número de variables: pasamos de \(|S| \times |G|\) a \(|I| \times |G|\). La baja depende de cuántos alumnos sean parecidos entre sí: en cursos con muchos alumnos parecidos es grande, y en el peor caso, cuando todos los alumnos son distintos, hay un tipo por alumno y la cantidad de variables queda igual. En la nómina de ejemplo, 48 alumnos se juntan en 25 tipos y las variables de asignación bajan de 1.408 a 688 (una reducción del 51%).

2. Formulación del modelo MIP

Cada corrida de MATE es un programa entero mixto (MIP): un modelo matemático con variables de decisión, restricciones y una función objetivo, escrito con los conjuntos y parámetros de la sección anterior.

Variables de decisión

\(y_{ig} \in \mathbb{N}\)cuántos alumnos del tipo \(i \in I\) van al grupo \(g \in G\)
\(w_g \in \{0, 1\}\)si el grupo \(g \in G\) llega a formarse
\(z_i \in \mathbb{N}\)la preferencia con la que termina el tipo \(i \in I\)
\(z_{\max} \in \mathbb{N}\)la peor preferencia entre todos los tipos
\(q_{gr} \in \mathbb{N}\)alumnos con la característica \(r \in R\) en el grupo \(g \in G\)
\(p_{gr} \in \{0, 1\}\)si el grupo \(g \in G\) queda sin alumnos con la característica \(r \in R\)
\(o_t \in \{0, 1\}\)si el tema \(t \in T\) se usa
\(u_{tm} \in \{0, 1\}\)si el tema \(t \in T\) queda fijado al módulo \(m \in M\)
\(m_g \in \mathbb{N}\)alumnos sin respuesta en el grupo \(g \in G\)
\(m_{\max} \in \mathbb{N}\)la peor concentración de alumnos sin respuesta en un grupo

Restricciones

a

Cada estudiante queda asignado a exactamente un grupo.

\[\sum_{g \in G} y_{ig} = R_i \quad \forall i \in I\]
b

Un grupo solo puede recibir estudiantes si está activado.

\[y_{ig} \le Q_{\max}\, w_g \quad \forall i \in I,\ g \in G\]
c

Se activa exactamente la cantidad de grupos objetivo.

\[\sum_{g \in G} w_g = N_{\text{grupos}}\]
d

Cada grupo activo mantiene su tamaño entre el mínimo y el máximo.

\[Q_{\min}\, w_g \le \sum_{i \in I} y_{ig} \le Q_{\max}\, w_g \quad \forall g \in G\]
e

Los grupos por tema se mantienen dentro del rango configurado.

\[LT_t \le \sum_{g \in G_t} w_g \le UT_t \quad \forall t \in T\]
f, g

Un tema cuenta como "usado" si se asigna en algún módulo, y se usan a lo más \(UP\) temas en total.

\[u_{tm} \le o_t \qquad \sum_{t \in T} o_t \le UP\]
h, i

A lo más un alumno sin respuesta puede quedar en el mismo grupo sin penalización; los demás se cuentan y se penalizan en el objetivo.

\[\sum_{i \in NA} y_{ig} \le 1 + m_g \quad \forall g \in G \qquad m_g \le m_{\max}\]
j

Se registra la preferencia que obtiene cada tipo.

\[z_i = \sum_{g \in G} y_{ig}\, P_{ig} \quad \forall i \in I\]
k

Se sigue la peor preferencia de todas, para poder penalizarla en el objetivo.

\[z_i \le z_{\max} \quad \forall i \in I\]
l, m

El conteo por característica se mantiene en rango, o es cero si se permite.

\[LR_r\,(w_g - p_{gr}) \le q_{gr} \le UR_r\,(1 - p_{gr}) \quad \forall g \in G,\ r \in R\]
n

Un módulo no puede recibir más estudiantes que su capacidad.

\[\sum_{i \in I} \sum_{g \in G_m} y_{ig} \le C_m \quad \forall m \in M\]
ñ

Un grupo solo incluye estudiantes disponibles para ese módulo.

\[\sum_{g \in G_m} y_{ig} \le D_{im} \quad \forall i \in I,\ m \in M\]
o

Si los temas deben quedar en un solo módulo, todos sus grupos lo comparten.

\[\sum_{g \in G_{tm}} w_g \le N_{\text{grupos}}\, u_{tm} \quad \forall t \in T,\ m \in M\]
p

Un tema solo puede quedar en un módulo si no está excluido de él.

\[u_{tm} \le FD_{tm} \quad \forall t \in T,\ m \in M\]

Función objetivo

\[\min \sum_{i \in I} N_i\, z_i \;+\; K\,\bigl(z_{\max} + m_{\max}\bigr)\]

MATE minimiza la suma de las preferencias obtenidas por todos los estudiantes, ponderada por la flexibilidad horaria de cada tipo (\(N_i\)), de modo que una primera opción siempre cuesta menos que una segunda. A eso le suma un término de balance, multiplicado por una constante \(K\) mayor que la cantidad total de alumnos, que castiga tanto al estudiante peor ubicado como al grupo con más alumnos sin respuesta. Así, primero se prioriza la equidad y el promedio queda como criterio de desempate: el solver no sacrifica a un tipo de alumno solo para mejorar levemente el promedio. Esa es la tensión de fondo del problema: cada regla que se exige (balance de atributos, tamaño de grupo, cupos por tema, horarios en común) reduce los repartos posibles y, con ellos, el espacio para dar a cada alumno su primera opción. Y dentro de ese espacio hay que elegir entre mejorar el promedio o proteger al más perjudicado; MATE elige lo segundo.

3. Warm start: heurística para obtener una solución inicial

Antes de que el solver empiece a buscar, una heurística simple construye una asignación de partida y se la pasa al solver como warm start. No tiene que ser buena ni siquiera factible: es solo un punto de partida razonable para la búsqueda, en lugar de empezar desde cero.

Ejemplo: cómo se llenan los grupos

Tipos de alumno, con su ranking Grupos candidatos, 4 cupos cada uno Tipo A · 5 alumnos 1ª opción: X · 2ª opción: Y Tipo B · 3 alumnos 1ª opción: Y · 2ª opción: X Tipo C · 3 alumnos 1ª opción: X · 2ª opción: Y 1ª opción: caben 4 2ª opción: cabe 1 1ª opción: caben 3 lleno lleno sobrante, sin mirar preferencias: caben 3 Grupo tema X 4/4 Grupo tema Y 4/4 Grupo tema Z 3/4
Primero se recorren los rankings en orden: la primera opción de todos los tipos, luego la segunda, y así. El tipo A llena el grupo del tema X con 4 alumnos y le sobra 1, que en su segunda opción cabe en el grupo del tema Y. El tipo C encuentra los grupos de los temas X e Y llenos (línea punteada), así que le sobran sus 3 alumnos. Esos sobrantes se mandan al grupo del tema Z, un tema que C nunca pidió, solo para que la asignación quede completa.

La heurística solo respeta lo estructural (disponibilidad horaria y capacidad del grupo) y se olvida del objetivo y del balance. Construirla tiene un costo mínimo, porque es código Python simple que no llama al solver. Esta heurística de warm start redujo el tiempo de ejecución en cerca de un 20% en promedio, al probarla con distintos parámetros y comparándola con el mismo modelo sin warm start.

4. Resolución del modelo

Con el modelo construido y el warm start listo, el solver busca la mejor asignación posible, o demuestra que con esas reglas no existe ninguna. Usamos CP-SAT, el solver de Google OR-Tools, porque permite resolver modelos MIP, es open source, es gratuito y no limita la cantidad de variables del modelo.

Para saber si CP-SAT alcanzaba para nuestro problema, medimos el tiempo de resolución contra la cantidad de variables en 14 puntos de prueba. Hasta unas 3.000 o 4.000 variables resuelve entre menos de un segundo y unos pocos segundos. Pasadas las 5.000 o 6.000, el tiempo crece mucho y se vuelve impredecible, así que fijamos en 4.000 variables el tamaño máximo de un modelo que MATE resuelve en línea.

La nómina de ejemplo de 48 alumnos genera unas 1.100 variables, muy por debajo de ese límite. Con nóminas sintéticas de 400 alumnos, el tiempo de resolución fue de unos 23 segundos en promedio usando el warm start.

"No hay solución": ¿por qué?

Cuando el modelo es infactible, un mensaje genérico como "prueba con otros parámetros" no le sirve a quien configura. Lo útil es encontrar un subsistema irreducible infactible (IIS, por Irreducible Infeasible Subsystem): un conjunto de restricciones que no se pueden cumplir juntas, pero que se vuelve factible apenas se saca cualquiera de ellas.

Para obtenerlo, MATE usa un filtro de borrado (deletion filter). Vuelve a resolver el modelo quitando una familia de reglas a la vez (tamaño de grupo, balance de atributos, cobertura de temas, cupos, tope de temas), con topes de tiempo cortos por prueba. Si sin esa familia el modelo se vuelve factible, la familia era necesaria y se queda. Si sigue siendo infactible, la familia sobra y se descarta. Lo que sobrevive al final es el IIS: un conjunto mínimo de reglas que, juntas, no se pueden cumplir. MATE te dice cuáles, no solo que algo falla.

Esto es lo que ve quien ejecuta MATE con 8 grupos de entre 7 y 9 alumnos para 48 estudiantes: el rango de tamaño y la cantidad de grupos son las dos reglas que no caben juntas.

MATE: pantalla de resultados cuando el modelo es infactible, con el conjunto mínimo de reglas en conflicto

Referencias: Gleeson, J. y Ryan, J. (1990). Identifying minimally infeasible subsystems of inequalities. ORSA Journal on Computing, 2(1), 61-63. Chinneck, J. W. y Dravnieks, E. W. (1991). Locating minimal infeasible constraint sets in linear programs. ORSA Journal on Computing, 3(2), 157-168.

5. Postprocesamiento

Una vez que se encuentra una solución factible, se pasa al postprocesamiento: mapear la solución del modelo a una asignación de alumnos y hacer el análisis de sensibilidad.

De tipos a alumnos reales

La solución solo dice cuántos alumnos de cada tipo van a cada grupo, no quiénes. Para cada tipo y grupo con \(y_{ig}\) mayor a cero, sacamos esa cantidad de alumnos reales de la lista del tipo. Como son intercambiables por construcción, da exactamente lo mismo cuál va a dónde. De ahí salen los grupos que muestra la pantalla de resultados, el conteo de preferencias cumplidas y el Excel descargable.

"Funciona, pero ¿qué me cuesta?"

El análisis de sensibilidad responde una pregunta práctica: de las reglas que se cumplieron, ¿cuáles están limitando el resultado? Para saberlo, MATE quita una regla a la vez y vuelve a optimizar. Si el mejor resultado posible mejora, esa regla tiene un costo; si no, no es la que limita. Y como el solver demuestra cuándo no existe una repartición mejor, un "no cambia nada" es una prueba, no una estimación.

La mejora se mide en puntos del objetivo del modelo, que juzga el resultado por su peor caso: qué tan mal le va al perfil de alumnos más perjudicado. Un punto equivale aproximadamente a un alumno que deja de quedar sin ninguna de sus opciones. Parte de la solución que ya mostró la pantalla, así que no se resuelve dos veces, y solo corre si lo pides.

Técnicamente es reoptimización por familias de restricciones, la forma estándar de análisis postóptimo en programación entera (Geoffrion y Nauss, 1977), cercana al diagnóstico de infactibilidad (Chinneck y Dravnieks, 1991). Se vuelve a resolver en lugar de leer precios sombra, porque en programación entera estos no bastan (Blair y Jeroslow, 1982; Wolsey, 1981).

Detalle del método y referencias

Resumen

  • Un problema de esta escala suele resolverse con un solver comercial como Gurobi o CPLEX. Lo reformulamos para que lo resuelva CP-SAT, una librería open source de Google OR-Tools, en segundos, sin licencias ni infraestructura de cómputo aparte.
  • Agrupamos a los alumnos intercambiables en tipos antes de construir el modelo. Así hay muchas menos variables y desaparecen las simetrías que le harían perder tiempo al branch and bound.
  • Una solución inicial por heurística (warm start) le da al solver un punto de partida y reduce el tiempo de resolución cerca de un 20% a 400 alumnos, sin empeorar nunca la solución final.
  • Chequeos de factibilidad en vivo, que detectan combinaciones de parámetros infactibles antes de correr el solver.
  • Interfaz en React (en español e inglés) sobre un backend Django con API REST, desplegado en AWS Lambda con archivos estáticos en S3.
  • Si el solver encuentra una solución, hacemos un análisis de sensibilidad para ver cuánto cuesta cada regla. Si no la encuentra, generamos un reporte con las restricciones que hacen infactible al modelo.

Resultado

El equipo académico de Ingeniería UC dejó de armar a mano los grupos de Desafíos de la Ingeniería y Sustentabilidad. Hoy suben la nómina, miden en pantalla si sus reglas son factibles, corren el modelo y revisan la asignación. Si quieren probar otra cantidad de grupos u otra regla de balance, vuelven a resolver y comparan. Lo que antes eran varios días de trabajo manual hoy es cuestión de minutos, y se repite cada semestre.

Prueba la demo en mate.compile.cl

Volver al
Resumen del caso
Conversemos
Sobre tu caso