Saltar al contenido

Restringir scipy.optimize.minimize a valores enteros

Haz todo lo posible por entender el código bien antes de adaptarlo a tu trabajo y si ttienes algo que aportar puedes compartirlo con nosotros.

Solución:

solución de pulpa

Después de algunas investigaciones, no creo que su función objetivo sea lineal. Recreé el problema en la biblioteca pulp de Python, pero a pulp no le gusta que estemos dividiendo por un flotador y ‘LpAffineExpression’. Esta respuesta sugiere que la programación lineal “no comprende las divisiones”, pero ese comentario está en el contexto de agregar restricciones, no la función objetivo. Ese comentario me señaló “Programación fraccional lineal de enteros mixtos (MILFP)” y en Wikipedia.

Así es como podría hacerlo en pulpa si realmente funcionara (tal vez alguien pueda averiguar por qué):

import pulp

data = [(481.79, 5), (412.04, 4), (365.54, 3)] #, (375.88, 3), (379.75, 3), (632.92, 5), (127.89, 1), (835.71, 6), (200.21, 1)]
x = pulp.LpVariable.dicts('x', range(len(data)), lowBound=0, upBound=7, cat=pulp.LpInteger)

numerator = dict((i,tup[0]) for i,tup in enumerate(data))
denom_int = dict((i,tup[1]) for i,tup in enumerate(data))

problem = pulp.LpProblem('Mixed Integer Linear Programming', sense=pulp.LpMinimize)

# objective function (doesn't work)
# TypeError: unsupported operand type(s) for /: 'float' and 'LpAffineExpression'
problem += sum([numerator[i] / (denom_int[i] + x[i]) for i in range(len(data))])

problem.solve()

for v in problem.variables():
  print(v.name, "=", v.varValue)

solución bruta con scipy.optimize

Puedes usar brute y rangos de slices para cada x en su función. Si tienes 3 xs en su función, también tendrá 3 slices en su tupla de rangos. El key a todo esto es para agregar el paso tamaño de 1 al slice(start, stop,step) entonces slice(#, #, 1).

from scipy.optimize import brute
import itertools

def f(x):
  return (481.79/(5+x[0]))+(412.04/(4+x[1]))+(365.54/(3+x[2]))

ranges = (slice(0, 9, 1),) * 3
result = brute(f, ranges, disp=True, finish=None)
print(result)

solución itertools

O puede usar itertools para generar todas las combinaciones:

combinations = list(itertools.product(*[[0,1,2,3,4,5,6,7,8]]*3))

values = []
for combination in combinations:
  values.append((combination, f(combination)))

best = [c for c,v in values if v == min([v for c,v in values])]
print(best)

Nota: esta es una versión reducida de su función original para fines de ejemplo.

Una cosa que podría ayudar a su problema podría tener una restricción como:

max([x-int(x)])=0

Esto no va a resolver completamente su problema, el algoritmo aún intentará hacer trampa y obtendrá valores con algún nivel de error. ~±5e-10 que todavía tratará de optimizar solo por el error en el algoritmo de scipy, pero es mejor que nada.

cons = ('type':'eq', 'fun': con,
        'type':'eq','fun': lambda x : max([x[i]-int(x[i]) for i in range(len(x))]))

Después de haber probado este proceso en algunas optimizaciones, conozco la solución, este proceso es más sensible a los valores iniciales que la búsqueda sin restricciones, obtiene respuestas bastante precisas, sin embargo, es posible que la solución no encuentre el true valor, básicamente está requiriendo el gran salto del proceso de optimización (lo que usa para asegurarse de que no está optimizando a un mínimo local) para buscar en el espacio muestral, ya que los incrementos más pequeños generalmente no son lo suficientemente fuertes como para pasar al siguiente número.

Aquí hay una forma de resolver el problema de programación no lineal de enteros mixtos con Python Gekko (un paquete que mantengo):

from gekko import GEKKO

m = GEKKO(remote=False)
x = m.Array(m.Var,9,lb=0,ub=7,integer=True)

def f(x):
    return (481.79/(5+x[0]))+(412.04/(4+x[1]))
           +(365.54/(3+x[2]))+(375.88/(3+x[3]))
           +(379.75/(3+x[4]))+(632.92/(5+x[5]))
           +(127.89/(1+x[6]))+(835.71/(6+x[7]))
           +(200.21/(1+x[8]))

m.Minimize(f(x))
m.Equation(sum(x)==7)
m.options.SOLVER=1
m.solve()
print(x)

Esto da la solución:

 ---------------------------------------------------
 Solver         :  APOPT (v1.0)
 Solution time  :  0.0529 sec
 Objective      :  859.5269999999999
 Successful solution
 ---------------------------------------------------


[[0.0] [0.0] [1.0] [1.0] [1.0] [0.0] [1.0] [0.0] [3.0]]

Si posees algún recelo o capacidad de progresar nuestro sección eres capaz de escribir un paráfrasis y con deseo lo ojearemos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *