Investigación de operaciones

Programación lineal en Google OR-Tools

Optimización lineal

Tal como lo hemos mencionado en artículos anteriores (programación lineal); la optimización lineal, es el nombre con el que se conoce al cálculo de la mejor solución a un problema modelado como un conjunto de restricciones lineales y una función objetivo también lineal.


    El objetivo de este artículo consiste en utilizar las librerías del software Google OR-Tools para abordar problemas de programación lineal (optimización lineal). 

    El problema

    Con el propósito de evaluar los resultados obtenidos a través del tratamiento de un problema técnicamente formulado y abordado, utilizaremos un caso descrito en el libro Applied Mathematical Programming, de Bradley, Hax, and Magnanti (Addison-Wesley, 1977), del MIT (Cápitulo 2 página 50).

    El propietario de una tienda que produce remolques para automóviles desea determinar la mejor combinación para sus tres productos: remolques de plataforma plana, remolques económicos y remolques de lujo. Su taller se limita a trabajar 24 días al mes en el trabajo de los metales y 60 días al mes en el trabajo de la madera para estos productos. La siguiente tabla indica los datos de producción de los remolques.

    Uso por cada unidad de tráiler Recursos disponibles
    Plataforma plana Económica Lujosa
    Días de trabajo en metales 0,5 2 1 24
    Días de trabajo en madera 1 2 4 60
    Contribución ($ x 100) 6 14 13

    Modelamiento del problema

    Sean las variables de decisión del problema:

    x0 = Número de remolques de plataforma plana producidos por mes

    x1 = Número de remolques económicos producidos por mes

    x2 = Número de remolques de lujo producidos por mes

    Suponiendo que los costos de la capacidad de trabajo en metal y madera sean fijos, el problema se convierte en un problema de maximización:

    Zmax = 6x0 + 14x1 + 13x2

    Sujeto a las siguientes restricciones de capacidad:

    0,5x0 + 2x1 + x2 <= 24,

    x0 + 2x1 + 4x2 <= 60,

    Sujeto a las siguientes restricciones de no-negatividad:

    x0 >= 0,

    x1 >= 0,

    x2 >= 0,

    Podemos, del mismo modo, establecer un par de variables que correspondan a las horas ociosas para las dos tareas establecidas (metal y madera):

    x3 = Número de horas ociosas en el trabajo en metal al mes,

    x4 = Número de horas ociosas en el trabajo en madera al mes,

    Reescribimos las restricciones (adicionando las variables de horas ociosas). Podemos observar que las inecuaciones ahora serán igualdades, para que de esta forma ahora podamos tener información relacionada a los recursos. En otras palabras, lo que se utiliza (horas productivas) + lo que sobre (horas ociosas) = tiempo disponible:

    0,5x0 + 2x1 + x2 + x3 = 24,

    x0 + 2x1 + 4x2 + x4 = 60,

    x0 >= 0,

    x1 >= 0,

    x2 >= 0,

    x3 >= 0,

    x4 >= 0,

    Así entonces, tenemos el problema completamente modelado.


    Resolución del modelo mediante Google OR-Tools

    De acuerdo a lo mencionado en el artículo de introducción a Google OR-Tools, esta herramienta soporta múltiples lenguajes de programación, así entonces, haremos uso del l enguaje de programación Python.

    Paso 1: Importar la librería

    El siguiente fragmento de código importa las librerías necesarias:

    # Importar la librería de Google OR-Tools
    from ortools.linear_solver import pywraplp
    

    Paso 2: Declarar el solucionador

    El siguiente fragmento de código declara el solucionador GLOP (Google OR-Tools posee múltiples solucionadores):

    solver = pywraplp.Solver.CreateSolver('GLOP')

    Paso 3: Crear las variables del modelo

    El siguiente fragmento crea las variables del modelo, así mismo indica el tipo de variables correspondientes y su rango de valores. Así entonces, desde la creación de las variables se pueden abordar las restricciones de no-negatividad (entre 0 e infinito):

    x0 = solver.NumVar(0, solver.infinity(), 'x0')
    x1 = solver.NumVar(0, solver.infinity(), 'x1')
    x2 = solver.NumVar(0, solver.infinity(), 'x2')
    x3 = solver.NumVar(0, solver.infinity(), 'x3')
    x4 = solver.NumVar(0, solver.infinity(), 'x4')
    
    print('Número de variables =', solver.NumVariables())
    

    Paso 4: Definir las restricciones del modelo

    El siguiente fragmento de código define las restricciones del modelo:

    # Restricción 0: 0.5x0 + 2x1 + x2 + x3 = 24.
    solver.Add(0.5 * x0 + 2 * x1 + x2 + x3 == 24.0)
    
    # Restricción 1: x0 + 2x1 + 4x2 + x4 = 60.
    solver.Add(x0 + 2 * x1 + 4 *x2 + x4 == 60.0)
    
    print('Número de restricciones =', solver.NumConstraints())
    

    Paso 5: Definir la función objetivo del modelo

    El siguiente fragmento de código define la función objetivo del modelo (maximizar):

    # Función objetivo (max): 6x0 + 14x1 + 13x2
    solver.Maximize(6 * x0 + 14 * x1 + 13 * x2)
    

    Paso 6: Invocar el solucionador

    El siguiente fragmento de código sirve para invocar el solucionador del modelo:

    status = solver.Solve()
    

    Paso 7: Definir las salidas del solucionador

    if status == pywraplp.Solver.OPTIMAL:
        print('Solución:')
        print('Valor objetivo =', solver.Objective().Value())
        print('Número de remolques de plataforma plana producidos por mes =', x0.solution_value())
        print('Número de remolques económicos producidos por mes =', x1.solution_value())
        print('Número de remolques de lujo producidos por mes =', x2.solution_value())
        print('Número de horas ociosas en el trabajo en metal al mes =', x3.solution_value())
        print('Número de horas ociosas en el trabajo en madera al mes =', x4.solution_value())
    else:
        print('El problema no tiene solución óptima.')
    

    Es posible que el desarrollo de los siete pasos anteriores demande algún grado de complejidad subyacente del uso de un lenguaje de programación; sin embargo, es preciso mencionar que, el modelo anterior queda perfectamente configurado, y puede replicarse con modificaciones menores en múltiples problemas de optimización lineal.

    ¿Cómo ejecutar el modelo?

    Alternativa 1, ejecución en nuestro equipo:

    Lo primero que debemos considerar, en el caso de que queramos ejecutar este código en nuestro equipo, es que es preciso contar con la instalación de Python en nuestro equipo de cómputo, así mismo debemos contar con la última versión del comando pip y por supuesto, el software OR-Tools. Una guía detallada de la instalación de estos requerimientos la podrás encontrar en el siguiente enlace:

    Instalación de OR-Tools para Python

    Ahora, lo recomendable es trabajar con algún editor de código práctico (IDE), por ejemplo: Sublime Text, o Spyder (Una herramienta más completa y por ende más robusta y pesada).

    Alternativa 2, ejecución en un entorno virtual (Recomendado):

    Podemos utilizar del mismo modo, un entorno virtual. En este caso recomendamos el uso de Colaboratory de Google, un entorno que cuenta con todas las herramientas necesarias para nuestros desarrollos. No tendremos que instalar nada en nuestro equipo, y aprovecharemos la potencia de las máquinas de Google.


    El código completo de nuestro desarrollo lo presentamos a continuación. También puedes ver el cuaderno de este módulo en nuestro Colaboratory: Programación Lineal.

    #Desde: Bradley, Hax, and Magnanti, 'Applied Mathematical Programming', Chapter 2
    
    # Importar la librería de Google OR-Tools
    from ortools.linear_solver import pywraplp
    
    def LinearProgrammingExample():
        solver = pywraplp.Solver.CreateSolver('GLOP')
    
        x0 = solver.NumVar(0, solver.infinity(), 'x0')
        x1 = solver.NumVar(0, solver.infinity(), 'x1')
        x2 = solver.NumVar(0, solver.infinity(), 'x2')
        x3 = solver.NumVar(0, solver.infinity(), 'x3')
        x4 = solver.NumVar(0, solver.infinity(), 'x4')
    
        print('Número de variables =', solver.NumVariables())
    
        # Restricción 0: 0.5x0 + 2x1 + x2 + x3 = 24.
        solver.Add(0.5 * x0 + 2 * x1 + x2 + x3 == 24.0)
    
        # Restricción 1: x0 + 2x1 + 4x2 + x4 = 60.
        solver.Add(x0 + 2 * x1 + 4 *x2 + x4 == 60.0)
    
        print('Número de restricciones =', solver.NumConstraints())
    
        # Función objetivo (max): 6x0 + 14x1 + 13x2
        solver.Maximize(6 * x0 + 14 * x1 + 13 * x2)
    
        # Declarar el solucionador.
        status = solver.Solve()
    
        # Declarar las salidas del solucionador
        if status == pywraplp.Solver.OPTIMAL:
            print('Solución:')
            print('Valor objetivo =', solver.Objective().Value())
            print('Número de remolques de plataforma plana producidos por mes =', x0.solution_value())
            print('Número de remolques económicos producidos por mes =', x1.solution_value())
            print('Número de remolques de lujo producidos por mes =', x2.solution_value())
            print('Número de horas ociosas en el trabajo en metal al mes =', x3.solution_value())
            print('Número de horas ociosas en el trabajo en madera al mes =', x4.solution_value())
        else:
          if status == solver.FEASIBLE:
            print('Se encontró una solución potencialmente subóptima.')
          else:
            print('El problema no tiene solución óptima.')
    
        # Información avanzada del solucionador
    
        print('\nUso avanzado:')
        print('Problema resuelto en %f  milisegundos' % solver.wall_time())
        print('Problema resuelto en %d iteraciones' % solver.iterations())
    
    LinearProgrammingExample() 

    Al ejecutar nuestro desarrollo en Colaboratory, tenemos:

    Podemos observar que se ha obtenido la misma respuesta que se encuentra consignada en el libro Applied Mathematical Programming (Página 51).


    Ahora bien, el modelo de optimización lineal y el script del solucionador quedaron desarrollados en un lenguaje de programación estándar y ampliamente utilizado. Desde luego, las posibilidades de integrar datos de entrada y procesar los datos de salidas son interesantes. Por ejemplo, es posible desarrollar un script mediante el cual el código ya desarrollado tome los datos de entrada desde un archivo de Excel, o desde un servidor externo.

    También, es posible desarrollar una interfaz amigable desde la cual se ingrese la información; o vincular los datos de salida con algún modelo o documento determinado.

    En próximos artículos abordaremos algunos scripts haciendo uso de las librerías de Google OR-Tools que nos permitan utilizar bucles de matrices, con el objetivo de automatizar el proceso de definición de variables y restricciones, mejorando la eficiencia del modelamiento.

    Bryan Salazar López

    Ingeniero Industrial y Magíster en Logística Integral especializado en productividad y modelamiento de procesos bajo dimensiones de sostenibilidad, industria 4.0, transformación digital y modelos de optimización. Docente universitario de pregrado y posgrado con experiencia en la enseñanza de estos temas. Fundador de Ingenieriaindustrialonline.com, un sitio en donde se recogen las aportaciones de investigaciones, artículos y referencias relevantes para la industria.

    Entradas recientes

    El sentido común y la Teoría de Restricciones (TOC)

    En una pequeña comunidad agrícola en Michoacán, México, un niño llamado José Hernández soñaba con…

    hace % días

    La restricción nos blinda contra las malas noticias

    Sábado por la mañana, Robert acaba de acompañar a su mujer a su clase de…

    hace % días