CAMLprim value sunml_spils_qr_fact(value vn, value vh, value vq, value vnewjob)
{
    CAMLparam4(vn, vh, vq, vnewjob);
    int r;
    int n = Int_val(vn);

#if SUNDIALS_ML_SAFE == 1
    struct caml_ba_array *bh = ARRAY2_DATA(vh);
    intnat hn = bh->dim[0];
    intnat hm = bh->dim[1];

    if (hn < n + 1)
	caml_invalid_argument("qr_fact: h is too small (< n + 1).");
    if (hm < n)
	caml_invalid_argument("qr_fact: h is too small (< n).");
    if (ARRAY1_LEN(vq) < 2 * n)
	caml_invalid_argument("qr_fact: q is too small (< 2n).");
#endif

    r = QRfact(n, ARRAY2_ACOLS(vh), REAL_ARRAY(vq), Bool_val(vnewjob));

    if (r != 0) {
	caml_raise_with_arg(MATRIX_EXN_TAG(ZeroDiagonalElement),
			    Val_long(r));
    }

    CAMLreturn (Val_unit);
}
Example #2
0
int SpgmrSolve(SpgmrMem mem, void *A_data, N_Vector x, N_Vector b,
               int pretype, int gstype, real delta, int max_restarts,
	       void *P_data, N_Vector sx, N_Vector sb, ATimesFn atimes,
	       PSolveFn psolve, real *res_norm, int *nli, int *nps)
{
  N_Vector *V, xcor, vtemp;
  real **Hes, *givens, *yg;
  real s_r0_norm, beta, rotation_product, r_norm, s_product, rho;
  boole preOnLeft, preOnRight, scale_x, scale_b, converged;
  int i, j, k, l, l_plus_1, l_max, krydim, ier, ntries;

  if (mem == NULL) return(SPGMR_MEM_NULL);

  /* Make local copies of mem variables */
  l_max  = mem->l_max;
  V      = mem->V;
  Hes    = mem->Hes;
  givens = mem->givens;
  xcor   = mem->xcor;
  yg     = mem->yg;
  vtemp  = mem->vtemp;

  *nli = *nps = 0;     /* Initialize counters */
  converged = FALSE;   /* Initialize converged flag */

  if (max_restarts < 0) max_restarts = 0;

  if ((pretype != LEFT) && (pretype != RIGHT) && (pretype != BOTH))
    pretype = NONE;
  
  preOnLeft  = ((pretype == LEFT) || (pretype == BOTH));
  preOnRight = ((pretype == RIGHT) || (pretype == BOTH));
  scale_x    = (sx != NULL);
  scale_b    = (sb != NULL);

  /* Set vtemp and V[0] to initial (unscaled) residual r_0 = b - A*x_0  */

  if (N_VDotProd(x, x) == ZERO) {
    N_VScale(ONE, b, vtemp);
  } else {
    if (atimes(A_data, x, vtemp) != 0)
      return(SPGMR_ATIMES_FAIL);
    N_VLinearSum(ONE, b, -ONE, vtemp, vtemp);
  }
  N_VScale(ONE, vtemp, V[0]);

  /* Apply b-scaling to vtemp, get L2 norm of sb r_0, and return if small */
/*
  if (scale_b) N_VProd(sb, vtemp, vtemp);
  s_r0_norm = RSqrt(N_VDotProd(vtemp, vtemp));
  if (s_r0_norm <= delta) return(SPGMR_SUCCESS);
*/  
  /* Apply left preconditioner and b-scaling to V[0] = r_0 */
  
  if (preOnLeft) {
    ier = psolve(P_data, V[0], vtemp, LEFT);
    (*nps)++;
    if (ier != 0)
      return((ier < 0) ? SPGMR_PSOLVE_FAIL_UNREC : SPGMR_PSOLVE_FAIL_REC);
  } else {
    N_VScale(ONE, V[0], vtemp);
  }
  
  if (scale_b) {
    N_VProd(sb, vtemp, V[0]);   
  } else {
    N_VScale(ONE, vtemp, V[0]);
  }

  /* Set r_norm = beta to L2 norm of V[0] = sb P1_inv r_0, and
     return if small  */

  *res_norm = r_norm = beta = RSqrt(N_VDotProd(V[0], V[0])); 
  if (r_norm <= delta)
    return(SPGMR_SUCCESS);

  /* Set xcor = 0 */

  N_VConst(ZERO, xcor);


  /* Begin outer iterations: up to (max_restarts + 1) attempts */
  
  for (ntries = 0; ntries <= max_restarts; ntries++) {

    /* Initialize the Hessenberg matrix Hes and Givens rotation
       product.  Normalize the initial vector V[0].             */
   
    for (i=0; i <= l_max; i++)
      for (j=0; j < l_max; j++)
	Hes[i][j] = ZERO;

    rotation_product = ONE;
    
    N_VScale(ONE/r_norm, V[0], V[0]);

    /* Inner loop: generate Krylov sequence and Arnoldi basis */
    
    for(l=0; l < l_max; l++) {

      (*nli)++;

      krydim = l_plus_1 = l + 1;
      
      /* Generate A-tilde V[l], where A-tilde = sb P1_inv A P2_inv sx_inv */

      /* Apply x-scaling: vtemp = sx_inv V[l] */
      if (scale_x) {
	N_VDiv(V[l], sx, vtemp);
      } else {
	N_VScale(ONE, V[l], vtemp);
      }

      /* Apply right precoditioner: vtemp = P2_inv sx_inv V[l] */ 
      N_VScale(ONE, vtemp, V[l_plus_1]);
      if (preOnRight) {
	ier = psolve(P_data, V[l_plus_1], vtemp, RIGHT);
	(*nps)++;
	if (ier != 0)
	  return((ier < 0) ? SPGMR_PSOLVE_FAIL_UNREC : SPGMR_PSOLVE_FAIL_REC);
      }

      /* Apply A: V[l+1] = A P2_inv sx_inv V[l] */
      if (atimes(A_data, vtemp, V[l_plus_1] ) != 0)
	return(SPGMR_ATIMES_FAIL);

      /* Apply left preconditioning: vtemp = P1_inv A P2_inv sx_inv V[l] */
      if (preOnLeft) {
	ier = psolve(P_data, V[l_plus_1], vtemp, LEFT);
	(*nps)++;
	if (ier != 0)
	  return((ier < 0) ? SPGMR_PSOLVE_FAIL_UNREC : SPGMR_PSOLVE_FAIL_REC);
      } else {
	N_VScale(ONE, V[l_plus_1], vtemp);
      }

      /* Apply b-scaling: V[l+1] = sb P1_inv A P2_inv sx_inv V[l] */
      if (scale_b) {
	N_VProd(sb, vtemp, V[l_plus_1]);
      } else {
	N_VScale(ONE, vtemp, V[l_plus_1]);
      }
      
      /*  Orthogonalize V[l+1] against previous V[i]: V[l+1] = w_tilde. */

      if (gstype == CLASSICAL_GS) {
	if (ClassicalGS(V, Hes, l_plus_1, l_max, &(Hes[l_plus_1][l]),
			vtemp, yg) != 0)
	  return(SPGMR_GS_FAIL);
      } else {
	if (ModifiedGS(V, Hes, l_plus_1, l_max, &(Hes[l_plus_1][l])) != 0) 
	  return(SPGMR_GS_FAIL);
      }

      /*  Update the QR factorization of Hes  */

      if(QRfact(krydim, Hes, givens, l) != 0 )
	return(SPGMR_QRFACT_FAIL);

      /*  Update residual norm estimate; break if convergence test passes */
      
      rotation_product *= givens[2*l+1];
    
      if ((*res_norm = rho = ABS(rotation_product*r_norm)) <= delta) {
	converged = TRUE;
	break;
      }
      
      /* Normalize V[l+1] with norm value from the Gram-Schmidt */
      N_VScale(ONE/Hes[l_plus_1][l], V[l_plus_1], V[l_plus_1]);
    }
    
    /* Inner loop is done.  Compute the new correction vector xcor */

    /* Construct g, then solve for y */
    yg[0] = r_norm;
    for (i=1; i <= krydim; i++) yg[i]=ZERO;
    if (QRsol(krydim, Hes, givens, yg) != 0)
      return(SPGMR_QRSOL_FAIL);
    
    /* Add correction vector V_l y to xcor */
    for (k=0; k < krydim; k++)
      N_VLinearSum(yg[k], V[k], ONE, xcor, xcor);

    /* If converged, construct the final solution vector x */
    if (converged) {
     
      /* Apply x-scaling and right precond.: vtemp = P2_inv sx_inv xcor */
  
      if (scale_x) N_VDiv(xcor, sx, xcor);
      if (preOnRight) {
	ier = psolve(P_data, xcor, vtemp, RIGHT);
	(*nps)++;
	if (ier != 0)
	   return((ier < 0) ? SPGMR_PSOLVE_FAIL_UNREC : SPGMR_PSOLVE_FAIL_REC);
      } else {
	N_VScale(ONE, xcor, vtemp);
      }

      /* Add correction to initial x to get final solution x, and return */
      N_VLinearSum(ONE, x, ONE, vtemp, x);

      return(SPGMR_SUCCESS);
    }

    /* Not yet converged; if allowed, prepare for restart */

    if (ntries == max_restarts) break;

    /* Construct last column of Q in yg */
    s_product = ONE;
    for (i=krydim; i > 0; i--) {
      yg[i] = s_product*givens[2*i-2];
      s_product *= givens[2*i-1];
    }
    yg[0] = s_product;

    /* Scale r_norm and yg */
    r_norm *= s_product;
    for (i=0; i <= krydim; i++)
      yg[i] *= r_norm;
    r_norm = ABS(r_norm);

    /* Multiply yg by V_(krydim+1) to get last residual vector; restart */
    N_VScale(yg[0], V[0], V[0]);
    for( k=1; k <= krydim; k++)
      N_VLinearSum(yg[k], V[k], ONE, V[0], V[0]);

  }

  /* Failed to converge, even after allowed restarts.
     If the residual norm was reduced below its initial value, compute
     and return x anyway.  Otherwise return failure flag.              */

  if (rho < beta) {

    /* Apply the x-scaling and right precond.: vtemp = P2_inv sx_inv xcor */
    
    if (scale_x) N_VDiv(xcor, sx, xcor);
    if (preOnRight) {
      ier = psolve(P_data, xcor, vtemp, RIGHT);
      (*nps)++;
      if (ier != 0)
	return((ier < 0) ? SPGMR_PSOLVE_FAIL_UNREC : SPGMR_PSOLVE_FAIL_REC);
    } else {
      N_VScale(ONE, xcor, vtemp);
    }

    /* Add vtemp to initial x to get final solution x, and return */
    N_VLinearSum(ONE, x, ONE, vtemp, x);
    
    return(SPGMR_RES_REDUCED);
  }

  return(SPGMR_CONV_FAIL); 
}
Example #3
0
/*----------------------------------------------------------------
 Function : SpfgmrSolve
 ---------------------------------------------------------------*/
