MATE: asignación de grupos

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 famila 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, MATE hace dos cosas: comprueba que los parámetros no sean imposibles de cumplir y agrupa a los alumnos intercambiables en tipos. Lo primero evita resolver en vano, y lo segundo es la decisión que más pesa en el rendimiento.

a. Chequeo de parámetros

Hay combinaciones de parámetros que son imposibles 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} UT_t \ge N_{\text{grupos}} \qquad \sum_{t \in T} LT_t \le N_{\text{grupos}}\]

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 repartirlas 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, cuando hay módulos configurados, disponibilidad horaria. En vez de una variable binaria por alumno, usamos una variable entera \(y_{ig}\) por tipo \(i \in I\) y grupo \(g \in G\), que cuenta cuántos alumnos del tipo \(i\) van al grupo \(g\). En el ejemplo, las 3 alumnas forman un solo tipo \(i \in I\) con \(R_i = 3\), 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}\).

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. Antes de cada fórmula va la versión en castellano, así que si las fórmulas no son lo tuyo, igual puedes quedarte con la idea.

Variables de decisión

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

Restricciones

Algunas son hechos del problema (por ejemplo, un alumno solo puede ir a un horario en que está disponible) y otras son decisiones de política que el equipo académico mueve desde la pantalla de configuración.

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 una constante de balance \(K\) que castiga tanto al estudiante peor ubicado como al grupo con más alumnos sin respuesta.

\(K\) es muy grande a propósito: debe ser un número al menos mayor que la cantidad total de alumnos \(\sum_{i \in I} R_i\), para que pese más que cualquier término de preferencia. En la práctica, 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.

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.

a. Pasada por las preferencias

Para cada tipo de alumno, en orden de ranking (primero la primera opción, después la segunda, etc.), asignamos a todos los que quepan a un grupo de ese tema, y si hay horarios, solo en uno donde el tipo está disponible. Es una regla simple de primer ajuste.

b. Pasada por los que sobraron

Quienes no alcanzaron lugar en ninguno de sus temas (porque los grupos se llenaron antes de que les llegara el turno) se asignan a cualquier grupo donde físicamente puedan estar, ignorando sus preferencias. Así el warm start queda como una asignación 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.

CP-SAT, de Google OR-Tools

Resolvemos el modelo con CP-SAT, el solver open source de Google OR-Tools. Para saber si 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 usa el mismo mecanismo pero con una solución exitosa: de las reglas que se cumplieron, ¿cuál es la que más cuesta mantener? Se mide en puntos porcentuales de alumnos con su primera opción, que es una unidad comparable entre tipos de regla. La línea base se resuelve al óptimo con exactamente los mismos ajustes que cada prueba, para que una diferencia refleje el efecto de la regla y no la variabilidad de la búsqueda. Solo corre si lo pides, nunca de forma automática.

La demo, paso a paso

Paso 1

Definir parámetros

Primero le cuentas a MATE qué tiene tu curso: qué atributos quieres equilibrar (género, universidad, carrera), en qué horarios se juntan los grupos y qué temas pueden elegir los alumnos. Lo que no se use se desactiva, y el modelo se adapta a lo que se configure.

MATE, paso 1: definir atributos, secciones y temas
Paso 2

Subir la nómina

Subes una planilla de Excel (MATE te genera la plantilla). Apenas la lee te muestra un resumen: cuántos alumnos hay por atributo, cuántos tienen disponibilidad en cada horario y cuántos pidieron cada tema como primera y segunda opción. Sirve para detectar a simple vista si algo viene incorrecto antes de resolver.

MATE, paso 2: resumen de la nómina subida
Paso 3

Configurar y ejecutar

Aquí defines las reglas del problema: cuántos grupos quieres, el rango de tamaño, cuántos alumnos con cierta característica puede tener un grupo, los cupos por horario y cuántos grupos puede abrir cada tema. El panel de la derecha se actualiza en vivo, estima cuántas variables tendrá el modelo y te avisa si los límites son factibles antes de tocar el solver.

MATE, paso 3: configurar límites y ejecutar
Paso 4

Resultados

La corrida terminó en 1,1 segundos y quedó marcada como OPTIMAL: el solver demostró que no existe una repartición mejor bajo esas reglas. 45 de 48 alumnos quedaron en su primera preferencia, 3 en la segunda y nadie quedó fuera de lo que pidió. Cada grupo muestra su tema, su horario y sus integrantes, y todo se baja en Excel.

MATE, paso 4: resultados con grupos formados y preferencias cumplidas
Análisis de sensibilidad

¿Cuánto me cuesta cada regla?

Si quieres saber qué regla tiene mayor costo, MATE vuelve a resolver relajando cada una por separado. En este ejemplo, flexibilizar el rango de tamaño subiría en 6,2 puntos los alumnos con su primera opción, y soltar la cantidad de grupos, 2,1 puntos. Las demás reglas no cuestan nada. Es información útil para decidir qué regla conviene flexibilizar, y no solo para resolver.

MATE: análisis de sensibilidad por requisito

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 parámetros imposibles 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 a
Casos
Conversemos
Sobre tu caso