initialize.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. '''
  2. This module contains subroutines for initialization.
  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. from ..common.checkbreak import checkbreak_con
  8. from ..common.consts import DEBUGGING, REALMAX
  9. from ..common.infos import INFO_DEFAULT
  10. from ..common.evaluate import evaluate
  11. from ..common.history import savehist
  12. from ..common.linalg import inv
  13. from ..common.message import fmsg
  14. from ..common.selectx import savefilt
  15. import numpy as np
  16. def initxfc(calcfc, iprint, maxfun, constr0, amat, bvec, ctol, f0, ftarget, rhobeg, x0,
  17. xhist, fhist, chist, conhist, maxhist):
  18. '''
  19. This subroutine does the initialization concerning X, function values, and
  20. constraints.
  21. '''
  22. # Local variables
  23. solver = 'COBYLA'
  24. srname = "INITIALIZE"
  25. # Sizes
  26. num_constraints = np.size(constr0)
  27. m_lcon = np.size(bvec) if bvec is not None else 0
  28. m_nlcon = num_constraints - m_lcon
  29. num_vars = np.size(x0)
  30. # Preconditions
  31. if DEBUGGING:
  32. assert num_constraints >= 0, f'M >= 0 {srname}'
  33. assert num_vars >= 1, f'N >= 1 {srname}'
  34. assert abs(iprint) <= 3, f'IPRINT is 0, 1, -1, 2, -2, 3, or -3 {srname}'
  35. # assert conmat.shape == (num_constraints , num_vars + 1), f'CONMAT.shape = [M, N+1] {srname}'
  36. # assert cval.size == num_vars + 1, f'CVAL.size == N+1 {srname}'
  37. # assert maxchist * (maxchist - maxhist) == 0, f'CHIST.shape == 0 or MAXHIST {srname}'
  38. # assert conhist.shape[0] == num_constraints and maxconhist * (maxconhist - maxhist) == 0, 'CONHIST.shape[0] == num_constraints, SIZE(CONHIST, 2) == 0 or MAXHIST {srname)}'
  39. # assert maxfhist * (maxfhist - maxhist) == 0, f'FHIST.shape == 0 or MAXHIST {srname}'
  40. # assert xhist.shape[0] == num_vars and maxxhist * (maxxhist - maxhist) == 0, 'XHIST.shape[0] == N, SIZE(XHIST, 2) == 0 or MAXHIST {srname)}'
  41. assert all(np.isfinite(x0)), f'X0 is finite {srname}'
  42. assert rhobeg > 0, f'RHOBEG > 0 {srname}'
  43. #====================#
  44. # Calculation starts #
  45. #====================#
  46. # Initialize info to the default value. At return, a value different from this
  47. # value will indicate an abnormal return
  48. info = INFO_DEFAULT
  49. # Initialize the simplex. It will be revised during the initialization.
  50. sim = np.eye(num_vars, num_vars+1) * rhobeg
  51. sim[:, num_vars] = x0
  52. # Initialize the matrix simi. In most cases simi is overwritten, but not always.
  53. simi = np.eye(num_vars) / rhobeg
  54. # evaluated[j] = True iff the function/constraint of SIM[:, j] has been evaluated.
  55. evaluated = np.zeros(num_vars+1, dtype=bool)
  56. # Initialize fval
  57. fval = np.zeros(num_vars+1) + REALMAX
  58. cval = np.zeros(num_vars+1) + REALMAX
  59. conmat = np.zeros((num_constraints, num_vars+1)) + REALMAX
  60. for k in range(num_vars + 1):
  61. x = sim[:, num_vars].copy()
  62. # We will evaluate F corresponding to SIM(:, J).
  63. if k == 0:
  64. j = num_vars
  65. f = f0
  66. constr = constr0
  67. else:
  68. j = k - 1
  69. x[j] += rhobeg
  70. f, constr = evaluate(calcfc, x, m_nlcon, amat, bvec)
  71. cstrv = np.max(np.append(0, constr))
  72. # Print a message about the function/constraint evaluation according to IPRINT.
  73. fmsg(solver, 'Initialization', iprint, k, rhobeg, f, x, cstrv, constr)
  74. # Save X, F, CONSTR, CSTRV into the history.
  75. savehist(maxhist, x, xhist, f, fhist, cstrv, chist, constr, conhist)
  76. # Save F, CONSTR, and CSTRV to FVAL, CONMAT, and CVAL respectively.
  77. evaluated[j] = True
  78. fval[j] = f
  79. conmat[:, j] = constr
  80. cval[j] = cstrv
  81. # Check whether to exit.
  82. subinfo = checkbreak_con(maxfun, k, cstrv, ctol, f, ftarget, x)
  83. if subinfo != INFO_DEFAULT:
  84. info = subinfo
  85. break
  86. # Exchange the new vertex of the initial simplex with the optimal vertex if necessary.
  87. # This is the ONLY part that is essentially non-parallel.
  88. if j < num_vars and fval[j] < fval[num_vars]:
  89. fval[j], fval[num_vars] = fval[num_vars], fval[j]
  90. cval[j], cval[num_vars] = cval[num_vars], cval[j]
  91. conmat[:, [j, num_vars]] = conmat[:, [num_vars, j]]
  92. sim[:, num_vars] = x
  93. sim[j, :j+1] = -rhobeg # SIM[:, :j+1] is lower triangular
  94. nf = np.count_nonzero(evaluated)
  95. if evaluated.all():
  96. # Initialize SIMI to the inverse of SIM[:, :num_vars]
  97. simi = inv(sim[:, :num_vars])
  98. #==================#
  99. # Calculation ends #
  100. #==================#
  101. # Postconditions
  102. if DEBUGGING:
  103. assert nf <= maxfun, f'NF <= MAXFUN {srname}'
  104. assert evaluated.size == num_vars + 1, f'EVALUATED.size == Num_vars + 1 {srname}'
  105. # assert chist.size == maxchist, f'CHIST.size == MAXCHIST {srname}'
  106. # assert conhist.shape== (num_constraints, maxconhist), f'CONHIST.shape == [M, MAXCONHIST] {srname}'
  107. assert conmat.shape == (num_constraints, num_vars + 1), f'CONMAT.shape = [M, N+1] {srname}'
  108. assert not (np.isnan(conmat).any() or np.isneginf(conmat).any()), f'CONMAT does not contain NaN/-Inf {srname}'
  109. assert cval.size == num_vars + 1 and not (any(cval < 0) or any(np.isnan(cval)) or any(np.isposinf(cval))), f'CVAL.shape == Num_vars+1 and CVAL does not contain negative values or NaN/+Inf {srname}'
  110. # assert fhist.shape == maxfhist, f'FHIST.shape == MAXFHIST {srname}'
  111. # assert maxfhist * (maxfhist - maxhist) == 0, f'FHIST.shape == 0 or MAXHIST {srname}'
  112. assert fval.size == num_vars + 1 and not (any(np.isnan(fval)) or any(np.isposinf(fval))), f'FVAL.shape == Num_vars+1 and FVAL is not NaN/+Inf {srname}'
  113. # assert xhist.shape == (num_vars, maxxhist), f'XHIST.shape == [N, MAXXHIST] {srname}'
  114. assert sim.shape == (num_vars, num_vars + 1), f'SIM.shape == [N, N+1] {srname}'
  115. assert np.isfinite(sim).all(), f'SIM is finite {srname}'
  116. assert all(np.max(abs(sim[:, :num_vars]), axis=0) > 0), f'SIM(:, 1:N) has no zero column {srname}'
  117. assert simi.shape == (num_vars, num_vars), f'SIMI.shape == [N, N] {srname}'
  118. assert np.isfinite(simi).all(), f'SIMI is finite {srname}'
  119. assert np.allclose(sim[:, :num_vars] @ simi, np.eye(num_vars), rtol=0.1, atol=0.1) or not all(evaluated), f'SIMI = SIM(:, 1:N)^{-1} {srname}'
  120. return evaluated, conmat, cval, sim, simi, fval, nf, info
  121. def initfilt(conmat, ctol, cweight, cval, fval, sim, evaluated, cfilt, confilt, ffilt, xfilt):
  122. '''
  123. This function initializes the filter (XFILT, etc) that will be used when selecting
  124. x at the end of the solver.
  125. N.B.:
  126. 1. Why not initialize the filters using XHIST, etc? Because the history is empty if
  127. the user chooses not to output it.
  128. 2. We decouple INITXFC and INITFILT so that it is easier to parallelize the former
  129. if needed.
  130. '''
  131. # Sizes
  132. num_constraints = conmat.shape[0]
  133. num_vars = sim.shape[0]
  134. maxfilt = len(ffilt)
  135. # Preconditions
  136. if DEBUGGING:
  137. assert num_constraints >= 0
  138. assert num_vars >= 1
  139. assert maxfilt >= 1
  140. assert np.size(confilt, 0) == num_constraints and np.size(confilt, 1) == maxfilt
  141. assert np.size(cfilt) == maxfilt
  142. assert np.size(xfilt, 0) == num_vars and np.size(xfilt, 1) == maxfilt
  143. assert np.size(ffilt) == maxfilt
  144. assert np.size(conmat, 0) == num_constraints and np.size(conmat, 1) == num_vars + 1
  145. assert not (np.isnan(conmat) | np.isneginf(conmat)).any()
  146. assert np.size(cval) == num_vars + 1 and not any(cval < 0 | np.isnan(cval) | np.isposinf(cval))
  147. assert np.size(fval) == num_vars + 1 and not any(np.isnan(fval) | np.isposinf(fval))
  148. assert np.size(sim, 0) == num_vars and np.size(sim, 1) == num_vars + 1
  149. assert np.isfinite(sim).all()
  150. assert all(np.max(abs(sim[:, :num_vars]), axis=0) > 0)
  151. assert np.size(evaluated) == num_vars + 1
  152. #====================#
  153. # Calculation starts #
  154. #====================#
  155. nfilt = 0
  156. for i in range(num_vars+1):
  157. if evaluated[i]:
  158. if i < num_vars:
  159. x = sim[:, i] + sim[:, num_vars]
  160. else:
  161. x = sim[:, i] # i == num_vars, i.e. the last column
  162. nfilt, cfilt, ffilt, xfilt, confilt = savefilt(cval[i], ctol, cweight, fval[i], x, nfilt, cfilt, ffilt, xfilt, conmat[:, i], confilt)
  163. #==================#
  164. # Calculation ends #
  165. #==================#
  166. # Postconditions
  167. if DEBUGGING:
  168. assert nfilt <= maxfilt
  169. assert np.size(confilt, 0) == num_constraints and np.size(confilt, 1) == maxfilt
  170. assert not (np.isnan(confilt[:, :nfilt]) | np.isneginf(confilt[:, :nfilt])).any()
  171. assert np.size(cfilt) == maxfilt
  172. assert not any(cfilt[:nfilt] < 0 | np.isnan(cfilt[:nfilt]) | np.isposinf(cfilt[:nfilt]))
  173. assert np.size(xfilt, 0) == num_vars and np.size(xfilt, 1) == maxfilt
  174. assert not (np.isnan(xfilt[:, :nfilt])).any()
  175. # The last calculated X can be Inf (finite + finite can be Inf numerically).
  176. assert np.size(ffilt) == maxfilt
  177. assert not any(np.isnan(ffilt[:nfilt]) | np.isposinf(ffilt[:nfilt]))
  178. return nfilt