| 1 | /* $Id: ClpLsqr.cpp 1665 2011-01-04 17:55:54Z lou $ */ |
| 2 | // Copyright (C) 2003, International Business Machines |
| 3 | // Corporation and others. All Rights Reserved. |
| 4 | // This code is licensed under the terms of the Eclipse Public License (EPL). |
| 5 | |
| 6 | #ifdef COIN_DO_PDCO |
| 7 | |
| 8 | #include "ClpLsqr.hpp" |
| 9 | #include "ClpPdco.hpp" |
| 10 | |
| 11 | |
| 12 | void ClpLsqr::do_lsqr( CoinDenseVector<double> &b, |
| 13 | double damp, double atol, double btol, double conlim, int itnlim, |
| 14 | bool show, Info info, CoinDenseVector<double> &x , int *istop, |
| 15 | int *itn, Outfo *outfo, bool precon, CoinDenseVector<double> &Pr) |
| 16 | { |
| 17 | |
| 18 | /** |
| 19 | Special version of LSQR for use with pdco.m. |
| 20 | It continues with a reduced atol if a pdco-specific test isn't |
| 21 | satisfied with the input atol. |
| 22 | */ |
| 23 | |
| 24 | // Initialize. |
| 25 | static char term_msg[8][80] = { |
| 26 | "The exact solution is x = 0" , |
| 27 | "The residual Ax - b is small enough, given ATOL and BTOL" , |
| 28 | "The least squares error is small enough, given ATOL" , |
| 29 | "The estimated condition number has exceeded CONLIM" , |
| 30 | "The residual Ax - b is small enough, given machine precision" , |
| 31 | "The least squares error is small enough, given machine precision" , |
| 32 | "The estimated condition number has exceeded machine precision" , |
| 33 | "The iteration limit has been reached" |
| 34 | }; |
| 35 | |
| 36 | // printf("***************** Entering LSQR *************\n"); |
| 37 | assert (model_); |
| 38 | |
| 39 | char str1[100], str2[100], str3[100], str4[100], head1[100], head2[100]; |
| 40 | |
| 41 | int m = nrows_; |
| 42 | int n = ncols_; // set m,n from lsqr object |
| 43 | |
| 44 | *itn = 0; |
| 45 | *istop = 0; |
| 46 | int nstop = 0; |
| 47 | double ctol = 0; |
| 48 | if (conlim > 0) ctol = 1 / conlim; |
| 49 | |
| 50 | double anorm = 0; |
| 51 | double acond = 0; |
| 52 | double dampsq = damp * damp; |
| 53 | double ddnorm = 0; |
| 54 | double res2 = 0; |
| 55 | double xnorm = 0; |
| 56 | double xxnorm = 0; |
| 57 | double z = 0; |
| 58 | double cs2 = -1; |
| 59 | double sn2 = 0; |
| 60 | |
| 61 | // Set up the first vectors u and v for the bidiagonalization. |
| 62 | // These satisfy beta*u = b, alfa*v = A'u. |
| 63 | |
| 64 | CoinDenseVector<double> u(b); |
| 65 | CoinDenseVector<double> v(n, 0.0); |
| 66 | x.clear(); |
| 67 | double alfa = 0; |
| 68 | double beta = u.twoNorm(); |
| 69 | if (beta > 0) { |
| 70 | u = (1 / beta) * u; |
| 71 | matVecMult( 2, v, u ); |
| 72 | if (precon) |
| 73 | v = v * Pr; |
| 74 | alfa = v.twoNorm(); |
| 75 | } |
| 76 | if (alfa > 0) { |
| 77 | v.scale(1 / alfa); |
| 78 | } |
| 79 | CoinDenseVector<double> w(v); |
| 80 | |
| 81 | double arnorm = alfa * beta; |
| 82 | if (arnorm == 0) { |
| 83 | printf(" %s\n\n" , term_msg[0]); |
| 84 | return; |
| 85 | } |
| 86 | |
| 87 | double rhobar = alfa; |
| 88 | double phibar = beta; |
| 89 | double bnorm = beta; |
| 90 | double rnorm = beta; |
| 91 | sprintf(head1, " Itn x(1) Function" ); |
| 92 | sprintf(head2, " Compatible LS Norm A Cond A" ); |
| 93 | |
| 94 | if (show) { |
| 95 | printf(" %s%s\n" , head1, head2); |
| 96 | double test1 = 1; |
| 97 | double test2 = alfa / beta; |
| 98 | sprintf(str1, "%6d %12.5e %10.3e" , *itn, x[0], rnorm ); |
| 99 | sprintf(str2, " %8.1e %8.1e" , test1, test2 ); |
| 100 | printf("%s%s\n" , str1, str2); |
| 101 | } |
| 102 | |
| 103 | //---------------------------------------------------------------- |
| 104 | // Main iteration loop. |
| 105 | //---------------------------------------------------------------- |
| 106 | while (*itn < itnlim) { |
| 107 | *itn += 1; |
| 108 | // Perform the next step of the bidiagonalization to obtain the |
| 109 | // next beta, u, alfa, v. These satisfy the relations |
| 110 | // beta*u = a*v - alfa*u, |
| 111 | // alfa*v = A'*u - beta*v. |
| 112 | |
| 113 | u.scale((-alfa)); |
| 114 | if (precon) { |
| 115 | CoinDenseVector<double> pv(v * Pr); |
| 116 | matVecMult( 1, u, pv); |
| 117 | } else { |
| 118 | matVecMult( 1, u, v); |
| 119 | } |
| 120 | beta = u.twoNorm(); |
| 121 | if (beta > 0) { |
| 122 | u.scale((1 / beta)); |
| 123 | anorm = sqrt(anorm * anorm + alfa * alfa + beta * beta + damp * damp); |
| 124 | v.scale((-beta)); |
| 125 | CoinDenseVector<double> vv(n); |
| 126 | vv.clear(); |
| 127 | matVecMult( 2, vv, u ); |
| 128 | if (precon) |
| 129 | vv = vv * Pr; |
| 130 | v = v + vv; |
| 131 | alfa = v.twoNorm(); |
| 132 | if (alfa > 0) |
| 133 | v.scale((1 / alfa)); |
| 134 | } |
| 135 | |
| 136 | // Use a plane rotation to eliminate the damping parameter. |
| 137 | // This alters the diagonal (rhobar) of the lower-bidiagonal matrix. |
| 138 | |
| 139 | double rhobar1 = sqrt(rhobar * rhobar + damp * damp); |
| 140 | double cs1 = rhobar / rhobar1; |
| 141 | double sn1 = damp / rhobar1; |
| 142 | double psi = sn1 * phibar; |
| 143 | phibar *= cs1; |
| 144 | |
| 145 | // Use a plane rotation to eliminate the subdiagonal element (beta) |
| 146 | // of the lower-bidiagonal matrix, giving an upper-bidiagonal matrix. |
| 147 | |
| 148 | double rho = sqrt(rhobar1 * rhobar1 + beta * beta); |
| 149 | double cs = rhobar1 / rho; |
| 150 | double sn = beta / rho; |
| 151 | double theta = sn * alfa; |
| 152 | rhobar = - cs * alfa; |
| 153 | double phi = cs * phibar; |
| 154 | phibar = sn * phibar; |
| 155 | double tau = sn * phi; |
| 156 | |
| 157 | // Update x and w. |
| 158 | |
| 159 | double t1 = phi / rho; |
| 160 | double t2 = - theta / rho; |
| 161 | // dk = ((1/rho)*w); |
| 162 | |
| 163 | double w_norm = w.twoNorm(); |
| 164 | x = x + t1 * w; |
| 165 | w = v + t2 * w; |
| 166 | ddnorm = ddnorm + (w_norm / rho) * (w_norm / rho); |
| 167 | // if wantvar, var = var + dk.*dk; end |
| 168 | |
| 169 | // Use a plane rotation on the right to eliminate the |
| 170 | // super-diagonal element (theta) of the upper-bidiagonal matrix. |
| 171 | // Then use the result to estimate norm(x). |
| 172 | |
| 173 | double delta = sn2 * rho; |
| 174 | double gambar = - cs2 * rho; |
| 175 | double rhs = phi - delta * z; |
| 176 | double zbar = rhs / gambar; |
| 177 | xnorm = sqrt(xxnorm + zbar * zbar); |
| 178 | double gamma = sqrt(gambar * gambar + theta * theta); |
| 179 | cs2 = gambar / gamma; |
| 180 | sn2 = theta / gamma; |
| 181 | z = rhs / gamma; |
| 182 | xxnorm = xxnorm + z * z; |
| 183 | |
| 184 | // Test for convergence. |
| 185 | // First, estimate the condition of the matrix Abar, |
| 186 | // and the norms of rbar and Abar'rbar. |
| 187 | |
| 188 | acond = anorm * sqrt( ddnorm ); |
| 189 | double res1 = phibar * phibar; |
| 190 | double res2 = res1 + psi * psi; |
| 191 | rnorm = sqrt( res1 + res2 ); |
| 192 | arnorm = alfa * fabs( tau ); |
| 193 | |
| 194 | // Now use these norms to estimate certain other quantities, |
| 195 | // some of which will be small near a solution. |
| 196 | |
| 197 | double test1 = rnorm / bnorm; |
| 198 | double test2 = arnorm / ( anorm * rnorm ); |
| 199 | double test3 = 1 / acond; |
| 200 | t1 = test1 / (1 + anorm * xnorm / bnorm); |
| 201 | double rtol = btol + atol * anorm * xnorm / bnorm; |
| 202 | |
| 203 | // The following tests guard against extremely small values of |
| 204 | // atol, btol or ctol. (The user may have set any or all of |
| 205 | // the parameters atol, btol, conlim to 0.) |
| 206 | // The effect is equivalent to the normal tests using |
| 207 | // atol = eps, btol = eps, conlim = 1/eps. |
| 208 | |
| 209 | if (*itn >= itnlim) |
| 210 | *istop = 7; |
| 211 | if (1 + test3 <= 1) |
| 212 | *istop = 6; |
| 213 | if (1 + test2 <= 1) |
| 214 | *istop = 5; |
| 215 | if (1 + t1 <= 1) |
| 216 | *istop = 4; |
| 217 | |
| 218 | // Allow for tolerances set by the user. |
| 219 | |
| 220 | if (test3 <= ctol) |
| 221 | *istop = 3; |
| 222 | if (test2 <= atol) |
| 223 | *istop = 2; |
| 224 | if (test1 <= rtol) |
| 225 | *istop = 1; |
| 226 | |
| 227 | //------------------------------------------------------------------- |
| 228 | // SPECIAL TEST THAT DEPENDS ON pdco.m. |
| 229 | // Aname in pdco is iw in lsqr. |
| 230 | // dy is x |
| 231 | // Other stuff is in info. |
| 232 | |
| 233 | // We allow for diagonal preconditioning in pdDDD3. |
| 234 | //------------------------------------------------------------------- |
| 235 | if (*istop > 0) { |
| 236 | double r3new = arnorm; |
| 237 | double r3ratio = r3new / info.r3norm; |
| 238 | double atolold = atol; |
| 239 | double atolnew = atol; |
| 240 | |
| 241 | if (atol > info.atolmin) { |
| 242 | if (r3ratio <= 0.1) { // dy seems good |
| 243 | // Relax |
| 244 | } else if (r3ratio <= 0.5) { // Accept dy but make next one more accurate. |
| 245 | atolnew = atolnew * 0.1; |
| 246 | } else { // Recompute dy more accurately |
| 247 | if (show) { |
| 248 | printf("\n " ); |
| 249 | printf(" \n" ); |
| 250 | printf(" %5.1f%7d%7.3f" , log10(atolold), *itn, r3ratio); |
| 251 | } |
| 252 | atol = atol * 0.1; |
| 253 | atolnew = atol; |
| 254 | *istop = 0; |
| 255 | } |
| 256 | |
| 257 | outfo->atolold = atolold; |
| 258 | outfo->atolnew = atolnew; |
| 259 | outfo->r3ratio = r3ratio; |
| 260 | } |
| 261 | |
| 262 | //------------------------------------------------------------------- |
| 263 | // See if it is time to print something. |
| 264 | //------------------------------------------------------------------- |
| 265 | int prnt = 0; |
| 266 | if (n <= 40 ) prnt = 1; |
| 267 | if (*itn <= 10 ) prnt = 1; |
| 268 | if (*itn >= itnlim - 10) prnt = 1; |
| 269 | if (fmod(*itn, 10) == 0 ) prnt = 1; |
| 270 | if (test3 <= 2 * ctol ) prnt = 1; |
| 271 | if (test2 <= 10 * atol ) prnt = 1; |
| 272 | if (test1 <= 10 * rtol ) prnt = 1; |
| 273 | if (*istop != 0 ) prnt = 1; |
| 274 | |
| 275 | if (prnt == 1) { |
| 276 | if (show) { |
| 277 | sprintf(str1, " %6d %12.5e %10.3e" , *itn, x[0], rnorm ); |
| 278 | sprintf(str2, " %8.1e %8.1e" , test1, test2 ); |
| 279 | sprintf(str3, " %8.1e %8.1e" , anorm, acond ); |
| 280 | printf("%s%s%s\n" , str1, str2, str3); |
| 281 | } |
| 282 | } |
| 283 | if (*istop > 0) |
| 284 | break; |
| 285 | } |
| 286 | } |
| 287 | // End of iteration loop. |
| 288 | // Print the stopping condition. |
| 289 | |
| 290 | if (show) { |
| 291 | printf("\n LSQR finished\n" ); |
| 292 | // disp(msg(istop+1,:)) |
| 293 | // disp(' ') |
| 294 | printf("%s\n" , term_msg[*istop]); |
| 295 | sprintf(str1, "istop =%8d itn =%8d" , *istop, *itn ); |
| 296 | sprintf(str2, "anorm =%8.1e acond =%8.1e" , anorm, acond ); |
| 297 | sprintf(str3, "rnorm =%8.1e arnorm =%8.1e" , rnorm, arnorm ); |
| 298 | sprintf(str4, "bnorm =%8.1e xnorm =%8.1e" , bnorm, xnorm ); |
| 299 | printf("%s %s\n" , str1, str2); |
| 300 | printf("%s %s\n" , str3, str4); |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | |
| 305 | |
| 306 | |
| 307 | void ClpLsqr::matVecMult( int mode, CoinDenseVector<double> *x, CoinDenseVector<double> *y) |
| 308 | { |
| 309 | int n = model_->numberColumns(); |
| 310 | int m = model_->numberRows(); |
| 311 | CoinDenseVector<double> *temp = new CoinDenseVector<double>(n, 0.0); |
| 312 | double *t_elts = temp->getElements(); |
| 313 | double *x_elts = x->getElements(); |
| 314 | double *y_elts = y->getElements(); |
| 315 | ClpPdco * pdcoModel = (ClpPdco *) model_; |
| 316 | if (mode == 1) { |
| 317 | pdcoModel->matVecMult( 2, temp, y); |
| 318 | for (int k = 0; k < n; k++) |
| 319 | x_elts[k] += (diag1_[k] * t_elts[k]); |
| 320 | for (int k = 0; k < m; k++) |
| 321 | x_elts[n+k] += (diag2_ * y_elts[k]); |
| 322 | } else { |
| 323 | for (int k = 0; k < n; k++) |
| 324 | t_elts[k] = diag1_[k] * y_elts[k]; |
| 325 | pdcoModel->matVecMult( 1, x, temp); |
| 326 | for (int k = 0; k < m; k++) |
| 327 | x_elts[k] += diag2_ * y_elts[n+k]; |
| 328 | } |
| 329 | delete temp; |
| 330 | return; |
| 331 | } |
| 332 | |
| 333 | void ClpLsqr::matVecMult( int mode, CoinDenseVector<double> &x, CoinDenseVector<double> &y) |
| 334 | { |
| 335 | matVecMult( mode, &x, &y); |
| 336 | return; |
| 337 | } |
| 338 | /* Default constructor */ |
| 339 | ClpLsqr::ClpLsqr() : |
| 340 | nrows_(0), |
| 341 | ncols_(0), |
| 342 | model_(NULL), |
| 343 | diag1_(NULL), |
| 344 | diag2_(0.0) |
| 345 | {} |
| 346 | |
| 347 | /* Constructor for use with Pdco model (note modified for pdco!!!!) */ |
| 348 | ClpLsqr::ClpLsqr(ClpInterior *model) : |
| 349 | diag1_(NULL), |
| 350 | diag2_(0.0) |
| 351 | { |
| 352 | model_ = model; |
| 353 | nrows_ = model->numberRows() + model->numberColumns(); |
| 354 | ncols_ = model->numberRows(); |
| 355 | } |
| 356 | /** Destructor */ |
| 357 | ClpLsqr::~ClpLsqr() |
| 358 | { |
| 359 | // delete [] diag1_; no as we just borrowed it |
| 360 | } |
| 361 | bool |
| 362 | ClpLsqr::setParam(char *parmName, int parmValue) |
| 363 | { |
| 364 | std::cout << "Set lsqr integer parameter " << parmName << "to " << parmValue |
| 365 | << std::endl; |
| 366 | if ( strcmp(parmName, "nrows" ) == 0) { |
| 367 | nrows_ = parmValue; |
| 368 | return 1; |
| 369 | } else if ( strcmp(parmName, "ncols" ) == 0) { |
| 370 | ncols_ = parmValue; |
| 371 | return 1; |
| 372 | } |
| 373 | std::cout << "Attempt to set unknown integer parameter name " << parmName << std::endl; |
| 374 | return 0; |
| 375 | } |
| 376 | ClpLsqr::ClpLsqr(const ClpLsqr &rhs) : |
| 377 | nrows_(rhs.nrows_), |
| 378 | ncols_(rhs.ncols_), |
| 379 | model_(rhs.model_), |
| 380 | diag2_(rhs.diag2_) |
| 381 | { |
| 382 | diag1_ = ClpCopyOfArray(rhs.diag1_, nrows_); |
| 383 | } |
| 384 | // Assignment operator. This copies the data |
| 385 | ClpLsqr & |
| 386 | ClpLsqr::operator=(const ClpLsqr & rhs) |
| 387 | { |
| 388 | if (this != &rhs) { |
| 389 | delete [] diag1_; |
| 390 | diag1_ = ClpCopyOfArray(rhs.diag1_, nrows_); |
| 391 | nrows_ = rhs.nrows_; |
| 392 | ncols_ = rhs.ncols_; |
| 393 | model_ = rhs.model_; |
| 394 | diag2_ = rhs.diag2_; |
| 395 | } |
| 396 | return *this; |
| 397 | } |
| 398 | #endif |
| 399 | |