evaluate.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. '''
  2. This is a module evaluating the objective/constraint function with Nan/Inf handling.
  3. Translated from Zaikun Zhang's modern-Fortran reference implementation in PRIMA.
  4. Dedicated to late Professor M. J. D. Powell FRS (1936--2015).
  5. Python translation by Nickolai Belakovski.
  6. '''
  7. import numpy as np
  8. from .consts import FUNCMAX, CONSTRMAX, REALMAX, DEBUGGING
  9. from .linalg import matprod, primasum
  10. # This is a module evaluating the objective/constraint function with Nan/Inf handling.
  11. def moderatex(x):
  12. '''
  13. This function moderates a decision variable. It replaces NaN by 0 and Inf/-Inf by
  14. REALMAX/-REALMAX.
  15. '''
  16. x[np.isnan(x)] = 0
  17. x = np.clip(x, -REALMAX, REALMAX)
  18. return x
  19. def moderatef(f):
  20. """
  21. This function moderates the function value of a MINIMIZATION problem. It replaces
  22. NaN and any value above FUNCMAX by FUNCMAX.
  23. """
  24. f = FUNCMAX if np.isnan(f) else f
  25. f = np.clip(f, -REALMAX, FUNCMAX)
  26. # We may moderate huge negative function values as follows, but we decide not to.
  27. # f = np.clip(f, -FUNCMAX, FUNCMAX)
  28. return f
  29. def moderatec(c):
  30. """
  31. This function moderates the constraint value, the constraint demanding this value
  32. to be NONNEGATIVE. It replaces any value below -CONSTRMAX by -CONSTRMAX, and any
  33. NaN or value above CONSTRMAX by CONSTRMAX.
  34. """
  35. np.nan_to_num(c, copy=False, nan=CONSTRMAX)
  36. c = np.clip(c, -CONSTRMAX, CONSTRMAX)
  37. return c
  38. def evaluate(calcfc, x, m_nlcon, amat, bvec):
  39. """
  40. This function evaluates CALCFC at X, returning the objective function value and the
  41. constraint value. Nan/Inf are handled by a moderated extreme barrier.
  42. """
  43. # Sizes
  44. m_lcon = len(bvec) if bvec is not None else 0
  45. # Preconditions
  46. if DEBUGGING:
  47. # X should not contain NaN if the initial X does not contain NaN and the
  48. # subroutines generating # trust-region/geometry steps work properly so that
  49. # they never produce a step containing NaN/Inf.
  50. assert not any(np.isnan(x))
  51. #====================#
  52. # Calculation starts #
  53. #====================#
  54. constr = np.zeros(m_lcon + m_nlcon)
  55. if amat is not None:
  56. constr[:m_lcon] = matprod(x, amat.T) - bvec
  57. if any(np.isnan(x)):
  58. # Although this should not happen unless there is a bug, we include this case
  59. # for robustness.
  60. f = primasum(x)
  61. constr = np.ones(m_nlcon) * f
  62. else:
  63. f, constr[m_lcon:] = calcfc(moderatex(x))
  64. # Moderated extreme barrier: replace NaN/huge objective or constraint values
  65. # with a large but finite value. This is naive, and better approaches surely
  66. # exist.
  67. f = moderatef(f)
  68. constr[m_lcon:] = moderatec(constr[m_lcon:])
  69. #==================#
  70. # Calculation ends #
  71. #==================#
  72. # Postconditions
  73. if DEBUGGING:
  74. # With X not containing NaN, and with the moderated extreme barrier, F cannot
  75. # be NaN/+Inf, and CONSTR cannot be NaN/-Inf.
  76. assert not (np.isnan(f) or np.isposinf(f))
  77. assert not any(np.isnan(constr) | np.isposinf(constr))
  78. return f, constr