models.py 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531
  1. import warnings
  2. import numpy as np
  3. from scipy.linalg import eigh
  4. from .settings import Options
  5. from .utils import MaxEvalError, TargetSuccess, FeasibleSuccess
  6. EPS = np.finfo(float).eps
  7. class Interpolation:
  8. """
  9. Interpolation set.
  10. This class stores a base point around which the models are expanded and the
  11. interpolation points. The coordinates of the interpolation points are
  12. relative to the base point.
  13. """
  14. def __init__(self, pb, options):
  15. """
  16. Initialize the interpolation set.
  17. Parameters
  18. ----------
  19. pb : `cobyqa.problem.Problem`
  20. Problem to be solved.
  21. options : dict
  22. Options of the solver.
  23. """
  24. # Reduce the initial trust-region radius if necessary.
  25. self._debug = options[Options.DEBUG]
  26. max_radius = 0.5 * np.min(pb.bounds.xu - pb.bounds.xl)
  27. if options[Options.RHOBEG] > max_radius:
  28. options[Options.RHOBEG.value] = max_radius
  29. options[Options.RHOEND.value] = np.min(
  30. [
  31. options[Options.RHOEND],
  32. max_radius,
  33. ]
  34. )
  35. # Set the initial point around which the models are expanded.
  36. self._x_base = np.copy(pb.x0)
  37. very_close_xl_idx = (
  38. self.x_base <= pb.bounds.xl + 0.5 * options[Options.RHOBEG]
  39. )
  40. self.x_base[very_close_xl_idx] = pb.bounds.xl[very_close_xl_idx]
  41. close_xl_idx = (
  42. pb.bounds.xl + 0.5 * options[Options.RHOBEG] < self.x_base
  43. ) & (self.x_base <= pb.bounds.xl + options[Options.RHOBEG])
  44. self.x_base[close_xl_idx] = np.minimum(
  45. pb.bounds.xl[close_xl_idx] + options[Options.RHOBEG],
  46. pb.bounds.xu[close_xl_idx],
  47. )
  48. very_close_xu_idx = (
  49. self.x_base >= pb.bounds.xu - 0.5 * options[Options.RHOBEG]
  50. )
  51. self.x_base[very_close_xu_idx] = pb.bounds.xu[very_close_xu_idx]
  52. close_xu_idx = (
  53. self.x_base < pb.bounds.xu - 0.5 * options[Options.RHOBEG]
  54. ) & (pb.bounds.xu - options[Options.RHOBEG] <= self.x_base)
  55. self.x_base[close_xu_idx] = np.maximum(
  56. pb.bounds.xu[close_xu_idx] - options[Options.RHOBEG],
  57. pb.bounds.xl[close_xu_idx],
  58. )
  59. # Set the initial interpolation set.
  60. self._xpt = np.zeros((pb.n, options[Options.NPT]))
  61. for k in range(1, options[Options.NPT]):
  62. if k <= pb.n:
  63. if very_close_xu_idx[k - 1]:
  64. self.xpt[k - 1, k] = -options[Options.RHOBEG]
  65. else:
  66. self.xpt[k - 1, k] = options[Options.RHOBEG]
  67. elif k <= 2 * pb.n:
  68. if very_close_xl_idx[k - pb.n - 1]:
  69. self.xpt[k - pb.n - 1, k] = 2.0 * options[Options.RHOBEG]
  70. elif very_close_xu_idx[k - pb.n - 1]:
  71. self.xpt[k - pb.n - 1, k] = -2.0 * options[Options.RHOBEG]
  72. else:
  73. self.xpt[k - pb.n - 1, k] = -options[Options.RHOBEG]
  74. else:
  75. spread = (k - pb.n - 1) // pb.n
  76. k1 = k - (1 + spread) * pb.n - 1
  77. k2 = (k1 + spread) % pb.n
  78. self.xpt[k1, k] = self.xpt[k1, k1 + 1]
  79. self.xpt[k2, k] = self.xpt[k2, k2 + 1]
  80. self._lhs_cache = None
  81. @property
  82. def n(self):
  83. """
  84. Number of variables.
  85. Returns
  86. -------
  87. int
  88. Number of variables.
  89. """
  90. return self.xpt.shape[0]
  91. @property
  92. def npt(self):
  93. """
  94. Number of interpolation points.
  95. Returns
  96. -------
  97. int
  98. Number of interpolation points.
  99. """
  100. return self.xpt.shape[1]
  101. @property
  102. def xpt(self):
  103. """
  104. Interpolation points.
  105. Returns
  106. -------
  107. `numpy.ndarray`, shape (n, npt)
  108. Interpolation points.
  109. """
  110. return self._xpt
  111. @xpt.setter
  112. def xpt(self, xpt):
  113. """
  114. Set the interpolation points.
  115. Parameters
  116. ----------
  117. xpt : `numpy.ndarray`, shape (n, npt)
  118. New interpolation points.
  119. """
  120. if self._debug:
  121. assert xpt.shape == (
  122. self.n,
  123. self.npt,
  124. ), "The shape of `xpt` is not valid."
  125. self._xpt = xpt
  126. @property
  127. def x_base(self):
  128. """
  129. Base point around which the models are expanded.
  130. Returns
  131. -------
  132. `numpy.ndarray`, shape (n,)
  133. Base point around which the models are expanded.
  134. """
  135. return self._x_base
  136. @x_base.setter
  137. def x_base(self, x_base):
  138. """
  139. Set the base point around which the models are expanded.
  140. Parameters
  141. ----------
  142. x_base : `numpy.ndarray`, shape (n,)
  143. New base point around which the models are expanded.
  144. """
  145. if self._debug:
  146. assert x_base.shape == (
  147. self.n,
  148. ), "The shape of `x_base` is not valid."
  149. self._x_base = x_base
  150. def point(self, k):
  151. """
  152. Get the `k`-th interpolation point.
  153. The return point is relative to the origin.
  154. Parameters
  155. ----------
  156. k : int
  157. Index of the interpolation point.
  158. Returns
  159. -------
  160. `numpy.ndarray`, shape (n,)
  161. `k`-th interpolation point.
  162. """
  163. if self._debug:
  164. assert 0 <= k < self.npt, "The index `k` is not valid."
  165. return self.x_base + self.xpt[:, k]
  166. def build_system(interpolation):
  167. """
  168. Build the left-hand side matrix of the interpolation system. The
  169. matrix below stores W * diag(right_scaling),
  170. where W is the theoretical matrix of the interpolation system. The
  171. right scaling matrices is chosen to keep the elements in
  172. the matrix well-balanced.
  173. Parameters
  174. ----------
  175. interpolation : `cobyqa.models.Interpolation`
  176. Interpolation set.
  177. """
  178. _cache = interpolation._lhs_cache
  179. # Compute the scaled directions from the base point to the
  180. # interpolation points. We scale the directions to avoid numerical
  181. # difficulties.
  182. if _cache is not None and np.array_equal(
  183. interpolation.xpt, _cache["xpt"]
  184. ):
  185. return _cache["a"], _cache["right_scaling"], _cache["eigh"]
  186. scale = np.max(np.linalg.norm(interpolation.xpt, axis=0), initial=EPS)
  187. xpt_scale = interpolation.xpt / scale
  188. n, npt = xpt_scale.shape
  189. a = np.zeros((npt + n + 1, npt + n + 1))
  190. a[:npt, :npt] = 0.5 * (xpt_scale.T @ xpt_scale) ** 2.0
  191. a[:npt, npt] = 1.0
  192. a[:npt, npt + 1:] = xpt_scale.T
  193. a[npt, :npt] = 1.0
  194. a[npt + 1:, :npt] = xpt_scale
  195. # Build the left and right scaling diagonal matrices.
  196. right_scaling = np.empty(npt + n + 1)
  197. right_scaling[:npt] = 1.0 / scale**2.0
  198. right_scaling[npt] = scale**2.0
  199. right_scaling[npt + 1:] = scale
  200. eig_values, eig_vectors = eigh(a, check_finite=False)
  201. new_cache = {
  202. "xpt": np.copy(interpolation.xpt),
  203. "a": np.copy(a),
  204. "right_scaling": np.copy(right_scaling),
  205. "eigh": (eig_values, eig_vectors),
  206. }
  207. interpolation._lhs_cache = new_cache
  208. return a, right_scaling, (eig_values, eig_vectors)
  209. class Quadratic:
  210. """
  211. Quadratic model.
  212. This class stores the Hessian matrix of the quadratic model using the
  213. implicit/explicit representation designed by Powell for NEWUOA [1]_.
  214. References
  215. ----------
  216. .. [1] M. J. D. Powell. The NEWUOA software for unconstrained optimization
  217. without derivatives. In G. Di Pillo and M. Roma, editors, *Large-Scale
  218. Nonlinear Optimization*, volume 83 of Nonconvex Optim. Appl., pages
  219. 255--297. Springer, Boston, MA, USA, 2006. `doi:10.1007/0-387-30065-1_16
  220. <https://doi.org/10.1007/0-387-30065-1_16>`_.
  221. """
  222. def __init__(self, interpolation, values, debug):
  223. """
  224. Initialize the quadratic model.
  225. Parameters
  226. ----------
  227. interpolation : `cobyqa.models.Interpolation`
  228. Interpolation set.
  229. values : `numpy.ndarray`, shape (npt,)
  230. Values of the interpolated function at the interpolation points.
  231. debug : bool
  232. Whether to make debugging tests during the execution.
  233. Raises
  234. ------
  235. `numpy.linalg.LinAlgError`
  236. If the interpolation system is ill-defined.
  237. """
  238. self._debug = debug
  239. if self._debug:
  240. assert values.shape == (
  241. interpolation.npt,
  242. ), "The shape of `values` is not valid."
  243. if interpolation.npt < interpolation.n + 1:
  244. raise ValueError(
  245. f"The number of interpolation points must be at least "
  246. f"{interpolation.n + 1}."
  247. )
  248. self._const, self._grad, self._i_hess, _ = self._get_model(
  249. interpolation,
  250. values,
  251. )
  252. self._e_hess = np.zeros((self.n, self.n))
  253. def __call__(self, x, interpolation):
  254. """
  255. Evaluate the quadratic model at a given point.
  256. Parameters
  257. ----------
  258. x : `numpy.ndarray`, shape (n,)
  259. Point at which the quadratic model is evaluated.
  260. interpolation : `cobyqa.models.Interpolation`
  261. Interpolation set.
  262. Returns
  263. -------
  264. float
  265. Value of the quadratic model at `x`.
  266. """
  267. if self._debug:
  268. assert x.shape == (self.n,), "The shape of `x` is not valid."
  269. x_diff = x - interpolation.x_base
  270. return (
  271. self._const
  272. + self._grad @ x_diff
  273. + 0.5
  274. * (
  275. self._i_hess @ (interpolation.xpt.T @ x_diff) ** 2.0
  276. + x_diff @ self._e_hess @ x_diff
  277. )
  278. )
  279. @property
  280. def n(self):
  281. """
  282. Number of variables.
  283. Returns
  284. -------
  285. int
  286. Number of variables.
  287. """
  288. return self._grad.size
  289. @property
  290. def npt(self):
  291. """
  292. Number of interpolation points used to define the quadratic model.
  293. Returns
  294. -------
  295. int
  296. Number of interpolation points used to define the quadratic model.
  297. """
  298. return self._i_hess.size
  299. def grad(self, x, interpolation):
  300. """
  301. Evaluate the gradient of the quadratic model at a given point.
  302. Parameters
  303. ----------
  304. x : `numpy.ndarray`, shape (n,)
  305. Point at which the gradient of the quadratic model is evaluated.
  306. interpolation : `cobyqa.models.Interpolation`
  307. Interpolation set.
  308. Returns
  309. -------
  310. `numpy.ndarray`, shape (n,)
  311. Gradient of the quadratic model at `x`.
  312. """
  313. if self._debug:
  314. assert x.shape == (self.n,), "The shape of `x` is not valid."
  315. x_diff = x - interpolation.x_base
  316. return self._grad + self.hess_prod(x_diff, interpolation)
  317. def hess(self, interpolation):
  318. """
  319. Evaluate the Hessian matrix of the quadratic model.
  320. Parameters
  321. ----------
  322. interpolation : `cobyqa.models.Interpolation`
  323. Interpolation set.
  324. Returns
  325. -------
  326. `numpy.ndarray`, shape (n, n)
  327. Hessian matrix of the quadratic model.
  328. """
  329. return self._e_hess + interpolation.xpt @ (
  330. self._i_hess[:, np.newaxis] * interpolation.xpt.T
  331. )
  332. def hess_prod(self, v, interpolation):
  333. """
  334. Evaluate the right product of the Hessian matrix of the quadratic model
  335. with a given vector.
  336. Parameters
  337. ----------
  338. v : `numpy.ndarray`, shape (n,)
  339. Vector with which the Hessian matrix of the quadratic model is
  340. multiplied from the right.
  341. interpolation : `cobyqa.models.Interpolation`
  342. Interpolation set.
  343. Returns
  344. -------
  345. `numpy.ndarray`, shape (n,)
  346. Right product of the Hessian matrix of the quadratic model with
  347. `v`.
  348. """
  349. if self._debug:
  350. assert v.shape == (self.n,), "The shape of `v` is not valid."
  351. return self._e_hess @ v + interpolation.xpt @ (
  352. self._i_hess * (interpolation.xpt.T @ v)
  353. )
  354. def curv(self, v, interpolation):
  355. """
  356. Evaluate the curvature of the quadratic model along a given direction.
  357. Parameters
  358. ----------
  359. v : `numpy.ndarray`, shape (n,)
  360. Direction along which the curvature of the quadratic model is
  361. evaluated.
  362. interpolation : `cobyqa.models.Interpolation`
  363. Interpolation set.
  364. Returns
  365. -------
  366. float
  367. Curvature of the quadratic model along `v`.
  368. """
  369. if self._debug:
  370. assert v.shape == (self.n,), "The shape of `v` is not valid."
  371. return (
  372. v @ self._e_hess @ v
  373. + self._i_hess @ (interpolation.xpt.T @ v) ** 2.0
  374. )
  375. def update(self, interpolation, k_new, dir_old, values_diff):
  376. """
  377. Update the quadratic model.
  378. This method applies the derivative-free symmetric Broyden update to the
  379. quadratic model. The `knew`-th interpolation point must be updated
  380. before calling this method.
  381. Parameters
  382. ----------
  383. interpolation : `cobyqa.models.Interpolation`
  384. Updated interpolation set.
  385. k_new : int
  386. Index of the updated interpolation point.
  387. dir_old : `numpy.ndarray`, shape (n,)
  388. Value of ``interpolation.xpt[:, k_new]`` before the update.
  389. values_diff : `numpy.ndarray`, shape (npt,)
  390. Differences between the values of the interpolated nonlinear
  391. function and the previous quadratic model at the updated
  392. interpolation points.
  393. Raises
  394. ------
  395. `numpy.linalg.LinAlgError`
  396. If the interpolation system is ill-defined.
  397. """
  398. if self._debug:
  399. assert 0 <= k_new < self.npt, "The index `k_new` is not valid."
  400. assert dir_old.shape == (
  401. self.n,
  402. ), "The shape of `dir_old` is not valid."
  403. assert values_diff.shape == (
  404. self.npt,
  405. ), "The shape of `values_diff` is not valid."
  406. # Forward the k_new-th element of the implicit Hessian matrix to the
  407. # explicit Hessian matrix. This must be done because the implicit
  408. # Hessian matrix is related to the interpolation points, and the
  409. # k_new-th interpolation point is modified.
  410. self._e_hess += self._i_hess[k_new] * np.outer(dir_old, dir_old)
  411. self._i_hess[k_new] = 0.0
  412. # Update the quadratic model.
  413. const, grad, i_hess, ill_conditioned = self._get_model(
  414. interpolation,
  415. values_diff,
  416. )
  417. self._const += const
  418. self._grad += grad
  419. self._i_hess += i_hess
  420. return ill_conditioned
  421. def shift_x_base(self, interpolation, new_x_base):
  422. """
  423. Shift the point around which the quadratic model is defined.
  424. Parameters
  425. ----------
  426. interpolation : `cobyqa.models.Interpolation`
  427. Previous interpolation set.
  428. new_x_base : `numpy.ndarray`, shape (n,)
  429. Point that will replace ``interpolation.x_base``.
  430. """
  431. if self._debug:
  432. assert new_x_base.shape == (
  433. self.n,
  434. ), "The shape of `new_x_base` is not valid."
  435. self._const = self(new_x_base, interpolation)
  436. self._grad = self.grad(new_x_base, interpolation)
  437. shift = new_x_base - interpolation.x_base
  438. update = np.outer(
  439. shift,
  440. (interpolation.xpt - 0.5 * shift[:, np.newaxis]) @ self._i_hess,
  441. )
  442. self._e_hess += update + update.T
  443. @staticmethod
  444. def solve_systems(interpolation, rhs):
  445. """
  446. Solve the interpolation systems.
  447. Parameters
  448. ----------
  449. interpolation : `cobyqa.models.Interpolation`
  450. Interpolation set.
  451. rhs : `numpy.ndarray`, shape (npt + n + 1, m)
  452. Right-hand side vectors of the ``m`` interpolation systems.
  453. Returns
  454. -------
  455. `numpy.ndarray`, shape (npt + n + 1, m)
  456. Solutions of the interpolation systems.
  457. `numpy.ndarray`, shape (m, )
  458. Whether the interpolation systems are ill-conditioned.
  459. Raises
  460. ------
  461. `numpy.linalg.LinAlgError`
  462. If the interpolation systems are ill-defined.
  463. """
  464. n, npt = interpolation.xpt.shape
  465. assert (
  466. rhs.ndim == 2 and rhs.shape[0] == npt + n + 1
  467. ), "The shape of `rhs` is not valid."
  468. # Build the left-hand side matrix of the interpolation system. The
  469. # matrix below stores diag(left_scaling) * W * diag(right_scaling),
  470. # where W is the theoretical matrix of the interpolation system. The
  471. # left and right scaling matrices are chosen to keep the elements in
  472. # the matrix well-balanced.
  473. a, right_scaling, eig = build_system(interpolation)
  474. # Build the solution. After a discussion with Mike Saunders and Alexis
  475. # Montoison during their visit to the Hong Kong Polytechnic University
  476. # in 2024, we decided to use the eigendecomposition of the symmetric
  477. # matrix a. This is more stable than the previously employed LBL
  478. # decomposition, and allows us to directly detect ill-conditioning of
  479. # the system and to build the least-squares solution if necessary.
  480. # Numerical experiments have shown that this strategy improves the
  481. # performance of the solver.
  482. rhs_scaled = rhs * right_scaling[:, np.newaxis]
  483. if not (np.all(np.isfinite(a)) and np.all(np.isfinite(rhs_scaled))):
  484. raise np.linalg.LinAlgError(
  485. "The interpolation system is ill-defined."
  486. )
  487. # calculated in build_system
  488. eig_values, eig_vectors = eig
  489. large_eig_values = np.abs(eig_values) > EPS
  490. eig_vectors = eig_vectors[:, large_eig_values]
  491. inv_eig_values = 1.0 / eig_values[large_eig_values]
  492. ill_conditioned = ~np.all(large_eig_values, 0)
  493. left_scaled_solutions = eig_vectors @ (
  494. (eig_vectors.T @ rhs_scaled) * inv_eig_values[:, np.newaxis]
  495. )
  496. return (
  497. left_scaled_solutions * right_scaling[:, np.newaxis],
  498. ill_conditioned,
  499. )
  500. @staticmethod
  501. def _get_model(interpolation, values):
  502. """
  503. Solve the interpolation system.
  504. Parameters
  505. ----------
  506. interpolation : `cobyqa.models.Interpolation`
  507. Interpolation set.
  508. values : `numpy.ndarray`, shape (npt,)
  509. Values of the interpolated function at the interpolation points.
  510. Returns
  511. -------
  512. float
  513. Constant term of the quadratic model.
  514. `numpy.ndarray`, shape (n,)
  515. Gradient of the quadratic model at ``interpolation.x_base``.
  516. `numpy.ndarray`, shape (npt,)
  517. Implicit Hessian matrix of the quadratic model.
  518. Raises
  519. ------
  520. `numpy.linalg.LinAlgError`
  521. If the interpolation system is ill-defined.
  522. """
  523. assert values.shape == (
  524. interpolation.npt,
  525. ), "The shape of `values` is not valid."
  526. n, npt = interpolation.xpt.shape
  527. x, ill_conditioned = Quadratic.solve_systems(
  528. interpolation,
  529. np.block(
  530. [
  531. [
  532. values,
  533. np.zeros(n + 1),
  534. ]
  535. ]
  536. ).T,
  537. )
  538. return x[npt, 0], x[npt + 1:, 0], x[:npt, 0], ill_conditioned
  539. class Models:
  540. """
  541. Models for a nonlinear optimization problem.
  542. """
  543. def __init__(self, pb, options, penalty):
  544. """
  545. Initialize the models.
  546. Parameters
  547. ----------
  548. pb : `cobyqa.problem.Problem`
  549. Problem to be solved.
  550. options : dict
  551. Options of the solver.
  552. penalty : float
  553. Penalty parameter used to select the point in the filter to forward
  554. to the callback function.
  555. Raises
  556. ------
  557. `cobyqa.utils.MaxEvalError`
  558. If the maximum number of evaluations is reached.
  559. `cobyqa.utils.TargetSuccess`
  560. If a nearly feasible point has been found with an objective
  561. function value below the target.
  562. `cobyqa.utils.FeasibleSuccess`
  563. If a feasible point has been found for a feasibility problem.
  564. `numpy.linalg.LinAlgError`
  565. If the interpolation system is ill-defined.
  566. """
  567. # Set the initial interpolation set.
  568. self._debug = options[Options.DEBUG]
  569. self._interpolation = Interpolation(pb, options)
  570. # Evaluate the nonlinear functions at the initial interpolation points.
  571. x_eval = self.interpolation.point(0)
  572. fun_init, cub_init, ceq_init = pb(x_eval, penalty)
  573. self._fun_val = np.full(options[Options.NPT], np.nan)
  574. self._cub_val = np.full((options[Options.NPT], cub_init.size), np.nan)
  575. self._ceq_val = np.full((options[Options.NPT], ceq_init.size), np.nan)
  576. for k in range(options[Options.NPT]):
  577. if k >= options[Options.MAX_EVAL]:
  578. raise MaxEvalError
  579. if k == 0:
  580. self.fun_val[k] = fun_init
  581. self.cub_val[k, :] = cub_init
  582. self.ceq_val[k, :] = ceq_init
  583. else:
  584. x_eval = self.interpolation.point(k)
  585. self.fun_val[k], self.cub_val[k, :], self.ceq_val[k, :] = pb(
  586. x_eval,
  587. penalty,
  588. )
  589. # Stop the iterations if the problem is a feasibility problem and
  590. # the current interpolation point is feasible.
  591. if (
  592. pb.is_feasibility
  593. and pb.maxcv(
  594. self.interpolation.point(k),
  595. self.cub_val[k, :],
  596. self.ceq_val[k, :],
  597. )
  598. <= options[Options.FEASIBILITY_TOL]
  599. ):
  600. raise FeasibleSuccess
  601. # Stop the iterations if the current interpolation point is nearly
  602. # feasible and has an objective function value below the target.
  603. if (
  604. self._fun_val[k] <= options[Options.TARGET]
  605. and pb.maxcv(
  606. self.interpolation.point(k),
  607. self.cub_val[k, :],
  608. self.ceq_val[k, :],
  609. )
  610. <= options[Options.FEASIBILITY_TOL]
  611. ):
  612. raise TargetSuccess
  613. # Build the initial quadratic models.
  614. self._fun = Quadratic(
  615. self.interpolation,
  616. self._fun_val,
  617. options[Options.DEBUG],
  618. )
  619. self._cub = np.empty(self.m_nonlinear_ub, dtype=Quadratic)
  620. self._ceq = np.empty(self.m_nonlinear_eq, dtype=Quadratic)
  621. for i in range(self.m_nonlinear_ub):
  622. self._cub[i] = Quadratic(
  623. self.interpolation,
  624. self.cub_val[:, i],
  625. options[Options.DEBUG],
  626. )
  627. for i in range(self.m_nonlinear_eq):
  628. self._ceq[i] = Quadratic(
  629. self.interpolation,
  630. self.ceq_val[:, i],
  631. options[Options.DEBUG],
  632. )
  633. if self._debug:
  634. self._check_interpolation_conditions()
  635. @property
  636. def n(self):
  637. """
  638. Dimension of the problem.
  639. Returns
  640. -------
  641. int
  642. Dimension of the problem.
  643. """
  644. return self.interpolation.n
  645. @property
  646. def npt(self):
  647. """
  648. Number of interpolation points.
  649. Returns
  650. -------
  651. int
  652. Number of interpolation points.
  653. """
  654. return self.interpolation.npt
  655. @property
  656. def m_nonlinear_ub(self):
  657. """
  658. Number of nonlinear inequality constraints.
  659. Returns
  660. -------
  661. int
  662. Number of nonlinear inequality constraints.
  663. """
  664. return self.cub_val.shape[1]
  665. @property
  666. def m_nonlinear_eq(self):
  667. """
  668. Number of nonlinear equality constraints.
  669. Returns
  670. -------
  671. int
  672. Number of nonlinear equality constraints.
  673. """
  674. return self.ceq_val.shape[1]
  675. @property
  676. def interpolation(self):
  677. """
  678. Interpolation set.
  679. Returns
  680. -------
  681. `cobyqa.models.Interpolation`
  682. Interpolation set.
  683. """
  684. return self._interpolation
  685. @property
  686. def fun_val(self):
  687. """
  688. Values of the objective function at the interpolation points.
  689. Returns
  690. -------
  691. `numpy.ndarray`, shape (npt,)
  692. Values of the objective function at the interpolation points.
  693. """
  694. return self._fun_val
  695. @property
  696. def cub_val(self):
  697. """
  698. Values of the nonlinear inequality constraint functions at the
  699. interpolation points.
  700. Returns
  701. -------
  702. `numpy.ndarray`, shape (npt, m_nonlinear_ub)
  703. Values of the nonlinear inequality constraint functions at the
  704. interpolation points.
  705. """
  706. return self._cub_val
  707. @property
  708. def ceq_val(self):
  709. """
  710. Values of the nonlinear equality constraint functions at the
  711. interpolation points.
  712. Returns
  713. -------
  714. `numpy.ndarray`, shape (npt, m_nonlinear_eq)
  715. Values of the nonlinear equality constraint functions at the
  716. interpolation points.
  717. """
  718. return self._ceq_val
  719. def fun(self, x):
  720. """
  721. Evaluate the quadratic model of the objective function at a given
  722. point.
  723. Parameters
  724. ----------
  725. x : `numpy.ndarray`, shape (n,)
  726. Point at which to evaluate the quadratic model of the objective
  727. function.
  728. Returns
  729. -------
  730. float
  731. Value of the quadratic model of the objective function at `x`.
  732. """
  733. if self._debug:
  734. assert x.shape == (self.n,), "The shape of `x` is not valid."
  735. return self._fun(x, self.interpolation)
  736. def fun_grad(self, x):
  737. """
  738. Evaluate the gradient of the quadratic model of the objective function
  739. at a given point.
  740. Parameters
  741. ----------
  742. x : `numpy.ndarray`, shape (n,)
  743. Point at which to evaluate the gradient of the quadratic model of
  744. the objective function.
  745. Returns
  746. -------
  747. `numpy.ndarray`, shape (n,)
  748. Gradient of the quadratic model of the objective function at `x`.
  749. """
  750. if self._debug:
  751. assert x.shape == (self.n,), "The shape of `x` is not valid."
  752. return self._fun.grad(x, self.interpolation)
  753. def fun_hess(self):
  754. """
  755. Evaluate the Hessian matrix of the quadratic model of the objective
  756. function.
  757. Returns
  758. -------
  759. `numpy.ndarray`, shape (n, n)
  760. Hessian matrix of the quadratic model of the objective function.
  761. """
  762. return self._fun.hess(self.interpolation)
  763. def fun_hess_prod(self, v):
  764. """
  765. Evaluate the right product of the Hessian matrix of the quadratic model
  766. of the objective function with a given vector.
  767. Parameters
  768. ----------
  769. v : `numpy.ndarray`, shape (n,)
  770. Vector with which the Hessian matrix of the quadratic model of the
  771. objective function is multiplied from the right.
  772. Returns
  773. -------
  774. `numpy.ndarray`, shape (n,)
  775. Right product of the Hessian matrix of the quadratic model of the
  776. objective function with `v`.
  777. """
  778. if self._debug:
  779. assert v.shape == (self.n,), "The shape of `v` is not valid."
  780. return self._fun.hess_prod(v, self.interpolation)
  781. def fun_curv(self, v):
  782. """
  783. Evaluate the curvature of the quadratic model of the objective function
  784. along a given direction.
  785. Parameters
  786. ----------
  787. v : `numpy.ndarray`, shape (n,)
  788. Direction along which the curvature of the quadratic model of the
  789. objective function is evaluated.
  790. Returns
  791. -------
  792. float
  793. Curvature of the quadratic model of the objective function along
  794. `v`.
  795. """
  796. if self._debug:
  797. assert v.shape == (self.n,), "The shape of `v` is not valid."
  798. return self._fun.curv(v, self.interpolation)
  799. def fun_alt_grad(self, x):
  800. """
  801. Evaluate the gradient of the alternative quadratic model of the
  802. objective function at a given point.
  803. Parameters
  804. ----------
  805. x : `numpy.ndarray`, shape (n,)
  806. Point at which to evaluate the gradient of the alternative
  807. quadratic model of the objective function.
  808. Returns
  809. -------
  810. `numpy.ndarray`, shape (n,)
  811. Gradient of the alternative quadratic model of the objective
  812. function at `x`.
  813. Raises
  814. ------
  815. `numpy.linalg.LinAlgError`
  816. If the interpolation system is ill-defined.
  817. """
  818. if self._debug:
  819. assert x.shape == (self.n,), "The shape of `x` is not valid."
  820. model = Quadratic(self.interpolation, self.fun_val, self._debug)
  821. return model.grad(x, self.interpolation)
  822. def cub(self, x, mask=None):
  823. """
  824. Evaluate the quadratic models of the nonlinear inequality functions at
  825. a given point.
  826. Parameters
  827. ----------
  828. x : `numpy.ndarray`, shape (n,)
  829. Point at which to evaluate the quadratic models of the nonlinear
  830. inequality functions.
  831. mask : `numpy.ndarray`, shape (m_nonlinear_ub,), optional
  832. Mask of the quadratic models to consider.
  833. Returns
  834. -------
  835. `numpy.ndarray`
  836. Values of the quadratic model of the nonlinear inequality
  837. functions.
  838. """
  839. if self._debug:
  840. assert x.shape == (self.n,), "The shape of `x` is not valid."
  841. assert mask is None or mask.shape == (
  842. self.m_nonlinear_ub,
  843. ), "The shape of `mask` is not valid."
  844. return np.array(
  845. [model(x, self.interpolation) for model in self._get_cub(mask)]
  846. )
  847. def cub_grad(self, x, mask=None):
  848. """
  849. Evaluate the gradients of the quadratic models of the nonlinear
  850. inequality functions at a given point.
  851. Parameters
  852. ----------
  853. x : `numpy.ndarray`, shape (n,)
  854. Point at which to evaluate the gradients of the quadratic models of
  855. the nonlinear inequality functions.
  856. mask : `numpy.ndarray`, shape (m_nonlinear_eq,), optional
  857. Mask of the quadratic models to consider.
  858. Returns
  859. -------
  860. `numpy.ndarray`
  861. Gradients of the quadratic model of the nonlinear inequality
  862. functions.
  863. """
  864. if self._debug:
  865. assert x.shape == (self.n,), "The shape of `x` is not valid."
  866. assert mask is None or mask.shape == (
  867. self.m_nonlinear_ub,
  868. ), "The shape of `mask` is not valid."
  869. return np.reshape(
  870. [model.grad(x, self.interpolation)
  871. for model in self._get_cub(mask)],
  872. (-1, self.n),
  873. )
  874. def cub_hess(self, mask=None):
  875. """
  876. Evaluate the Hessian matrices of the quadratic models of the nonlinear
  877. inequality functions.
  878. Parameters
  879. ----------
  880. mask : `numpy.ndarray`, shape (m_nonlinear_ub,), optional
  881. Mask of the quadratic models to consider.
  882. Returns
  883. -------
  884. `numpy.ndarray`
  885. Hessian matrices of the quadratic models of the nonlinear
  886. inequality functions.
  887. """
  888. if self._debug:
  889. assert mask is None or mask.shape == (
  890. self.m_nonlinear_ub,
  891. ), "The shape of `mask` is not valid."
  892. return np.reshape(
  893. [model.hess(self.interpolation) for model in self._get_cub(mask)],
  894. (-1, self.n, self.n),
  895. )
  896. def cub_hess_prod(self, v, mask=None):
  897. """
  898. Evaluate the right product of the Hessian matrices of the quadratic
  899. models of the nonlinear inequality functions with a given vector.
  900. Parameters
  901. ----------
  902. v : `numpy.ndarray`, shape (n,)
  903. Vector with which the Hessian matrices of the quadratic models of
  904. the nonlinear inequality functions are multiplied from the right.
  905. mask : `numpy.ndarray`, shape (m_nonlinear_ub,), optional
  906. Mask of the quadratic models to consider.
  907. Returns
  908. -------
  909. `numpy.ndarray`
  910. Right products of the Hessian matrices of the quadratic models of
  911. the nonlinear inequality functions with `v`.
  912. """
  913. if self._debug:
  914. assert v.shape == (self.n,), "The shape of `v` is not valid."
  915. assert mask is None or mask.shape == (
  916. self.m_nonlinear_ub,
  917. ), "The shape of `mask` is not valid."
  918. return np.reshape(
  919. [
  920. model.hess_prod(v, self.interpolation)
  921. for model in self._get_cub(mask)
  922. ],
  923. (-1, self.n),
  924. )
  925. def cub_curv(self, v, mask=None):
  926. """
  927. Evaluate the curvature of the quadratic models of the nonlinear
  928. inequality functions along a given direction.
  929. Parameters
  930. ----------
  931. v : `numpy.ndarray`, shape (n,)
  932. Direction along which the curvature of the quadratic models of the
  933. nonlinear inequality functions is evaluated.
  934. mask : `numpy.ndarray`, shape (m_nonlinear_ub,), optional
  935. Mask of the quadratic models to consider.
  936. Returns
  937. -------
  938. `numpy.ndarray`
  939. Curvature of the quadratic models of the nonlinear inequality
  940. functions along `v`.
  941. """
  942. if self._debug:
  943. assert v.shape == (self.n,), "The shape of `v` is not valid."
  944. assert mask is None or mask.shape == (
  945. self.m_nonlinear_ub,
  946. ), "The shape of `mask` is not valid."
  947. return np.array(
  948. [model.curv(v, self.interpolation)
  949. for model in self._get_cub(mask)]
  950. )
  951. def ceq(self, x, mask=None):
  952. """
  953. Evaluate the quadratic models of the nonlinear equality functions at a
  954. given point.
  955. Parameters
  956. ----------
  957. x : `numpy.ndarray`, shape (n,)
  958. Point at which to evaluate the quadratic models of the nonlinear
  959. equality functions.
  960. mask : `numpy.ndarray`, shape (m_nonlinear_eq,), optional
  961. Mask of the quadratic models to consider.
  962. Returns
  963. -------
  964. `numpy.ndarray`
  965. Values of the quadratic model of the nonlinear equality functions.
  966. """
  967. if self._debug:
  968. assert x.shape == (self.n,), "The shape of `x` is not valid."
  969. assert mask is None or mask.shape == (
  970. self.m_nonlinear_eq,
  971. ), "The shape of `mask` is not valid."
  972. return np.array(
  973. [model(x, self.interpolation) for model in self._get_ceq(mask)]
  974. )
  975. def ceq_grad(self, x, mask=None):
  976. """
  977. Evaluate the gradients of the quadratic models of the nonlinear
  978. equality functions at a given point.
  979. Parameters
  980. ----------
  981. x : `numpy.ndarray`, shape (n,)
  982. Point at which to evaluate the gradients of the quadratic models of
  983. the nonlinear equality functions.
  984. mask : `numpy.ndarray`, shape (m_nonlinear_eq,), optional
  985. Mask of the quadratic models to consider.
  986. Returns
  987. -------
  988. `numpy.ndarray`
  989. Gradients of the quadratic model of the nonlinear equality
  990. functions.
  991. """
  992. if self._debug:
  993. assert x.shape == (self.n,), "The shape of `x` is not valid."
  994. assert mask is None or mask.shape == (
  995. self.m_nonlinear_eq,
  996. ), "The shape of `mask` is not valid."
  997. return np.reshape(
  998. [model.grad(x, self.interpolation)
  999. for model in self._get_ceq(mask)],
  1000. (-1, self.n),
  1001. )
  1002. def ceq_hess(self, mask=None):
  1003. """
  1004. Evaluate the Hessian matrices of the quadratic models of the nonlinear
  1005. equality functions.
  1006. Parameters
  1007. ----------
  1008. mask : `numpy.ndarray`, shape (m_nonlinear_eq,), optional
  1009. Mask of the quadratic models to consider.
  1010. Returns
  1011. -------
  1012. `numpy.ndarray`
  1013. Hessian matrices of the quadratic models of the nonlinear equality
  1014. functions.
  1015. """
  1016. if self._debug:
  1017. assert mask is None or mask.shape == (
  1018. self.m_nonlinear_eq,
  1019. ), "The shape of `mask` is not valid."
  1020. return np.reshape(
  1021. [model.hess(self.interpolation) for model in self._get_ceq(mask)],
  1022. (-1, self.n, self.n),
  1023. )
  1024. def ceq_hess_prod(self, v, mask=None):
  1025. """
  1026. Evaluate the right product of the Hessian matrices of the quadratic
  1027. models of the nonlinear equality functions with a given vector.
  1028. Parameters
  1029. ----------
  1030. v : `numpy.ndarray`, shape (n,)
  1031. Vector with which the Hessian matrices of the quadratic models of
  1032. the nonlinear equality functions are multiplied from the right.
  1033. mask : `numpy.ndarray`, shape (m_nonlinear_eq,), optional
  1034. Mask of the quadratic models to consider.
  1035. Returns
  1036. -------
  1037. `numpy.ndarray`
  1038. Right products of the Hessian matrices of the quadratic models of
  1039. the nonlinear equality functions with `v`.
  1040. """
  1041. if self._debug:
  1042. assert v.shape == (self.n,), "The shape of `v` is not valid."
  1043. assert mask is None or mask.shape == (
  1044. self.m_nonlinear_eq,
  1045. ), "The shape of `mask` is not valid."
  1046. return np.reshape(
  1047. [
  1048. model.hess_prod(v, self.interpolation)
  1049. for model in self._get_ceq(mask)
  1050. ],
  1051. (-1, self.n),
  1052. )
  1053. def ceq_curv(self, v, mask=None):
  1054. """
  1055. Evaluate the curvature of the quadratic models of the nonlinear
  1056. equality functions along a given direction.
  1057. Parameters
  1058. ----------
  1059. v : `numpy.ndarray`, shape (n,)
  1060. Direction along which the curvature of the quadratic models of the
  1061. nonlinear equality functions is evaluated.
  1062. mask : `numpy.ndarray`, shape (m_nonlinear_eq,), optional
  1063. Mask of the quadratic models to consider.
  1064. Returns
  1065. -------
  1066. `numpy.ndarray`
  1067. Curvature of the quadratic models of the nonlinear equality
  1068. functions along `v`.
  1069. """
  1070. if self._debug:
  1071. assert v.shape == (self.n,), "The shape of `v` is not valid."
  1072. assert mask is None or mask.shape == (
  1073. self.m_nonlinear_eq,
  1074. ), "The shape of `mask` is not valid."
  1075. return np.array(
  1076. [model.curv(v, self.interpolation)
  1077. for model in self._get_ceq(mask)]
  1078. )
  1079. def reset_models(self):
  1080. """
  1081. Set the quadratic models of the objective function, nonlinear
  1082. inequality constraints, and nonlinear equality constraints to the
  1083. alternative quadratic models.
  1084. Raises
  1085. ------
  1086. `numpy.linalg.LinAlgError`
  1087. If the interpolation system is ill-defined.
  1088. """
  1089. self._fun = Quadratic(self.interpolation, self.fun_val, self._debug)
  1090. for i in range(self.m_nonlinear_ub):
  1091. self._cub[i] = Quadratic(
  1092. self.interpolation,
  1093. self.cub_val[:, i],
  1094. self._debug,
  1095. )
  1096. for i in range(self.m_nonlinear_eq):
  1097. self._ceq[i] = Quadratic(
  1098. self.interpolation,
  1099. self.ceq_val[:, i],
  1100. self._debug,
  1101. )
  1102. if self._debug:
  1103. self._check_interpolation_conditions()
  1104. def update_interpolation(self, k_new, x_new, fun_val, cub_val, ceq_val):
  1105. """
  1106. Update the interpolation set.
  1107. This method updates the interpolation set by replacing the `knew`-th
  1108. interpolation point with `xnew`. It also updates the function values
  1109. and the quadratic models.
  1110. Parameters
  1111. ----------
  1112. k_new : int
  1113. Index of the updated interpolation point.
  1114. x_new : `numpy.ndarray`, shape (n,)
  1115. New interpolation point. Its value is interpreted as relative to
  1116. the origin, not the base point.
  1117. fun_val : float
  1118. Value of the objective function at `x_new`.
  1119. Objective function value at `x_new`.
  1120. cub_val : `numpy.ndarray`, shape (m_nonlinear_ub,)
  1121. Values of the nonlinear inequality constraints at `x_new`.
  1122. ceq_val : `numpy.ndarray`, shape (m_nonlinear_eq,)
  1123. Values of the nonlinear equality constraints at `x_new`.
  1124. Raises
  1125. ------
  1126. `numpy.linalg.LinAlgError`
  1127. If the interpolation system is ill-defined.
  1128. """
  1129. if self._debug:
  1130. assert 0 <= k_new < self.npt, "The index `k_new` is not valid."
  1131. assert x_new.shape == (self.n,), \
  1132. "The shape of `x_new` is not valid."
  1133. assert isinstance(fun_val, float), \
  1134. "The function value is not valid."
  1135. assert cub_val.shape == (
  1136. self.m_nonlinear_ub,
  1137. ), "The shape of `cub_val` is not valid."
  1138. assert ceq_val.shape == (
  1139. self.m_nonlinear_eq,
  1140. ), "The shape of `ceq_val` is not valid."
  1141. # Compute the updates in the interpolation conditions.
  1142. fun_diff = np.zeros(self.npt)
  1143. cub_diff = np.zeros(self.cub_val.shape)
  1144. ceq_diff = np.zeros(self.ceq_val.shape)
  1145. fun_diff[k_new] = fun_val - self.fun(x_new)
  1146. cub_diff[k_new, :] = cub_val - self.cub(x_new)
  1147. ceq_diff[k_new, :] = ceq_val - self.ceq(x_new)
  1148. # Update the function values.
  1149. self.fun_val[k_new] = fun_val
  1150. self.cub_val[k_new, :] = cub_val
  1151. self.ceq_val[k_new, :] = ceq_val
  1152. # Update the interpolation set.
  1153. dir_old = np.copy(self.interpolation.xpt[:, k_new])
  1154. self.interpolation.xpt[:, k_new] = x_new - self.interpolation.x_base
  1155. # Update the quadratic models.
  1156. ill_conditioned = self._fun.update(
  1157. self.interpolation,
  1158. k_new,
  1159. dir_old,
  1160. fun_diff,
  1161. )
  1162. for i in range(self.m_nonlinear_ub):
  1163. ill_conditioned = ill_conditioned or self._cub[i].update(
  1164. self.interpolation,
  1165. k_new,
  1166. dir_old,
  1167. cub_diff[:, i],
  1168. )
  1169. for i in range(self.m_nonlinear_eq):
  1170. ill_conditioned = ill_conditioned or self._ceq[i].update(
  1171. self.interpolation,
  1172. k_new,
  1173. dir_old,
  1174. ceq_diff[:, i],
  1175. )
  1176. if self._debug:
  1177. self._check_interpolation_conditions()
  1178. return ill_conditioned
  1179. def determinants(self, x_new, k_new=None):
  1180. """
  1181. Compute the normalized determinants of the new interpolation systems.
  1182. Parameters
  1183. ----------
  1184. x_new : `numpy.ndarray`, shape (n,)
  1185. New interpolation point. Its value is interpreted as relative to
  1186. the origin, not the base point.
  1187. k_new : int, optional
  1188. Index of the updated interpolation point. If `k_new` is not
  1189. specified, all the possible determinants are computed.
  1190. Returns
  1191. -------
  1192. {float, `numpy.ndarray`, shape (npt,)}
  1193. Determinant(s) of the new interpolation system.
  1194. Raises
  1195. ------
  1196. `numpy.linalg.LinAlgError`
  1197. If the interpolation system is ill-defined.
  1198. Notes
  1199. -----
  1200. The determinants are normalized by the determinant of the current
  1201. interpolation system. For stability reasons, the calculations are done
  1202. using the formula (2.12) in [1]_.
  1203. References
  1204. ----------
  1205. .. [1] M. J. D. Powell. On updating the inverse of a KKT matrix.
  1206. Technical Report DAMTP 2004/NA01, Department of Applied Mathematics
  1207. and Theoretical Physics, University of Cambridge, Cambridge, UK,
  1208. 2004.
  1209. """
  1210. if self._debug:
  1211. assert x_new.shape == (self.n,), \
  1212. "The shape of `x_new` is not valid."
  1213. assert (
  1214. k_new is None or 0 <= k_new < self.npt
  1215. ), "The index `k_new` is not valid."
  1216. # Compute the values independent of k_new.
  1217. shift = x_new - self.interpolation.x_base
  1218. new_col = np.empty((self.npt + self.n + 1, 1))
  1219. new_col[: self.npt, 0] = (
  1220. 0.5 * (self.interpolation.xpt.T @ shift) ** 2.0)
  1221. new_col[self.npt, 0] = 1.0
  1222. new_col[self.npt + 1:, 0] = shift
  1223. inv_new_col = Quadratic.solve_systems(self.interpolation, new_col)[0]
  1224. beta = 0.5 * (shift @ shift) ** 2.0 - new_col[:, 0] @ inv_new_col[:, 0]
  1225. # Compute the values that depend on k.
  1226. if k_new is None:
  1227. coord_vec = np.eye(self.npt + self.n + 1, self.npt)
  1228. alpha = np.diag(
  1229. Quadratic.solve_systems(
  1230. self.interpolation,
  1231. coord_vec,
  1232. )[0]
  1233. )
  1234. tau = inv_new_col[: self.npt, 0]
  1235. else:
  1236. coord_vec = np.eye(self.npt + self.n + 1, 1, -k_new)
  1237. alpha = Quadratic.solve_systems(
  1238. self.interpolation,
  1239. coord_vec,
  1240. )[
  1241. 0
  1242. ][k_new, 0]
  1243. tau = inv_new_col[k_new, 0]
  1244. return alpha * beta + tau**2.0
  1245. def shift_x_base(self, new_x_base, options):
  1246. """
  1247. Shift the base point without changing the interpolation set.
  1248. Parameters
  1249. ----------
  1250. new_x_base : `numpy.ndarray`, shape (n,)
  1251. New base point.
  1252. options : dict
  1253. Options of the solver.
  1254. """
  1255. if self._debug:
  1256. assert new_x_base.shape == (
  1257. self.n,
  1258. ), "The shape of `new_x_base` is not valid."
  1259. # Update the models.
  1260. self._fun.shift_x_base(self.interpolation, new_x_base)
  1261. for model in self._cub:
  1262. model.shift_x_base(self.interpolation, new_x_base)
  1263. for model in self._ceq:
  1264. model.shift_x_base(self.interpolation, new_x_base)
  1265. # Update the base point and the interpolation points.
  1266. shift = new_x_base - self.interpolation.x_base
  1267. self.interpolation.x_base += shift
  1268. self.interpolation.xpt -= shift[:, np.newaxis]
  1269. if options[Options.DEBUG]:
  1270. self._check_interpolation_conditions()
  1271. def _get_cub(self, mask=None):
  1272. """
  1273. Get the quadratic models of the nonlinear inequality constraints.
  1274. Parameters
  1275. ----------
  1276. mask : `numpy.ndarray`, shape (m_nonlinear_ub,), optional
  1277. Mask of the quadratic models to return.
  1278. Returns
  1279. -------
  1280. `numpy.ndarray`
  1281. Quadratic models of the nonlinear inequality constraints.
  1282. """
  1283. return self._cub if mask is None else self._cub[mask]
  1284. def _get_ceq(self, mask=None):
  1285. """
  1286. Get the quadratic models of the nonlinear equality constraints.
  1287. Parameters
  1288. ----------
  1289. mask : `numpy.ndarray`, shape (m_nonlinear_eq,), optional
  1290. Mask of the quadratic models to return.
  1291. Returns
  1292. -------
  1293. `numpy.ndarray`
  1294. Quadratic models of the nonlinear equality constraints.
  1295. """
  1296. return self._ceq if mask is None else self._ceq[mask]
  1297. def _check_interpolation_conditions(self):
  1298. """
  1299. Check the interpolation conditions of all quadratic models.
  1300. """
  1301. error_fun = 0.0
  1302. error_cub = 0.0
  1303. error_ceq = 0.0
  1304. for k in range(self.npt):
  1305. error_fun = np.max(
  1306. [
  1307. error_fun,
  1308. np.abs(
  1309. self.fun(self.interpolation.point(k)) - self.fun_val[k]
  1310. ),
  1311. ]
  1312. )
  1313. error_cub = np.max(
  1314. np.abs(
  1315. self.cub(self.interpolation.point(k)) - self.cub_val[k, :]
  1316. ),
  1317. initial=error_cub,
  1318. )
  1319. error_ceq = np.max(
  1320. np.abs(
  1321. self.ceq(self.interpolation.point(k)) - self.ceq_val[k, :]
  1322. ),
  1323. initial=error_ceq,
  1324. )
  1325. tol = 10.0 * np.sqrt(EPS) * max(self.n, self.npt)
  1326. if error_fun > tol * np.max(np.abs(self.fun_val), initial=1.0):
  1327. warnings.warn(
  1328. "The interpolation conditions for the objective function are "
  1329. "not satisfied.",
  1330. RuntimeWarning,
  1331. 2,
  1332. )
  1333. if error_cub > tol * np.max(np.abs(self.cub_val), initial=1.0):
  1334. warnings.warn(
  1335. "The interpolation conditions for the inequality constraint "
  1336. "function are not satisfied.",
  1337. RuntimeWarning,
  1338. 2,
  1339. )
  1340. if error_ceq > tol * np.max(np.abs(self.ceq_val), initial=1.0):
  1341. warnings.warn(
  1342. "The interpolation conditions for the equality constraint "
  1343. "function are not satisfied.",
  1344. RuntimeWarning,
  1345. 2,
  1346. )