Prueba de modelos de AI Generativa.

 Me plantee comparar las capacidades de los diferentes modelos a los que tengo acceso. 

La prueba que quiero hacer, es poder subir una imagen de un problema de programacion lineal entera, y que el modelo interprete la imagen y me explique el problema. 

Luego tratare de generarlo en GLPK (lenguaje para resolucion de problemas de este tipo, el cual conozco) y OR-TOOLS (las herramientas de Google para modelos de optimizacion en investigacion operativa). 

El problema corresponde a un problema llamado "Capacitated Facility Location" y para probar solo  tengo como imagen las formulas:




Le pido a los diferentes modelos que me lo expliquen y luego que lo traduzcan a lenguajes de programación. 


ChatGPT 4






Es muy llamativo para mi como logra de un conjunto de ecuaciones, llegar a un problema practico donde esas ecuaciones pueden resolver un problema. 

ChatGPT 4 - Generarlo en MathProg 


# Definiciones de conjuntos
set I; # Conjunto de recursos
set J; # Conjunto de requerimientos
# Parámetros
param f{i in I}; # Costo fijo del recurso i
param c{i in I, j in J}; # Costo variable de usar el recurso i para el requerimiento j
param C{i in I}; # Capacidad máxima del recurso i
param K{i in I, j in J}; # Máxima cantidad del recurso i que puede ser usada para el requerimiento j
param D{j in J}; # Demanda del requerimiento j
# Variables de decisión
var delta{i in I}, binary; # 1 si se usa el recurso i, 0 de lo contrario
var y{i in I, j in J}, >= 0, integer; # Cantidad del recurso i usada para el requerimiento j
var x{i in I, j in J}, >= 0; # Representación de la vinculación de x y y
# Función objetivo
minimize TotalCost: sum{i in I} (f[i]*delta[i] + sum{j in J} c[i,j]*y[i,j]);
# Restricciones
s.t. CapacityCon{i in I}: sum{j in J} x[i,j] <= C[i]*delta[i];
s.t. LinkCon{i in I, j in J}: 0 <= x[i,j] <= K[i,j]*y[i,j];
s.t. DemandCon{j in J}: sum{i in I} x[i,j] = D[j];
# Límites de las variables
s.t. BinVar{i in I}: delta[i] = 0 \/ delta[i] = 1;
s.t. IntVar{i in I, j in J}: y[i,j] is integer;
# Resolver el modelo
solve;
# Mostrar resultados
printf "Costo total: %f\n", TotalCost;
for {i in I} {
  printf "Recurso %s: %s\n", i, if delta[i] > 0.5 then "usado" else "no usado";
  for {j in J} {
    printf "  Requerimiento %s, y[%s,%s]: %d\n", j, i, j, y[i,j];
  }
}
end;

