forked from emmt/Algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bobyqa.c
2926 lines (2704 loc) · 80.9 KB
/
bobyqa.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* bobyqa.c -
*
* Implementation of Mike Powell's BOBYQA algorithm for minimizing a function
* of many variables. The method is "derivatives free" (only the function
* values are needed) and accounts for bound constraints on the variables. The
* algorithm is described in:
*
* M.J.D. Powell, "The BOBYQA Algorithm for Bound Constrained Optimization
* Without Derivatives." Technical report, Department of Applied Mathematics
* and Theoretical Physics, University of Cambridge (2009).
*
* The present code is based on the original FORTRAN version written by Mike
* Powell who kindly provides his code on demand (at [email protected]) and has
* been converted to C by É. Thiébaut.
*
* Copyright (c) 2009, Mike Powell (FORTRAN version).
* Copyright (c) 2015, Éric Thiébaut (C version).
*
* Read the accompanying `LICENSE` file for details.
*/
#include <stdio.h>
#include <math.h>
#include "bobyqa.h"
#define OUTPUT stdout
/* Macros to deal with single/double precision. */
#undef REAL
#ifdef SINGLE_PRECISION
# define FLT(x) x ## f /* floating point literal constant */
# define REAL float
# define ABS(x) fabsf(x)
# define SQRT(x) sqrtf(x)
# define HYPOT(x) hypotf(x)
# define LOG(x) logf(x)
# define EXP(x) expf(x)
# define SIN(x) sinf(x)
# define COS(x) cosf(x)
# define TAN(x) tanf(x)
# define ASIN(x) asinf(x)
# define ACOS(x) acosf(x)
# define ATAN(x) atanf(x)
#else
# define FLT(x) x /* floating point literal constant */
# define REAL double
# define ABS(x) fabs(x)
# define SQRT(x) sqrt(x)
# define HYPOT(x) hypot(x)
# define LOG(x) log(x)
# define EXP(x) exp(x)
# define SIN(x) sin(x)
# define COS(x) cos(x)
# define TAN(x) tan(x)
# define ASIN(x) asin(x)
# define ACOS(x) acos(x)
# define ATAN(x) atan(x)
#endif
/* Helper macro for simple FORTRAN-like loops. */
#define LOOP(var,num) for (var = 1; var <= num; ++var)
/* Macros yielding the min/max of two values. */
#define MAX(a,b) ((a) >= (b) ? (a) : (b))
#define MIN(a,b) ((a) <= (b) ? (a) : (b))
/* Macro `HOW_MANY(a,b)` yields the minimal number of chunks with `b` elements
needed to store `a` elements. Both `a` and `b` must be integers. */
#define HOW_MANY(a,b) ((((b) - 1) + (a))/(b))
/* Macro `ROUND_UP(a,b)` yields the integer `a` rounded up to a multiple of
integer `b`. */
#define ROUND_UP(a,b) (HOW_MANY(a,b)*(b))
/* Macro `ADDRESS(type,base,offset)` yields a `type*` address at `offset` (in
bytes) from `base` address. */
#define ADDRESS(type, base, offset) ((type*)((char*)(base) + (offset)))
/* Macro `OFFSET(type, field)` yields the offset (in bytes) of member `field`
in structure/class `type`. */
#ifdef offsetof
# define OFFSET(type, field) offsetof(type, field)
#else
# define OFFSET(type, field) ((char*)&((type*)0)->field - (char*)0)
#endif
/*---------------------------------------------------------------------------*/
/* DECLARATIONS OF PRIVATE FUNCTIONS */
static void
print_error(const char* reason);
static void
print_x(FILE* output, const INTEGER n, const REAL x[], const REAL dx[]);
static int
bobyqb(const INTEGER n, const INTEGER npt,
bobyqa_objfun* objfun, void* data,
REAL* x, const REAL* xl, const REAL* xu,
const REAL rhobeg, const REAL rhoend,
const INTEGER iprint, const INTEGER maxfun,
REAL* xbase, REAL* xpt, REAL* fval, REAL* xopt,
REAL* gopt, REAL* hq, REAL* pq, REAL* bmat, REAL* zmat,
const INTEGER ndim, REAL* sl, REAL* su, REAL* xnew,
REAL* xalt, REAL* d, REAL* vlag, REAL* w);
static void
altmov(const INTEGER n, const INTEGER npt, REAL xpt[],
REAL* xopt, REAL* bmat, REAL* zmat, const INTEGER ndim,
REAL* sl, REAL* su, const INTEGER kopt, const INTEGER knew,
const REAL adelt, REAL* xnew, REAL* xalt, REAL* alpha,
REAL* cauchy, REAL* glag, REAL* hcol, REAL* w);
static void
prelim(const INTEGER n, const INTEGER npt,
bobyqa_objfun* objfun, void* data,
REAL* x, const REAL* xl, const REAL* xu,
const REAL rhobeg, const INTEGER iprint,
const INTEGER maxfun, REAL* xbase, REAL* xpt, REAL* fval,
REAL* gopt, REAL* hq, REAL* pq, REAL* bmat,
REAL* zmat, const INTEGER ndim, REAL* sl, REAL* su,
INTEGER* nf, INTEGER* kopt);
static void
trsbox(const INTEGER n, const INTEGER npt, REAL* xpt,
REAL* xopt, REAL* gopt, REAL* hq, REAL* pq,
REAL* sl, REAL* su, const REAL delta, REAL* xnew,
REAL* d, REAL* gnew, REAL* xbdi, REAL* s,
REAL* hs, REAL* hred, REAL* dsq, REAL* crvmin);
static void
rescue(const INTEGER n, const INTEGER npt,
bobyqa_objfun* objfun, void* data,
const REAL* xl, const REAL* xu,
const INTEGER iprint, const INTEGER maxfun,
REAL* xbase, REAL* xpt, REAL* fval, REAL* xopt,
REAL* gopt, REAL* hq, REAL* pq, REAL* bmat, REAL* zmat,
const INTEGER ndim, REAL* sl, REAL* su, INTEGER* nf,
const REAL delta, INTEGER* kopt, REAL* vlag, REAL* ptsaux,
REAL* ptsid, REAL* w);
static void
update(const INTEGER n, const INTEGER npt, REAL* bmat,
REAL* zmat, const INTEGER ndim, REAL* vlag, const REAL beta,
const REAL denom, const INTEGER knew, REAL* w);
/*---------------------------------------------------------------------------*/
/* TESTING */
static REAL
objfun_test(const INTEGER n, const REAL* x, void* data);
#ifdef TESTING
int
main(int argc, char* argv[])
{
bobyqa_test();
return 0;
}
#ifdef FORTRAN_NAME
int
FORTRAN_NAME(calfun,CALFUN)(const INTEGER* n, REAL* x, REAL* f)
{
*f = objfun_test(*n, x, NULL);
return 0;
}
#endif /* FORTRAN_NAME */
#endif
void
bobyqa_test(void)
{
/* Constants. */
const REAL twopi = 2.0*M_PI;
/* Local variables. */
REAL bdl, bdu, rhobeg, rhoend, temp;
REAL w[500000], x[100], xl[100], xu[100];
INTEGER i, iprint, j, jcase, m, maxfun;
bdl = -1.0;
bdu = 1.0;
iprint = 2;
maxfun = 500000;
rhobeg = 0.1;
rhoend = 1e-6;
for (m = 5; m <= 10; m += m) {
REAL q = twopi/(REAL)m;
INTEGER n = 2*m;
LOOP(i,n) {
xl[i - 1] = bdl;
xu[i - 1] = bdu;
}
for (jcase = 1; jcase <= 2; ++jcase) {
INTEGER npt = n + 6;
if (jcase == 2) {
npt = 2*n + 1;
}
fprintf(OUTPUT,
"\n\n 2D output with M =%4ld, N =%4ld and NPT =%4ld\n",
(long)m, (long)n, (long)npt);
LOOP(j,m) {
temp = ((REAL)j)*q;
x[2*j - 2] = COS(temp);
x[2*j - 1] = SIN(temp);
}
bobyqa(n, npt, objfun_test, NULL, x, xl, xu, rhobeg, rhoend,
iprint, maxfun, w);
}
}
}
static REAL
objfun_test(const INTEGER n, const REAL* x, void* data)
{
INTEGER i;
REAL f, temp, tempa, tempb;
f = 0.0;
for (i = 4; i <= n; i += 2) {
INTEGER j, j1 = i - 2;
for (j = 2; j <= j1; j += 2) {
tempa = x[i - 2] - x[j - 2];
tempb = x[i - 1] - x[j - 1];
temp = tempa*tempa + tempb*tempb;
temp = MAX(temp,1e-6);
f += 1.0/SQRT(temp);
}
}
return f;
}
/*---------------------------------------------------------------------------*/
/* BOBYQA DRIVER ROUTINES */
int
bobyqa(const INTEGER n, const INTEGER npt,
bobyqa_objfun* objfun, void* data,
REAL* x, const REAL* xl, const REAL* xu,
const REAL rhobeg, const REAL rhoend,
const INTEGER iprint, const INTEGER maxfun, REAL* w)
{
/* Constants. */
const REAL zero = 0.0;
/* Local variables. */
REAL temp, tempa, tempb;
INTEGER ibmat, id, ifv, igo, ihq, ipq, isl, isu, ivl, iw, ixa, ixb, ixn,
ixo, ixp, izmat, j, jsl, jsu, ndim, np;
/* Parameter adjustments to comply with FORTRAN indexing. */
w -= 1;
xu -= 1;
xl -= 1;
x -= 1;
/* Return if the value of NPT is unacceptable. */
np = n + 1;
if (npt < n + 2 || npt > (n + 2)*np/2) {
print_error("NPT is not in the required interval");
return BOBYQA_BAD_NPT;
}
/* Partition the working space array, so that different parts of it can
be treated separately during the calculation of BOBYQB. The partition
requires the first (NPT+2)*(NPT+N)+3*N*(N+5)/2 elements of W plus the
space that is taken by the last array in the argument list of
BOBYQB. */
ndim = npt + n;
ixb = 1;
ixp = ixb + n;
ifv = ixp + n*npt;
ixo = ifv + npt;
igo = ixo + n;
ihq = igo + n;
ipq = ihq + n*np/2;
ibmat = ipq + npt;
izmat = ibmat + ndim*n;
isl = izmat + npt*(npt - np);
isu = isl + n;
ixn = isu + n;
ixa = ixn + n;
id = ixa + n;
ivl = id + n;
iw = ivl + ndim;
/* Return if there is insufficient space between the bounds. Modify the
initial X if necessary in order to avoid conflicts between the bounds and
the construction of the first quadratic model. The lower and upper bounds
on moves from the updated X are set now, in the ISL and ISU partitions of
W, in order to provide useful and exact information about components of X
that become within distance RHOBEG from their bounds. */
LOOP(j,n) {
temp = xu[j] - xl[j];
if (temp < rhobeg + rhobeg) {
print_error("one of the differences XU(I)-XL(I) is less than 2*RHOBEG");
return BOBYQA_TOO_CLOSE;
}
jsl = isl + j - 1;
jsu = jsl + n;
w[jsl] = xl[j] - x[j];
w[jsu] = xu[j] - x[j];
if (w[jsl] >= -rhobeg) {
if (w[jsl] >= zero) {
x[j] = xl[j];
w[jsl] = zero;
w[jsu] = temp;
} else {
x[j] = xl[j] + rhobeg;
w[jsl] = -rhobeg;
temp = xu[j] - x[j];
w[jsu] = MAX(temp,rhobeg);
}
} else if (w[jsu] <= rhobeg) {
if (w[jsu] <= zero) {
x[j] = xu[j];
w[jsl] = -temp;
w[jsu] = zero;
} else {
x[j] = xu[j] - rhobeg;
tempa = xl[j] - x[j];
tempb = -rhobeg;
w[jsl] = MIN(tempa,tempb);
w[jsu] = rhobeg;
}
}
}
/* Make the call of BOBYQB. */
return bobyqb(n, npt, objfun, data, &x[1], &xl[1], &xu[1],
rhobeg, rhoend, iprint, maxfun,
&w[ixb], &w[ixp], &w[ifv], &w[ixo], &w[igo],
&w[ihq], &w[ipq], &w[ibmat], &w[izmat],
ndim, &w[isl], &w[isu], &w[ixn], &w[ixa],
&w[id], &w[ivl], &w[iw]);
} /* bobyqa */
/*---------------------------------------------------------------------------*/
/* FORTRAN SUPPORT */
#ifdef FORTRAN_NAME
REAL
bobyqa_calfun_wrapper(const INTEGER n, const REAL* x, void* data)
{
REAL f;
FORTRAN_NAME(calfun,CALFUN)(&n, (REAL*)x, &f);
return f;
}
int
FORTRAN_NAME(bobyqa,BOBYQA)(const INTEGER* n, const INTEGER* npt,
REAL* x, const REAL* xl, const REAL* xu,
const REAL* rhobeg, const REAL* rhoend,
const INTEGER* iprint, const INTEGER* maxfun,
REAL* w)
{
bobyqa(*n, *npt, bobyqa_calfun_wrapper, NULL, x, xl, xu,
*rhobeg, *rhoend, *iprint, *maxfun, w);
return 0;
}
#endif /* FORTRAN_NAME */
/*---------------------------------------------------------------------------*/
/* BOBYQA SUBROUTINES */
static int
bobyqb(const INTEGER n, const INTEGER npt,
bobyqa_objfun* objfun, void* data,
REAL* x, const REAL* xl, const REAL* xu,
const REAL rhobeg, const REAL rhoend,
const INTEGER iprint, const INTEGER maxfun,
REAL* xbase, REAL* xpt, REAL* fval, REAL* xopt,
REAL* gopt, REAL* hq, REAL* pq, REAL* bmat, REAL* zmat,
const INTEGER ndim, REAL* sl, REAL* su, REAL* xnew,
REAL* xalt, REAL* d, REAL* vlag, REAL* w)
{
/* The arguments N, NPT, X, XL, XU, RHOBEG, RHOEND, IPRINT and MAXFUN are
identical to the corresponding arguments in SUBROUTINE BOBYQA.
XBASE holds a shift of origin that should reduce the contributions from
rounding errors to values of the model and Lagrange functions.
XPT is a two-dimensional array that holds the coordinates of the
interpolation points relative to XBASE.
FVAL holds the values of F at the interpolation points.
XOPT is set to the displacement from XBASE of the trust region centre.
GOPT holds the gradient of the quadratic model at XBASE+XOPT.
HQ holds the explicit second derivatives of the quadratic model.
PQ contains the parameters of the implicit second derivatives of the
quadratic model.
BMAT holds the last N columns of H.
ZMAT holds the factorization of the leading NPT by NPT submatrix of H,
this factorization being ZMAT times ZMAT^T, which provides both the
correct rank and positive semi-definiteness.
NDIM is the first dimension of BMAT and has the value NPT+N.
SL and SU hold the differences XL-XBASE and XU-XBASE, respectively. All
the components of every XOPT are going to satisfy the bounds
SL(I) <= XOPT(I) <= SU(I), with appropriate equalities when XOPT is on a
constraint boundary.
XNEW is chosen by SUBROUTINE TRSBOX or ALTMOV. Usually XBASE+XNEW is the
vector of variables for the next call of OBJFUN. XNEW also satisfies the
SL and SU constraints in the way that has just been mentioned.
XALT is an alternative to XNEW, chosen by ALTMOV, that may replace XNEW in
order to increase the denominator in the updating of UPDATE.
D is reserved for a trial step from XOPT, which is usually XNEW-XOPT.
VLAG contains the values of the Lagrange functions at a new point X. They
are part of a product that requires VLAG to be of length NDIM.
W is a one-dimensional array that is used for working space. Its length
must be at least 3*NDIM = 3*(NPT+N). */
/* Constants. */
const REAL half = 0.5;
const REAL one = 1.0;
const REAL ten = 10.0;
const REAL tenth = 0.1;
const REAL two = 2.0;
const REAL zero = 0.0;
/* Local variables. */
REAL adelt, alpha, bdtest, bdtol, beta, biglsq, bsum, cauchy, crvmin,
curv, delsq, delta, den, denom, densav, diff, diffa, diffb, diffc,
dist, distsq, dnorm, dsq, dx, errbig, f, fopt, fracsq, frhosq, fsave,
gisq, gqsq, hdiag, pqold, ratio, rho, scaden, sum, suma, sumb, sumpq,
sumw, sumz, temp, tempa, tempb, vquad, xoptsq;
INTEGER i, ih, ip, itest, j, jj, jp, k, kbase, knew, kopt, ksav, nf,
nfsav, nh, np, nptm, nresc, ntrits;
int status = BOBYQA_SUCCESS;
const char* reason = NULL;
/* Parameter adjustments to comply with FORTRAN indexing. */
x -= 1;
xl -= 1;
xu -= 1;
xbase -= 1;
xpt -= 1 + npt;
fval -= 1;
xopt -= 1;
gopt -= 1;
hq -= 1;
pq -= 1;
bmat -= 1 + ndim;
zmat -= 1 + npt;
sl -= 1;
su -= 1;
xnew -= 1;
xalt -= 1;
d -= 1;
vlag -= 1;
w -= 1;
#define XPT(a1,a2) xpt[(a2)*npt + a1]
#define BMAT(a1,a2) bmat[(a2)*ndim + a1]
#define ZMAT(a1,a2) zmat[(a2)*npt + a1]
/* Set uninitialized variables to avoid compiler warnings. */
adelt = zero;
alpha = zero;
cauchy = zero;
denom = zero;
diff = zero;
diffc = zero;
f = zero;
knew = 0;
/* Set some constants. */
np = n + 1;
nptm = npt - np;
nh = n*np/2;
/* The call of PRELIM sets the elements of XBASE, XPT, FVAL, GOPT, HQ, PQ,
BMAT and ZMAT for the first iteration, with the corresponding values of of
NF and KOPT, which are the number of calls of OBJFUN so far and the index
of the interpolation point at the trust region centre. Then the initial
XOPT is set too. The branch to label 720 occurs if MAXFUN is less than
NPT. GOPT will be updated if KOPT is different from KBASE. */
prelim(n, npt, objfun, data, &x[1], &xl[1], &xu[1], rhobeg, iprint, maxfun,
&xbase[1], &XPT(1,1), &fval[1], &gopt[1], &hq[1], &pq[1], &BMAT(1,1),
&ZMAT(1,1), ndim, &sl[1], &su[1], &nf, &kopt);
xoptsq = zero;
LOOP(i,n) {
xopt[i] = XPT(kopt,i);
xoptsq += xopt[i]*xopt[i];
}
fsave = fval[1];
if (nf < npt) {
goto too_many_evaluations;
}
kbase = 1;
/* Complete the settings that are required for the iterative procedure. */
rho = rhobeg;
delta = rho;
nresc = nf;
ntrits = 0;
diffa = zero;
diffb = zero;
itest = 0;
nfsav = nf;
/* Update GOPT if necessary before the first iteration and after each
call of RESCUE that makes a call of OBJFUN. */
L20:
if (kopt != kbase) {
ih = 0;
LOOP(j,n) {
LOOP(i,j) {
++ih;
if (i < j) {
gopt[j] += hq[ih]*xopt[i];
}
gopt[i] += hq[ih]*xopt[j];
}
}
if (nf > npt) {
LOOP(k,npt) {
temp = zero;
LOOP(j,n) {
temp += XPT(k,j)*xopt[j];
}
temp = pq[k]*temp;
LOOP(i,n) {
gopt[i] += temp*XPT(k,i);
}
}
}
}
/* Generate the next point in the trust region that provides a small value
of the quadratic model subject to the constraints on the variables.
The integer NTRITS is set to the number "trust region" iterations that
have occurred since the last "alternative" iteration. If the length
of XNEW-XOPT is less than HALF*RHO, however, then there is a branch to
label 650 or 680 with NTRITS=-1, instead of calculating F at XNEW. */
L60:
trsbox(n, npt, &XPT(1,1), &xopt[1], &gopt[1], &hq[1], &pq[1],
&sl[1], &su[1], delta, &xnew[1], &d[1], &w[1], &w[np],
&w[np + n], &w[np + 2*n], &w[np + n*3], &dsq, &crvmin);
dnorm = SQRT(dsq);
dnorm = MIN(dnorm,delta);
if (dnorm < half*rho) {
ntrits = -1;
tempa = ten*rho;
distsq = tempa*tempa;
if (nf <= nfsav + 2) {
goto L650;
}
/* The following choice between labels 650 and 680 depends on whether or
not our work with the current RHO seems to be complete. Either RHO is
decreased or termination occurs if the errors in the quadratic model at
the last three interpolation points compare favourably with predictions
of likely improvements to the model within distance HALF*RHO of XOPT. */
errbig = MAX(diffa,diffb);
errbig = MAX(errbig,diffc);
frhosq = rho*0.125*rho;
if (crvmin > zero && errbig > frhosq*crvmin) {
goto L650;
}
bdtol = errbig/rho;
LOOP(j,n) {
bdtest = bdtol;
if (xnew[j] == sl[j]) {
bdtest = w[j];
}
if (xnew[j] == su[j]) {
bdtest = -w[j];
}
if (bdtest < bdtol) {
curv = hq[(j + j*j)/2];
LOOP(k,npt) {
curv += pq[k]*(XPT(k,j)*XPT(k,j));
}
bdtest += half*curv*rho;
if (bdtest < bdtol) {
goto L650;
}
}
}
goto L680;
}
++ntrits;
/* Severe cancellation is likely to occur if XOPT is too far from XBASE.
If the following test holds, then XBASE is shifted so that XOPT becomes
zero. The appropriate changes are made to BMAT and to the second
derivatives of the current model, beginning with the changes to BMAT
that do not depend on ZMAT. VLAG is used temporarily for working space. */
L90:
if (dsq <= xoptsq*0.001) {
fracsq = xoptsq*0.25;
sumpq = zero;
LOOP(k,npt) {
sumpq += pq[k];
sum = -half*xoptsq;
LOOP(i,n) {
sum += XPT(k,i)*xopt[i];
}
w[npt + k] = sum;
temp = fracsq - half*sum;
LOOP(i,n) {
w[i] = BMAT(k,i);
vlag[i] = sum*XPT(k,i) + temp*xopt[i];
ip = npt + i;
LOOP(j,i) {
BMAT(ip,j) = BMAT(ip,j) + w[i]*vlag[j] + vlag[i]*w[j];
}
}
}
/* Then the revisions of BMAT that depend on ZMAT are calculated. */
LOOP(jj,nptm) {
sumz = zero;
sumw = zero;
LOOP(k,npt) {
sumz += ZMAT(k,jj);
vlag[k] = w[npt + k]*ZMAT(k,jj);
sumw += vlag[k];
}
LOOP(j,n) {
sum = (fracsq*sumz - half*sumw)*xopt[j];
LOOP(k,npt) {
sum += vlag[k]*XPT(k,j);
}
w[j] = sum;
LOOP(k,npt) {
BMAT(k,j) = BMAT(k,j) + sum*ZMAT(k,jj);
}
}
LOOP(i,n) {
ip = i + npt;
temp = w[i];
LOOP(j,i) {
BMAT(ip,j) = BMAT(ip,j) + temp*w[j];
}
}
}
/* The following instructions complete the shift, including the changes to
the second derivative parameters of the quadratic model. */
ih = 0;
LOOP(j,n) {
w[j] = -half*sumpq*xopt[j];
LOOP(k,npt) {
w[j] += pq[k]*XPT(k,j);
XPT(k,j) = XPT(k,j) - xopt[j];
}
LOOP(i,j) {
++ih;
hq[ih] = hq[ih] + w[i]*xopt[j] + xopt[i]*w[j];
BMAT(npt + i, j) = BMAT(npt + j, i);
}
}
LOOP(i,n) {
xbase[i] += xopt[i];
xnew[i] -= xopt[i];
sl[i] -= xopt[i];
su[i] -= xopt[i];
xopt[i] = zero;
}
xoptsq = zero;
}
if (ntrits == 0) {
goto L210;
}
goto L230;
/* XBASE is also moved to XOPT by a call of RESCUE. This calculation is more
expensive than the previous shift, because new matrices BMAT and ZMAT are
generated from scratch, which may include the replacement of interpolation
points whose positions seem to be causing near linear dependence in the
interpolation conditions. Therefore RESCUE is called only if rounding
errors have reduced by at least a factor of two the denominator of the
formula for updating the H matrix. It provides a useful safeguard, but is
not invoked in most applications of BOBYQA. */
L190:
nfsav = nf;
kbase = kopt;
rescue(n, npt, objfun, data, &xl[1], &xu[1], iprint, maxfun, &xbase[1],
&XPT(1,1), &fval[1], &xopt[1], &gopt[1], &hq[1],
&pq[1], &BMAT(1,1), &ZMAT(1,1), ndim, &sl[1], &su[1],
&nf, delta, &kopt, &vlag[1], &w[1], &w[n + np], &w[ndim + np]);
/* XOPT is updated now in case the branch below to label 720 is taken. Any
updating of GOPT occurs after the branch below to label 20, which leads to
a trust region iteration as does the branch to label 60. */
xoptsq = zero;
if (kopt != kbase) {
LOOP(i,n) {
xopt[i] = XPT(kopt,i);
xoptsq += xopt[i]*xopt[i];
}
}
if (nf < 0) {
nf = maxfun;
goto too_many_evaluations;
}
nresc = nf;
if (nfsav < nf) {
nfsav = nf;
goto L20;
}
if (ntrits > 0) {
goto L60;
}
/* Pick two alternative vectors of variables, relative to XBASE, that are
suitable as new positions of the KNEW-th interpolation point. Firstly,
XNEW is set to the point on a line through XOPT and another interpolation
point that minimizes the predicted value of the next denominator, subject
to ||XNEW - XOPT|| <= ADELT and to the SL and SU bounds. Secondly, XALT
is set to the best feasible point on a constrained version of the Cauchy
step of the KNEW-th Lagrange function, the corresponding value of the
square of this function being returned in CAUCHY. The choice between
these alternatives is going to be made when the denominator is
calculated. */
L210:
altmov(n, npt, &XPT(1,1), &xopt[1], &BMAT(1,1), &ZMAT(1,1), ndim,
&sl[1], &su[1], kopt, knew, adelt, &xnew[1], &xalt[1],
&alpha, &cauchy, &w[1], &w[np], &w[ndim + 1]);
LOOP(i,n) {
d[i] = xnew[i] - xopt[i];
}
/* Calculate VLAG and BETA for the current choice of D. The scalar product
of D with XPT(K,.) is going to be held in W(NPT+K) for use when VQUAD is
calculated. */
L230:
LOOP(k,npt) {
suma = zero;
sumb = zero;
sum = zero;
LOOP(j,n) {
suma += XPT(k,j)*d[j];
sumb += XPT(k,j)*xopt[j];
sum += BMAT(k,j)*d[j];
}
w[k] = suma*(half*suma + sumb);
vlag[k] = sum;
w[npt + k] = suma;
}
beta = zero;
LOOP(jj,nptm) {
sum = zero;
LOOP(k,npt) {
sum += ZMAT(k,jj)*w[k];
}
beta -= sum*sum;
LOOP(k,npt) {
vlag[k] += sum*ZMAT(k,jj);
}
}
dsq = zero;
bsum = zero;
dx = zero;
LOOP(j,n) {
dsq += d[j]*d[j];
sum = zero;
LOOP(k,npt) {
sum += w[k]*BMAT(k,j);
}
bsum += sum*d[j];
jp = npt + j;
LOOP(i,n) {
sum += BMAT(jp,i)*d[i];
}
vlag[jp] = sum;
bsum += sum*d[j];
dx += d[j]*xopt[j];
}
beta = dx*dx + dsq*(xoptsq + dx + dx + half*dsq) + beta - bsum;
vlag[kopt] += one;
/* If NTRITS is zero, the denominator may be increased by replacing the step
D of ALTMOV by a Cauchy step. Then RESCUE may be called if rounding
errors have damaged the chosen denominator. */
if (ntrits == 0) {
denom = vlag[knew]*vlag[knew] + alpha*beta;
if (denom < cauchy && cauchy > zero) {
LOOP(i,n) {
xnew[i] = xalt[i];
d[i] = xnew[i] - xopt[i];
}
cauchy = zero;
goto L230;
}
if (denom <= half*(vlag[knew]*vlag[knew])) {
if (nf > nresc) {
goto L190;
}
goto cancellation_of_denominator;
}
/* Alternatively, if NTRITS is positive, then set KNEW to the index of the
next interpolation point to be deleted to make room for a trust region
step. Again RESCUE may be called if rounding errors have damaged the
chosen denominator, which is the reason for attempting to select KNEW
before calculating the next value of the objective function. */
} else {
delsq = delta*delta;
scaden = zero;
biglsq = zero;
knew = 0;
LOOP(k,npt) {
if (k == kopt) continue;
hdiag = zero;
LOOP(jj,nptm) {
hdiag += ZMAT(k,jj)*ZMAT(k,jj);
}
den = beta*hdiag + vlag[k]*vlag[k];
distsq = zero;
LOOP(j,n) {
tempa = XPT(k,j) - xopt[j];
distsq += tempa*tempa;
}
temp = distsq/delsq;
temp = temp*temp;
temp = MAX(one,temp);
if (temp*den > scaden) {
scaden = temp*den;
knew = k;
denom = den;
}
temp *= vlag[k]*vlag[k];
biglsq = MAX(biglsq,temp);
}
if (scaden <= half*biglsq) {
if (nf > nresc) {
goto L190;
}
goto cancellation_of_denominator;
}
}
/* Put the variables for the next calculation of the objective function in
XNEW, with any adjustments for the bounds.
Calculate the value of the objective function at XBASE+XNEW, unless the
limit on the number of calculations of F has been reached. */
L360:
LOOP(i,n) {
tempa = xbase[i] + xnew[i];
tempa = MAX(tempa,xl[i]);
x[i] = MIN(tempa,xu[i]);
if (xnew[i] == sl[i]) {
x[i] = xl[i];
}
if (xnew[i] == su[i]) {
x[i] = xu[i];
}
}
if (nf >= maxfun) {
goto too_many_evaluations;
}
++nf;
f = objfun(n, &x[1], data);
if (iprint == 3) {
fprintf(OUTPUT,
" Function number%6ld F =%18.10E"
" The corresponding X is:\n",
(long)nf, (double)f);
print_x(OUTPUT, n, &x[1], NULL);
}
if (ntrits == -1) {
fsave = f;
goto done;
}
/* Use the quadratic model to predict the change in F due to the step D, and
set DIFF to the error of this prediction. */
fopt = fval[kopt];
vquad = zero;
ih = 0;
LOOP(j,n) {
vquad += d[j]*gopt[j];
LOOP(i,j) {
++ih;
temp = d[i]*d[j];
if (i == j) {
temp = half*temp;
}
vquad += hq[ih]*temp;
}
}
LOOP(k,npt) {
vquad += half*pq[k]*(w[npt + k]*w[npt + k]);
}
diff = f - fopt - vquad;
diffc = diffb;
diffb = diffa;
diffa = ABS(diff);
if (dnorm > rho) {
nfsav = nf;
}
/* Pick the next value of DELTA after a trust region step. */
if (ntrits > 0) {
if (vquad >= zero) {
goto step_failed;
}
ratio = (f - fopt)/vquad;
if (ratio <= tenth) {
delta *= half;
delta = MIN(delta,dnorm);
} else if (ratio <= 0.7) {
delta *= half;
delta = MAX(delta,dnorm);
} else {
tempa = dnorm + dnorm;
delta *= half;
delta = MAX(delta,tempa);
}
if (delta <= rho*1.5) {
delta = rho;
}
/* Recalculate KNEW and DENOM if the new F is less than FOPT. */
if (f < fopt) {
ksav = knew;
densav = denom;
delsq = delta*delta;
scaden = zero;
biglsq = zero;
knew = 0;
LOOP(k,npt) {
hdiag = zero;
LOOP(jj,nptm) {
hdiag += ZMAT(k,jj)*ZMAT(k,jj);
}
den = beta*hdiag + vlag[k]*vlag[k];
distsq = zero;
LOOP(j,n) {
temp = XPT(k,j) - xnew[j];
distsq += temp*temp;
}
temp = distsq/delsq;
temp = temp*temp;
temp = MAX(one,temp);
if (temp*den > scaden) {
scaden = temp*den;
knew = k;
denom = den;
}
temp *= (vlag[k]*vlag[k]);
biglsq = MAX(biglsq,temp);
}
if (scaden <= half*biglsq) {
knew = ksav;
denom = densav;
}
}
}
/* Update BMAT and ZMAT, so that the KNEW-th interpolation point can be
moved. Also update the second derivative terms of the model. */
update(n, npt, &BMAT(1,1), &ZMAT(1,1), ndim, &vlag[1],
beta, denom, knew, &w[1]);
ih = 0;
pqold = pq[knew];
pq[knew] = zero;
LOOP(i,n) {
temp = pqold*XPT(knew,i);
LOOP(j,i) {
++ih;
hq[ih] += temp*XPT(knew,j);
}
}
LOOP(jj,nptm) {
temp = diff*ZMAT(knew,jj);
LOOP(k,npt) {
pq[k] += temp*ZMAT(k,jj);
}
}
/* Include the new interpolation point, and make the changes to GOPT at the
old XOPT that are caused by the updating of the quadratic model. */
fval[knew] = f;
LOOP(i,n) {
XPT(knew,i) = xnew[i];
w[i] = BMAT(knew,i);