forked from emmt/Algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
newuoa.c
2588 lines (2376 loc) · 61.8 KB
/
newuoa.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
/*
* newuoa.c -
*
* C implemtation of Mike Powell's NEWUOA algorithm for minimizing a function
* of many variables. The method is "derivatives free" (only the function
* values are needed). The algorithm is described in:
*
* M.J.D. Powell, "The NEWUOA software for unconstrained minimization
* without derivatives", in Large-Scale Nonlinear Optimization, editors
* G. Di Pillo and M. Roma, Springer (2006), pages 255-297.
*
* 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) 2004, Mike Powell (FORTRAN version).
* Copyright (c) 2015, Éric Thiébaut (C version).
*
* Read the accompanying `LICENSE` file for details.
*/
/* To re-use as much as the code for the reverse communication routines, we use
a trick which consists in "self-including" this file with different macros
(_NEWUOA_PART1, _NEWUOA_PART2, etc.) defined so as to skip or modify certain
parts of the source file. */
#ifndef _NEWUOA_PART1
#define _NEWUOA_PART1 1
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "newuoa.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, INTEGER n, const REAL x[], const REAL dx[]);
/*---------------------------------------------------------------------------*/
/* TESTING */
static REAL
objfun_test(const INTEGER n, const REAL* x, void* data);
#ifdef TESTING
int
main(int argc, char* argv[])
{
newuoa_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 /* TESTING */
void
newuoa_test(void)
{
REAL rhobeg, rhoend;
REAL w[10000], x[10];
INTEGER i, n, npt, maxfun, iprint;
iprint = 2;
maxfun = 5000;
rhoend = 1e-6;
for (n = 2; n <= 8; n += 2) {
npt = 2*n + 1;
LOOP(i,n) {
x[i - 1] = (REAL)i/(REAL)(n + 1);
}
rhobeg = x[0]*0.2;
fprintf(stdout, "\n\n Results with N =%2d and NPT =%3d\n",
(int)n, (int)npt);
#ifndef FORTRAN_NAME
/* Test the C-code. */
newuoa(n, npt, objfun_test, NULL, x, rhobeg, rhoend, iprint, maxfun, w);
#else
/* Test the FORTRAN wrapper. */
newuoa_(&n, &npt, x, &rhobeg, &rhoend, &iprint, &maxfun, w);
#endif
}
} /* newuoa_test */
/* The Chebyquad test problem (Fletcher, 1965) for N = 2,4,6 and 8,
with NPT = 2N+1. */
static REAL
objfun_test(const INTEGER n, const REAL* x, void* data)
{
REAL sum, f;
INTEGER i, iw, j, np;
REAL y[100];
#define X(a1) x[a1 - 1]
#define Y(a1,a2) y[(a2)*10 + a1 - 11]
LOOP(j,n) {
Y(1,j) = 1.0;
Y(2,j) = X(j)*2.0 - 1.0;
}
for (i = 2; i <= n; ++i) {
LOOP(j,n) {
Y(i+1,j) = Y(2,j)*2.0*Y(i,j) - Y(i-1,j);
}
}
f = 0.0;
np = n + 1;
iw = 1;
LOOP(i,np) {
sum = 0.0;
LOOP(j,n) {
sum += Y(i,j);
}
sum /= (REAL)n;
if (iw > 0) {
sum += 1.0/(REAL)(i*i - 2*i);
}
iw = -iw;
f += sum*sum;
}
return f;
} /* objfun_test */
#undef X
#undef Y
/*---------------------------------------------------------------------------*/
/* NEWUOA DRIVER ROUTINES */
static int
newuob(const INTEGER n, const INTEGER npt,
newuoa_objfun* objfun, void* data,
REAL* x, const REAL rhobeg, const REAL rhoend,
const INTEGER iprint, const INTEGER maxfun,
REAL* xbase, REAL* xopt, REAL* xnew,
REAL* xpt, REAL* fval, REAL* gq, REAL* hq,
REAL* pq, REAL* bmat, REAL* zmat, const INTEGER ndim,
REAL* d, REAL* vlag, REAL* w);
static void
bigden(const INTEGER n, const INTEGER npt, REAL* xopt,
REAL* xpt, REAL* bmat, REAL* zmat, const INTEGER idz,
const INTEGER ndim, const INTEGER kopt, const INTEGER knew, REAL* d,
REAL* w, REAL* vlag, REAL* beta, REAL* s,
REAL* wvec, REAL* prod);
static void
biglag(const INTEGER n, const INTEGER npt, REAL* xopt,
REAL* xpt, REAL* bmat, REAL* zmat, INTEGER* idz,
const INTEGER ndim, const INTEGER knew, const REAL delta, REAL* d,
REAL* alpha, REAL* hcol, REAL* gc, REAL* gd,
REAL* s, REAL* w);
static void
update(const INTEGER n, const INTEGER npt, REAL* bmat,
REAL* zmat, INTEGER* idz, const INTEGER ndim, REAL* vlag,
const REAL beta, const INTEGER knew, REAL* w);
static void
trsapp(const INTEGER n, const INTEGER npt, REAL* xopt,
REAL* xpt, REAL* gq, REAL* hq, REAL* pq,
const REAL delta, REAL* step, REAL* d, REAL* g,
REAL* hd, REAL* hs, REAL* crvmin);
int
newuoa(const INTEGER n, const INTEGER npt,
newuoa_objfun* objfun, void* data,
REAL* x, const REAL rhobeg, const REAL rhoend,
const INTEGER iprint, const INTEGER maxfun,
REAL* w)
{
INTEGER id, np, iw, igq, ihq, ixb, ifv, ipq, ivl, ixn, ixo, ixp, ndim,
nptm, ibmat, izmat;
/* Partition the working space array, so that different parts of it can be
treated separately by the subroutine that performs the main
calculation. */
if (npt < n + 2 || npt > (n + 2)*(n + 1)/2) {
if (iprint > 0) {
print_error("NPT is not in the required interval");
}
return NEWUOA_BAD_NPT;
}
ndim = npt + n;
np = n + 1;
nptm = npt - np;
ixb = 0; /* C-indices start at 0 */
ixo = ixb + n;
ixn = ixo + n;
ixp = ixn + n;
ifv = ixp + n*npt;
igq = ifv + npt;
ihq = igq + n;
ipq = ihq + n*np/2;
ibmat = ipq + npt;
izmat = ibmat + ndim*n;
id = izmat + npt*nptm;
ivl = id + n;
iw = ivl + ndim;
/* The above settings provide a partition of W for subroutine NEWUOB. The
partition requires the first NPT*(NPT+N)+5*N*(N+3)/2 elements of W plus
the space that is needed by the last array of NEWUOB. */
return newuob(n, npt, objfun, data, x, rhobeg, rhoend, iprint, maxfun,
&w[ixb], &w[ixo], &w[ixn], &w[ixp], &w[ifv], &w[igq],
&w[ihq], &w[ipq], &w[ibmat], &w[izmat], ndim, &w[id],
&w[ivl], &w[iw]);
} /* newuoa */
/*---------------------------------------------------------------------------*/
/* FORTRAN SUPPORT */
#ifdef FORTRAN_NAME
REAL
newuoa_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(newuoa,NEWUOA)(const INTEGER* n, const INTEGER* npt, REAL* x,
const REAL* rhobeg, const REAL* rhoend,
const INTEGER* iprint, const INTEGER* maxfun,
REAL* w)
{
newuoa(*n, *npt, newuoa_calfun_wrapper, NULL,
x, *rhobeg, *rhoend, *iprint, *maxfun, w);
return 0;
}
#endif /* FORTRAN_NAME */
/*---------------------------------------------------------------------------*/
/* REVERSE COMMUNICATION VERSION */
struct _newuoa_context {
/* Constants during the iterations. */
INTEGER n;
INTEGER npt;
REAL rhobeg;
REAL rhoend;
INTEGER iprint;
INTEGER maxfun;
/* Floating-point local variables. */
REAL alpha;
REAL beta;
REAL crvmin;
REAL delta;
REAL diff;
REAL diffa;
REAL diffb;
REAL diffc;
REAL dnorm;
REAL dsq;
REAL dstep;
REAL fbeg;
REAL fopt;
REAL gqsq;
REAL ratio;
REAL recip;
REAL reciq;
REAL rho;
REAL rhosq;
REAL vquad;
REAL xipt;
REAL xjpt;
REAL xoptsq;
/* Workspace arrays (all constants). */
REAL* xbase;
REAL* xopt;
REAL* xnew;
REAL* xpt;
REAL* fval;
REAL* gq;
REAL* hq;
REAL* pq;
REAL* bmat;
REAL* zmat;
REAL* d;
REAL* vlag;
REAL* w;
/* Integer local variables. */
INTEGER idz;
INTEGER ih;
INTEGER ipt;
INTEGER itest;
INTEGER jpt;
INTEGER knew;
INTEGER kopt;
INTEGER ksave;
INTEGER nf;
INTEGER nfm;
INTEGER nfmm;
INTEGER nfsav;
/* Record the current state of the algorithm. */
INTEGER nevals;
int status;
};
#define SAVE(var) ctx->var = var
#define RESTORE(var) var = ctx->var
/* FIXME: remove maxfun, replace nevals by nf? */
newuoa_context_t*
newuoa_create(const INTEGER n, const INTEGER npt,
const REAL rhobeg, const REAL rhoend,
const INTEGER iprint, const INTEGER maxfun)
{
INTEGER np, nptm, ndim;
newuoa_context_t* ctx;
size_t size, offset;
/* Check arguments. */
if (n < 0) {
if (iprint > 0) {
print_error("invalid number of variables N");
}
errno = EINVAL;
return NULL;
}
if (npt < n + 2 || npt > (n + 2)*(n + 1)/2) {
if (iprint > 0) {
print_error("NPT is not in the required interval");
}
return NULL;
}
if (rhoend <= 0 || rhoend > rhobeg) {
if (iprint > 0) {
print_error("invalid RHOBEG and/or RHOEND");
}
errno = EINVAL;
return NULL;
}
if (maxfun < 1) {
if (iprint > 0) {
print_error("invalid MAXFUN");
}
errno = EINVAL;
return NULL;
}
/* Allocate memory. */
size = sizeof(newuoa_context_t);
offset = ROUND_UP(size, sizeof(REAL));
size = offset + ((npt+13)*(npt+n)+3*n*(n+3)/2)*sizeof(REAL);
ctx = (newuoa_context_t*)malloc(size);
if (ctx == NULL) {
return NULL;
}
memset(ctx, 0, size);
SAVE(n);
SAVE(npt);
SAVE(rhobeg);
SAVE(rhoend);
SAVE(iprint);
SAVE(maxfun);
/* Partition the working space array, so that different parts of it can be
treated separately by the subroutine that performs the main
calculation. */
ndim = npt + n;
np = n + 1;
nptm = npt - np;
ctx->xbase = ADDRESS(REAL, ctx, offset);
ctx->xopt = ctx->xbase + n;
ctx->xnew = ctx->xopt + n;
ctx->xpt = ctx->xnew + n;
ctx->fval = ctx->xpt + n*npt;
ctx->gq = ctx->fval + npt;
ctx->hq = ctx->gq + n;
ctx->pq = ctx->hq + n*np/2;
ctx->bmat = ctx->pq + npt;
ctx->zmat = ctx->bmat + ndim*n;
ctx->d = ctx->zmat + npt*nptm;
ctx->vlag = ctx->d + n;
ctx->w = ctx->vlag + ndim;
/* Return context ready for the first iteration. */
ctx->status = NEWUOA_ITERATE;
return ctx;
} /* newuoa_create */
void
newuoa_delete(newuoa_context_t* ctx)
{
if (ctx != NULL) {
free((void*)ctx);
}
}
int
newuoa_restart(newuoa_context_t* ctx)
{
if (ctx == NULL) {
errno = EFAULT;
return NEWUOA_BAD_ADDRESS;
}
ctx->nevals = 0;
ctx->status = NEWUOA_ITERATE;
return ctx->status;
}
int
newuoa_get_status(const newuoa_context_t* ctx)
{
if (ctx == NULL) {
errno = EFAULT;
return NEWUOA_BAD_ADDRESS;
}
return ctx->status;
}
INTEGER
newuoa_get_nevals(const newuoa_context_t* ctx)
{
if (ctx == NULL) {
errno = EFAULT;
return -1;
}
return ctx->nevals;
}
REAL
newuoa_get_rho(const newuoa_context_t* ctx)
{
if (ctx == NULL) {
errno = EFAULT;
return -1;
}
return ctx->rho;
}
const char* newuoa_reason(int status)
{
switch (status) {
case NEWUOA_ITERATE:
return "caller is requested to evaluate the objective function";
case NEWUOA_SUCCESS:
return "algorithm converged";
case NEWUOA_BAD_NPT:
return "NPT is not in the required interval";
case NEWUOA_ROUNDING_ERRORS:
return "too much cancellation in a denominator";
case NEWUOA_TOO_MANY_EVALUATIONS:
return "maximum number of function evaluations exceeded";
case NEWUOA_STEP_FAILED:
return "trust region step has failed to reduce quadratic approximation";
case NEWUOA_BAD_ADDRESS:
return "illegal NULL address";
case NEWUOA_CORRUPTED:
return "corrupted or misused workspace";
default:
return "unknown status";
}
}
/* Include this file with the macro _NEWUOA_REVCOM defined to
generate the code `newuoa_iterate` in place of `newuob`. */
#define _NEWUOA_REVCOM 1
#include __FILE__
#undef _NEWUOA_REVCOM
#endif /* _NEWUOA_PART1 */
/*---------------------------------------------------------------------------*/
/* NEWUOA SUBROUTINES */
#ifdef _NEWUOA_REVCOM
int newuoa_iterate(newuoa_context_t* ctx, REAL f, REAL* x)
#else
static int newuob(const INTEGER n, const INTEGER npt,
newuoa_objfun* objfun, void* data,
REAL* x, const REAL rhobeg, const REAL rhoend,
const INTEGER iprint, const INTEGER maxfun,
REAL* xbase, REAL* xopt, REAL* xnew,
REAL* xpt, REAL* fval, REAL* gq, REAL* hq,
REAL* pq, REAL* bmat, REAL* zmat, const INTEGER ndim,
REAL* d, REAL* vlag, REAL* w)
#endif /* _NEWUOA_REVCOM */
{
/* The arguments N, NPT, X, RHOBEG, RHOEND, IPRINT and MAXFUN are identical
to the corresponding arguments in SUBROUTINE NEWUOA.
XBASE will hold a shift of origin that should reduce the contributions
from rounding errors to values of the model and Lagrange functions.
XOPT will be set to the displacement from XBASE of the vector of variables
that provides the least calculated F so far.
XNEW will be set to the displacement from XBASE of the vector of variables
for the current calculation of F.
XPT will contain the interpolation point coordinates relative to XBASE.
FVAL will hold the values of F at the interpolation points.
GQ will hold the gradient of the quadratic model at XBASE.
HQ will hold the explicit second derivatives of the quadratic model.
PQ will contain the parameters of the implicit second derivatives of the
quadratic model.
BMAT will hold the last N columns of H.
ZMAT will hold the factorization of the leading NPT by NPT submatrix of H,
this factorization being ZMAT times Diag(DZ) times ZMAT^T, where the
elements of DZ are plus or minus one, as specified by IDZ.
NDIM is the first dimension of BMAT and has the value NPT+N.
D is reserved for trial steps from XOPT.
VLAG will contain 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.
The array W will be used for working space. Its length must be at least
10*NDIM = 10*(NPT+N). */
/* Constants. */
const REAL one = 1.0;
const REAL zero = 0.0;
const REAL half = 0.5;
const REAL tenth = 0.1;
/* Local variables. */
REAL alpha, beta, crvmin, delta, diff, diffa, diffb, diffc, dnorm, dsq,
dstep, fbeg, fopt, gqsq, ratio, recip, reciq, rho, rhosq, vquad,
xipt, xjpt, xoptsq;
#ifdef _NEWUOA_REVCOM
REAL rhobeg, rhoend;
#else
REAL f;
#endif
const char* reason;
#ifdef _NEWUOA_REVCOM
REAL *xbase, *xopt, *xnew, *xpt, *fval, *gq, *hq, *pq, *bmat, *zmat,
*d, *vlag, *w;
#endif
INTEGER idz, ih, ipt, itest, jpt, knew, kopt,
ksave, nf, nfm, nfmm, nfsav, nh, np, nptm;
#ifdef _NEWUOA_REVCOM
INTEGER n, npt, ndim, iprint, maxfun;
#endif
int status;
/* Temporary variables (these variables do not have to be saved in the
reverse communication version of the algorithm). */
REAL bsum, detrat, distsq, dx, fsave, temp, tempa, tempb;
INTEGER i, j, k, ktemp;
#ifdef _NEWUOA_REVCOM
/* Minimal checking (FIXME: print?). */
if (ctx == NULL) {
return NEWUOA_BAD_ADDRESS;
}
if (x == NULL) {
ctx->status = NEWUOA_BAD_ADDRESS;
return ctx->status;
}
if (ctx->status != NEWUOA_ITERATE) {
ctx->status = NEWUOA_CORRUPTED;
return ctx->status;
}
/* Restore local variables. */
RESTORE(n);
RESTORE(npt);
RESTORE(rhobeg);
RESTORE(rhoend);
RESTORE(iprint);
RESTORE(maxfun);
RESTORE(alpha);
RESTORE(beta);
RESTORE(crvmin);
RESTORE(delta);
RESTORE(diff);
RESTORE(diffa);
RESTORE(diffb);
RESTORE(diffc);
RESTORE(dnorm);
RESTORE(dsq);
RESTORE(dstep);
RESTORE(fbeg);
RESTORE(fopt);
RESTORE(gqsq);
RESTORE(ratio);
RESTORE(recip);
RESTORE(reciq);
RESTORE(rho);
RESTORE(rhosq);
RESTORE(vquad);
RESTORE(xipt);
RESTORE(xjpt);
RESTORE(xoptsq);
RESTORE(idz);
RESTORE(ih);
RESTORE(ipt);
RESTORE(itest);
RESTORE(jpt);
RESTORE(knew);
RESTORE(kopt);
RESTORE(ksave);
RESTORE(nf);
RESTORE(nfm);
RESTORE(nfmm);
RESTORE(nfsav);
RESTORE(status);
ndim = npt + n;
#else
/* Set uninitialized variables. */
idz = 0;
ipt = 0;
itest = 0;
jpt = 0;
knew = 0;
kopt = 0;
nfsav = 0;
alpha = zero;
crvmin = zero;
delta = zero;
diffa = zero;
diffb = zero;
diffc = zero;
dnorm = zero;
f = zero;
fbeg = zero;
fopt = zero;
ratio = zero;
rho = zero;
xoptsq = zero;
#endif
/* Initialization. */
np = n + 1;
nh = n*np/2;
nptm = npt - np;
reason = NULL;
/* Parameter adjustments and macros to comply with FORTRAN indexing. */
#ifdef _NEWUOA_REVCOM
x -= 1;
xbase = ctx->xbase - 1;
xopt = ctx->xopt - 1;
xnew = ctx->xnew - 1;
xpt = ctx->xpt - (1 + npt);
fval = ctx->fval - 1;
gq = ctx->gq - 1;
hq = ctx->hq - 1;
pq = ctx->pq - 1;
bmat = ctx->bmat - (1 + ndim);
zmat = ctx->zmat - (1 + npt);
d = ctx->d - 1;
vlag = ctx->vlag - 1;
w = ctx->w - 1;
#else
x -= 1;
xbase -= 1;
xopt -= 1;
xnew -= 1;
xpt -= 1 + npt;
fval -= 1;
gq -= 1;
hq -= 1;
pq -= 1;
bmat -= 1 + ndim;
zmat -= 1 + npt;
d -= 1;
vlag -= 1;
w -= 1;
#endif
#define XPT(a1,a2) xpt[(a2)*npt + a1]
#define BMAT(a1,a2) bmat[(a2)*ndim + a1]
#define ZMAT(a1,a2) zmat[(a2)*npt + a1]
#ifdef _NEWUOA_REVCOM
/* Increment the number of function evaluation and, if this is not the first
evaluation: branch to the code where the function value was requested. */
if (++ctx->nevals > 1) {
goto new_eval;
}
/* Must be first function evaluation. */
if (ctx->nevals != 1) {
ctx->status = NEWUOA_CORRUPTED;
return ctx->status;
}
status = NEWUOA_INITIAL_ITERATE;
#endif
/* Set the initial elements of XPT, BMAT, HQ, PQ and ZMAT to zero. */
LOOP(j,n) {
xbase[j] = x[j];
LOOP(k,npt) {
XPT(k,j) = zero;
}
LOOP(i,ndim) {
BMAT(i,j) = zero;
}
}
LOOP(ih,nh) {
hq[ih] = zero;
}
LOOP(k,npt) {
pq[k] = zero;
LOOP(j,nptm) {
ZMAT(k,j) = zero;
}
}
/* Begin the initialization procedure. NF becomes one more than the number of
function values so far. The coordinates of the displacement of the next
initial interpolation point from XBASE are set in XPT(NF,.). */
rhosq = rhobeg*rhobeg;
recip = one/rhosq;
reciq = SQRT(half)/rhosq;
nf = 0;
L50:
nfm = nf;
nfmm = nf - n;
++nf;
if (nfm <= 2*n) {
if (nfm >= 1 && nfm <= n) {
XPT(nf,nfm) = rhobeg;
} else if (nfm > n) {
XPT(nf,nfmm) = -rhobeg;
}
} else {
INTEGER itemp = (nfmm - 1)/n;
jpt = nfm - itemp*n - n;
ipt = jpt + itemp;
if (ipt > n) {
itemp = jpt;
jpt = ipt - n;
ipt = itemp;
}
xipt = rhobeg;
if (fval[ipt + np] < fval[ipt + 1]) {
xipt = -xipt;
}
xjpt = rhobeg;
if (fval[jpt + np] < fval[jpt + 1]) {
xjpt = -xjpt;
}
XPT(nf,ipt) = xipt;
XPT(nf,jpt) = xjpt;
}
/* Calculate the next value of F, label 70 being reached immediately after
this calculation. The least function value so far and its index are
required. */
LOOP(j,n) {
x[j] = XPT(nf,j) + xbase[j];
}
goto L310;
L70:
fval[nf] = f;
if (nf == 1) {
fbeg = f;
fopt = f;
kopt = 1;
} else if (f < fopt) {
fopt = f;
kopt = nf;
}
/* Set the nonzero initial elements of BMAT and the quadratic model in the
cases when NF is at most 2*N+1. */
if (nfm <= 2*n) {
if (nfm >= 1 && nfm <= n) {
gq[nfm] = (f - fbeg)/rhobeg;
if (npt < nf + n) {
BMAT(1,nfm) = -one/rhobeg;
BMAT(nf,nfm) = one/rhobeg;
BMAT(npt + nfm, nfm) = -half*rhosq;
}
} else if (nfm > n) {
BMAT(nf - n, nfmm) = half/rhobeg;
BMAT(nf,nfmm) = -half/rhobeg;
ZMAT(1,nfmm) = -reciq - reciq;
ZMAT(nf - n, nfmm) = reciq;
ZMAT(nf,nfmm) = reciq;
ih = nfmm*(nfmm + 1)/2;
temp = (fbeg - f)/rhobeg;
hq[ih] = (gq[nfmm] - temp)/rhobeg;
gq[nfmm] = half*(gq[nfmm] + temp);
}
} else {
/* Set the off-diagonal second derivatives of the Lagrange functions and
the initial quadratic model. */
ih = ipt*(ipt - 1)/2 + jpt;
if (xipt < zero) {
ipt += n;
}
if (xjpt < zero) {
jpt += n;
}
ZMAT(1,nfmm) = recip;
ZMAT(nf,nfmm) = recip;
ZMAT(ipt + 1, nfmm) = -recip;
ZMAT(jpt + 1, nfmm) = -recip;
hq[ih] = (fbeg - fval[ipt + 1] - fval[jpt + 1] + f)/(xipt*xjpt);
}
if (nf < npt) {
goto L50;
}
/* Begin the iterative procedure, because the initial model is complete. */
rho = rhobeg;
delta = rho;
idz = 1;
diffa = zero;
diffb = zero;
itest = 0;
xoptsq = zero;
LOOP(i,n) {
xopt[i] = XPT(kopt,i);
xoptsq += xopt[i]*xopt[i];
}
L90:
nfsav = nf;
/* Generate the next trust region step and test its length. Set KNEW to -1 if
the purpose of the next F will be to improve the model. */
L100:
knew = 0;
trsapp(n, npt, &xopt[1], &XPT(1,1), &gq[1], &hq[1], &pq[1],
delta, &d[1], &w[1], &w[np], &w[np + n],
&w[np + 2*n], &crvmin);
dsq = zero;
LOOP(i,n) {
dsq += d[i]*d[i];
}
dnorm = SQRT(dsq);
dnorm = MIN(dnorm,delta);
if (dnorm < half*rho) {
knew = -1;
delta = tenth*delta;
ratio = -1.0;
if (delta <= rho*1.5) {
delta = rho;
}
if (nf <= nfsav + 2) {
goto L460;
}
temp = crvmin*0.125*rho*rho;
tempa = MAX(diffa,diffb);
if (temp <= MAX(tempa,diffc)) {
goto L460;
}
goto L490;
}
/* Shift XBASE if XOPT may be too far from XBASE. First make the changes
to BMAT that do not depend on ZMAT. */
L120:
if (dsq <= xoptsq*0.001) {
REAL sum, sumz, tempq;
INTEGER ip;
tempq = xoptsq*0.25;
LOOP(k,npt) {
sum = zero;
LOOP(i,n) {
sum += XPT(k,i)*xopt[i];
}
temp = pq[k]*sum;
sum -= half*xoptsq;
w[npt + k] = sum;
LOOP(i,n) {
gq[i] += temp*XPT(k,i);
XPT(k,i) = XPT(k,i) - half*xopt[i];
vlag[i] = BMAT(k,i);
w[i] = sum*XPT(k,i) + tempq*xopt[i];
ip = npt + i;
LOOP(j,i) {
BMAT(ip,j) = BMAT(ip,j) + vlag[i]*w[j] + w[i]*vlag[j];
}
}
}
/* Then the revisions of BMAT that depend on ZMAT are calculated. */
LOOP(k,nptm) {
sumz = zero;
LOOP(i,npt) {
sumz += ZMAT(i,k);
w[i] = w[npt + i]*ZMAT(i,k);
}
LOOP(j,n) {
sum = tempq*sumz*xopt[j];
LOOP(i,npt) {
sum += w[i]*XPT(i,j);
}
vlag[j] = sum;
if (k < idz) {
sum = -sum;
}
LOOP(i,npt) {
BMAT(i,j) = BMAT(i,j) + sum*ZMAT(i,k);
}
}
LOOP(i,n) {
ip = i + npt;
temp = vlag[i];
if (k < idz) {
temp = -temp;
}
LOOP(j,i) {
BMAT(ip,j) = BMAT(ip,j) + temp*vlag[j];
}
}
}
/* The following instructions complete the shift of XBASE, including the
changes to the parameters of the quadratic model. */
ih = 0;
LOOP(j,n) {