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
|
/* MIT License
*
* Copyright (c) 2016--2017 Felix Lenders
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include "trlib.h"
#include "trlib_private.h"
#include "_c99compat.h"
trlib_int_t trlib_leftmost(
trlib_int_t nirblk, trlib_int_t *irblk, trlib_flt_t *diag, trlib_flt_t *offdiag,
trlib_int_t warm, trlib_flt_t leftmost_minor, trlib_int_t itmax, trlib_flt_t tol_abs,
trlib_int_t verbose, trlib_int_t unicode, char *prefix, FILE *fout,
trlib_int_t *timing, trlib_int_t *ileftmost, trlib_flt_t *leftmost) {
trlib_int_t ret = 0, curit = 0;
if(! warm) {
trlib_int_t curret = 0;
trlib_int_t ii = 0;
ret = 0;
for(ii = 0; ii < nirblk; ++ii) {
curret = trlib_leftmost_irreducible(irblk[ii+1]-irblk[ii], diag+irblk[ii], offdiag+irblk[ii], 0, 0.0, itmax,
tol_abs, verbose, unicode, prefix, fout, timing, leftmost+ii, &curit);
if (curret == 0) { ret = curret; }
}
*ileftmost = 0;
for(ii = 1; ii < nirblk; ++ii) {
if (leftmost[ii] < leftmost[*ileftmost]) { *ileftmost = ii; }
}
}
else {
ret = trlib_leftmost_irreducible(irblk[nirblk] - irblk[nirblk-1], diag+irblk[nirblk-1], offdiag+irblk[nirblk-1],
1, leftmost_minor, itmax, tol_abs, verbose, unicode, prefix, fout, timing, leftmost+nirblk-1, &curit);
if (leftmost[nirblk-1] < leftmost[*ileftmost]) { *ileftmost = nirblk-1; }
}
return ret;
}
trlib_int_t trlib_leftmost_irreducible(
trlib_int_t n, trlib_flt_t *diag, trlib_flt_t *offdiag,
trlib_int_t warm, trlib_flt_t leftmost_minor, trlib_int_t itmax, trlib_flt_t tol_abs,
trlib_int_t verbose, trlib_int_t unicode, char *prefix, FILE *fout,
trlib_int_t *timing, trlib_flt_t *leftmost, trlib_int_t *iter_pr) {
// Local variables
#if TRLIB_MEASURE_TIME
struct timespec verystart, start, end;
TRLIB_TIC(verystart)
#endif
trlib_int_t jj = 0; // local counter variable
trlib_flt_t low = 0.0; // lower bracket variable: low <= leftmost for desired value
trlib_flt_t up = 0.0; // upper bracket variable: leftmost <= up for desired value
trlib_flt_t leftmost_attempt = 0.0; // trial step for leftmost eigenvalue
trlib_flt_t dleftmost = 0.0; // increment
trlib_flt_t prlp = 0.0; // value of Parlett-Reid-Last-Pivot function
trlib_flt_t obyprlp = 0.0; // quotient used in Cholesky computation
trlib_flt_t dprlp = 0.0; // derivative of Parlett-Reid-Last-Pivot function wrt to leftmost
trlib_flt_t ddprlp = 0.0; // second derivative of Parlett-Reid-Last-Pivot function wrt to leftmost
trlib_int_t n_neg_piv = 0; // number of negative pivots in factorization
trlib_flt_t quad_abs = 0.0; // absolute coefficient in quadratic model
trlib_flt_t quad_lin = 0.0; // linear coefficient in quadratic model
trlib_flt_t quad_qua = 0.0; // quadratic coefficient in quadratic model
trlib_flt_t zerodum = 0.0; // dummy return variables from quadratic equation
trlib_flt_t oabs0 = 0.0, oabs1 = 0.0; // temporaries in Gershgorin limit computation
trlib_int_t continue_outer_loop = 0; // local spaghetti code control variable
trlib_int_t model_type = 0;
trlib_int_t ii = 0;
*leftmost = 0.0; // estimation of desired leftmost eigenvalue
*iter_pr = 0; // iteration counter
// trivial case: one-dimensional. return diagonal value
if (n == 1) { *leftmost = diag[0]; TRLIB_RETURN(TRLIB_LMR_CONV) }
/* set bracket interval derived from Gershgorin circles
Gershgorin:
eigenvalues are contained in the union of balls centered at
diag_i with radius sum of absolute values in column i, except diagonal element
this estimation is rough and could be improved by circle component analysis
determine if worth doing */
oabs0 = fabs(offdiag[0]); oabs1 = fabs(offdiag[n-2]);
low = fmin( diag[0] - oabs0, diag[n-1] - oabs1 );
up = fmax( diag[0] + oabs0, diag[n-1] - oabs1 );
for(ii = 1; ii < n-1; ++ii ) {
oabs1 = fabs(offdiag[ii]);
low = fmin( low, diag[ii] - oabs0 - oabs1 );
up = fmax( up, diag[ii] + oabs0 + oabs1 );
oabs0 = oabs1;
}
/* set leftmost to sensible initialization
on warmstart, provided leftmost is eigenvalue of principal (n-1) * (n-1) submatrix
by eigenvalue interlacing theorem desired value <= provided leftmost
on coldstart, start close lower bound as hopefully this is a good estimation */
if ( warm ) {
// provided leftmost is an upper bound and a pole of Parlett-Reid Value, thus pertub a bit
up = fmin(up, leftmost_minor); *leftmost = leftmost_minor - .1*(up-low); //*leftmost = leftmost_minor - TRLIB_EPS_POW_4;
}
else { leftmost_minor = 0.0; *leftmost = low + .1*(up-low); }; // ensure sanity on leftmost_minor and start with lower bound
// Parlett-Reid Iteration, note we can assume n > 1
itmax = itmax*n;
while (1) {
/* iterate to obtain Parlett-Reid last pivot value of -leftmost == 0.0
this iteration uses a safeguard bracket [low, up] such that always low <= leftmost <= up
note that T - t*I is positive definite for t <= desired leftmost
steps of iteration:
(1) compute Parlett-Reid last pivot value which is D_n in a LDL^T factorization of T
obtain derivative d D_n / d leftmost as byproduct in the very same recursion
track if breakdown would occur in factorization, happens if either
(a) a pivot become zero
(b) more than one negative pivot present
if breakdown would occurs this means that Parlett-Reid value is infinite
end iteration at premature point and restart with adapted bounds and estimation:
(a) a pivot became zero:
if last pivot zero --> goal reached, exit
if previous zero --> T - leftmost I not positive definite, thus desired value <= leftmost
(b) multiple neg privots --> T - leftmost I indefinite, thus desired value <= leftmost
(2) compute a trial update for leftmost. Possibilities
(a) Newton update
(b) zero of suitable model of analytic expression,
analytic expression is given by prlp(t) = det(T-t*I)/det(U-t*I) with U principal (n-1)*(n-1) submatrix
prlp(t) has a pole at leftmost_minor, so better use lifted
lprlp(t) = (leftmost_minor-t)*prlp(t)
Gould proposes model m(t) = a+bt+t^2 (correct asymptotic, very good for t far away)
Other choices would be m(t) = a+bt (Newton on lifted model)
m(t) = a+bt+ct^2 (Taylor, very good close to zero, possibly really bad far away)
do (b) if warmstart where user provided leftmost(U), otherwise go route (a)
(3) take trial step if inside bracket, otherwise bisect
stop iteration if either bracket is sufficiently small or Parlett-Reid value is close enough to zero */
*iter_pr += 1;
// test if iteration limit exceeded
if ( *iter_pr > itmax ) { TRLIB_RETURN(TRLIB_LMR_ITMAX) }
// initialize: no negative pivots so far
n_neg_piv = 0;
// print iteration headline every 10 iterations
if (*iter_pr % 10 == 1) {
TRLIB_PRINTLN_1("%6s%8s%14s%14s%14s%14s%14s%6s%6s", " it ", " action ", " low ", " leftmost ", " up ", " dleftmost ", " prlp ", " nneg ", " br ")
}
TRLIB_PRINTLN_1("%6ld%8s%14e%14e%14e", *iter_pr, " entry ", low, *leftmost, up)
// compute pivot and derivative of LDL^T factorization of T - leftmost I
continue_outer_loop = 0;
for( jj = 0; jj < n; ++jj ) {
/* compute jj-th pivot
special case for jj == 0 since offdiagonal is missing */
if (jj == 0) { prlp = diag[0] - *leftmost; dprlp = -1.0; ddprlp = 0.0; }
else{
// update pivot as pivot = d_j - leftmost - o_{j-1}^2/pivot
// thus dpivot/dleftmost = - 1.0 + o_{j-1}^2/pivot^2 * dpivot
// d^2 pivot/dleftmost^2 = (o_{j-1}^2/pivot^2)(ddpivot - 2 dpivot^2/pivot)
obyprlp = offdiag[jj-1]/prlp;
//dprlp = -1.0 + obyprlp*dprlp*obyprlp;// * dprlp;
//prlp = diag[jj] - offdiag[jj-1]*offdiag[jj-1]/prlp - *leftmost;
ddprlp = obyprlp*obyprlp*(ddprlp - 2.0*dprlp*dprlp/prlp);
dprlp = -1.0 + offdiag[jj-1]*offdiag[jj-1]*dprlp / (prlp*prlp);
prlp = diag[jj] - *leftmost - offdiag[jj-1]*obyprlp;
}
// check for breakdown
if (prlp == 0.0) {
// if last pivot and no negative pivots encountered --> finished
if (n_neg_piv == 0 && jj+1 == n) { TRLIB_RETURN(TRLIB_LMR_CONV) }
else{
/* if not last pivot or negative pivots encountered:
estimation provides a new upper bound; reset estimation */
up = *leftmost;
*leftmost = 0.5 * (low+up);
continue_outer_loop = 1;
break; // continue outer loop
}
}
else if ( prlp < 0.0 ) {
n_neg_piv += 1;
up = *leftmost;
if (n_neg_piv > 1) {
// more than one negative pivot: factorization would fail, to the right of singularity!
*leftmost = 0.5 * (low+up);
continue_outer_loop = 1;
break; // continue outer loop
}
}
}
if (continue_outer_loop) {
TRLIB_PRINTLN_1("%6s%8s%14e%14e%14e%14s%14e%6ld%6ld", "", " bisecp ", low, *leftmost, up, "", prlp, n_neg_piv, jj)
continue;
}
// we have survived computing the Last-Pivot value without finding a zero pivot and at most one negative pivot
// adapt bracket, no negative pivots encountered: leftmost provides new lower bound, otherwise upper bound
if (n_neg_piv == 0) { low = *leftmost; }
else { up = *leftmost; }
// test if bracket interval is small or last pivot has converged to zero
if (up-low <= tol_abs * fmax(1.0, fmax(fabs(low), fabs(up))) || fabs(prlp) <= tol_abs) {
TRLIB_PRINTLN_1("%6s%8s%14e%14e%14e%14s%14e%6ld%6ld", "", " conv ", low, *leftmost, up, "", prlp, n_neg_piv, jj)
TRLIB_RETURN(TRLIB_LMR_CONV)
}
/* compute trial step for new leftmost
on coldstart do Newton iteration, on warmstart find zero of model of analytic expression */
// select suitable model for analytic expression in dependence of warm
// warm: 1 --> heuristic depending on estimation if already close to zero
// warm: 2 --> use asymptotic quadratic model of lifted prlp
// warm: 3 --> use taylor quadratic model of lifted prlp
// warm: 4 --> use linear (newton) model of lifted prlp
if (warm) {
if ( warm == 2 || (warm == 1 && up-low >= .1*fmax(1.0, fabs(*leftmost))) ) {
/* use analytic model m(t) = (t-a)(t-b)/(t-leftmost_minor) for prlp(t)
fit a, b such that m matches function value and derivative
at current estimation and compute left zero of numerator */
quad_lin = -(2.0*(*leftmost)+prlp+((*leftmost)-leftmost_minor)*dprlp);
quad_abs = -(((*leftmost)-leftmost_minor)*prlp+(*leftmost)*(quad_lin+(*leftmost)));
trlib_quadratic_zero(quad_abs, quad_lin, TRLIB_EPS_POW_75, 0, 0, "", NULL, &leftmost_attempt, &zerodum);
model_type = 2; dleftmost = leftmost_attempt - *leftmost;
}
if( warm > 2 || (warm == 1 && up-low <.1*fmax(1.0, fabs(*leftmost))) ) {
/* use quadratic taylor model for pole lifted function (leftmost_minor-t)*prlp(t) */
quad_qua = -(dprlp + .5*((*leftmost)-leftmost_minor)*ddprlp);
quad_lin = -(prlp + ((*leftmost)-leftmost_minor)*dprlp);
quad_abs = (leftmost_minor-(*leftmost))*prlp;
if ( warm == 4 || fabs(quad_qua) <= TRLIB_EPS * TRLIB_EPS || quad_lin*quad_lin - 4.0*quad_qua*quad_abs <= 0.0 ) { // tiny curvature, resort to Newton step of lifted function
model_type = 3; dleftmost = -quad_abs/quad_lin; leftmost_attempt = *leftmost + dleftmost;
}
else { // scale coefficients to normalize equation
quad_lin = quad_lin/quad_qua;
quad_abs = quad_abs/quad_qua;
trlib_quadratic_zero(quad_abs, quad_lin, TRLIB_EPS_POW_75, 0, 0, "", NULL, &dleftmost, &zerodum);
model_type = 4; leftmost_attempt = *leftmost + dleftmost;
}
}
}
else { model_type = 1; dleftmost = -prlp/dprlp; leftmost_attempt = *leftmost + dleftmost; } // Newton step
// assess if we can use trial step
if (low <= leftmost_attempt && leftmost_attempt <= up) {
if( fabs(dleftmost) <= tol_abs * fmax(1.0, fmax(fabs(low), fabs(up))) ) { TRLIB_RETURN(TRLIB_LMR_NEWTON_BREAK) }
*leftmost = leftmost_attempt;
}
else {
// if warmstart information available, lifted newton step may still be feasible
if(warm) {
quad_lin = -(prlp + ((*leftmost)-leftmost_minor)*dprlp);
quad_abs = (leftmost_minor-(*leftmost))*prlp;
model_type = 3; dleftmost = -quad_abs/quad_lin; leftmost_attempt = *leftmost + dleftmost;
}
// if that fails, newton step may still be feasible
if(low > leftmost_attempt || up < leftmost_attempt) {
model_type = 1; dleftmost = -prlp/dprlp; leftmost_attempt = *leftmost + dleftmost;
}
// now out of options, do bisection
if(low > leftmost_attempt || up < leftmost_attempt) {
model_type = 0; *leftmost = .5*(low+up);
}
}
if ( verbose > 0 ) {
if ( model_type == 0 ) { TRLIB_PRINTLN_1("%6s%8s%14e%14e%14e%14e%14e%6ld%6ld", "", " bisecs ", low, *leftmost, up, .5*(up-low), prlp, n_neg_piv, jj) }
if ( model_type == 1 ) { TRLIB_PRINTLN_1("%6s%8s%14e%14e%14e%14e%14e%6ld%6ld", "", " piv 1 ", low, *leftmost, up, dleftmost, prlp, n_neg_piv, jj) }
if ( model_type == 2 ) { TRLIB_PRINTLN_1("%6s%8s%14e%14e%14e%14e%14e%6ld%6ld", "", " lpiv q ", low, *leftmost, up, dleftmost, prlp, n_neg_piv, jj) }
if ( model_type == 3 ) { TRLIB_PRINTLN_1("%6s%8s%14e%14e%14e%14e%14e%6ld%6ld", "", " lpiv 1 ", low, *leftmost, up, dleftmost, prlp, n_neg_piv, jj) }
if ( model_type == 4 ) { TRLIB_PRINTLN_1("%6s%8s%14e%14e%14e%14e%14e%6ld%6ld", "", " lpiv 2 ", low, *leftmost, up, dleftmost, prlp, n_neg_piv, jj) }
}
}
}
trlib_int_t trlib_leftmost_timing_size() {
#if TRLIB_MEASURE_TIME
return 1;
#endif
return 0;
}
|