0

I am trying to optimize a function with 2 inputs, each being a list of numbers. I created these similar but simpler version of the function:

w1 = [1,2,3] 
w2 = [4,5,6] 
w = [w1,w2]

def objective(x):
    a = x[0][0]**2+x[0][1]**2+x[0][2]**2+x[1][0]**2+x[1][1]**2+x[1][2]**2
    return a 
bnds_1 = tuple((0.1, 1) for w in w1) 
bnds_2 = tuple((0,0.5) for w in w2)

result = minimize(objective,x0=w,bounds=(bnds_1,bnds_2)) 
result

where the bound for each number in w1 is (0.1,1) and bound for each number in w2 is (0,0.5)

I get the following error when running the code:

ValueError: length of x0 != length of bounds

Could you please advise on what's wrong with this?

P.S. I know that I could put both w1 and w2 in 1 list and just call the different items, but was just wondering why this method with 2 inputs doesn't work

  • You want one 2 element solution? Or 3 differerent solution of size 2 (i.e (3,2) solution)? – hpaulj Oct 20 '22 at 01:43
  • Hi hpaulj, sorry I am rather new to scipy so not quite sure what you mean. I am just curious on why I am getting this error and if there is anyway that I could fix it without changing the objective function – user20287252 Oct 20 '22 at 02:05
  • You are getting this error because you are not following `minimize` documentation. It should state clearly the expected sizes of the initial condition, the bounds, etc – hpaulj Oct 20 '22 at 04:12

2 Answers2

0

minimize is designed to minimise functions of the form R^n --> R^1. The documentation for minimize states:

fun: callable
  The objective function to be minimized.
  
    fun(x, *args) -> float

  where x is a 1-D array with shape (n,) 

The x0 input should be a 1-D array - you are providing a 2-D array for x0: w = [w1,w2]. For your system x0.shape should be (6,)

The bounds parameter should then be:

1. Instance of Bounds class.
or
2. Sequence of (min, max) pairs for each element in x. None is used to specify no bound.

If you use the second of these then len(bounds) == 6, and each of the entries in bounds should be a 2-tuple: (lower_lim_xi, upper_lim_xi).

Andrew Nelson
  • 460
  • 3
  • 11
  • Thanks Andrew. So based on your answer the only way I could use the minimize function here is to have w in the format of [1,2,3,4,5,6], then set bounds as ((0.1,1),(0.1,1),(0.1,1),(0,0.5),(0,0.5),(0,0.5))? – user20287252 Oct 20 '22 at 02:40
  • @user20287252 that's correct. There is another possibility of specifying the bounds as: ``` from scipy.optimize import Bounds bnds = Bounds(lb=[0.1, 0.1, 0.1, 0, 0, 0], ub=[1, 1, 1, 0.5, 0.5, 0.5]) ``` – Andrew Nelson Oct 20 '22 at 03:32
0

Looks like I need to quote the relevant parts of the minimize docs

x0 - ndarray, shape (n,)
Initial guess. Array of real elements of size (n,), 
where n is the number of independent variables.

np.array([w1,w2]) is a (2,3) array. Check the minimize code, but I think it will be flattened to (6,). You could also test the x that is passed to objective.

In [96]: w1 = [1,2,3] 
    ...: w2 = [4,5,6] 
    ...: w = [w1,w2]

In [97]: def objective(x):
    ...:     print('x.shape', x.shape)
    ...:     a = x[0][0]**2+x[0][1]**2+x[0][2]**2+x[1][0]**2+x[1][1]**2+x[1][2]**2
    ...:     return a

Run with the 2d initial condition, the objective returns:

In [99]: objective(np.array(w))
x.shape (2, 3)
Out[99]: 91

but called via minimize:

In [100]: minimize(objective, w)
x.shape (6,)
3     a = x[0][0]**2+x[0][1]**2+x[0][2]**2+x[1][0]**2+x[1][1]**2+x[1][2]**2
IndexError: invalid index to scalar variable.

x is (6,) but your function expects a 2 element list of 3 element lists, or a (2,3) array.

And that's not even getting to the bounds.

So with n being 6, it is expecting the bounds to be 6 tuples:

In [102]: minimize(objective,x0=w,bounds=(bnds_1,bnds_2))
    282     bounds = [(None, None)] * n
    283 if len(bounds) != n:
--> 284     raise ValueError('length of x0 != length of bounds')

The key when using scipy functions like minimize is that YOU have to conform to its usage. It is in control, not you.

Adapting objective to work with this (6,) input:

In [110]: def objective(x):
     ...:     #print('x.shape', x.shape)
     ...:     x = x.reshape(2,3)
     ...:     a = x[0][0]**2+x[0][1]**2+x[0][2]**2+x[1][0]**2+x[1][1]**2+x[1][2]**2
     ...:     return a
     ...:     

And changing the bounds to by (6,2):

In [111]: minimize(objective,x0=w,bounds=np.array((bnds_1,bnds_2)).reshape(-1,2))
Out[111]: 
      fun: 0.030000000000000006
 hess_inv: <6x6 LbfgsInvHessProduct with dtype=float64>
      jac: array([2.00000010e-01, 2.00000010e-01, 2.00000010e-01, 1.00613962e-08,
       1.00613962e-08, 1.00613962e-08])
  message: 'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
     nfev: 14
      nit: 1
     njev: 2
   status: 0
  success: True
        x: array([0.1, 0.1, 0.1, 0. , 0. , 0. ])
hpaulj
  • 221,503
  • 14
  • 230
  • 353