Jen
Jen

Reputation: 11

How to correctly set up a scipy.optimize minimization with constraints and multiple optima?

I'm very new to Python and am having some trouble with the minimize part of scipy.optimize. I've set up a simple example to show what I am trying to do. My objective is to determine the lowest purchase quantity of items that also minimizes stock.

Everything that I purchase is split up between two products - 25% goes into Product A, 75% into Product B. One unit of Product A has a unit size of 100, while Product B has a unit size of 300. I also have a minimum purchase quantity of 100, and a maximum purchase quantity of 2000.

The below code is the setup for my objective function.

import math
from scipy.optimize import minimize

unitsize =  [100, 300]
proportion = [0.25, 0.75]
minpurchase = 100
maxpurchase = 2000

def stock (purchase):
    test = []
    for i in xrange(len(unitsize)):    
        product_purchase = purchase * proportion[i]
        units = math.floor(product_purchase / unitsize[i])
        y = product_purchase - (units * unitsize[i])
        test.append(y)
        z = sum(test)
    return z

I have tested the stock function and it seems to work as desired. For instance, when I call stock(500), stock(400) and stock(300), it correctly returns 100, 0 and 300 respectively.

Then I tried, to implement the minimization as per below, with constraints based on the minimum and maximum purchase amounts.

However, when I start this with an initial guess of zero, it returns a result of 100, which I consider a wrong result, as it will cause 100 stock and no full units. I am expecting e.g. a result of 400 (where stock is zero). The same problem occurrs when I e.g. start with an initial guess of 100.

cons = ({'type': 'ineq', 'fun': lambda x:  x[0] - minpurchase},
        {'type': 'ineq', 'fun': lambda x:  x[0] + maxpurchase}
        )


print minimize(lambda x: stock(x[0]), [0],
                   method='COBYLA',
                   constraints = cons,
                   options={'maxiter':10000})
  status: 1
    nfev: 106
   maxcv: -0.0
 success: True
     fun: 100.0
       x: array([ 100.])
 message: 'Optimization terminated successfully.'

When I start with an initial guess of 500, the returned result is correct at 400. However, when I e.g. start with 1900, it returns 1600 as the result as well, as stock is also zero at that point.

Couple of questions:

  1. Is there a way to make it work with an initial guess of 0?
  2. In this example, there are multiple optima in the solution space (400, 800, 1200, 1600, 2000). Is it possible to set up the optimization such that it will find the entire vector of optima and then return the lowest item (i.e. 400)?

In trying to work out Question 2, I have considered passing an array of testvalues into the initial guess as below, but it returns "IndexError: too many indices for array".

Any idea what I am doing wrong here?

testvalues = [1000,2000]

for j in range(len(testvalues)):
    print minimize(lambda x: stock(x[0]), testvalues[j],
                       method='COBYLA',
                       constraints = cons,
                       options={'maxiter':10000})

[...]
IndexError: too many indices for array

Any help and pointers much appreciated, I have been trying to get this to work all day long.

Upvotes: 1

Views: 2559

Answers (1)

Baptiste F.
Baptiste F.

Reputation: 11

This error raise because COBYLA method does not handle bounds. Only BFGS, L-BFGS-B, and SLSQP methods do. See: documentation link

Upvotes: 1

Related Questions