Este es un trabajo importante, pues lograr este nivel de programacion da bastante trabajo. Considero que a mi me hubiese llevado unas 4 horas tener el modelo traducido y funcionando (con algunos datos de prueba, que ChatGPT tambien genero. 

ChatGPT - Generar Python con OR-Tools


from ortools.linear_solver import pywraplp

# Crea el solver. Aquí estamos usando SCIP, que es compatible con MILP.
solver = pywraplp.Solver.CreateSolver('SCIP')

# Suponiendo que tienes los datos del problema en las siguientes estructuras:
# Costos fijos por recurso
f = {...}
# Costos variables por utilizar recurso i para el requerimiento j
c = {...}
# Capacidad máxima por recurso
C = {...}
# Máximo de uso del recurso i para el requerimiento j
K = {...}
# Demanda por requerimiento
D = {...}

# Conjuntos de recursos y requerimientos
resources = f.keys()
requirements = D.keys()

# Variables de decisión
delta = {i: solver.BoolVar(f'delta_{i}') for i in resources}
y = {(i, j): solver.IntVar(0.0, solver.infinity(), f'y_{i}_{j}') for i in resources for j in requirements}
x = {(i, j): solver.NumVar(0.0, solver.infinity(), f'x_{i}_{j}') for i in resources for j in requirements}

# Función objetivo
objective_terms = [f[i]*delta[i] for i in resources] + [c[i, j]*y[i, j] for i in resources for j in requirements]
solver.Minimize(solver.Sum(objective_terms))

# Restricciones
# Restricciones de capacidad
for i in resources:
    solver.Add(solver.Sum([x[i, j] for j in requirements]) <= C[i]*delta[i])

# Restricciones de vinculación
for i in resources:
    for j in requirements:
        solver.Add(x[i, j] <= K[i, j]*y[i, j])

# Restricciones de demanda
for j in requirements:
    solver.Add(solver.Sum([x[i, j] for i in resources]) == D[j])

# Límites de las variables (implicitos en la creación de las variables)
# Resolver el modelo
status = solver.Solve()

# Verificar si se encontró una solución
if status == pywraplp.Solver.OPTIMAL:
    print('Solución:')
    print('Costo total = ', solver.Objective().Value())
    for i in resources:
        print(f'Recurso {i} usado: {delta[i].solution_value()}')
        for j in requirements:
            print(f'y[{i},{j}] = {y[i, j].solution_value()}')
else:
    print('El problema no tiene solución óptima.')

No estoy en condiciones de evaluar si es correcto solo viendo el fuente, pues no domino esta biblioteca , pero tiene buena pinta. Aunque tenga algun error, seguro que ahorra mucho trabajo. Y ademas teniendo una version en GLPK que funciona, se puede comparar el resultado de un modelo contra el otro y asi poder determinar si es correcto . 


GEMINI - Google 


La explicacion del problema, fue bastante mas concreta

Lo que si me gusto fue que propuso un ejemplo para mostrar donde podia usarse este modelo de programacion lineal.  ChatGPT no lo hizo, tampoco se lo pedi. 



Gemini Generado en MathProg


# Definición de los conjuntos set I := T1, T2; # Conjunto de tareas set J := R1, R2; # Conjunto de recursos # Definición de los parámetros param c(i) := 10, 8; # Costo fijo de cada tarea param dij := 5, 4, 3, 2; # Demanda de recursos para cada tarea param fi := 10, 0; # Demanda fija de cada recurso param cj := 1, 1; # Costo unitario de cada recurso # Definición de las variables var x(i,j) := 0; # Cantidad de recurso j asignado a la tarea i var y(i) := 0; # Variable binaria que indica si la tarea i se realiza o no # Función objetivo minimize obj: sum{i in I} (c(i) * y(i) + sum{j in J} (c(j) * x(i,j))); # Restricciones de capacidad subject to cap(j) { sum{i in I} x(i,j) <= fi(j) for j in J; } # Restricciones de asignación subject to assign(i) { sum{j in J} x(i,j) <= y(i) for i in I; } # Restricciones de necesidad subject to need(i) { y(i) >= 1 for i in I; } # Resolución del problema solve;

Gemini para OR-Tools

from ortools.linear_solver import pywraplp # Definición del modelo model = pywraplp.Solver('mip_example', pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING) # Definición de las variables x = {} for i in range(len(tareas)): for j in range(len(recursos)): x[i, j] = model.IntVar(0, model.infinity, 'x_%d_%d' % (i, j)) y = {} for i in range(len(tareas)): y[i] = model.IntVar(0, 1, 'y_%d' % i) # Función objetivo objective = 0 for i in range(len(tareas)): objective += c[i] * y[i] for i in range(len(tareas)): for j in range(len(recursos)): objective += c[j] * x[i, j] model.Minimize(objective) # Restricciones de capacidad for j in range(len(recursos)): constraint = model.Constraint(0, fi[j], 'cap_%d' % j) for i in range(len(tareas)): constraint.Add(x[i, j]) # Restricciones de asignación for i in range(len(tareas)): constraint = model.Constraint(0, 1, 'assign_%d' % i) for j in range(len(recursos)): constraint.Add(x[i, j]) # Restricciones de necesidad for i in range(len(tareas)): constraint = model.Constraint(1, 1, 'need_%d' % i) constraint.Add(y[i]) # Resolución del problema status = model.Solve() # Impresión de los resultados if status == pywraplp.Solver.OPTIMAL: print('Costo total:', model.Objective().Value()) for i in range(len(tareas)): if y[i].solution_value() > 0: print('Tarea %d realizada' % i) for j in range(len(recursos)): if x[i, j].solution_value() > 0: print(' Recurso %d asignado:', x[i, j].solution_value()) else: print('No se pudo encontrar una solución óptima.')

Gemma

Hice algunas pruebas adicionales, con otros po eejemplo con gemma y es muchisimo mas rapido que los anteriores, pero por no podia leer imagenes y cuando le pedi que generar con la biblioteca OR-Tools, utilizo otra
Import pyomo.environ # Definiciones de conjuntos I = pyomo.Set() # Conjunto de recursos J = pyomo.Set() # Conjunto de requerimientos # Parámetros f = pyomo.Param(I) # Costo fijo del recurso i c = pyomo.Param(I, J) # Costo variable de usar el recurso i para el requerimiento j C = pyomo.Param(I) # Capacidad máxima del recurso i K = pyomo.Param(I, J) # Máxima cantidad del recurso i que puede ser usada para el requerimiento j D = pyomo.Param(J) # Demanda del requerimiento j # Variables de decisión delta = pyomo.Var(I, domain=pyomo.Binary) # 1 si se usa el recurso i, 0 de lo contrario y = pyomo.Var(I, J, domain=pyomo.Integer) # Cantidad del recurso i usada para el requerimiento j x = pyomo.Var(I, J, domain=pyomo.NonNegative) # Representación de la vinculación de x y y # Función objetivo TotalCost = pyomo.Objective(expr=sum(f[i] * delta[i] + sum(c[i, j] * y[i, j] for i in I for j in J)) # Restricciones CapacityCon = pyomo.Constraint(expr=sum(y[i, j] for j in J) <= C[i] * delta[i] for i in I) LinkCon = pyomo.Constraint(expr=0 <= x[i, j] <= K[i, j] * y[i, j] for i in I for j in J) DemandCon = pyomo.Constraint(expr=sum(x[i, j] for i in I) = D[j] for j in J) # Límites de las variables BinVar = pyomo.Constraint(expr=delta[i] in [0, 1] for i in I) IntVar = pyomo.Constraint(expr=y[i, j].is_integer() for i in I for j in J) # Resolver el modelo solver = pyomo.SolverFactory("glpk") solver.solve(model=TotalCost) # Resultados print("Solución:") print("Costo total:", solver.obj) print("Asignación de recursos:") print("delta:", delta.value) print("y:", y.value) print("x:", x.value)

Tambien hice prubas con Copilot de Microsoft, pero el resultado fue bastante peor que con los primeros.

Conclusiones:

  • Me resulto muy incesante la precisión que han alcanzado estos modelos en la generación de código, en lenguajes y bibliotecas no demasiado comunes.
  • Es llamativo como sobre todo ChatGPT, puede desde las fórmulas llegar al planteo del problema.
  • En pocos meses / años, este post se va a ver anacrónico.  Escribir todo ese código a mano como hacemos ahora va a ser cosa del pasado. 

Comentarios

Entradas más populares de este blog

La nefasta influencia del golero de Cacho Bochinche en el fútbol uruguayo

Aplicación monolítica o distribuida?

Funcionalidades de GeneXus que vale la pena conocer: DATE Constants.