qoro-divi 0.2.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. divi/__init__.py +8 -0
  2. divi/_pbar.py +73 -0
  3. divi/circuits.py +139 -0
  4. divi/exp/cirq/__init__.py +7 -0
  5. divi/exp/cirq/_lexer.py +126 -0
  6. divi/exp/cirq/_parser.py +889 -0
  7. divi/exp/cirq/_qasm_export.py +37 -0
  8. divi/exp/cirq/_qasm_import.py +35 -0
  9. divi/exp/cirq/exception.py +21 -0
  10. divi/exp/scipy/_cobyla.py +342 -0
  11. divi/exp/scipy/pyprima/LICENCE.txt +28 -0
  12. divi/exp/scipy/pyprima/__init__.py +263 -0
  13. divi/exp/scipy/pyprima/cobyla/__init__.py +0 -0
  14. divi/exp/scipy/pyprima/cobyla/cobyla.py +599 -0
  15. divi/exp/scipy/pyprima/cobyla/cobylb.py +849 -0
  16. divi/exp/scipy/pyprima/cobyla/geometry.py +240 -0
  17. divi/exp/scipy/pyprima/cobyla/initialize.py +269 -0
  18. divi/exp/scipy/pyprima/cobyla/trustregion.py +540 -0
  19. divi/exp/scipy/pyprima/cobyla/update.py +331 -0
  20. divi/exp/scipy/pyprima/common/__init__.py +0 -0
  21. divi/exp/scipy/pyprima/common/_bounds.py +41 -0
  22. divi/exp/scipy/pyprima/common/_linear_constraints.py +46 -0
  23. divi/exp/scipy/pyprima/common/_nonlinear_constraints.py +64 -0
  24. divi/exp/scipy/pyprima/common/_project.py +224 -0
  25. divi/exp/scipy/pyprima/common/checkbreak.py +107 -0
  26. divi/exp/scipy/pyprima/common/consts.py +48 -0
  27. divi/exp/scipy/pyprima/common/evaluate.py +101 -0
  28. divi/exp/scipy/pyprima/common/history.py +39 -0
  29. divi/exp/scipy/pyprima/common/infos.py +30 -0
  30. divi/exp/scipy/pyprima/common/linalg.py +452 -0
  31. divi/exp/scipy/pyprima/common/message.py +336 -0
  32. divi/exp/scipy/pyprima/common/powalg.py +131 -0
  33. divi/exp/scipy/pyprima/common/preproc.py +393 -0
  34. divi/exp/scipy/pyprima/common/present.py +5 -0
  35. divi/exp/scipy/pyprima/common/ratio.py +56 -0
  36. divi/exp/scipy/pyprima/common/redrho.py +49 -0
  37. divi/exp/scipy/pyprima/common/selectx.py +346 -0
  38. divi/interfaces.py +25 -0
  39. divi/parallel_simulator.py +258 -0
  40. divi/qasm.py +220 -0
  41. divi/qem.py +191 -0
  42. divi/qlogger.py +119 -0
  43. divi/qoro_service.py +343 -0
  44. divi/qprog/__init__.py +13 -0
  45. divi/qprog/_graph_partitioning.py +619 -0
  46. divi/qprog/_mlae.py +182 -0
  47. divi/qprog/_qaoa.py +440 -0
  48. divi/qprog/_vqe.py +275 -0
  49. divi/qprog/_vqe_sweep.py +144 -0
  50. divi/qprog/batch.py +235 -0
  51. divi/qprog/optimizers.py +75 -0
  52. divi/qprog/quantum_program.py +493 -0
  53. divi/utils.py +116 -0
  54. qoro_divi-0.2.0b1.dist-info/LICENSE +190 -0
  55. qoro_divi-0.2.0b1.dist-info/LICENSES/Apache-2.0.txt +73 -0
  56. qoro_divi-0.2.0b1.dist-info/METADATA +57 -0
  57. qoro_divi-0.2.0b1.dist-info/RECORD +58 -0
  58. qoro_divi-0.2.0b1.dist-info/WHEEL +4 -0