int SpfgmrSolve(SpfgmrMem mem, void *A_data, N_Vector x, 
		N_Vector b, int pretype, int gstype, realtype delta, 
		int max_restarts, int maxit, void *P_data, 
		N_Vector s1, N_Vector s2, ATimesFn atimes, 
		PSolveFn psolve, realtype *res_norm, int *nli, int *nps)
{
  N_Vector *V, *Z, xcor, vtemp;
  realtype **Hes, *givens, *yg;
  realtype beta, rotation_product, r_norm, s_product, rho;
  booleantype preOnRight, scale1, scale2, converged;
  int i, j, k, l, l_max, krydim, ier, ntries;

  if (mem == NULL) return(SPFGMR_MEM_NULL);

  /* Initialize some variables */
  krydim = 0;

  /* Make local copies of mem variables. */
  l_max  = mem->l_max;
  V      = mem->V;
  Z      = mem->Z;
  Hes    = mem->Hes;
  givens = mem->givens;
  xcor   = mem->xcor;
  yg     = mem->yg;
  vtemp  = mem->vtemp;

  *nli = *nps = 0;    /* Initialize counters */
  converged = SUNFALSE;  /* Initialize converged flag */

  /* If maxit is greater than l_max, then set maxit=l_max */
  if (maxit > l_max)  maxit = l_max;

  /* Check for legal value of max_restarts */
  if (max_restarts < 0)  max_restarts = 0;

  /* Set preconditioning flag (enabling any preconditioner implies right 
     preconditioning, since FGMRES does not support left preconditioning) */
  preOnRight = ((pretype == PREC_RIGHT) || (pretype == PREC_BOTH) || (pretype == PREC_LEFT));

  /* Set scaling flags */
  scale1 = (s1 != NULL);
  scale2 = (s2 != NULL);

  /* Set vtemp to initial (unscaled) residual r_0 = b - A*x_0. */
  if (N_VDotProd(x, x) == ZERO) {
    N_VScale(ONE, b, vtemp);
  } else {
    ier = atimes(A_data, x, vtemp);
    if (ier != 0)
      return((ier < 0) ? SPFGMR_ATIMES_FAIL_UNREC : SPFGMR_ATIMES_FAIL_REC);
    N_VLinearSum(ONE, b, -ONE, vtemp, vtemp);
  }

  /* Apply left scaling to vtemp = r_0 to fill V[0]. */
  if (scale1) {
    N_VProd(s1, vtemp, V[0]);   
  } else {
    N_VScale(ONE, vtemp, V[0]);
  }

  /* Set r_norm = beta to L2 norm of V[0] = s1 r_0, and return if small */
  *res_norm = r_norm = beta = SUNRsqrt(N_VDotProd(V[0], V[0]));
  if (r_norm <= delta)
    return(SPFGMR_SUCCESS);

  /* Initialize rho to avoid compiler warning message */
  rho = beta;

  /* Set xcor = 0. */
  N_VConst(ZERO, xcor);

  /* Begin outer iterations: up to (max_restarts + 1) attempts. */
  for (ntries=0; ntries<=max_restarts; ntries++) {
    
    /* Initialize the Hessenberg matrix Hes and Givens rotation
       product.  Normalize the initial vector V[0].             */
    for (i=0; i<=l_max; i++)
      for (j=0; j<l_max; j++)
        Hes[i][j] = ZERO;
    rotation_product = ONE;
    N_VScale(ONE/r_norm, V[0], V[0]);
    
    /* Inner loop: generate Krylov sequence and Arnoldi basis. */
    for (l=0; l<maxit; l++) {
      
      (*nli)++;
      
      krydim = l + 1;
      
      /* Generate A-tilde V[l], where A-tilde = s1 A P_inv s2_inv. */

      /*   Apply right scaling: vtemp = s2_inv V[l]. */
      if (scale2) N_VDiv(V[l], s2, vtemp);
      else N_VScale(ONE, V[l], vtemp);
      
      /*   Apply right preconditioner: vtemp = Z[l] = P_inv s2_inv V[l]. */ 
      if (preOnRight) {
        N_VScale(ONE, vtemp, V[l+1]);
        ier = psolve(P_data, V[l+1], vtemp, delta, PREC_RIGHT);
        (*nps)++;
        if (ier != 0)
          return((ier < 0) ? SPFGMR_PSOLVE_FAIL_UNREC : SPFGMR_PSOLVE_FAIL_REC);
      }
      N_VScale(ONE, vtemp, Z[l]);
      
      /*   Apply A: V[l+1] = A P_inv s2_inv V[l]. */
      ier = atimes(A_data, vtemp, V[l+1]);
      if (ier != 0)
        return((ier < 0) ? SPFGMR_ATIMES_FAIL_UNREC : SPFGMR_ATIMES_FAIL_REC);

      /*   Apply left scaling: V[l+1] = s1 A P_inv s2_inv V[l]. */
      if (scale1)  N_VProd(s1, V[l+1], V[l+1]);
      
      /* Orthogonalize V[l+1] against previous V[i]: V[l+1] = w_tilde. */
      if (gstype == CLASSICAL_GS) {
        if (ClassicalGS(V, Hes, l+1, l_max, &(Hes[l+1][l]),
                        vtemp, yg) != 0)
          return(SPFGMR_GS_FAIL);
      } else {
        if (ModifiedGS(V, Hes, l+1, l_max, &(Hes[l+1][l])) != 0) 
          return(SPFGMR_GS_FAIL);
      }
      
      /* Update the QR factorization of Hes. */
      if(QRfact(krydim, Hes, givens, l) != 0 )
        return(SPFGMR_QRFACT_FAIL);
      
      /* Update residual norm estimate; break if convergence test passes. */
      rotation_product *= givens[2*l+1];
      *res_norm = rho = SUNRabs(rotation_product*r_norm);
      if (rho <= delta) { converged = SUNTRUE; break; }
      
      /* Normalize V[l+1] with norm value from the Gram-Schmidt routine. */
      N_VScale(ONE/Hes[l+1][l], V[l+1], V[l+1]);
    }
    
    /* Inner loop is done.  Compute the new correction vector xcor. */
    
    /*   Construct g, then solve for y. */
    yg[0] = r_norm;
    for (i=1; i<=krydim; i++)  yg[i]=ZERO;
    if (QRsol(krydim, Hes, givens, yg) != 0)
      return(SPFGMR_QRSOL_FAIL);
    
    /*   Add correction vector Z_l y to xcor. */
    for (k=0; k<krydim; k++)
      N_VLinearSum(yg[k], Z[k], ONE, xcor, xcor);
    
    /* If converged, construct the final solution vector x and return. */
    if (converged) {
      N_VLinearSum(ONE, x, ONE, xcor, x);
      return(SPFGMR_SUCCESS);
    }
    
    /* Not yet converged; if allowed, prepare for restart. */
    if (ntries == max_restarts) break;
    
    /* Construct last column of Q in yg. */
    s_product = ONE;
    for (i=krydim; i>0; i--) {
      yg[i] = s_product*givens[2*i-2];
      s_product *= givens[2*i-1];
    }
    yg[0] = s_product;
    
    /* Scale r_norm and yg. */
    r_norm *= s_product;
    for (i=0; i<=krydim; i++)
      yg[i] *= r_norm;
    r_norm = SUNRabs(r_norm);
    
    /* Multiply yg by V_(krydim+1) to get last residual vector; restart. */
    N_VScale(yg[0], V[0], V[0]);
    for (k=1; k<=krydim; k++)
      N_VLinearSum(yg[k], V[k], ONE, V[0], V[0]);
    
  }
  
  /* Failed to converge, even after allowed restarts.
     If the residual norm was reduced below its initial value, compute
     and return x anyway.  Otherwise return failure flag. */
  if (rho < beta) {
    N_VLinearSum(ONE, x, ONE, xcor, x);
    return(SPFGMR_RES_REDUCED);
  }

  return(SPFGMR_CONV_FAIL); 
}