1 /*******************************************************************************
2  * Copyright (c) 2018, College of William & Mary
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the College of William & Mary nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE COLLEGE OF WILLIAM & MARY BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * PRIMME: https://github.com/primme/primme
28  * Contact: Andreas Stathopoulos, a n d r e a s _at_ c s . w m . e d u
29  *******************************************************************************
30  * File: primme_c.c
31  *
32  * Purpose - Real, SCALAR precision front end to the multimethod eigensolver
33  *
34  * For the moment please cite the following two papers:
35  *
36  *  A. Stathopoulos, Nearly optimal preconditioned methods for hermitian
37  *    eigenproblems under limited memory. Part I: Seeking one eigenvalue,
38  *    Tech Report: WM-CS-2005-03, July, 2005. To appear in SISC.
39  *  A. Stathopoulos and J. R. McCombs, Nearly optimal preconditioned methods
40  *    for hermitian eigenproblems under limited memory. Part II: Seeking many
41  *    eigenvalues, Tech Report: WM-CS-2006-02, June, 2006.
42  *
43  * Additional information on the algorithms appears in:
44  *
45  *  J. R. McCombs and A. Stathopoulos, Iterative Validation of Eigensolvers:
46  *    A Scheme for Improving the Reliability of Hermitian Eigenvalue Solvers
47  *    Tech Report: WM-CS-2005-02, July, 2005, to appear in SISC.
48  *  A. Stathopoulos, Locking issues for finding a large number of eigenvectors
49  *    of hermitian matrices, Tech Report: WM-CS-2005-03, July, 2005.
50  *
51  *   Some papers to be listed here. For the moment contact the author:
52  *
53  *                       andreas@cs.wm.edu
54  *
55  ******************************************************************************/
56 
57 #ifndef THIS_FILE
58 #define THIS_FILE "../eigs/primme_c.c"
59 #endif
60 
61 #include "numerical.h"
62 #include "template_normal.h"
63 #include "common_eigs.h"
64 #include "primme_interface.h"
65 /* Keep automatically generated headers under this section  */
66 #ifndef CHECK_TEMPLATE
67 #include "primme_c.h"
68 #include "main_iter.h"
69 #include "auxiliary_eigs.h"
70 #endif
71 
72 /*******************************************************************************
73  * Subroutine Xprimme - This routine is a front end used to perform
74  *    error checking on the input parameters, perform validation,
75  *    and make the call to main_iter.
76  *
77  * INPUT/OUTPUT ARRAYS AND PARAMETERS
78  * ----------------------------------
79  * evals  Contains the converged Ritz values upon return.   Should be of size
80  *        primme->numEvals.
81  *
82  * evecs  The local portions of the converged Ritz vectors.  The dimension of
83  *        the array is at least primme->nLocal*primme->numEvals
84  *
85  * resNorms  The residual norms of the converged Ritz vectors.  Should be of
86  *           size primme->numEvals
87  *
88  * primme  Structure containing various solver parameters and statistics
89  *         See readme.txt for INPUT/OUTPUT variables in primme
90  *
91  * Return Value
92  * ------------
93  *  0 - Success
94  *  1 - Reporting only memory space required
95  * -1 - Failure to allocate workspace
96  * -2 - Malloc failure in allocating a permutation integer array
97  * -3 - main_iter encountered a problem
98  * -4 ...-32 - Invalid input (parameters or primme struct) returned
99  *             by check_input()
100  *
101  ******************************************************************************/
102 
103 int Xprimme(XEVAL *evals, XSCALAR *evecs, XREAL *resNorms,
104             primme_params *primme) {
105 
106    return Xprimme_aux_Sprimme((void *)evals, (void *)evecs, (void *)resNorms, primme,
107          PRIMME_OP_SCALAR);
108 }
109 
110 // Definition for *hsprimme, *ksprimme, and *kcprimme
111 
112 #if defined(USE_HALF) || defined(USE_HALFCOMPLEX) ||                      \
113               defined(USE_HALF_MAGMA) || defined(USE_HALFCOMPLEX_MAGMA)
114 
115 #  ifdef USE_HERMITIAN
116       // Expand the terms {,magma_}{hs,ks}primme
117 #     define Xsprimme  CONCAT(CONCAT(STEM,USE_ARITH(hs,ks)),primme)
118 #  else
119       // Expand the terms {,magma_}kcprimme_normal
120 #     define Xsprimme  WITH_KIND(CONCAT(CONCAT(STEM,kc),primme))
121 #  endif
122 
123 int Xsprimme(KIND(float, PRIMME_COMPLEX_FLOAT) * evals, XSCALAR *evecs,
124       float *resNorms, primme_params *primme) {
125 
126    return Xprimme_aux_Sprimme((void *)evals, (void *)evecs, (void *)resNorms, primme,
127          primme_op_float);
128 }
129 
130 #  undef Xsprimme
131 #endif
inner_solve_Sprimme(int blockSize,SCALAR * x,PRIMME_INT ldx,SCALAR * Bx,PRIMME_INT ldBx,SCALAR * r,PRIMME_INT ldr,HREAL * rnorm,SCALAR * evecs,PRIMME_INT ldevecs,HSCALAR * Mfact,int * ipivot,HSCALAR * xKinvBx,SCALAR * LprojectorQ,PRIMME_INT ldLprojectorQ,SCALAR * LprojectorX,PRIMME_INT ldLprojectorX,SCALAR * LprojectorBQ,PRIMME_INT ldLprojectorBQ,SCALAR * LprojectorBX,PRIMME_INT ldLprojectorBX,SCALAR * RprojectorQ,PRIMME_INT ldRprojectorQ,SCALAR * RprojectorX,PRIMME_INT ldRprojectorX,int sizeLprojectorQ,int sizeLprojectorX,int sizeRprojectorQ,int sizeRprojectorX,SCALAR * sol,PRIMME_INT ldsol,HEVAL * eval,KIND (double,PRIMME_COMPLEX_DOUBLE)* shift,int * touch,double startTime,primme_context ctx)132 
133 /*******************************************************************************
134  * Subroutine Xprimme_aux - set defaults depending on the callee's type, and
135  *    call wrapper_Sprimme with type set in internalPrecision.
136  *
137  * INPUT/OUTPUT ARRAYS AND PARAMETERS
138  * ----------------------------------
139  * evals  Contains the converged Ritz values upon return.   Should be of size
140  *        primme->numEvals.
141  *
142  * evecs  The local portions of the converged Ritz vectors.  The dimension of
143  *        the array is at least primme->nLocal*primme->numEvals
144  *
145  * resNorms  The residual norms of the converged Ritz vectors.  Should be of
146  *           size primme->numEvals
147  *
148  * primme  the PRIMME parameters
149  *
150  * evals_resNorms_type The type of the arrays evals and resNorsm.
151  *
152  * Return Value
153  * ------------
154  * return  error code
155  ******************************************************************************/
156 
157 TEMPLATE_PLEASE
158 int Xprimme_aux_Sprimme(void *evals, void *evecs, void *resNorms,
159             primme_params *primme, primme_op_datatype evals_resNorms_type) {
160 
161 #ifdef SUPPORTED_TYPE
162 
163    /* Generate context */
164 
165    primme_context ctx = primme_get_context(primme);
166 
167    /* Set the current type as the default type for user's operators */
168 
169    if (primme->matrixMatvec && primme->matrixMatvec_type == primme_op_default)
170       primme->matrixMatvec_type = PRIMME_OP_SCALAR;
171    if (primme->massMatrixMatvec && primme->massMatrixMatvec_type == primme_op_default)
172       primme->massMatrixMatvec_type = PRIMME_OP_SCALAR;
173    if (primme->applyPreconditioner && primme->applyPreconditioner_type == primme_op_default)
174       primme->applyPreconditioner_type = PRIMME_OP_SCALAR;
175    if (primme->globalSumReal && primme->globalSumReal_type == primme_op_default)
176       primme->globalSumReal_type = PRIMME_OP_SCALAR;
177    if (primme->broadcastReal && primme->broadcastReal_type == primme_op_default)
178       primme->broadcastReal_type = PRIMME_OP_SCALAR;
179    if (primme->convTestFun && primme->convTestFun_type == primme_op_default)
180       primme->convTestFun_type = PRIMME_OP_SCALAR;
181    if (primme->monitorFun && primme->monitorFun_type == primme_op_default)
182       primme->monitorFun_type = PRIMME_OP_SCALAR;
183 
184    /* Number of returned eigenpairs */
185 
186    int outInitSize = 0;
187 
188    /* call primme for the internal working precision */
189 
190    int ret;
191    primme_op_datatype t = primme->internalPrecision;
192    if (t == primme_op_default) t = PRIMME_OP_SCALAR;
193    switch (t) {
194 #  ifdef SUPPORTED_HALF_TYPE
195    case primme_op_half:
196       CHKERRVAL(wrapper_Shprimme(evals, evecs, resNorms, evals_resNorms_type,
197                       PRIMME_OP_SCALAR, &outInitSize, ctx),
198             &ret);
199       break;
200 #  endif
201 #  ifndef PRIMME_WITHOUT_FLOAT
202    case primme_op_float:
203       CHKERRVAL(wrapper_Ssprimme(evals, evecs, resNorms, evals_resNorms_type,
204                       PRIMME_OP_SCALAR, &outInitSize, ctx),
205             &ret);
206       break;
207 #  endif
208    case primme_op_double:
209       CHKERRVAL(wrapper_Sdprimme(evals, evecs, resNorms, evals_resNorms_type,
210                       PRIMME_OP_SCALAR, &outInitSize, ctx),
211             &ret);
212       break;
213 #  ifdef PRIMME_WITH_NATIVE_QUAD
214    case primme_op_quad:
215       CHKERRVAL(wrapper_Sqprimme(evals, evecs, resNorms, evals_resNorms_type,
216                       PRIMME_OP_SCALAR, &outInitSize, ctx),
217             &ret);
218       break;
219 #  endif
220    default: ret = PRIMME_FUNCTION_UNAVAILABLE;
221    }
222 
223    /* Free context */
224 
225    primme_free_context(ctx);
226 
227    /* Set the number of returned eigenpairs */
228 
229    primme->initSize = outInitSize;
230 
231    return ret;
232 #else
233 
234    (void)evals;
235    (void)evecs;
236    (void)resNorms;
237    (void)evals_resNorms_type;
238 
239    primme->initSize = 0;
240    return PRIMME_FUNCTION_UNAVAILABLE;
241 #endif /* SUPPORTED_TYPE */
242 }
243 
244 
245 #ifdef SUPPORTED_TYPE
246 
247 /*******************************************************************************
248  * Subroutine wrapper_Sprimme - Perform error checking on the input parameters,
249  *    and make the call to main_iter.
250  *
251  * INPUT/OUTPUT ARRAYS AND PARAMETERS
252  * ----------------------------------
253  * evals  Contains the converged Ritz values upon return.   Should be of size
254  *        primme->numEvals.
255  *
256  * evecs  The local portions of the converged Ritz vectors.  The dimension of
257  *        the array is at least primme->nLocal*primme->numEvals
258  *
259  * resNorms  The residual norms of the converged Ritz vectors.  Should be of
260  *           size primme->numEvals
261  *
262  * evals_resNorms_type The type of the arrays evals and resNorsm.
263  *
264  * evecs_type The type of the array evecs
265  *
266  * outInitSize The number of columns returned back.
267  *
268  * ctx    primme context
269  *
270  * Return Value
271  * ------------
272  * return  error code
273  ******************************************************************************/
274 
275 
276 TEMPLATE_PLEASE
277 int wrapper_Sprimme(void *evals, void *evecs, void *resNorms,
278       primme_op_datatype evals_resNorms_type, primme_op_datatype evecs_type,
279       int *outInitSize, primme_context ctx) {
280 
281    primme_params *primme = ctx.primme;
282 
283    /* In case of error, return initSize = 0 */
284 
285    *outInitSize = 0;
286 
287    /* zero out the timer */
288 
289    double t0 = primme_wTimer();
290 
291    /* Set some defaults for sequential programs */
292 
293    if (primme->numProcs <= 1 && evals != NULL && evecs != NULL &&
294          resNorms != NULL) {
295       primme->nLocal = primme->n;
296       primme->procID = 0;
297    }
298 
299    /* Set some defaults  */
300 
301    primme_set_defaults(primme);
302 
303    /* Observed orthogonality issues finding the largest/smallest values in  */
304    /* single precision. Computing V'*B*V and solving the projected problem  */
305    /* V'AVx = V'BVxl mitigates the problem.                                 */
306    /* Also if maxBlockSize > 1, the code uses the block orthogonalization   */
307    /* instead of Gram-Schmidt. But the current block orthogonalization does */
308    /* not produce a machine precision orthonormal basis, and we deal with   */
309    /* this by computing V'*B*V also in this case.                           */
310 
311 
312    if (primme->orth == primme_orth_default) {
313       if (PRIMME_OP_SCALAR <= primme_op_float || primme->maxBlockSize > 1) {
314          primme->orth = primme_orth_explicit_I;
315       } else {
316          primme->orth = primme_orth_implicit_I;
317       }
318    }
319 
320    /* If we are free to choose the leading dimension of V and W, use    */
321    /* a multiple of PRIMME_BLOCK_SIZE. This may improve the performance */
322    /* of Num_update_VWXR_Sprimme and Bortho_block_Sprimme.              */
323 
324    if (primme->ldOPs == -1) {
325       if (PRIMME_BLOCK_SIZE < INT_MAX) {
326          primme->ldOPs = min(((primme->nLocal + PRIMME_BLOCK_SIZE - 1)
327                   /PRIMME_BLOCK_SIZE)*PRIMME_BLOCK_SIZE, primme->nLocal);
328       } else {
329          primme->ldOPs = primme->nLocal;
330       }
331    }
332 
333    /* Deprecated input:                                              */
334 
335    if (evals == NULL && evecs == NULL && resNorms == NULL)
336       return 0;
337 
338    /* Reset random number seed if inappropriate for DLARENV */
339    /* Yields unique quadruples per proc if procID < 4096^3  */
340 
341    if (primme->iseed[0]<0 || primme->iseed[0]>4095) primme->iseed[0] =
342       primme->procID % 4096;
343    if (primme->iseed[1]<0 || primme->iseed[1]>4095) primme->iseed[1] =
344       (int)(primme->procID/4096+1) % 4096;
345    if (primme->iseed[2]<0 || primme->iseed[2]>4095) primme->iseed[2] =
346       (int)((primme->procID/4096)/4096+2) % 4096;
347    if (primme->iseed[3]<0 || primme->iseed[3]>4095) primme->iseed[3] =
348       (2*(int)(((primme->procID/4096)/4096)/4096)+1) % 4096;
349 
350    /* Set default convTetFun  */
351 
352    if (!primme->convTestFun) {
353       primme->convTestFun = convTestFunAbsolute;
354       primme->convTestFun_type = PRIMME_OP_SCALAR;
355       if (primme->eps == 0.0) {
356          primme->eps = MACHINE_EPSILON * 1e4;
357          /* The default value of eps is too much for half precision */
358          if (primme->eps >= 1.0) primme->eps = 0.1;
359       }
360    }
361 
362    /* Set default monitor     */
363 
364    if (!primme->monitorFun) {
365       primme->monitorFun = default_monitor;
366       primme->monitorFun_type = PRIMME_OP_SCALAR;
367    }
368 
369    /* Check primme input data for bounds, correct values etc. */
370 
371    CHKERR(coordinated_exit(check_params_coherence(ctx), ctx));
372    CHKERR(check_input(evals, evecs, resNorms, primme))
373 
374    /* Cast evals, evecs and resNorms to working precision */
375 
376    HEVAL *evals0;
377    HREAL *resNorms0;
378    SCALAR *evecs0;
379    CHKERR(KIND(Num_matrix_astype_RHprimme, Num_matrix_astype_SHprimme)(evals, 1,
380          primme->numEvals, 1, evals_resNorms_type, (void **)&evals0, NULL,
381          PRIMME_OP_HREAL, 1 /* alloc */, 0 /* not copy */, ctx));
382    PRIMME_INT ldevecs0;
383    CHKERR(Num_matrix_astype_Sprimme(evecs, primme->nLocal,
384          primme->numOrthoConst + max(primme->numEvals, primme->initSize),
385          primme->ldevecs, evecs_type, (void **)&evecs0, &ldevecs0,
386          PRIMME_OP_SCALAR, 1 /* alloc */,
387          primme->numOrthoConst + primme->initSize > 0 ? 1 : 0 /* copy? */,
388          ctx));
389    CHKERR(Num_matrix_astype_RHprimme(resNorms, 1, primme->numEvals,
390          1, evals_resNorms_type, (void **)&resNorms0, NULL, PRIMME_OP_HREAL,
391          1 /* alloc */, 0 /* not copy */, ctx));
392 
393    /* Call the solver */
394 
395    int ret, numRet;
396    CHKERR(coordinated_exit(main_iter_Sprimme(evals0, evecs0, ldevecs0,
397                                  resNorms0, t0, &ret, &numRet, ctx),
398          ctx));
399 
400    /* Copy back evals, evecs and resNorms */
401 
402    CHKERR(KIND(Num_matrix_astype_RHprimme, Num_matrix_astype_SHprimme)(evals0,
403          1, numRet, 1, PRIMME_OP_HREAL, (void **)&evals, NULL,
404          evals_resNorms_type, -1 /* destroy */, 1 /* copy */, ctx));
405    CHKERR(Num_copy_matrix_astype_Sprimme(evecs0, 0, primme->numOrthoConst,
406          primme->nLocal, numRet, ldevecs0, PRIMME_OP_SCALAR, evecs, 0,
407          primme->numOrthoConst, primme->ldevecs, evecs_type, ctx));
408    if (evecs != evecs0) {
409       CHKERR(Num_free_Sprimme(evecs0, ctx));
410    }
411    CHKERR(Num_matrix_astype_RHprimme(resNorms0, 1, numRet, 1, PRIMME_OP_HREAL,
412          (void **)&resNorms, NULL, evals_resNorms_type, -1 /* destroy */,
413          1 /* copy */, ctx));
414 
415    /* If no error, return initSize */
416 
417    *outInitSize = primme->initSize;
418 
419    primme->stats.elapsedTime = primme_wTimer() - t0;
420    return ret;
421 }
422 
423 
424 /******************************************************************************
425  * Subroutine check_input - checks the value of the input arrays, evals,
426  *    evecs, and resNorms and the values of primme_params.
427  *
428  * INPUT
429  * -----
430  *  evals, evecs, resNorms   Output arrays for primme
431  *  primme                   the main structure of parameters
432  *
433  * return value -   0    If input parameters in primme are appropriate
434  *                 <0    Inappropriate input parameters were found
435  *
436  ******************************************************************************/
437 STATIC int check_input(
438       void *evals, void *evecs, void *resNorms, primme_params *primme) {
439    int ret;
440    ret = 0;
441 
442    if (primme == NULL)
443       ret = -4;
444    else if (primme->n < 0 || primme->nLocal < 0 || primme->nLocal > primme->n)
445       ret = -5;
446    else if (primme->numProcs < 1)
447       ret = -6;
448    else if (primme->matrixMatvec == NULL)
449       ret = -7;
450    else if (primme->applyPreconditioner == NULL &&
451             primme->correctionParams.precondition > 0 )
452       ret = -8;
453    else if (primme->numEvals > primme->n)
454       ret = -10;
455    else if (primme->numEvals < 0)
456       ret = -11;
457    else if (primme->convTestFun != NULL && fabs(primme->eps) != 0.0L &&
458             primme->eps < MACHINE_EPSILON)
459       ret = -12;
460    else if ( primme->target != primme_smallest  &&
461              primme->target != primme_largest  &&
462              primme->target != primme_largest_abs  &&
463              primme->target != primme_closest_geq  &&
464              primme->target != primme_closest_leq  &&
465              primme->target != primme_closest_abs    )
466       ret = -13;
467    else if (primme->numOrthoConst < 0 || primme->numOrthoConst > primme->n)
468       ret = -16;
469    else if (primme->maxBasisSize < 2 && primme->n > 2)
470       ret = -17;
471    else if (primme->minRestartSize < 0 || (primme->minRestartSize == 0
472                                     && primme->n > 2 && primme->numEvals > 0))
473       ret = -18;
474    else if (primme->maxBlockSize < 0
475              || (primme->maxBlockSize == 0 && primme->numEvals > 0))
476       ret = -19;
477    else if (primme->restartingParams.maxPrevRetain < 0)
478       ret = -20;
479    else if (primme->initSize < 0)
480       ret = -22;
481    else if (primme->locking == 0 && primme->initSize > primme->maxBasisSize)
482       ret = -23;
483    else if (primme->locking > 0 && primme->initSize > primme->numEvals)
484       ret = -24;
485    else if (primme->minRestartSize + primme->restartingParams.maxPrevRetain
486                    >= primme->maxBasisSize && primme->n > primme->maxBasisSize)
487       ret = -25;
488    else if (primme->minRestartSize > primme->n && primme->n > 2)
489       ret = -26;
490    else if (primme->printLevel < 0 || primme->printLevel > 5)
491       ret = -27;
492    else if (primme->correctionParams.convTest != primme_full_LTolerance &&
493             primme->correctionParams.convTest != primme_decreasing_LTolerance &&
494             primme->correctionParams.convTest != primme_adaptive_ETolerance &&
495             primme->correctionParams.convTest != primme_adaptive )
496       ret = -28;
497    else if (primme->correctionParams.convTest == primme_decreasing_LTolerance &&
498             primme->correctionParams.relTolBase <= 1.0L )
499       ret = -29;
500    else if (evals == NULL)
501       ret = -30;
502    else if (evecs == NULL || Num_check_pointer_Sprimme(evecs))
503       ret = -31;
504    else if (resNorms == NULL)
505       ret = -32;
506    else if (primme->locking == 0 && primme->minRestartSize < primme->numEvals &&
507             primme->n > 2)
508       ret = -33;
509    else if (primme->ldevecs < primme->nLocal)
510       ret = -34;
511    else if (primme->ldOPs != 0 && primme->ldOPs < primme->nLocal)
512       ret = -35;
513    else if (primme->locking == 0
514          && (primme->target == primme_closest_leq
515             || primme->target == primme_closest_geq))
516       ret = -38;
517    else if (primme->massMatrixMatvec &&
518             primme->projectionParams.projection != primme_proj_RR)
519       ret = -39;
520    /* Please keep this if instruction at the end */
521    else if ( primme->target == primme_largest_abs ||
522              primme->target == primme_closest_geq ||
523              primme->target == primme_closest_leq ||
524              primme->target == primme_closest_abs   ) {
525       if (primme->numTargetShifts <= 0) {
526          ret = -14;
527       }
528       else if (primme->targetShifts == NULL ) {
529          ret = -15;
530       }
531    }
532 
533    return ret;
534 }
535 
536 /*******************************************************************************
537  * Subroutine convTestFunAbsolute - This routine implements primme_params.
538  *    convTestFun and return an approximate eigenpair converged when
539  *    resNorm < eps*|A| for standard problems, and
540  *    resNorm < (|A| + max(|\lambda|)*|B|)*eps for generalized problems
541  *
542  * INPUT ARRAYS AND PARAMETERS
543  * ---------------------------
544  * evec         The approximate eigenvector
545  * eval         The approximate eigenvalue
546  * rNorm        The norm of the residual vector
547  * primme       Structure containing various solver parameters
548  *
549  * OUTPUT PARAMETERS
550  * ----------------------------------
551  * isConv      if it isn't zero the approximate pair is marked as converged
552  ******************************************************************************/
553 
554 STATIC void convTestFunAbsolute(double *eval, void *evec, double *rNorm,
555       int *isConv, primme_params *primme, int *ierr) {
556 
557    (void)eval; /* unused parameter */
558    (void)evec; /* unused parameter */
559 
560    if (primme->massMatrixMatvec == NULL) {
561       *isConv = *rNorm < max(primme->eps, MACHINE_EPSILON * 2) *
562                                problemNorm_Sprimme(0, primme);
563    }
564    else {
565       *isConv = *rNorm < max(primme->eps, MACHINE_EPSILON) *
566                                problemNorm_Sprimme(0, primme);
567    }
568    *ierr = 0;
569 }
570 
571 /*******************************************************************************
572  * Subroutine default_monitor - report iterations, #MV, residual norm,
573  *    eigenvalues, etc. at every inner/outer iteration and when some pair
574  *    converges.
575  *
576  * INPUT ARRAYS AND PARAMETERS
577  * ---------------------------
578  * basisEvals   The approximate eigenvalues of the basis
579  * basisSize    The size of the basis
580  * basisFlags   The state of every approximate pair of the basis (see conv_flags)
581  * iblock       Indices of the approximate pairs in the block
582  * blockSize    The size of the block
583  * basisNorms   The approximate residual norms of the pairs of the basis
584  * numConverged The number of pairs converged in the basis and the locked pairs
585  *              (this value isn't monotonic!)
586  * lockedEvals  The locked eigenvalues
587  * numLocked    The number of pairs locked
588  * lockedFlags  The state of each locked eigenpair (see conv_flags)
589  * lockedNorms  The residual norms of the locked pairs
590  * inner_its    The number of performed QMR iterations in the current correction equation
591  * LSRes        The residual norm of the linear system at the current QMR iteration
592  * event        The event reported
593  * primme       Structure containing various solver parameters and statistics
594  *
595  * OUTPUT
596  * ------
597  * err          Error code
598  *
599  ******************************************************************************/
600 
601 STATIC void default_monitor(void *basisEvals_, int *basisSize, int *basisFlags,
602       int *iblock, int *blockSize, void *basisNorms_, int *numConverged,
603       void *lockedEvals_, int *numLocked, int *lockedFlags, void *lockedNorms_,
604       int *inner_its, void *LSRes_, const char *msg, double *time,
605       primme_event *event, primme_params *primme, int *err) {
606 
607    XEVAL *basisEvals = (XEVAL *)basisEvals_,
608          *lockedEvals = (XEVAL *)lockedEvals_;
609    XREAL *basisNorms = (XREAL *)basisNorms_,
610          *lockedNorms = (XREAL *)lockedNorms_, *LSRes = (XREAL *)LSRes_;
611 
612    assert(event != NULL && primme != NULL);
613 
614    /* Only print report if this is proc zero or it is profiling */
615    if (primme->outputFile &&
616          (primme->procID == 0 || *event == primme_event_profile)) {
617       switch(*event) {
618       case primme_event_outer_iteration:
619          assert(basisSize && (!*basisSize || (basisEvals && basisFlags)) &&
620                 blockSize && (!*blockSize || (iblock && basisNorms)) &&
621                 numConverged);
622          assert(!primme->locking ||
623                 (numLocked && (!*numLocked || (lockedEvals && lockedFlags &&
624                                                     lockedNorms))));
625          if (primme->printLevel >= 3) {
626             int i;  /* Loop variable */
627             int found;  /* Reported eigenpairs found */
628 
629             if (primme->locking)
630                found = *numLocked;
631             else
632                found = *numConverged;
633 
634             for (i=0; i < *blockSize; i++) {
635                fprintf(primme->outputFile,
636                      "OUT %" PRIMME_INT_P " conv %d blk %d MV %" PRIMME_INT_P
637                      " Sec %E EV %13E " KIND(, "%13E i ") "|r| %.3E\n",
638                      primme->stats.numOuterIterations, found, i,
639                      primme->stats.numMatvecs, primme->stats.elapsedTime,
640                      (double)EVAL_REAL_PART(basisEvals[iblock[i]]),
641 #ifndef USE_HERMITIAN
642                      (double)EVAL_IMAGINARY_PART(basisEvals[iblock[i]]),
643 #endif
644 
645                      (double)basisNorms[iblock[i]]);
646             }
647          }
648          break;
649       case primme_event_inner_iteration:
650          assert(basisSize && iblock && basisNorms && inner_its && LSRes);
651          (void)inner_its;
652          if (primme->printLevel >= 4) {
653             fprintf(primme->outputFile,
654                   "INN MV %" PRIMME_INT_P " Sec %e Eval %13E " KIND(
655                         , "%13E i ") "Lin|r| %.3e EV|r| %.3e\n",
656                   primme->stats.numMatvecs, primme->stats.elapsedTime,
657                   (double)EVAL_REAL_PART(basisEvals[iblock[0]]),
658 #ifndef USE_HERMITIAN
659                   (double)EVAL_IMAGINARY_PART(basisEvals[iblock[0]]),
660 #endif
661                   (double)*LSRes, (double)basisNorms[iblock[0]]);
662          }
663         break;
664       case primme_event_converged:
665          assert(numConverged && iblock && basisEvals && basisNorms);
666          if ((!primme->locking && primme->printLevel >= 2)
667                || (primme->locking && primme->printLevel >= 5))
668             fprintf(primme->outputFile,
669                   "#Converged %d eval[ %d ]= %13E " KIND(,
670                         "%13E i ") "norm %e Mvecs %" PRIMME_INT_P " Time %g\n",
671                   *numConverged, iblock[0],
672                   (double)EVAL_REAL_PART(basisEvals[iblock[0]]),
673 #ifndef USE_HERMITIAN
674                   (double)EVAL_IMAGINARY_PART(basisEvals[iblock[0]]),
675 #endif
676                   (double)basisNorms[iblock[0]], primme->stats.numMatvecs,
677                   primme->stats.elapsedTime);
678          break;
679       case primme_event_locked:
680          assert(numLocked && lockedEvals && lockedNorms && lockedFlags);
681          if (primme->printLevel >= 2) {
682             fprintf(primme->outputFile,
683                   "Lock epair[ %d ]= %13E " KIND(
684                         , "%13E i ") "norm %.4e Mvecs %" PRIMME_INT_P
685                                      " Time %.4e Flag %d\n",
686                   *numLocked - 1,
687                   (double)EVAL_REAL_PART(lockedEvals[*numLocked - 1]),
688 #ifndef USE_HERMITIAN
689                   (double)EVAL_IMAGINARY_PART(lockedEvals[*numLocked - 1]),
690 #endif
691                   (double)lockedNorms[*numLocked - 1], primme->stats.numMatvecs,
692                   primme->stats.elapsedTime, lockedFlags[*numLocked - 1]);
693          }
694          break;
695       case primme_event_message:
696          assert(msg != NULL);
697          if (primme->printLevel >= 2) {
698             fprintf(primme->outputFile,
699                   "%s\n", msg);
700          }
701          break;
702       case primme_event_profile:
703          assert(msg != NULL && time != NULL);
704          if (primme->printLevel >= 3 && *time < 0.0) {
705             fprintf(primme->outputFile, "entering in %s proc %d\n", msg, primme->procID);
706          }
707          if (primme->printLevel >= 2 && *time >= 0.0) {
708             fprintf(primme->outputFile, "time %g for %s proc %d\n", *time, msg, primme->procID);
709          }
710          break;
711       default:
712          break;
713       }
apply_projected_preconditioner(SCALAR * v,PRIMME_INT ldv,SCALAR * Q,PRIMME_INT ldQ,SCALAR * RprojectorQ,PRIMME_INT ldRprojectorQ,SCALAR * x,PRIMME_INT ldx,SCALAR * RprojectorX,PRIMME_INT ldRprojectorX,int sizeRprojectorQ,int sizeRprojectorX,HSCALAR * xKinvBx,HSCALAR * Mfact,int * ipivot,SCALAR * result,PRIMME_INT ldresult,int blockSize,primme_context ctx)714       fflush(primme->outputFile);
715    }
716    *err = 0;
717 }
718 
719 /******************************************************************************
720  * check_params_coherence - check that all processes has the same values in
721  *    critical parameters.
722  *
723  * INPUT
724  * -----
725  *  primme         the main structure of parameters
726  *
727  * RETURN:
728  *    error code
729  ******************************************************************************/
730 
731 STATIC int check_params_coherence(primme_context ctx) {
732    primme_params *primme = ctx.primme;
733 
734    /* Check number of procs and procs with id zero */
735 
736    HREAL aux[2] = {(HREAL)1.0, (HREAL)(ctx.procID == 0 ? 1.0 : 0.0)};
737    CHKERR(globalSum_RHprimme(aux, 2, ctx));
738    CHKERRM((aux[0] > 1) != (ctx.numProcs > 1),
739          -1, "numProcs does not match the actual number of processes");
740    CHKERRM(aux[1] > 1, -1, "There is not a single process with ID zero");
741 
742    /* Check broadcast */
743 
744    HREAL val = 123, val0 = val;
745    CHKERR(broadcast_RHprimme(&val, 1, ctx));
746    CHKERRM(fabs(val - val0) > val0 * MACHINE_EPSILON * 1.3, -1,
747          "broadcast function does not work properly");
748 
749    /* Check that all processes has the same value for the next params */
750 
751    PARALLEL_CHECK(primme->n);
752    PARALLEL_CHECK(primme->numEvals);
753    PARALLEL_CHECK(primme->target);
754    PARALLEL_CHECK(primme->numTargetShifts);
755    PARALLEL_CHECK(primme->dynamicMethodSwitch);
756    PARALLEL_CHECK(primme->locking);
757    PARALLEL_CHECK(primme->initSize);
758    PARALLEL_CHECK(primme->numOrthoConst);
759    PARALLEL_CHECK(primme->maxBasisSize);
760    PARALLEL_CHECK(primme->minRestartSize);
761    PARALLEL_CHECK(primme->maxBlockSize);
762    PARALLEL_CHECK(primme->maxMatvecs);
763    PARALLEL_CHECK(primme->maxOuterIterations);
764    PARALLEL_CHECK(primme->aNorm);
765    PARALLEL_CHECK(primme->BNorm);
766    PARALLEL_CHECK(primme->invBNorm);
767    PARALLEL_CHECK(primme->eps);
768    PARALLEL_CHECK(primme->orth);
apply_skew_projector(SCALAR * Q,PRIMME_INT ldQ,SCALAR * Qhat,PRIMME_INT ldQhat,HSCALAR * Mfact,int * ipivot,int numCols,SCALAR * v,PRIMME_INT ldv,int blockSize,primme_context ctx)769    PARALLEL_CHECK(primme->initBasisMode);
770    PARALLEL_CHECK(primme->projectionParams.projection);
771    PARALLEL_CHECK(primme->restartingParams.maxPrevRetain);
772    PARALLEL_CHECK(primme->correctionParams.precondition);
773    PARALLEL_CHECK(primme->correctionParams.robustShifts);
774    PARALLEL_CHECK(primme->correctionParams.maxInnerIterations);
775    PARALLEL_CHECK(primme->correctionParams.projectors.LeftQ);
776    PARALLEL_CHECK(primme->correctionParams.projectors.LeftX);
777    PARALLEL_CHECK(primme->correctionParams.projectors.RightQ);
778    PARALLEL_CHECK(primme->correctionParams.projectors.RightX);
779    PARALLEL_CHECK(primme->correctionParams.projectors.SkewQ);
780    PARALLEL_CHECK(primme->correctionParams.projectors.SkewX);
781    PARALLEL_CHECK(primme->correctionParams.convTest);
782    PARALLEL_CHECK(primme->correctionParams.relTolBase);
783 
784    return 0;
785 }
786 
787 /******************************************************************************
788  * coordinated_exit - make sure that if main_iter returns error in some process,
789  *    then all processes return an error.
790  *
791  * INPUT
792  * -----
793  *  primme         the main structure of parameters
794  *
795  * RETURN:
796  *    error code
797  ******************************************************************************/
798 STATIC int coordinated_exit(int ret, primme_context ctx) {
799 
800    primme_params *primme = ctx.primme;
801 
802    if (ret != PRIMME_PARALLEL_FAILURE && primme->globalSumReal) {
803       HREAL pret = (HREAL)(ret != 0 ? 1 : 0);
804       int count = 1, ierr = 0;
805       CHKERRM(
806             (primme->globalSumReal(&pret, &pret, &count, primme, &ierr), ierr),
807             PRIMME_USER_FAILURE, "Error returned by 'globalSumReal' %d", ierr);
808       if (pret > 0.0) return ret ? ret : PRIMME_PARALLEL_FAILURE;
809    }
810 
811    return ret;
812 }
813 
814 #endif /* SUPPORTED_TYPE */
815