0

I have a problem with optimizing of a function that contains loops. I start with paricular lista=[0.002,0.006,0.003,0.02,0.008,0.006,0.05] of floats and the intervals `(0,k*0.0025),(0.005,k*0.005),(0.005,k*0.0125), where upper border depends. So, depending on which of the interval the float of list belong to, I assign to the function one of values k*0.005,k*0.01,k*0.025 and k*0.05 that also depend on k.

I want to minimize k such that the sum (new scalar function) of values of assign(k) or sum(assign(k)) is equal to 0.32.

I used scipy.optimize procedure to do that. My constraint is constraint=sum(assign(k))-0.32 and objective function iz fun(k)=k. So, I minimized k to satisfy the constraint.

import scipy
from scipy.optimize import minimize


lista=[0.002,0.006,0.003,0.02,0.008,0.006,0.05]

def assign(k):
    return list(map(lambda x:(k*0.005 if x in np.arange(0,k*0.0025,0.001) 
    else k*0.01 if x in np.arange(0.0025,k*0.005,0.001) else k*0.025 if x in 
    np.arange(0.005,k*0.0125,0.001) else k*0.05), lista))

def constraint(k):
    return sum(assign(k))-0.32

def fun(k):
    return k

k0=0
bnds=[(0,10)]
cons={'type':'eq','fun':constraint}
res=minimize(fun,k0,bounds=bnds,method='SLSQP',constraints=cons,options={'maxiter':2000})
print(res)

I got k=1.1999 which is a strange result, it does not satisfy the constraint. It should be 2 since sum(assign(2))=0.52. I also got a error message:

  message: 'Iteration limit exceeded'

Does anyone knows how to overcome this limitation? Thank you in advance!

fdelafuente
  • 1,114
  • 1
  • 13
  • 24
Neo R.
  • 9
  • 3

1 Answers1

0

You wrote:

lambda x:(k*0.005 if x in np.arange(0,k*0.0025,0.001) 
else k*0.01 if x in np.arange(0.0025,k*0.005,0.001) else k*0.025 if x in 
np.arange(0.005,k*0.0125,0.001) else k*0.05)

Please don't do that. A short lambda function is fine, but this one is long enough to deserve a name and a readable function definition:

def assign1(x, k):
    if x in np.arange(0, k * 0.0025, 0.001):
        return k * .01
    if x in np.arange(0.0025, k * 0.005, 0.001):
        return k * .025
    ...

But having taken a moment to start writing that out, it immediately appears to be wrong.

  1. I cannot believe you meant .0025, rather than k * .0025, as a lower bound.
  2. Suppose some random noise is added, so rather than x we have x + epsilon. Then your function returns a constant of 5% of k, which seems Bad.

In general, we do not expect that int(x * 1000) == x. So code it using proper ranges:

def assign1(x, k):
    if 0 <= x < k * .0025:
        return k * .01
    if k * .0025 <= x < k * .0050:
        return k * .025
    ...

Supply a better constraint and I believe you will be happier with how the optimizer converges.

EDIT

The optimization tutorial suggests that for this SLSQP method you should be passing in both fun and jac.

Also, the full_output=True exit mode offers diagnostic advice about what went wrong.

J_H
  • 17,926
  • 4
  • 24
  • 44
  • J_H thank you very much for your response. I agree with you about intervals. I also was suspicious but I got the problem in this form. I must check it. – Neo R. May 27 '19 at 09:19
  • But regarding to lambda function, I think that loops within comprehensive list better fit than usual loops, but within lambda function it is even better than comprehensive list. When I say better I mean faster. – Neo R. May 27 '19 at 09:23
  • I tried in form you proposed but I got the same message. – Neo R. May 27 '19 at 09:25