Reputation: 115
I am running scipy.optimize.minimize trying to maximize the likelihood for left-truncated data on a Gompertz distribution. Since the data is left-truncated at 1
, I get this likelihood:
# for a single point x_i, the left-truncated log-likelihood is:
# ln(tau) + tau*(ln(theta) - ln(x_i)) - (theta / x_i) ** tau - ln(x_i) - ln(1 - exp(-(theta / d) ** tau))
def to_minimize(args, data, d=1):
theta, tau = args
if tau <= 0 or theta <= 0 or theta / d < 0 or np.exp(-(theta / d) ** tau) >= 1:
print('ERROR')
term1 = len(data) * (np.log(tau) + tau * np.log(theta) - np.log(1 - np.exp(-(theta / d) ** tau)))
term2 = 0
for x in data:
term2 += (-(tau + 1) * np.log(x)) - (theta / x) ** tau
return term1 + term2
This will fail in all instances where the if
statement is true. In other words, tau
and theta
have to be strictly positive, and theta ** tau
must be sufficiently far away from 0
so that np.exp(-theta ** tau)
is "far enough away" from 1
, since otherwise the logarithm will be undefined.
These are the constraints which I thus defined. I used the notation with a dict
instead of a NonlinearConstraints
object since it seems that this methods accepts strict inequality (np.exp(-x[0] ** x[1])
must be strictly less than 1
). Maybe I have misunderstood the documentation on this.
def constraints(x):
return [1 - np.exp(-(x[0]) ** x[1])]
To maximize the likelihood, I minimize the negative likelihood.
opt = minimize(lambda args: -to_minimize(args, data),
x0=np.array((1, 1)),
constraints={'type': 'ineq', 'fun': constraints},
bounds=np.array([(1e-15, 10), (1e-15, 10)]))
As I take it, the two arguments should then never be chosen in a way such that my code fails. Yet, the algorithm tries to move theta
very close to its lower bound and tau
very close to its upper bound so that the logarithm becomes undefined.
What makes my code fail?
Upvotes: 0
Views: 666
Reputation: 7157
Both forms of constraints, i.e. NonlinearConstraint
and dict
constraints don't support strict inequalities. Typically, one therefore uses g(x) >= c + Ɛ
to model the strict inequality g(x) > c
, where Ɛ
is a sufficiently small number.
Note also that it is not guaranteed that each iteration lies inside the feasible region. Internally, most of the methods try to bring it back into the feasible region by a simple clipping of the bounds. In cases where this doesn't work, you can try NonlinearConstraint
s keep_feasible
option and then use the trust-constr
method:
import numpy as np
from scipy.optimize import NonlinearConstraint, minimize
def con_fun(x):
return 1 - np.exp(-(x[0]) ** x[1])
# 1.0e-8 <= con_fun <= np.inf
con = NonlinearConstraint(con_fun, 1.0e-8, np.inf, keep_feasible=True)
x0 = np.array((1., 1.))
bounds = np.array([(1e-5, 10), (1e-5, 10)])
opt = minimize(lambda args: -to_minimize(args, data),
x0=x0, constraints=(con,),
bounds=bounds, method="trust-constr")
Upvotes: 1