2
$\begingroup$

I'm learning scipy.optimize.minimize. I thought of a simple function to see how it works:

$$f(x) = x$$

With the goal to minimise $f(x)$, subject to the constraint that: $$x \ge 0.1$$

Without the constraint, there is no solution (except in the limit $\lim_{x\to -\infty} f(x) = \infty$). But with the constraint $x \ge 0.1$, my logic says that the solution must be also $x=0.1$ since it's the smallest permissible number according to the constraint.

Here is my code:

import numpy
import scipy

def f(x, *args):
    return x[0]

def fc1(x):
    # x[0]       >= 0.1
    # x[0] - 0.1 >= 0
    return x[0] - 0.1

x0 = numpy.array([1])

c1 = {'type':'ineq', 'fun':fc1}

scipy.optimize.minimize(f, x0, [c1])

But it results in a failure:

/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:241: RuntimeWarning: overflow encountered in square
  return np.sum(np.abs(x)**ord, axis=0)**(1.0 / ord)
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:1417: RuntimeWarning: invalid value encountered in scalar multiply
  if (alpha_k*vecnorm(pk) <= xrtol*(xrtol + vecnorm(xk))):
/usr/lib/python3.10/site-packages/scipy/optimize/_optimize.py:1439: RuntimeWarning: overflow encountered in multiply
  Hk = np.dot(A1, np.dot(Hk, A2)) + (rhok * sk[:, np.newaxis] *
/usr/lib/python3.10/site-packages/scipy/optimize/_linesearch.py:276: RuntimeWarning: invalid value encountered in multiply
  return f(xk + alpha * pk, *args)
Out[4]: 
  message: Desired error not necessarily achieved due to precision loss.
  success: False
   status: 2
      fun: -3.3921181109909714e+155
        x: [-3.392e+155]
      nit: 47
      jac: [ 1.000e+00]
 hess_inv: [[       inf]]
     nfev: 7037
     njev: 3518

Question: What am I misunderstanding about the concept? I think I have a fundamental understanding mistake about how this works.

$\endgroup$

2 Answers 2

6
$\begingroup$

The problem is that you are passing the constraint list as a positional argument, but it should be a keyword argument: scipy.optimize.minimize(f, x0, constraints=[c1]).

As you have written it [c1] is assumed to be args and thus is passed to your objective funcitonf, but f doesn't do anything with args[0]. See https://docs.python.org/3/reference/compound_stmts.html#function-definitions

$\endgroup$
1
$\begingroup$

do not enclose constraints into list brackets [...], just need dictionary brackets {...}, that you already defined in c1:

import numpy
import scipy.optimize as opt

def f(x, *args):
    return x[0]

def create_constr(x):
    # x[0]       >= 0.1
    # x[0] - 0.1 >= 0
    return x[0] - 0.1

x0 = numpy.array([1])

c1 = {'type':'ineq', 'fun': create_constr}

res= opt.minimize(f, x0, c1)
print(res)

if need several cons - use parenthesis like this e.g.:

# inequality means that it is to be non-negative!
# multiplication to -1 reformulates min problem to maximization problem
cons = ({'type': 'ineq', 'fun': lambda x:  -1*(3*x[0] - 12)},
        {'type': 'ineq', 'fun': lambda x: x[0] })

P.S.: () is a tuple, [] is a list, {} is a dict.

P.P.S. or can make switcher (if need several cons )

cons = []
# https://www.geeksforgeeks.org/switch-case-in-python-replacement/
# Switcher is dictionary data type here
def cons_create(flag):
    switcher = {
        0: lambda x: x[0] - 2 * x[1] + i,
        1: lambda x: -x[0] - 2 * x[1] + 6,
        2: lambda x: -x[0] + 2 * x[1] + 2,
    }

    # get() method of dictionary data type returns
    # value of passed argument if it is present
    # in dictionary otherwise second argument will
    # be assigned as default value of passed argument
    return switcher.get(flag, "nothing")


for i in range(3):
    cons.append({'type': 'ineq', 'fun': cons_create(i)})
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.