99 lines
3.1 KiB
Python
99 lines
3.1 KiB
Python
'''
|
|
This is a module evaluating the objective/constraint function with Nan/Inf handling.
|
|
|
|
Translated from Zaikun Zhang's modern-Fortran reference implementation in PRIMA.
|
|
|
|
Dedicated to late Professor M. J. D. Powell FRS (1936--2015).
|
|
|
|
Python translation by Nickolai Belakovski.
|
|
'''
|
|
|
|
import numpy as np
|
|
from .consts import FUNCMAX, CONSTRMAX, REALMAX, DEBUGGING
|
|
from .linalg import matprod, primasum
|
|
|
|
# This is a module evaluating the objective/constraint function with Nan/Inf handling.
|
|
|
|
|
|
def moderatex(x):
|
|
'''
|
|
This function moderates a decision variable. It replaces NaN by 0 and Inf/-Inf by
|
|
REALMAX/-REALMAX.
|
|
'''
|
|
x[np.isnan(x)] = 0
|
|
x = np.clip(x, -REALMAX, REALMAX)
|
|
return x
|
|
|
|
def moderatef(f):
|
|
"""
|
|
This function moderates the function value of a MINIMIZATION problem. It replaces
|
|
NaN and any value above FUNCMAX by FUNCMAX.
|
|
"""
|
|
f = FUNCMAX if np.isnan(f) else f
|
|
f = np.clip(f, -REALMAX, FUNCMAX)
|
|
# We may moderate huge negative function values as follows, but we decide not to.
|
|
# f = np.clip(f, -FUNCMAX, FUNCMAX)
|
|
return f
|
|
|
|
|
|
def moderatec(c):
|
|
"""
|
|
This function moderates the constraint value, the constraint demanding this value
|
|
to be NONNEGATIVE. It replaces any value below -CONSTRMAX by -CONSTRMAX, and any
|
|
NaN or value above CONSTRMAX by CONSTRMAX.
|
|
"""
|
|
np.nan_to_num(c, copy=False, nan=CONSTRMAX)
|
|
c = np.clip(c, -CONSTRMAX, CONSTRMAX)
|
|
return c
|
|
|
|
|
|
def evaluate(calcfc, x, m_nlcon, amat, bvec):
|
|
"""
|
|
This function evaluates CALCFC at X, returning the objective function value and the
|
|
constraint value. Nan/Inf are handled by a moderated extreme barrier.
|
|
"""
|
|
|
|
# Sizes
|
|
m_lcon = len(bvec) if bvec is not None else 0
|
|
|
|
# Preconditions
|
|
if DEBUGGING:
|
|
# X should not contain NaN if the initial X does not contain NaN and the
|
|
# subroutines generating # trust-region/geometry steps work properly so that
|
|
# they never produce a step containing NaN/Inf.
|
|
assert not any(np.isnan(x))
|
|
|
|
#====================#
|
|
# Calculation starts #
|
|
#====================#
|
|
|
|
constr = np.zeros(m_lcon + m_nlcon)
|
|
if amat is not None:
|
|
constr[:m_lcon] = matprod(x, amat.T) - bvec
|
|
|
|
if any(np.isnan(x)):
|
|
# Although this should not happen unless there is a bug, we include this case
|
|
# for robustness.
|
|
f = primasum(x)
|
|
constr = np.ones(m_nlcon) * f
|
|
else:
|
|
f, constr[m_lcon:] = calcfc(moderatex(x))
|
|
|
|
# Moderated extreme barrier: replace NaN/huge objective or constraint values
|
|
# with a large but finite value. This is naive, and better approaches surely
|
|
# exist.
|
|
f = moderatef(f)
|
|
constr[m_lcon:] = moderatec(constr[m_lcon:])
|
|
|
|
#==================#
|
|
# Calculation ends #
|
|
#==================#
|
|
|
|
# Postconditions
|
|
if DEBUGGING:
|
|
# With X not containing NaN, and with the moderated extreme barrier, F cannot
|
|
# be NaN/+Inf, and CONSTR cannot be NaN/-Inf.
|
|
assert not (np.isnan(f) or np.isposinf(f))
|
|
assert not any(np.isnan(constr) | np.isposinf(constr))
|
|
|
|
return f, constr
|