trustregion.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. '''
  2. This module provides subroutines concerning the trust-region calculations of COBYLA.
  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. import numpy.typing as npt
  9. from ..common.consts import DEBUGGING, REALMIN, REALMAX, EPS
  10. from ..common.powalg import qradd_Rdiag, qrexc_Rdiag
  11. from ..common.linalg import isminor, matprod, inprod, lsqr, primasum
  12. def trstlp(A, b, delta, g):
  13. '''
  14. This function calculated an n-component vector d by the following two stages. In the first
  15. stage, d is set to the shortest vector that minimizes the greatest violation of the constraints
  16. A.T @ D <= B, K = 1, 2, 3, ..., M,
  17. subject to the Euclidean length of d being at most delta. If its length is strictly less than
  18. delta, then the second stage uses the resultant freedom in d to minimize the objective function
  19. G.T @ D
  20. subject to no increase in any greatest constraint violation.
  21. It is possible but rare that a degeneracy may prevent d from attaining the target length delta.
  22. cviol is the largest constraint violation of the current d: max(max(A.T@D - b), 0)
  23. icon is the index of a most violated constraint if cviol is positive.
  24. nact is the number of constraints in the active set and iact[0], ..., iact[nact-1] are their indices,
  25. while the remainder of the iact contains a permutation of the remaining constraint indicies.
  26. N.B.: nact <= min(num_constraints, num_vars). Obviously nact <= num_constraints. In addition, the constraints
  27. in iact[0, ..., nact-1] have linearly independent gradients (see the comments above the instruction
  28. that delete a constraint from the active set to make room for the new active constraint with index iact[icon]);
  29. it can also be seen from the update of nact: starting from 0, nact is incremented only if nact < n.
  30. Further, Z is an orthogonal matrix whose first nact columns can be regarded as the result of
  31. Gram-Schmidt applied to the active constraint gradients. For j = 0, 1, ..., nact-1, the number
  32. zdota[j] is the scalar product of the jth column of Z with the gradient of the jth active
  33. constraint. d is the current vector of variables and here the residuals of the active constraints
  34. should be zero. Further, the active constraints have nonnegative Lagrange multipliers that are
  35. held at the beginning of vmultc. The remainder of this vector holds the residuals of the inactive
  36. constraints at d, the ordering of the components of vmultc being in agreement with the permutation
  37. of the indices of the constraints that is in iact. All these residuals are nonnegative, which is
  38. achieved by the shift cviol that makes the least residual zero.
  39. N.B.:
  40. 0. In Powell's implementation, the constraints are A.T @ D >= B. In other words, the A and B in
  41. our implementation are the negative of those in Powell's implementation.
  42. 1. The algorithm was NOT documented in the COBYLA paper. A note should be written to introduce it!
  43. 2. As a major part of the algorithm (see trstlp_sub), the code maintains and updates the QR
  44. factorization of A[iact[:nact]], i.e. the gradients of all the active (linear) constraints. The
  45. matrix Z is indeed Q, and the vector zdota is the diagonal of R. The factorization is updated by
  46. Givens rotations when an index is added in or removed from iact.
  47. 3. There are probably better algorithms available for the trust-region linear programming problem.
  48. '''
  49. # Sizes
  50. num_constraints = A.shape[1]
  51. num_vars = A.shape[0]
  52. # Preconditions
  53. if DEBUGGING:
  54. assert num_vars >= 1
  55. assert num_constraints >= 0
  56. assert np.size(g) == num_vars
  57. assert np.size(b) == num_constraints
  58. assert delta > 0
  59. vmultc = np.zeros(num_constraints + 1)
  60. iact = np.zeros(num_constraints + 1, dtype=int)
  61. nact = 0
  62. d = np.zeros(num_vars)
  63. z = np.zeros((num_vars, num_vars))
  64. # ==================
  65. # Calculation starts
  66. # ==================
  67. # Form A_aug and B_aug. This allows the gradient of the objective function to be regarded as the
  68. # gradient of a constraint in the second stage.
  69. A_aug = np.hstack([A, g.reshape((num_vars, 1))])
  70. b_aug = np.hstack([b, 0])
  71. # Scale the problem if A contains large values. Otherwise floating point exceptions may occur.
  72. # Note that the trust-region step is scale invariant.
  73. for i in range(num_constraints+1): # Note that A_aug.shape[1] == num_constraints+1
  74. if (maxval:=max(abs(A_aug[:, i]))) > 1e12:
  75. modscal = max(2*REALMIN, 1/maxval)
  76. A_aug[:, i] *= modscal
  77. b_aug[i] *= modscal
  78. # Stage 1: minimize the 1+infinity constraint violation of the linearized constraints.
  79. iact[:num_constraints], nact, d, vmultc[:num_constraints], z = trstlp_sub(iact[:num_constraints], nact, 1, A_aug[:, :num_constraints], b_aug[:num_constraints], delta, d, vmultc[:num_constraints], z)
  80. # Stage 2: minimize the linearized objective without increasing the 1_infinity constraint violation.
  81. iact, nact, d, vmultc, z = trstlp_sub(iact, nact, 2, A_aug, b_aug, delta, d, vmultc, z)
  82. # ================
  83. # Calculation ends
  84. # ================
  85. # Postconditions
  86. if DEBUGGING:
  87. assert all(np.isfinite(d))
  88. # Due to rounding, it may happen that ||D|| > DELTA, but ||D|| > 2*DELTA is highly improbable.
  89. assert np.linalg.norm(d) <= 2 * delta
  90. return d
  91. def trstlp_sub(iact: npt.NDArray, nact: int, stage, A, b, delta, d, vmultc, z):
  92. '''
  93. This subroutine does the real calculations for trstlp, both stage 1 and stage 2.
  94. Major differences between stage 1 and stage 2:
  95. 1. Initialization. Stage 2 inherits the values of some variables from stage 1, so they are
  96. initialized in stage 1 but not in stage 2.
  97. 2. cviol. cviol is updated after at iteration in stage 1, while it remains a constant in stage2.
  98. 3. sdirn. See the definition of sdirn in the code for details.
  99. 4. optnew. The two stages have different objectives, so optnew is updated differently.
  100. 5. step. step <= cviol in stage 1.
  101. '''
  102. zdasav = np.zeros(z.shape[1])
  103. vmultd = np.zeros(np.size(vmultc))
  104. zdota = np.zeros(np.size(z, 1))
  105. # Sizes
  106. mcon = np.size(A, 1)
  107. num_vars = np.size(A, 0)
  108. # Preconditions
  109. if DEBUGGING:
  110. assert num_vars >= 1
  111. assert stage == 1 or stage == 2
  112. assert (mcon >= 0 and stage == 1) or (mcon >= 1 and stage == 2)
  113. assert np.size(b) == mcon
  114. assert np.size(iact) == mcon
  115. assert np.size(vmultc) == mcon
  116. assert np.size(d) == num_vars
  117. assert np.size(z, 0) == num_vars and np.size(z, 1) == num_vars
  118. assert delta > 0
  119. if stage == 2:
  120. assert all(np.isfinite(d)) and np.linalg.norm(d) <= 2 * delta
  121. assert nact >= 0 and nact <= np.minimum(mcon, num_vars)
  122. assert all(vmultc[:mcon]) >= 0
  123. # N.B.: Stage 1 defines only VMULTC(1:M); VMULTC(M+1) is undefined!
  124. # Initialize according to stage
  125. if stage == 1:
  126. iact = np.linspace(0, mcon-1, mcon, dtype=int)
  127. nact = 0
  128. d = np.zeros(num_vars)
  129. cviol = np.max(np.append(0, -b))
  130. vmultc = cviol + b
  131. z = np.eye(num_vars)
  132. if mcon == 0 or cviol <= 0:
  133. # Check whether a quick return is possible. Make sure the in-outputs have been initialized.
  134. return iact, nact, d, vmultc, z
  135. if all(np.isnan(b)):
  136. return iact, nact, d, vmultc, z
  137. else:
  138. icon = np.nanargmax(-b)
  139. num_constraints = mcon
  140. sdirn = np.zeros(len(d))
  141. else:
  142. if inprod(d, d) >= delta*delta:
  143. # Check whether a quick return is possible.
  144. return iact, nact, d, vmultc, z
  145. iact[mcon-1] = mcon-1
  146. vmultc[mcon-1] = 0
  147. num_constraints = mcon - 1
  148. icon = mcon - 1
  149. # In Powell's code, stage 2 uses the zdota and cviol calculated by stage1. Here we recalculate
  150. # them so that they need not be passed from stage 1 to 2, and hence the coupling is reduced.
  151. cviol = np.max(np.append(0, matprod(d, A[:, :num_constraints]) - b[:num_constraints]))
  152. zdota[:nact] = [inprod(z[:, k], A[:, iact[k]]) for k in range(nact)]
  153. # More initialization
  154. optold = REALMAX
  155. nactold = nact
  156. nfail = 0
  157. # Zaikun 20211011: vmultd is computed from scratch at each iteration, but vmultc is inherited
  158. # Powell's code can encounter infinite cycling, which did happen when testing the following CUTEst
  159. # problems: DANWOODLS, GAUSS1LS, GAUSS2LS, GAUSS3LS, KOEBHELB, TAX13322, TAXR13322. Indeed, in all
  160. # these cases, Inf/NaN appear in d due to extremely large values in A (up to 10^219). To resolve
  161. # this, we set the maximal number of iterations to maxiter, and terminate if Inf/NaN occurs in d.
  162. maxiter = np.minimum(10000, 100*max(num_constraints, num_vars))
  163. for iter in range(maxiter):
  164. if DEBUGGING:
  165. assert all(vmultc >= 0)
  166. if stage == 1:
  167. optnew = cviol
  168. else:
  169. optnew = inprod(d, A[:, mcon-1])
  170. # End the current stage of the calculation if 3 consecutive iterations have either failed to
  171. # reduce the best calculated value of the objective function or to increase the number of active
  172. # constraints since the best value was calculated. This strategy prevents cycling, but there is
  173. # a remote possibility that it will cause premature termination.
  174. if optnew < optold or nact > nactold:
  175. nactold = nact
  176. nfail = 0
  177. else:
  178. nfail += 1
  179. optold = np.minimum(optold, optnew)
  180. if nfail == 3:
  181. break
  182. # If icon exceeds nact, then we add the constraint with index iact[icon] to the active set.
  183. if icon >= nact: # In Python this needs to be >= since Python is 0-indexed (in Fortran we have 1 > 0, in Python we need 0 >= 0)
  184. zdasav[:nact] = zdota[:nact]
  185. nactsav = nact
  186. z, zdota, nact = qradd_Rdiag(A[:, iact[icon]], z, zdota, nact) # May update nact to nact+1
  187. # Indeed it suffices to pass zdota[:min(num_vars, nact+1)] to qradd as follows:
  188. # qradd(A[:, iact[icon]], z, zdota[:min(num_vars, nact+1)], nact)
  189. if nact == nactsav + 1:
  190. # N.B.: It is possible to index arrays using [nact, icon] when nact == icon.
  191. # Zaikun 20211012: Why should vmultc[nact] = 0?
  192. if nact != (icon + 1): # Need to add 1 to Python for 0 indexing
  193. vmultc[[icon, nact-1]] = vmultc[nact-1], 0
  194. iact[[icon, nact-1]] = iact[[nact-1, icon]]
  195. else:
  196. vmultc[nact-1] = 0
  197. else:
  198. # Zaikun 20211011:
  199. # 1. VMULTD is calculated from scratch for the first time (out of 2) in one iteration.
  200. # 2. Note that IACT has not been updated to replace IACT[NACT] with IACT[ICON]. Thus
  201. # A[:, IACT[:NACT]] is the UNUPDATED version before QRADD (note Z[:, :NACT] remains the
  202. # same before and after QRADD). Therefore if we supply ZDOTA to LSQR (as Rdiag) as
  203. # Powell did, we should use the UNUPDATED version, namely ZDASAV.
  204. # vmultd[:nact] = lsqr(A[:, iact[:nact]], A[:, iact[icon]], z[:, :nact], zdasav[:nact])
  205. vmultd[:nact] = lsqr(A[:, iact[:nact]], A[:, iact[icon]], z[:, :nact], zdasav[:nact])
  206. if not any(np.logical_and(vmultd[:nact] > 0, iact[:nact] <= num_constraints)):
  207. # N.B.: This can be triggered by NACT == 0 (among other possibilities)! This is
  208. # important, because NACT will be used as an index in the sequel.
  209. break
  210. # vmultd[NACT+1:mcon] is not used, but we have to initialize it in Fortran, or compilers
  211. # complain about the where construct below (another solution: restrict where to 1:NACT).
  212. vmultd[nact:mcon] = -1 # len(vmultd) == mcon
  213. # Revise the Lagrange multipliers. The revision is not applicable to vmultc[nact:num_constraints].
  214. fracmult = [vmultc[i]/vmultd[i] if vmultd[i] > 0 and iact[i] <= num_constraints else REALMAX for i in range(nact)]
  215. # Only the places with vmultd > 0 and iact <= m is relevant below, if any.
  216. frac = min(fracmult[:nact]) # fracmult[nact:mcon] may contain garbage
  217. vmultc[:nact] = np.maximum(np.zeros(len(vmultc[:nact])), vmultc[:nact] - frac*vmultd[:nact])
  218. # Reorder the active constraints so that the one to be replaced is at the end of the list.
  219. # Exit if the new value of zdota[nact] is not acceptable. Powell's condition for the
  220. # following If: not abs(zdota[nact]) > 0. Note that it is different from
  221. # 'abs(zdota[nact]) <=0)' as zdota[nact] can be NaN.
  222. # N.B.: We cannot arrive here with nact == 0, which should have triggered a break above
  223. if np.isnan(zdota[nact - 1]) or abs(zdota[nact - 1]) <= EPS**2:
  224. break
  225. vmultc[[icon, nact - 1]] = 0, frac # vmultc[[icon, nact]] is valid as icon > nact
  226. iact[[icon, nact - 1]] = iact[[nact - 1, icon]]
  227. # end if nact == nactsav + 1
  228. # In stage 2, ensure that the objective continues to be treated as the last active constraint.
  229. # Zaikun 20211011, 20211111: Is it guaranteed for stage 2 that iact[nact-1] = mcon when
  230. # iact[nact] != mcon??? If not, then how does the following procedure ensure that mcon is
  231. # the last of iact[:nact]?
  232. if stage == 2 and iact[nact - 1] != (mcon - 1):
  233. if nact <= 1:
  234. # We must exit, as nact-2 is used as an index below. Powell's code does not have this.
  235. break
  236. z, zdota[:nact] = qrexc_Rdiag(A[:, iact[:nact]], z, zdota[:nact], nact - 2) # We pass nact-2 in Python instead of nact-1
  237. # Indeed, it suffices to pass Z[:, :nact] to qrexc as follows:
  238. # z[:, :nact], zdota[:nact] = qrexc(A[:, iact[:nact]], z[:, :nact], zdota[:nact], nact - 1)
  239. iact[[nact-2, nact-1]] = iact[[nact-1, nact-2]]
  240. vmultc[[nact-2, nact-1]] = vmultc[[nact-1, nact-2]]
  241. # Zaikun 20211117: It turns out that the last few lines do not guarantee iact[nact] == num_vars in
  242. # stage 2; the following test cannot be passed. IS THIS A BUG?!
  243. # assert iact[nact] == mcon or stage == 1, 'iact[nact] must == mcon in stage 2'
  244. # Powell's code does not have the following. It avoids subsequent floating points exceptions.
  245. if np.isnan(zdota[nact-1]) or abs(zdota[nact-1]) <= EPS**2:
  246. break
  247. # Set sdirn to the direction of the next change to the current vector of variables
  248. # Usually during stage 1 the vector sdirn gives a search direction that reduces all the
  249. # active constraint violations by one simultaneously.
  250. if stage == 1:
  251. sdirn -= ((inprod(sdirn, A[:, iact[nact-1]]) + 1)/zdota[nact-1])*z[:, nact-1]
  252. else:
  253. sdirn = -1/zdota[nact-1]*z[:, nact-1]
  254. else: # icon < nact
  255. # Delete the constraint with the index iact[icon] from the active set, which is done by
  256. # reordering iact[icon:nact] into [iact[icon+1:nact], iact[icon]] and then reduce nact to
  257. # nact - 1. In theory, icon > 0.
  258. # assert icon > 0, "icon > 0 is required" # For Python I think this is irrelevant
  259. z, zdota[:nact] = qrexc_Rdiag(A[:, iact[:nact]], z, zdota[:nact], icon) # qrexc does nothing if icon == nact
  260. # Indeed, it suffices to pass Z[:, :nact] to qrexc as follows:
  261. # z[:, :nact], zdota[:nact] = qrexc(A[:, iact[:nact]], z[:, :nact], zdota[:nact], icon)
  262. iact[icon:nact] = [*iact[icon+1:nact], iact[icon]]
  263. vmultc[icon:nact] = [*vmultc[icon+1:nact], vmultc[icon]]
  264. nact -= 1
  265. # Powell's code does not have the following. It avoids subsequent exceptions.
  266. # Zaikun 20221212: In theory, nact > 0 in stage 2, as the objective function should always
  267. # be considered as an "active constraint" --- more precisely, iact[nact] = mcon. However,
  268. # looking at the code, I cannot see why in stage 2 nact must be positive after the reduction
  269. # above. It did happen in stage 1 that nact became 0 after the reduction --- this is
  270. # extremely rare, and it was never observed until 20221212, after almost one year of
  271. # random tests. Maybe nact is theoretically positive even in stage 1?
  272. if stage == 2 and nact < 0:
  273. break # If this case ever occurs, we have to break, as nact is used as an index below.
  274. if nact > 0:
  275. if np.isnan(zdota[nact-1]) or abs(zdota[nact-1]) <= EPS**2:
  276. break
  277. # Set sdirn to the direction of the next change to the current vector of variables.
  278. if stage == 1:
  279. sdirn -= inprod(sdirn, z[:, nact]) * z[:, nact]
  280. # sdirn is orthogonal to z[:, nact+1]
  281. else:
  282. sdirn = -1/zdota[nact-1] * z[:, nact-1]
  283. # end if icon > nact
  284. # Calculate the step to the trust region boundary or take the step that reduces cviol to 0.
  285. # ----------------------------------------------------------------------------------------- #
  286. # The following calculation of step is adopted from NEWUOA/BOBYQA/LINCOA. It seems to improve
  287. # the performance of COBYLA. We also found that removing the precaution about underflows is
  288. # beneficial to the overall performance of COBYLA --- the underflows are harmless anyway.
  289. dd = delta*delta - inprod(d, d)
  290. ss = inprod(sdirn, sdirn)
  291. sd = inprod(sdirn, d)
  292. if dd <= 0 or ss <= EPS * delta*delta or np.isnan(sd):
  293. break
  294. # sqrtd: square root of a discriminant. The max avoids sqrtd < abs(sd) due to underflow
  295. sqrtd = max(np.sqrt(ss*dd + sd*sd), abs(sd), np.sqrt(ss * dd))
  296. if sd > 0:
  297. step = dd / (sqrtd + sd)
  298. else:
  299. step = (sqrtd - sd) / ss
  300. # step < 0 should not happen. Step can be 0 or NaN when, e.g., sd or ss becomes inf
  301. if step <= 0 or not np.isfinite(step):
  302. break
  303. # Powell's approach and comments are as follows.
  304. # -------------------------------------------------- #
  305. # The two statements below that include the factor eps prevent
  306. # some harmless underflows that occurred in a test calculation
  307. # (Zaikun: here, eps is the machine epsilon; Powell's original
  308. # code used 1.0e-6, and Powell's code was written in single
  309. # precision). Further, we skip the step if it could be 0 within
  310. # a reasonable tolerance for computer rounding errors.
  311. # !dd = delta*delta - sum(d**2, mask=(abs(d) >= EPS * delta))
  312. # !ss = inprod(sdirn, sdirn)
  313. # !if (dd <= 0) then
  314. # ! exit
  315. # !end if
  316. # !sd = inprod(sdirn, d)
  317. # !if (abs(sd) >= EPS * sqrt(ss * dd)) then
  318. # ! step = dd / (sqrt(ss * dd + sd*sd) + sd)
  319. # !else
  320. # ! step = dd / (sqrt(ss * dd) + sd)
  321. # !end if
  322. # -------------------------------------------------- #
  323. if stage == 1:
  324. if isminor(cviol, step):
  325. break
  326. step = min(step, cviol)
  327. # Set dnew to the new variables if step is the steplength, and reduce cviol to the corresponding
  328. # maximum residual if stage 1 is being done
  329. dnew = d + step * sdirn
  330. if stage == 1:
  331. cviol = np.max(np.append(0, matprod(dnew, A[:, iact[:nact]]) - b[iact[:nact]]))
  332. # N.B.: cviol will be used when calculating vmultd[nact+1:mcon].
  333. # Zaikun 20211011:
  334. # 1. vmultd is computed from scratch for the second (out of 2) time in one iteration.
  335. # 2. vmultd[:nact] and vmultd[nact:mcon] are calculated separately with no coupling.
  336. # 3. vmultd will be calculated from scratch again in the next iteration.
  337. # Set vmultd to the vmultc vector that would occur if d became dnew. A device is included to
  338. # force vmultd[k] = 0 if deviations from this value can be attributed to computer rounding
  339. # errors. First calculate the new Lagrange multipliers.
  340. vmultd[:nact] = -lsqr(A[:, iact[:nact]], dnew, z[:, :nact], zdota[:nact])
  341. if stage == 2:
  342. vmultd[nact-1] = max(0, vmultd[nact-1]) # This seems never activated.
  343. # Complete vmultd by finding the new constraint residuals. (Powell wrote "Complete vmultc ...")
  344. cvshift = cviol - (matprod(dnew, A[:, iact]) - b[iact]) # Only cvshift[nact+1:mcon] is needed
  345. cvsabs = matprod(abs(dnew), abs(A[:, iact])) + abs(b[iact]) + cviol
  346. cvshift[isminor(cvshift, cvsabs)] = 0
  347. vmultd[nact:mcon] = cvshift[nact:mcon]
  348. # Calculate the fraction of the step from d to dnew that will be taken
  349. fracmult = [vmultc[i]/(vmultc[i] - vmultd[i]) if vmultd[i] < 0 else REALMAX for i in range(len(vmultd))]
  350. # Only the places with vmultd < 0 are relevant below, if any.
  351. icon = np.argmin(np.append(1, fracmult)) - 1
  352. frac = min(np.append(1, fracmult))
  353. # Update d, vmultc, and cviol
  354. dold = d
  355. d = (1 - frac)*d + frac * dnew
  356. vmultc = np.maximum(0, (1 - frac)*vmultc + frac*vmultd)
  357. # Break in the case of inf/nan in d or vmultc.
  358. if not (np.isfinite(primasum(abs(d))) and np.isfinite(primasum(abs(vmultc)))):
  359. d = dold # Should we restore also iact, nact, vmultc, and z?
  360. break
  361. if stage == 1:
  362. # cviol = (1 - frac) * cvold + frac * cviol # Powell's version
  363. # In theory, cviol = np.max(np.append(d@A - b, 0)), yet the
  364. # cviol updated as above can be quite different from this value if A has huge entries (e.g., > 1e20)
  365. cviol = np.max(np.append(0, matprod(d, A) - b))
  366. if icon < 0 or icon >= mcon:
  367. # In Powell's code, the condition is icon == 0. Indeed, icon < 0 cannot hold unless
  368. # fracmult contains only nan, which should not happen; icon >= mcon should never occur.
  369. break
  370. #==================#
  371. # Calculation ends #
  372. #==================#
  373. # Postconditions
  374. if DEBUGGING:
  375. assert np.size(iact) == mcon
  376. assert np.size(vmultc) == mcon
  377. assert all(vmultc >= 0)
  378. assert np.size(d) == num_vars
  379. assert all(np.isfinite(d))
  380. assert np.linalg.norm(d) <= 2 * delta
  381. assert np.size(z, 0) == num_vars and np.size(z, 1) == num_vars
  382. assert nact >= 0 and nact <= np.minimum(mcon, num_vars)
  383. return iact, nact, d, vmultc, z
  384. def trrad(delta_in, dnorm, eta1, eta2, gamma1, gamma2, ratio):
  385. '''
  386. This function updates the trust region radius according to RATIO and DNORM.
  387. '''
  388. # Preconditions
  389. if DEBUGGING:
  390. assert delta_in >= dnorm > 0
  391. assert 0 <= eta1 <= eta2 < 1
  392. assert 0 < gamma1 < 1 < gamma2
  393. # By the definition of RATIO in ratio.f90, RATIO cannot be NaN unless the
  394. # actual reduction is NaN, which should NOT happen due to the moderated extreme
  395. # barrier.
  396. assert not np.isnan(ratio)
  397. #====================#
  398. # Calculation starts #
  399. #====================#
  400. if ratio <= eta1:
  401. delta = gamma1 * dnorm # Powell's UOBYQA/NEWUOA
  402. # delta = gamma1 * delta_in # Powell's COBYLA/LINCOA
  403. # delta = min(gamma1 * delta_in, dnorm) # Powell's BOBYQA
  404. elif ratio <= eta2:
  405. delta = max(gamma1 * delta_in, dnorm) # Powell's UOBYQA/NEWUOA/BOBYQA/LINCOA
  406. else:
  407. delta = max(gamma1 * delta_in, gamma2 * dnorm) # Powell's NEWUOA/BOBYQA
  408. # delta = max(delta_in, gamma2 * dnorm) # Modified version. Works well for UOBYQA
  409. # For noise-free CUTEst problems of <= 100 variables, Powell's version works slightly better
  410. # than the modified one.
  411. # delta = max(delta_in, 1.25*dnorm, dnorm + rho) # Powell's UOBYQA
  412. # delta = min(max(gamma1 * delta_in, gamma2 * dnorm), gamma3 * delta_in) # Powell's LINCOA, gamma3 = np.sqrt(2)
  413. # For noisy problems, the following may work better.
  414. # if ratio <= eta1:
  415. # delta = gamma1 * dnorm
  416. # elseif ratio <= eta2: # Ensure DELTA >= DELTA_IN
  417. # delta = delta_in
  418. # else: # Ensure DELTA > DELTA_IN with a constant factor
  419. # delta = max(delta_in * (1 + gamma2) / 2, gamma2 * dnorm)
  420. #==================#
  421. # Calculation ends #
  422. #==================#
  423. # Postconditions
  424. if DEBUGGING:
  425. assert delta > 0
  426. return delta