# Why does scipy.optimize.minimize(...) fail with this toy constrained minimisation case?

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.

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

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)})