@@ -0,0 +1,849 @@
1
+ """
2
+ This module performs the major calculations of COBYLA.
3
+
4
+ Translated from Zaikun Zhang's modern-Fortran reference implementation in PRIMA.
5
+
6
+ Dedicated to late Professor M. J. D. Powell FRS (1936--2015).
7
+
8
+ Python translation by Nickolai Belakovski.
9
+ """
10
+
11
+ import numpy as np
12
+
13
+ from ..common.checkbreak import checkbreak_con
14
+ from ..common.consts import DEBUGGING, EPS, MIN_MAXFILT, REALMAX
15
+ from ..common.evaluate import evaluate
16
+ from ..common.history import savehist
17
+ from ..common.infos import (
18
+ CALLBACK_TERMINATE,
19
+ DAMAGING_ROUNDING,
20
+ INFO_DEFAULT,
21
+ MAXTR_REACHED,
22
+ SMALL_TR_RADIUS,
23
+ )
24
+ from ..common.linalg import inprod, isinv, matprod, norm, primapow2, primasum
25
+ from ..common.message import fmsg, retmsg, rhomsg
26
+ from ..common.ratio import redrat
27
+ from ..common.redrho import redrho
28
+ from ..common.selectx import savefilt, selectx
29
+ from .geometry import geostep, setdrop_tr
30
+ from .initialize import initfilt, initxfc
31
+ from .trustregion import trrad, trstlp
32
+ from .update import findpole, updatepole, updatexfc
33
+
34
+
35
+ def cobylb(
36
+ calcfc,
37
+ iprint,
38
+ maxfilt,
39
+ maxfun,
40
+ amat,
41
+ bvec,
42
+ ctol,
43
+ cweight,
44
+ eta1,
45
+ eta2,
46
+ ftarget,
47
+ gamma1,
48
+ gamma2,
49
+ rhobeg,
50
+ rhoend,
51
+ constr,
52
+ f,
53
+ x,
54
+ maxhist,
55
+ callback,
56
+ ):
57
+ """
58
+ This subroutine performs the actual computations of COBYLA.
59
+ """
60
+
61
+ # Outputs
62
+ xhist = []
63
+ fhist = []
64
+ chist = []
65
+ conhist = []
66
+
67
+ # Local variables
68
+ solver = "COBYLA"
69
+ A = np.zeros(
70
+ (np.size(x), np.size(constr))
71
+ ) # A contains the approximate gradient for the constraints
72
+ distsq = np.zeros(np.size(x) + 1)
73
+ # CPENMIN is the minimum of the penalty parameter CPEN for the L-infinity
74
+ # constraint violation in the merit function. Note that CPENMIN = 0 in Powell's
75
+ # implementation, which allows CPEN to be 0. Here, we take CPENMIN > 0 so that CPEN
76
+ # is always positive. This avoids the situation where PREREM becomes 0 when
77
+ # PREREF = 0 = CPEN. It brings two advantages as follows.
78
+ # 1. If the trust-region subproblem solver works correctly and the trust-region
79
+ # center is not optimal for the subproblem, then PREREM > 0 is guaranteed. This
80
+ # is because, in theory, PREREC >= 0 and MAX(PREREC, PREREF) > 0, and the
81
+ # definition of CPEN in GETCPEN ensures that PREREM > 0.
82
+ # 2. There is no need to revise ACTREM and PREREM when CPEN = 0 and F = FVAL(N+1)
83
+ # as in lines 312--314 of Powell's cobylb.f code. Powell's code revises ACTREM
84
+ # to CVAL(N + 1) - CSTRV and PREREM to PREREC in this case, which is crucial for
85
+ # feasibility problems.
86
+ cpenmin = EPS
87
+
88
+ # Sizes
89
+ m_lcon = np.size(bvec) if bvec is not None else 0
90
+ num_constraints = np.size(constr)
91
+ m_nlcon = num_constraints - m_lcon
92
+ num_vars = np.size(x)
93
+
94
+ # Preconditions
95
+ if DEBUGGING:
96
+ assert abs(iprint) <= 3
97
+ assert num_constraints >= m_lcon and m_lcon >= 0
98
+ assert num_vars >= 1
99
+ assert maxfun >= num_vars + 2
100
+ assert rhobeg >= rhoend and rhoend > 0
101
+ assert all(np.isfinite(x))
102
+ assert 0 <= eta1 <= eta2 < 1
103
+ assert 0 < gamma1 < 1 < gamma2
104
+ assert 0 <= ctol
105
+ assert 0 <= cweight
106
+ assert 0 <= maxhist <= maxfun
107
+ assert amat is None or np.shape(amat) == (m_lcon, num_vars)
108
+ assert min(MIN_MAXFILT, maxfun) <= maxfilt <= maxfun
109
+
110
+ # ====================#
111
+ # Calculation starts #
112
+ # ====================#
113
+
114
+ # Initialize SIM, FVAL, CONMAT, and CVAL, together with the history.
115
+ # After the initialization, SIM[:, NUM_VARS] holds the vertex of the initial
116
+ # simplex with the smallest function value (regardless of the constraint
117
+ # violation), and SIM[:, :NUM_VARS] holds the displacements from the other vertices
118
+ # to SIM[:, NUM_VARS]. FVAL, CONMAT, and CVAL hold the function values, constraint
119
+ # values, and constraint violations on the vertices in the order corresponding to
120
+ # SIM.
121
+ evaluated, conmat, cval, sim, simi, fval, nf, subinfo = initxfc(
122
+ calcfc,
123
+ iprint,
124
+ maxfun,
125
+ constr,
126
+ amat,
127
+ bvec,
128
+ ctol,
129
+ f,
130
+ ftarget,
131
+ rhobeg,
132
+ x,
133
+ xhist,
134
+ fhist,
135
+ chist,
136
+ conhist,
137
+ maxhist,
138
+ )
139
+
140
+ # Initialize the filter, including xfilt, ffilt, confilt, cfilt, and nfilt.
141
+ # N.B.: The filter is used only when selecting which iterate to return. It does not
142
+ # interfere with the iterations. COBYLA is NOT a filter method but a trust-region
143
+ # method based on an L-infinity merit function. Powell's implementation does not
144
+ # use a filter to select the iterate, possibly returning a suboptimal iterate.
145
+ cfilt = np.zeros(np.minimum(np.maximum(maxfilt, 1), maxfun))
146
+ confilt = np.zeros((np.size(constr), np.size(cfilt)))
147
+ ffilt = np.zeros(np.size(cfilt))
148
+ xfilt = np.zeros((np.size(x), np.size(cfilt)))
149
+ nfilt = initfilt(
150
+ conmat, ctol, cweight, cval, fval, sim, evaluated, cfilt, confilt, ffilt, xfilt
151
+ )
152
+
153
+ # Check whether to return due to abnormal cases that may occur during the initialization.
154
+ if subinfo != INFO_DEFAULT:
155
+ info = subinfo
156
+ # Return the best calculated values of the variables
157
+ # N.B: Selectx and findpole choose X by different standards, one cannot replace the other
158
+ kopt = selectx(ffilt[:nfilt], cfilt[:nfilt], cweight, ctol)
159
+ x = xfilt[:, kopt]
160
+ f = ffilt[kopt]
161
+ constr = confilt[:, kopt]
162
+ cstrv = cfilt[kopt]
163
+ # print a return message according to IPRINT.
164
+ retmsg(solver, info, iprint, nf, f, x, cstrv, constr)
165
+ # Postconditions
166
+ if DEBUGGING:
167
+ assert nf <= maxfun
168
+ assert np.size(x) == num_vars and not any(np.isnan(x))
169
+ assert not (np.isnan(f) or np.isposinf(f))
170
+ # assert np.size(xhist, 0) == n and np.size(xhist, 1) == maxxhist
171
+ # assert not any(np.isnan(xhist(:, 1:min(nf, maxxhist))))
172
+ # The last calculated X can be Inf (finite + finite can be Inf numerically).
173
+ # assert np.size(fhist) == maxfhist
174
+ # assert not any(np.isnan(fhist(1:min(nf, maxfhist))) or np.isposinf(fhist(1:min(nf, maxfhist))))
175
+ # assert np.size(conhist, 0) == m and np.size(conhist, 1) == maxconhist
176
+ # assert not any(np.isnan(conhist(:, 1:min(nf, maxconhist))) or np.isneginf(conhist(:, 1:min(nf, maxconhist))))
177
+ # assert np.size(chist) == maxchist
178
+ # assert not any(chist(1:min(nf, maxchist)) < 0 or np.isnan(chist(1:min(nf, maxchist))) or np.isposinf(chist(1:min(nf, maxchist))))
179
+ # nhist = minval([nf, maxfhist, maxchist])
180
+ # assert not any(isbetter(fhist(1:nhist), chist(1:nhist), f, cstrv, ctol))
181
+ return x, f, constr, cstrv, nf, xhist, fhist, chist, conhist, info
182
+
183
+ # Set some more initial values.
184
+ # We must initialize shortd, ratio, and jdrop_tr because these get defined on
185
+ # branches that are not guaranteed to be executed, but their values are used later.
186
+ # Our initialization of CPEN differs from Powell's in two ways. First, we use the
187
+ # ratio defined in (13) of Powell's COBYLA paper to initialize CPEN. Second, we
188
+ # impose CPEN >= CPENMIN > 0. Powell's code simply initializes CPEN to 0.
189
+ rho = rhobeg
190
+ delta = rhobeg
191
+ cpen = np.maximum(
192
+ cpenmin, np.minimum(1.0e3, fcratio(conmat, fval))
193
+ ) # Powell's code: CPEN = ZERO
194
+ shortd = False
195
+ ratio = -1
196
+ jdrop_tr = 0
197
+
198
+ # If DELTA <= GAMMA3*RHO after an update, we set DELTA to RHO. GAMMA3 must be less
199
+ # than GAMMA2. The reason is as follows. Imagine a very successful step with
200
+ # DNORM = the un-updated DELTA = RHO. The TRRAD will update DELTA to GAMMA2*RHO.
201
+ # If GAMMA3 >= GAMMA2, then DELTA will be reset to RHO, which is not reasonable as
202
+ # D is very successful. See paragraph two of Sec 5.2.5 in T. M. Ragonneau's thesis:
203
+ # "Model-Based Derivative-Free Optimization Methods and Software." According to
204
+ # test on 20230613, for COBYLA, this Powellful updating scheme of DELTA works
205
+ # slightly better than setting directly DELTA = max(NEW_DELTA, RHO).
206
+ gamma3 = np.maximum(1, np.minimum(0.75 * gamma2, 1.5))
207
+
208
+ # MAXTR is the maximal number of trust region iterations. Each trust-region
209
+ # iteration takes 1 or 2 function evaluations unless the trust-region step is short
210
+ # or the trust-region subproblem solver fails but the geometry step is not invoked.
211
+ # Thus the following MAXTR is unlikely to be reached.
212
+ maxtr = 10 * maxfun
213
+ info = MAXTR_REACHED
214
+
215
+ # Begin the iterative procedure
216
+ # After solving a trust-region subproblem, we use three boolean variables to
217
+ # control the workflow.
218
+ # SHORTD - Is the trust-region trial step too short to invoke # a function
219
+ # evaluation?
220
+ # IMPROVE_GEO - Will we improve the model after the trust-region iteration? If yes,
221
+ # a geometry step will be taken, corresponding to the "Branch (Delta)"
222
+ # in the COBYLA paper.
223
+ # REDUCE_RHO - Will we reduce rho after the trust-region iteration?
224
+ # COBYLA never sets IMPROVE_GEO and REDUCE_RHO to True simultaneously.
225
+ for tr in range(maxtr):
226
+ # Increase the penalty parameter CPEN, if needed, so that
227
+ # PREREM = PREREF + CPEN * PREREC > 0.
228
+ # This is the first (out of two) update of CPEN, where CPEN increases or
229
+ # remains the same.
230
+ # N.B.: CPEN and the merit function PHI = FVAL + CPEN*CVAL are used in three
231
+ # places only.
232
+ # 1. In FINDPOLE/UPDATEPOLE, deciding the optimal vertex of the current simplex.
233
+ # 2. After the trust-region trial step, calculating the reduction ratio.
234
+ # 3. In GEOSTEP, deciding the direction of the geometry step.
235
+ # They do not appear explicitly in the trust-region subproblem, though the
236
+ # trust-region center (i.e. the current optimal vertex) is defined by them.
237
+ cpen = getcpen(amat, bvec, conmat, cpen, cval, delta, fval, rho, sim, simi)
238
+
239
+ # Switch the best vertex of the current simplex to SIM[:, NUM_VARS].
240
+ conmat, cval, fval, sim, simi, subinfo = updatepole(
241
+ cpen, conmat, cval, fval, sim, simi
242
+ )
243
+ # Check whether to exit due to damaging rounding in UPDATEPOLE.
244
+ if subinfo == DAMAGING_ROUNDING:
245
+ info = subinfo
246
+ break # Better action to take? Geometry step, or simply continue?
247
+
248
+ # Does the interpolation set have adequate geometry? It affects improve_geo and
249
+ # reduce_rho.
250
+ adequate_geo = all(
251
+ primasum(primapow2(sim[:, :num_vars]), axis=0) <= 4 * primapow2(delta)
252
+ )
253
+
254
+ # Calculate the linear approximations to the objective and constraint functions.
255
+ # N.B.: TRSTLP accesses A mostly by columns, so it is more reasonable to save A
256
+ # instead of A^T.
257
+ # Zaikun 2023108: According to a test on 2023108, calculating G and
258
+ # A(:, M_LCON+1:M) by solving the linear systems SIM^T*G = FVAL(1:N)-FVAL(N+1)
259
+ # and SIM^T*A = CONMAT(:, 1:N)-CONMAT(:, N+1) does not seem to improve or worsen
260
+ # the performance of COBYLA in terms of the number of function evaluations. The
261
+ # system was solved by SOLVE in LINALG_MOD based on a QR factorization of SIM
262
+ # (not necessarily a good algorithm). No preconditioning or scaling was used.
263
+ g = matprod((fval[:num_vars] - fval[num_vars]), simi)
264
+ A[:, :m_lcon] = amat.T if amat is not None else amat
265
+ A[:, m_lcon:] = matprod(
266
+ (
267
+ conmat[m_lcon:, :num_vars]
268
+ - np.tile(conmat[m_lcon:, num_vars], (num_vars, 1)).T
269
+ ),
270
+ simi,
271
+ ).T
272
+
273
+ # Calculate the trust-region trial step d. Note that d does NOT depend on cpen.
274
+ d = trstlp(A, -conmat[:, num_vars], delta, g)
275
+ dnorm = min(delta, norm(d))
276
+
277
+ # Is the trust-region trial step short? N.B.: we compare DNORM with RHO, not
278
+ # DELTA. Powell's code especially defines SHORTD by SHORTD = (DNORM < 0.5 *
279
+ # RHO). In our tests 1/10 seems to work better than 1/2 or 1/4, especially for
280
+ # linearly constrained problems. Note that LINCOA has a slightly more
281
+ # sophisticated way of defining SHORTD, taking into account whether D causes a
282
+ # change to the active set. Should we try the same here?
283
+ shortd = dnorm <= 0.1 * rho
284
+
285
+ # Predict the change to F (PREREF) and to the constraint violation (PREREC) due
286
+ # to D. We have the following in precise arithmetic. They may fail to hold due
287
+ # to rounding errors.
288
+ # 1. B[:NUM_CONSTRAINTS] = -CONMAT[:, NUM_VARS] and hence
289
+ # np.max(np.append(B[:NUM_CONSTRAINTS] - D@A[:, :NUM_CONSTRAINTS], 0)) is the
290
+ # L-infinity violation of the linearized constraints corresponding to D. When
291
+ # D=0, the violation is np.max(np.append(B[:NUM_CONSTRAINTS], 0)) =
292
+ # CVAL[NUM_VARS]. PREREC is the reduction of this violation achieved by D,
293
+ # which is nonnegative in theory; PREREC = 0 iff B[:NUM_CONSTRAINTS] <= 0, i.e.
294
+ # the trust-region center satisfies the linearized constraints.
295
+ # 2. PREREF may be negative or 0, but it is positive when PREREC = 0 and shortd
296
+ # is False
297
+ # 3. Due to 2, in theory, max(PREREC, PREREF) > 0 if shortd is False.
298
+ preref = -inprod(d, g) # Can be negative
299
+ prerec = cval[num_vars] - np.max(
300
+ np.append(0, conmat[:, num_vars] + matprod(d, A))
301
+ )
302
+
303
+ # Evaluate PREREM, which is the predicted reduction in the merit function.
304
+ # In theory, PREREM >= 0 and it is 0 iff CPEN = 0 = PREREF. This may not be true
305
+ # numerically.
306
+ prerem = preref + cpen * prerec
307
+ trfail = not (prerem > 1.0e-6 * min(cpen, 1) * rho)
308
+
309
+ if shortd or trfail:
310
+ # Reduce DELTA if D is short or if D fails to render PREREM > 0. The latter
311
+ # can only happen due to rounding errors. This seems quite important for
312
+ # performance
313
+ delta *= 0.1
314
+ if delta <= gamma3 * rho:
315
+ delta = rho # set delta to rho when it is close to or below
316
+ else:
317
+ # Calculate the next value of the objective and constraint functions.
318
+ # If X is close to one of the points in the interpolation set, then we do
319
+ # not evaluate the objective and constraints at X, assuming them to have
320
+ # the values at the closest point.
321
+ # N.B.: If this happens, do NOT include X into the filter, as F and CONSTR
322
+ # are inaccurate.
323
+ x = sim[:, num_vars] + d
324
+ distsq[num_vars] = primasum(primapow2(x - sim[:, num_vars]))
325
+ distsq[:num_vars] = primasum(
326
+ primapow2(
327
+ x.reshape(num_vars, 1)
328
+ - (sim[:, num_vars].reshape(num_vars, 1) + sim[:, :num_vars])
329
+ ),
330
+ axis=0,
331
+ )
332
+ j = np.argmin(distsq)
333
+ if distsq[j] <= primapow2(1e-4 * rhoend):
334
+ f = fval[j]
335
+ constr = conmat[:, j]
336
+ cstrv = cval[j]
337
+ else:
338
+ # Evaluate the objective and constraints at X, taking care of possible
339
+ # inf/nan values.
340
+ f, constr = evaluate(calcfc, x, m_nlcon, amat, bvec)
341
+ cstrv = np.max(np.append(0, constr))
342
+ nf += 1
343
+ # Save X, F, CONSTR, CSTRV into the history.
344
+ savehist(maxhist, x, xhist, f, fhist, cstrv, chist, constr, conhist)
345
+ # Save X, F, CONSTR, CSTRV into the filter.
346
+ nfilt, cfilt, ffilt, xfilt, confilt = savefilt(
347
+ cstrv,
348
+ ctol,
349
+ cweight,
350
+ f,
351
+ x,
352
+ nfilt,
353
+ cfilt,
354
+ ffilt,
355
+ xfilt,
356
+ constr,
357
+ confilt,
358
+ )
359
+
360
+ # Print a message about the function/constraint evaluation according to
361
+ # iprint
362
+ fmsg(solver, "Trust region", iprint, nf, delta, f, x, cstrv, constr)
363
+
364
+ # Evaluate ACTREM, which is the actual reduction in the merit function
365
+ actrem = (fval[num_vars] + cpen * cval[num_vars]) - (f + cpen * cstrv)
366
+
367
+ # Calculate the reduction ratio by redrat, which hands inf/nan carefully
368
+ ratio = redrat(actrem, prerem, eta1)
369
+
370
+ # Update DELTA. After this, DELTA < DNORM may hold.
371
+ # N.B.:
372
+ # 1. Powell's code uses RHO as the trust-region radius and updates it as
373
+ # follows.
374
+ # Reduce RHO to GAMMA1*RHO if ADEQUATE_GEO is TRUE and either SHORTD is
375
+ # TRUE or RATIO < ETA1, and then revise RHO to RHOEND if its new value is
376
+ # not more than GAMMA3*RHOEND; RHO remains unchanged in all other cases;
377
+ # in particular, RHO is never increased.
378
+ # 2. Our implementation uses DELTA as the trust-region radius, while using
379
+ # RHO as a lower bound for DELTA. DELTA is updated in a way that is
380
+ # typical for trust-region methods, and it is revised to RHO if its new
381
+ # value is not more than GAMMA3*RHO. RHO reflects the current resolution
382
+ # of the algorithm; its update is essentially the same as the update of
383
+ # RHO in Powell's code (see the definition of REDUCE_RHO below). Our
384
+ # implementation aligns with UOBYQA/NEWUOA/BOBYQA/LINCOA and improves the
385
+ # performance of COBYLA.
386
+ # 3. The same as Powell's code, we do not reduce RHO unless ADEQUATE_GEO is
387
+ # TRUE. This is also how Powell updated RHO in
388
+ # UOBYQA/NEWUOA/BOBYQA/LINCOA. What about we also use ADEQUATE_GEO ==
389
+ # TRUE as a prerequisite for reducing DELTA? The argument would be that
390
+ # the bad (small) value of RATIO may be because of a bad geometry (and
391
+ # hence a bad model) rather than an improperly large DELTA, and it might
392
+ # be good to try improving the geometry first without reducing DELTA.
393
+ # However, according to a test on 20230206, it does not improve the
394
+ # performance if we skip the update of DELTA when ADEQUATE_GEO is FALSE
395
+ # and RATIO < 0.1. Therefore, we choose to update DELTA without checking
396
+ # ADEQUATE_GEO.
397
+
398
+ delta = trrad(delta, dnorm, eta1, eta2, gamma1, gamma2, ratio)
399
+ if delta <= gamma3 * rho:
400
+ delta = rho # Set delta to rho when it is close to or below.
401
+
402
+ # Is the newly generated X better than the current best point?
403
+ ximproved = (
404
+ actrem > 0
405
+ ) # If ACTREM is NaN, then XIMPROVED should and will be False
406
+
407
+ # Set JDROP_TR to the index of the vertex to be replaced with X. JDROP_TR = 0 means there
408
+ # is no good point to replace, and X will not be included into the simplex; in this case,
409
+ # the geometry of the simplex likely needs improvement, which will be handled below.
410
+ jdrop_tr = setdrop_tr(ximproved, d, delta, rho, sim, simi)
411
+
412
+ # Update SIM, SIMI, FVAL, CONMAT, and CVAL so that SIM[:, JDROP_TR] is replaced with D.
413
+ # UPDATEXFC does nothing if JDROP_TR is None, as the algorithm decides to discard X.
414
+ sim, simi, fval, conmat, cval, subinfo = updatexfc(
415
+ jdrop_tr, constr, cpen, cstrv, d, f, conmat, cval, fval, sim, simi
416
+ )
417
+ # Check whether to break due to damaging rounding in UPDATEXFC
418
+ if subinfo == DAMAGING_ROUNDING:
419
+ info = subinfo
420
+ break # Better action to take? Geometry step, or a RESCUE as in BOBYQA?
421
+
422
+ # Check whether to break due to maxfun, ftarget, etc.
423
+ subinfo = checkbreak_con(maxfun, nf, cstrv, ctol, f, ftarget, x)
424
+ if subinfo != INFO_DEFAULT:
425
+ info = subinfo
426
+ break
427
+ # End of if SHORTD or TRFAIL. The normal trust-region calculation ends.
428
+
429
+ # Before the next trust-region iteration, we possibly improve the geometry of the simplex or
430
+ # reduce RHO according to IMPROVE_GEO and REDUCE_RHO. Now we decide these indicators.
431
+ # N.B.: We must ensure that the algorithm does not set IMPROVE_GEO = True at infinitely many
432
+ # consecutive iterations without moving SIM[:, NUM_VARS] or reducing RHO. Otherwise, the algorithm
433
+ # will get stuck in repetitive invocations of GEOSTEP. This is ensured by the following facts:
434
+ # 1. If an iteration sets IMPROVE_GEO to True, it must also reduce DELTA or set DELTA to RHO.
435
+ # 2. If SIM[:, NUM_VARS] and RHO remain unchanged, then ADEQUATE_GEO will become True after at
436
+ # most NUM_VARS invocations of GEOSTEP.
437
+
438
+ # BAD_TRSTEP: Is the last trust-region step bad?
439
+ bad_trstep = shortd or trfail or ratio <= 0 or jdrop_tr is None
440
+ # IMPROVE_GEO: Should we take a geometry step to improve the geometry of the interpolation set?
441
+ improve_geo = bad_trstep and not adequate_geo
442
+ # REDUCE_RHO: Should we enhance the resolution by reducing rho?
443
+ reduce_rho = bad_trstep and adequate_geo and max(delta, dnorm) <= rho
444
+
445
+ # COBYLA never sets IMPROVE_GEO and REDUCE_RHO to True simultaneously.
446
+ # assert not (IMPROVE_GEO and REDUCE_RHO), 'IMPROVE_GEO or REDUCE_RHO are not both TRUE, COBYLA'
447
+
448
+ # If SHORTD or TRFAIL is True, then either IMPROVE_GEO or REDUCE_RHO is True unless ADEQUATE_GEO
449
+ # is True and max(DELTA, DNORM) > RHO.
450
+ # assert not (shortd or trfail) or (improve_geo or reduce_rho or (adequate_geo and max(delta, dnorm) > rho)), \
451
+ # 'If SHORTD or TRFAIL is TRUE, then either IMPROVE_GEO or REDUCE_RHO is TRUE unless ADEQUATE_GEO is TRUE and MAX(DELTA, DNORM) > RHO'
452
+
453
+ # Comments on BAD_TRSTEP:
454
+ # 1. Powell's definition of BAD_TRSTEP is as follows. The one used above seems to work better,
455
+ # especially for linearly constrained problems due to the factor TENTH (= ETA1).
456
+ # !bad_trstep = (shortd .or. actrem <= 0 .or. actrem < TENTH * prerem .or. jdrop_tr == 0)
457
+ # Besides, Powell did not check PREREM > 0 in BAD_TRSTEP, which is reasonable to do but has
458
+ # little impact upon the performance.
459
+ # 2. NEWUOA/BOBYQA/LINCOA would define BAD_TRSTEP, IMPROVE_GEO, and REDUCE_RHO as follows. Two
460
+ # different thresholds are used in BAD_TRSTEP. It outperforms Powell's version.
461
+ # !bad_trstep = (shortd .or. trfail .or. ratio <= eta1 .or. jdrop_tr == 0)
462
+ # !improve_geo = bad_trstep .and. .not. adequate_geo
463
+ # !bad_trstep = (shortd .or. trfail .or. ratio <= 0 .or. jdrop_tr == 0)
464
+ # !reduce_rho = bad_trstep .and. adequate_geo .and. max(delta, dnorm) <= rho
465
+ # 3. Theoretically, JDROP_TR > 0 when ACTREM > 0 (guaranteed by RATIO > 0). However, in Powell's
466
+ # implementation, JDROP_TR may be 0 even RATIO > 0 due to NaN. The modernized code has rectified
467
+ # this in the function SETDROP_TR. After this rectification, we can indeed simplify the
468
+ # definition of BAD_TRSTEP by removing the condition JDROP_TR == 0. We retain it for robustness.
469
+
470
+ # Comments on REDUCE_RHO:
471
+ # When SHORTD is TRUE, UOBYQA/NEWUOA/BOBYQA/LINCOA all set REDUCE_RHO to TRUE if the recent
472
+ # models are sufficiently accurate according to certain criteria. See the paragraph around (37)
473
+ # in the UOBYQA paper and the discussions about Box 14 in the NEWUOA paper. This strategy is
474
+ # crucial for the performance of the solvers. However, as of 20221111, we have not managed to
475
+ # make it work in COBYLA. As in NEWUOA, we recorded the errors of the recent models, and set
476
+ # REDUCE_RHO to true if they are small (e.g., ALL(ABS(MODERR_REC) <= 0.1 * MAXVAL(ABS(A))*RHO) or
477
+ # ALL(ABS(MODERR_REC) <= RHO**2)) when SHORTD is TRUE. It made little impact on the performance.
478
+
479
+ # Since COBYLA never sets IMPROVE_GEO and REDUCE_RHO to TRUE simultaneously, the following
480
+ # two blocks are exchangeable: IF (IMPROVE_GEO) ... END IF and IF (REDUCE_RHO) ... END IF.
481
+
482
+ # Improve the geometry of the simplex by removing a point and adding a new one.
483
+ # If the current interpolation set has acceptable geometry, then we skip the geometry step.
484
+ # The code has a small difference from Powell's original code here: If the current geometry
485
+ # is acceptable, then we will continue with a new trust-region iteration; however, at the
486
+ # beginning of the iteration, CPEN may be updated, which may alter the pole point SIM(:, N+1)
487
+ # by UPDATEPOLE; the quality of the interpolation point depends on SIM(:, N + 1), meaning
488
+ # that the same interpolation set may have good or bad geometry with respect to different
489
+ # "poles"; if the geometry turns out bad with the new pole, the original COBYLA code will
490
+ # take a geometry step, but our code here will NOT do it but continue to take a trust-region
491
+ # step. The argument is this: even if the geometry step is not skipped in the first place, the
492
+ # geometry may turn out bad again after the pole is altered due to an update to CPEN; should
493
+ # we take another geometry step in that case? If no, why should we do it here? Indeed, this
494
+ # distinction makes no practical difference for CUTEst problems with at most 100 variables
495
+ # and 5000 constraints, while the algorithm framework is simplified.
496
+ if improve_geo and not all(
497
+ primasum(primapow2(sim[:, :num_vars]), axis=0) <= 4 * primapow2(delta)
498
+ ):
499
+ # Before the geometry step, updatepole has been called either implicitly by UPDATEXFC or
500
+ # explicitly after CPEN is updated, so that SIM[:, :NUM_VARS] is the optimal vertex.
501
+
502
+ # Decide a vertex to drop from the simplex. It will be replaced with SIM[:, NUM_VARS] + D to
503
+ # improve the geometry of the simplex.
504
+ # N.B.:
505
+ # 1. COBYLA never sets JDROP_GEO = num_vars.
506
+ # 2. The following JDROP_GEO comes from UOBYQA/NEWUOA/BOBYQA/LINCOA.
507
+ # 3. In Powell's original algorithm, the geometry of the simplex is considered acceptable
508
+ # iff the distance between any vertex and the pole is at most 2.1*DELTA, and the distance
509
+ # between any vertex and the opposite face of the simplex is at least 0.25*DELTA, as
510
+ # specified in (14) of the COBYLA paper. Correspondingly, JDROP_GEO is set to the index of
511
+ # the vertex with the largest distance to the pole provided that the distance is larger than
512
+ # 2.1*DELTA, or the vertex with the smallest distance to the opposite face of the simplex,
513
+ # in which case the distance must be less than 0.25*DELTA, as the current simplex does not
514
+ # have acceptable geometry (see (15)--(16) of the COBYLA paper). Once JDROP_GEO is set, the
515
+ # algorithm replaces SIM(:, JDROP_GEO) with D specified in (17) of the COBYLA paper, which
516
+ # is orthogonal to the face opposite to SIM(:, JDROP_GEO) and has a length of 0.5*DELTA,
517
+ # intending to improve the geometry of the simplex as per (14).
518
+ # 4. Powell's geometry-improving procedure outlined above has an intrinsic flaw: it may lead
519
+ # to infinite cycling, as was observed in a test on 20240320. In this test, the geometry-
520
+ # improving point introduced in the previous iteration was replaced with the trust-region
521
+ # trial point in the current iteration, which was then replaced with the same geometry-
522
+ # improving point in the next iteration, and so on. In this process, the simplex alternated
523
+ # between two configurations, neither of which had acceptable geometry. Thus RHO was never
524
+ # reduced, leading to infinite cycling. (N.B.: Our implementation uses DELTA as the trust
525
+ # region radius, with RHO being its lower bound. When the infinite cycling occurred in this
526
+ # test, DELTA = RHO and it could not be reduced due to the requirement that DELTA >= RHO.)
527
+ jdrop_geo = np.argmax(
528
+ primasum(primapow2(sim[:, :num_vars]), axis=0), axis=0
529
+ )
530
+
531
+ # Calculate the geometry step D.
532
+ delbar = delta / 2
533
+ d = geostep(jdrop_geo, amat, bvec, conmat, cpen, cval, delbar, fval, simi)
534
+
535
+ # Calculate the next value of the objective and constraint functions.
536
+ # If X is close to one of the points in the interpolation set, then we do not evaluate the
537
+ # objective and constraints at X, assuming them to have the values at the closest point.
538
+ # N.B.:
539
+ # 1. If this happens, do NOT include X into the filter, as F and CONSTR are inaccurate.
540
+ # 2. In precise arithmetic, the geometry improving step ensures that the distance between X
541
+ # and any interpolation point is at least DELBAR, yet X may be close to them due to
542
+ # rounding. In an experiment with single precision on 20240317, X = SIM(:, N+1) occurred.
543
+ x = sim[:, num_vars] + d
544
+ distsq[num_vars] = primasum(primapow2(x - sim[:, num_vars]))
545
+ distsq[:num_vars] = primasum(
546
+ primapow2(
547
+ x.reshape(num_vars, 1)
548
+ - (sim[:, num_vars].reshape(num_vars, 1) + sim[:, :num_vars])
549
+ ),
550
+ axis=0,
551
+ )
552
+ j = np.argmin(distsq)
553
+ if distsq[j] <= primapow2(1e-4 * rhoend):
554
+ f = fval[j]
555
+ constr = conmat[:, j]
556
+ cstrv = cval[j]
557
+ else:
558
+ # Evaluate the objective and constraints at X, taking care of possible
559
+ # inf/nan values.
560
+ f, constr = evaluate(calcfc, x, m_nlcon, amat, bvec)
561
+ cstrv = np.max(np.append(0, constr))
562
+ nf += 1
563
+ # Save X, F, CONSTR, CSTRV into the history.
564
+ savehist(maxhist, x, xhist, f, fhist, cstrv, chist, constr, conhist)
565
+ # Save X, F, CONSTR, CSTRV into the filter.
566
+ nfilt, cfilt, ffilt, xfilt, confilt = savefilt(
567
+ cstrv,
568
+ ctol,
569
+ cweight,
570
+ f,
571
+ x,
572
+ nfilt,
573
+ cfilt,
574
+ ffilt,
575
+ xfilt,
576
+ constr,
577
+ confilt,
578
+ )
579
+
580
+ # Print a message about the function/constraint evaluation according to iprint
581
+ fmsg(solver, "Geometry", iprint, nf, delta, f, x, cstrv, constr)
582
+ # Update SIM, SIMI, FVAL, CONMAT, and CVAL so that SIM(:, JDROP_GEO) is replaced with D.
583
+ sim, simi, fval, conmat, cval, subinfo = updatexfc(
584
+ jdrop_geo, constr, cpen, cstrv, d, f, conmat, cval, fval, sim, simi
585
+ )
586
+ # Check whether to break due to damaging rounding in UPDATEXFC
587
+ if subinfo == DAMAGING_ROUNDING:
588
+ info = subinfo
589
+ break # Better action to take? Geometry step, or simply continue?
590
+
591
+ # Check whether to break due to maxfun, ftarget, etc.
592
+ subinfo = checkbreak_con(maxfun, nf, cstrv, ctol, f, ftarget, x)
593
+ if subinfo != INFO_DEFAULT:
594
+ info = subinfo
595
+ break
596
+ # end of if improve_geo. The procedure of improving the geometry ends.
597
+
598
+ # The calculations with the current RHO are complete. Enhance the resolution of the algorithm
599
+ # by reducing RHO; update DELTA and CPEN at the same time.
600
+ if reduce_rho:
601
+ if rho <= rhoend:
602
+ info = SMALL_TR_RADIUS
603
+ break
604
+ delta = max(0.5 * rho, redrho(rho, rhoend))
605
+ rho = redrho(rho, rhoend)
606
+ # THe second (out of two) updates of CPEN, where CPEN decreases or remains the same.
607
+ # Powell's code: cpen = min(cpen, fcratio(fval, conmat)), which may set CPEN to 0.
608
+ cpen = np.maximum(cpenmin, np.minimum(cpen, fcratio(conmat, fval)))
609
+ # Print a message about the reduction of rho according to iprint
610
+ rhomsg(
611
+ solver,
612
+ iprint,
613
+ nf,
614
+ fval[num_vars],
615
+ rho,
616
+ sim[:, num_vars],
617
+ cval[num_vars],
618
+ conmat[:, num_vars],
619
+ cpen,
620
+ )
621
+ conmat, cval, fval, sim, simi, subinfo = updatepole(
622
+ cpen, conmat, cval, fval, sim, simi
623
+ )
624
+ # Check whether to break due to damaging rounding detected in updatepole
625
+ if subinfo == DAMAGING_ROUNDING:
626
+ info = subinfo
627
+ break # Better action to take? Geometry step, or simply continue?
628
+ # End of if reduce_rho. The procedure of reducing RHO ends.
629
+ # Report the current best value, and check if user asks for early termination.
630
+ if callback:
631
+ terminate = callback(
632
+ sim[:, num_vars],
633
+ fval[num_vars],
634
+ nf,
635
+ tr,
636
+ cval[num_vars],
637
+ conmat[:, num_vars],
638
+ )
639
+ if terminate:
640
+ info = CALLBACK_TERMINATE
641
+ break
642
+ # End of for loop. The iterative procedure ends
643
+
644
+ # Return from the calculation, after trying the last trust-region step if it has not been tried yet.
645
+ # Ensure that D has not been updated after SHORTD == TRUE occurred, or the code below is incorrect.
646
+ x = sim[:, num_vars] + d
647
+ if (
648
+ info == SMALL_TR_RADIUS
649
+ and shortd
650
+ and norm(x - sim[:, num_vars]) > 1.0e-3 * rhoend
651
+ and nf < maxfun
652
+ ):
653
+ # Zaikun 20230615: UPDATEXFC or UPDATEPOLE is not called since the last trust-region step. Hence
654
+ # SIM[:, NUM_VARS] remains unchanged. Otherwise SIM[:, NUM_VARS] + D would not make sense.
655
+ f, constr = evaluate(calcfc, x, m_nlcon, amat, bvec)
656
+ cstrv = np.max(np.append(0, constr))
657
+ nf += 1
658
+ savehist(maxhist, x, xhist, f, fhist, cstrv, chist, constr, conhist)
659
+ nfilt, cfilt, ffilt, xfilt, confilt = savefilt(
660
+ cstrv, ctol, cweight, f, x, nfilt, cfilt, ffilt, xfilt, constr, confilt
661
+ )
662
+ # Zaikun 20230512: DELTA has been updated. RHO is only indicative here. TO BE IMPROVED.
663
+ fmsg(solver, "Trust region", iprint, nf, rho, f, x, cstrv, constr)
664
+
665
+ # Return the best calculated values of the variables
666
+ # N.B.: SELECTX and FINDPOLE choose X by different standards, one cannot replace the other.
667
+ kopt = selectx(ffilt[:nfilt], cfilt[:nfilt], max(cpen, cweight), ctol)
668
+ x = xfilt[:, kopt]
669
+ f = ffilt[kopt]
670
+ constr = confilt[:, kopt]
671
+ cstrv = cfilt[kopt]
672
+
673
+ # Print a return message according to IPRINT.
674
+ retmsg(solver, info, iprint, nf, f, x, cstrv, constr)
675
+ return x, f, constr, cstrv, nf, xhist, fhist, chist, conhist, info
676
+
677
+
678
+ def getcpen(amat, bvec, conmat, cpen, cval, delta, fval, rho, sim, simi):
679
+ """
680
+ This function gets the penalty parameter CPEN so that PREREM = PREREF + CPEN * PREREC > 0.
681
+ See the discussions around equation (9) of the COBYLA paper.
682
+ """
683
+
684
+ # Even after nearly all of the pycutest problems were showing nearly bit for bit
685
+ # identical results between Python and the Fortran bindings, HS102 was still off by
686
+ # more than machine epsilon. It turned out to be due to the fact that getcpen was
687
+ # modifying fval, among other. It just goes to show that even when you're nearly
688
+ # perfect, you can still have non trivial bugs.
689
+ conmat = conmat.copy()
690
+ cval = cval.copy()
691
+ fval = fval.copy()
692
+ sim = sim.copy()
693
+ simi = simi.copy()
694
+
695
+ # Intermediate variables
696
+ A = np.zeros((np.size(sim, 0), np.size(conmat, 0)))
697
+ itol = 1
698
+
699
+ # Sizes
700
+ m_lcon = np.size(bvec) if bvec is not None else 0
701
+ num_constraints = np.size(conmat, 0)
702
+ num_vars = np.size(sim, 0)
703
+
704
+ # Preconditions
705
+ if DEBUGGING:
706
+ assert num_constraints >= 0
707
+ assert num_vars >= 1
708
+ assert cpen > 0
709
+ assert (
710
+ np.size(conmat, 0) == num_constraints and np.size(conmat, 1) == num_vars + 1
711
+ )
712
+ assert not (np.isnan(conmat) | np.isneginf(conmat)).any()
713
+ assert np.size(cval) == num_vars + 1 and not any(
714
+ cval < 0 | np.isnan(cval) | np.isposinf(cval)
715
+ )
716
+ assert np.size(fval) == num_vars + 1 and not any(
717
+ np.isnan(fval) | np.isposinf(fval)
718
+ )
719
+ assert np.size(sim, 0) == num_vars and np.size(sim, 1) == num_vars + 1
720
+ assert np.isfinite(sim).all()
721
+ assert all(np.max(abs(sim[:, :num_vars]), axis=0) > 0)
722
+ assert np.size(simi, 0) == num_vars and np.size(simi, 1) == num_vars
723
+ assert np.isfinite(simi).all()
724
+ assert isinv(sim[:, :num_vars], simi, itol)
725
+ assert delta >= rho and rho > 0
726
+
727
+ # ====================#
728
+ # Calculation starts #
729
+ # ====================#
730
+
731
+ # Initialize INFO which is needed in the postconditions
732
+ info = INFO_DEFAULT
733
+
734
+ # Increase CPEN if necessary to ensure PREREM > 0. Branch back for the next loop
735
+ # if this change alters the optimal vertex of the current simplex.
736
+ # Note the following:
737
+ # 1. In each loop, CPEN is changed only if PREREC > 0 > PREREF, in which case
738
+ # PREREM is guaranteed positive after the update. Note that PREREC >= 0 and
739
+ # max(PREREC, PREREF) > 0 in theory. If this holds numerically as well then CPEN
740
+ # is not changed only if PREREC = 0 or PREREF >= 0, in which case PREREM is
741
+ # currently positive, explaining why CPEN needs no update.
742
+ # 2. Even without an upper bound for the loop counter, the loop can occur at most
743
+ # NUM_VARS+1 times. This is because the update of CPEN does not decrease CPEN,
744
+ # and hence it can make vertex J (J <= NUM_VARS) become the new optimal vertex
745
+ # only if CVAL[J] is less than CVAL[NUM_VARS], which can happen at most NUM_VARS
746
+ # times. See the paragraph below (9) in the COBYLA paper. After the "correct"
747
+ # optimal vertex is found, one more loop is needed to calculate CPEN, and hence
748
+ # the loop can occur at most NUM_VARS+1 times.
749
+ for iter in range(num_vars + 1):
750
+ # Switch the best vertex of the current simplex to SIM[:, NUM_VARS]
751
+ conmat, cval, fval, sim, simi, info = updatepole(
752
+ cpen, conmat, cval, fval, sim, simi
753
+ )
754
+ # Check whether to exit due to damaging rounding in UPDATEPOLE
755
+ if info == DAMAGING_ROUNDING:
756
+ break
757
+
758
+ # Calculate the linear approximations to the objective and constraint functions.
759
+ g = matprod(fval[:num_vars] - fval[num_vars], simi)
760
+ A[:, :m_lcon] = amat.T if amat is not None else amat
761
+ A[:, m_lcon:] = matprod(
762
+ (
763
+ conmat[m_lcon:, :num_vars]
764
+ - np.tile(conmat[m_lcon:, num_vars], (num_vars, 1)).T
765
+ ),
766
+ simi,
767
+ ).T
768
+
769
+ # Calculate the trust-region trial step D. Note that D does NOT depend on CPEN.
770
+ d = trstlp(A, -conmat[:, num_vars], delta, g)
771
+
772
+ # Predict the change to F (PREREF) and to the constraint violation (PREREC) due
773
+ # to D.
774
+ preref = -inprod(d, g) # Can be negative
775
+ prerec = cval[num_vars] - np.max(
776
+ np.append(0, conmat[:, num_vars] + matprod(d, A))
777
+ )
778
+
779
+ # PREREC <= 0 or PREREF >=0 or either is NaN
780
+ if not (prerec > 0 and preref < 0):
781
+ break
782
+
783
+ # Powell's code defines BARMU = -PREREF / PREREC, and CPEN is increased to
784
+ # 2*BARMU if and only if it is currently less than 1.5*BARMU, a very
785
+ # "Powellful" scheme. In our implementation, however, we set CPEN directly to
786
+ # the maximum between its current value and 2*BARMU while handling possible
787
+ # overflow. The simplifies the scheme without worsening the performance of
788
+ # COBYLA.
789
+ cpen = max(cpen, min(-2 * preref / prerec, REALMAX))
790
+
791
+ if findpole(cpen, cval, fval) == num_vars:
792
+ break
793
+
794
+ # ==================#
795
+ # Calculation ends #
796
+ # ==================#
797
+
798
+ # Postconditions
799
+ if DEBUGGING:
800
+ assert cpen >= cpen and cpen > 0
801
+ assert (
802
+ preref + cpen * prerec > 0
803
+ or info == DAMAGING_ROUNDING
804
+ or not (prerec >= 0 and np.maximum(prerec, preref) > 0)
805
+ or not np.isfinite(preref)
806
+ )
807
+
808
+ return cpen
809
+
810
+
811
+ def fcratio(conmat, fval):
812
+ """
813
+ This function calculates the ratio between the "typical change" of F and that of CONSTR.
814
+ See equations (12)-(13) in Section 3 of the COBYLA paper for the definition of the ratio.
815
+ """
816
+
817
+ # Preconditions
818
+ if DEBUGGING:
819
+ assert np.size(fval) >= 1
820
+ assert np.size(conmat, 1) == np.size(fval)
821
+
822
+ # ====================#
823
+ # Calculation starts #
824
+ # ====================#
825
+
826
+ cmin = np.min(-conmat, axis=1)
827
+ cmax = np.max(-conmat, axis=1)
828
+ fmin = min(fval)
829
+ fmax = max(fval)
830
+ if any(cmin < 0.5 * cmax) and fmin < fmax:
831
+ denom = np.min(
832
+ np.maximum(cmax, 0) - cmin, where=cmin < 0.5 * cmax, initial=np.inf
833
+ )
834
+ # Powell mentioned the following alternative in section 4 of his COBYLA paper. According to a test
835
+ # on 20230610, it does not make much difference to the performance.
836
+ # denom = np.max(max(*cmax, 0) - cmin, mask=(cmin < 0.5 * cmax))
837
+ r = (fmax - fmin) / denom
838
+ else:
839
+ r = 0
840
+
841
+ # ==================#
842
+ # Calculation ends #
843
+ # ==================#
844
+
845
+ # Postconditions
846
+ if DEBUGGING:
847
+ assert r >= 0
848
+
849
+ return r