1 /*
2  * Copyright (C) FFLAS-FFPACK
3  * Written by Clément Pernet
4  * This file is Free Software and part of FFLAS-FFPACK.
5  *
6  * ========LICENCE========
7  * This file is part of the library FFLAS-FFPACK.
8  *
9  * FFLAS-FFPACK is free software: you can redistribute it and/or modify
10  * it under the terms of the  GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  * ========LICENCE========
23  *.
24  */
25 
26 
27 //-------------------------------------------------------------------------
28 //      Test suite for the Gaussian elimination routines: LUdivine and PLUQ
29 //-------------------------------------------------------------------------
30 
31 // #define MONOTONIC_CYLCES
32 // #define MONOTONIC_MOREPIVOTS
33 // #define MONOTONIC_FEWPIVOTS
34 #ifdef MONOTONIC_CYLCES
35 #define MONOTONIC_APPLYP
36 #endif
37 #ifdef MONOTONIC_MOREPIVOTS
38 #define MONOTONIC_APPLYP
39 #endif
40 #ifdef MONOTONIC_FEWPIVOTS
41 #define MONOTONIC_APPLYP
42 #endif
43 
44 #define BASECASE_K 37 // Forcing a lower base case to be able to test a few recursive steps with smallish dimensions
45 
46 
47 #define  __FFLASFFPACK_SEQUENTIAL
48 #define __LUDIVINE_CUTOFF 1
49 #include "fflas-ffpack/fflas-ffpack-config.h"
50 #include <givaro/modular-balanced.h>
51 #include <iostream>
52 #include <iomanip>
53 Givaro::Timer tperm, tgemm, tBC, ttrsm,trest,timtot;
54 size_t mvcnt = 0;
55 #include "fflas-ffpack/utils/timer.h"
56 #include "fflas-ffpack/fflas/fflas.h"
57 #include "fflas-ffpack/ffpack/ffpack.h"
58 #include "fflas-ffpack/utils/test-utils.h"
59 
60 #include "fflas-ffpack/utils/args-parser.h"
61 
62 #include <random>
63 
64 using namespace std;
65 using namespace FFPACK;
66 using namespace FFLAS;
67 
68 /*! Tests the LUdivine routine.
69  * @tparam Field Field
70  * @tparam Diag  Unit diagonal in U
71  * @tparam Trans
72  * @param F field
73  * @param A Matrix (preallocated)
74  * @param r rank of A
75  * @param m rows
76  * @param n cols
77  * @param lda leading dim of A
78  * @return 0 iff correct, 1 otherwise
79  */
80 template<class Field, FFLAS::FFLAS_DIAG diag, FFLAS_TRANSPOSE trans>
test_LUdivine(const Field & F,typename Field::ConstElement_ptr A,size_t lda,size_t r,size_t m,size_t n)81 bool test_LUdivine(const Field & F,
82                    typename Field::ConstElement_ptr A, size_t lda,
83                    size_t r, size_t m, size_t n)
84 {
85     bool fail = false;
86     typename Field::Element_ptr B = fflas_new(F,m,lda) ;
87     fassign(F,m,n,A,lda,B,lda);
88 
89     size_t maxP, maxQ ;
90 
91     if (trans == FflasTrans){
92         maxP = m;
93         maxQ = n;
94     }
95     else{ // trans == FflasNoTrans
96         maxP = n;
97         maxQ = m;
98     }
99 
100     size_t * P = fflas_new<size_t>(maxP) ;
101     size_t * Q = fflas_new<size_t>(maxQ) ;
102 
103     size_t R = LUdivine (F, diag, trans, m, n, B, lda, P, Q);
104 
105     if (R != r) {
106         std::cout << "rank is wrong (expecting " << r << " but got " << R << ")" << std::endl;
107         fflas_delete( B );
108         fflas_delete( P );
109         fflas_delete( Q );
110         return fail = true;
111     }
112 
113     /*  Build L,U */
114     typename Field::Element_ptr L = fflas_new(F, m, R);
115     typename Field::Element_ptr U = fflas_new(F, R, n);
116 
117     if (trans == FflasNoTrans){
118         getTriangular (F, FflasUpper, diag, m, n, R, B, lda, U, n, true);
119         getEchelonForm (F, FflasLower, (diag==FflasNonUnit)?FflasUnit:FflasNonUnit, m, n, R, Q, B, lda, L, R, true);
120         applyP (F, FflasRight, FflasNoTrans, R, 0, R, U, n, P);
121     } else {
122         getTriangular (F, FflasLower, diag, m, n, R, B, lda, L, R, true);
123         getEchelonForm (F, FflasUpper, (diag==FflasNonUnit)?FflasUnit:FflasNonUnit, m, n, R, Q, B, lda, U, n, true);
124         applyP (F, FflasLeft, FflasTrans, R, 0, R, L, R, P);
125     }
126     fgemm (F, FflasNoTrans, FflasNoTrans, m, n, R, 1.0, L, R, U, n, 0.0, B, lda);
127 
128     fail |= !fequal(F, m, n, A, lda, B, lda);
129 
130     if (fail){
131         FFLAS::WriteMatrix(cerr<<"A = "<<endl,F,m,n,A,lda);
132         FFLAS::WriteMatrix(cerr<<"LU = "<<endl,F,m,n,B,lda);
133         FFLAS::WriteMatrix(cerr<<"L = "<<endl,F,m,R,L,R);
134         FFLAS::WriteMatrix(cerr<<"U = "<<endl,F,R,n,U,n);
135     }
136 
137     fflas_delete( P);
138     fflas_delete( L);
139     fflas_delete( U);
140     fflas_delete( Q);
141     fflas_delete( B);
142     return fail;
143 }
144 
145 /*! Verifies that B = PLUQ where A stores [L\U]
146  * @tparam Field Field
147  * @tparam Diag  Unit diagonal in U
148  * @param F field
149  * @param A Matrix (preallocated)
150  * @param r rank of A
151  * @param m rows
152  * @param n cols
153  * @param lda leading dim of A
154  * @return 0 iff correct, 1 otherwise
155  */
156 template<class Field, FFLAS_DIAG diag>
verifPLUQ(const Field & F,typename Field::ConstElement_ptr A,size_t lda,typename Field::Element_ptr PLUQ,size_t ldpluq,size_t * P,size_t * Q,size_t m,size_t n,size_t R)157 bool verifPLUQ (const Field & F, typename Field::ConstElement_ptr A, size_t lda,
158                 typename Field::Element_ptr PLUQ, size_t ldpluq,
159                 size_t * P, size_t * Q, size_t m, size_t n, size_t R)
160 {
161 
162 
163     typename Field::Element_ptr X = fflas_new (F, m, n);
164     typename Field::Element_ptr L = fflas_new (F, m, R);
165     typename Field::Element_ptr	U = fflas_new (F, R, n);
166     fzero(F, m, R, L, R);
167     fzero(F, R, n, U, n);
168 
169     // FFLAS::WriteMatrix(std::cerr<<"PLUQ = "<<std::endl,F,m,n,PLUQ,ldpluq);
170     getTriangular(F, FflasUpper, diag, m,n,R, PLUQ, ldpluq, U, n, true);
171     getTriangular(F, FflasLower, (diag==FflasNonUnit)?FflasUnit:FflasNonUnit,
172                   m,n,R, PLUQ, ldpluq, L, R, true);
173     applyP( F, FflasLeft, FflasTrans, R,0,m, L, R, P);
174     applyP (F, FflasRight, FflasNoTrans, R,0,n, U, n, Q);
175     fgemm (F, FflasNoTrans, FflasNoTrans, m,n,R, F.one, L,R, U,n, F.zero, X,n);
176 
177     // FFLAS::WritePermutation(std::cerr<<"P = ",P,m);
178     // FFLAS::WritePermutation(std::cerr<<"Q = ",Q,n);
179     // FFLAS::WriteMatrix(std::cerr<<"L = "<<std::endl,F,m,R,L,R);
180     // FFLAS::WriteMatrix(std::cerr<<"U = "<<std::endl,F,R,n,U,n);
181 
182 
183     bool fail = false;
184     for(size_t i=0; i<m; ++i)
185         for (size_t j=0; j<n; ++j)
186             if (!F.areEqual (*(A+i*lda+j), *(X+i*n+j))){
187                 std::cerr << std::endl<<" A ["<<i<<","<<j<<"] = " << (*(A+i*lda+j))
188                 << " PLUQ ["<<i<<","<<j<<"] = " << (*(X+i*n+j));
189                 fail=true;
190             }
191     //FFLAS::WriteMatrix(std::cerr<<"X = "<<std::endl,F, m,n,X,n);
192     if (fail)
193         std::cerr << std::endl;
194 
195     fflas_delete( U);
196     fflas_delete( L);
197     fflas_delete( X);
198     return fail;
199 }
200 /*! Tests the LUdivine routine.
201  * @tparam Field Field
202  * @tparam Diag  Unit diagonal in U
203  * @tparam Trans
204  * @param F field
205  * @param A Matrix (preallocated)
206  * @param r rank of A
207  * @param m rows
208  * @param n cols
209  * @param lda leading dim of A
210  * @return 0 iff correct, 1 otherwise
211  */
212 template<class Field, FFLAS_DIAG diag, class RandIter>
test_pluq(const Field & F,typename Field::ConstElement_ptr A,size_t r,size_t m,size_t n,size_t lda,RandIter & G)213 bool test_pluq (const Field & F,
214                 typename Field::ConstElement_ptr A,
215                 size_t r, size_t m, size_t n, size_t lda, RandIter& G)
216 {
217     bool fail = false;
218     typedef typename Field::Element_ptr Element_ptr ;
219     Element_ptr B = fflas_new(F,m,lda) ;
220     fassign(F,m,n,A,lda,B,lda);
221 
222     size_t * P = fflas_new<size_t> (m);
223     size_t * Q = fflas_new<size_t> (n);
224 
225     ForceCheck_PLUQ<Field> checker (G,m,n,B,lda);
226 
227     size_t R = PLUQ (F, diag, m, n, B, lda, P, Q);
228 
229     try {
230         checker.check(B,lda,diag,R,P,Q);
231     } catch(FailurePLUQCheck &e) {
232         std::cout << m << 'x' << n << " pluq verification failed!\n";
233     }
234 
235     if (R != r) {
236         std::cout << "rank is wrong (expected " << r << " but got " << R << ")" << std::endl;
237         fflas_delete (B);
238         fflas_delete (P);
239         fflas_delete (Q);
240         return fail = true;
241     }
242     fail |=  verifPLUQ<Field,diag> (F,A, lda, B, lda, P, Q, m, n, r);
243     fflas_delete (B);
244     fflas_delete(P);
245     fflas_delete(Q);
246     return fail;
247 }
248 
249 template<class Field, FFLAS_DIAG diag, FFLAS_TRANSPOSE trans, class RandIter>
launch_test(const Field & F,size_t r,size_t m,size_t n,RandIter & G)250 bool launch_test(const Field & F,
251                  size_t r,
252                  size_t m, size_t n, RandIter& G)
253 {
254     //typedef typename Field::Element Element ;
255     typedef typename Field::Element_ptr Element_ptr ;
256     bool fail = false ;
257     { /*  user given and lda bigger */
258         size_t lda = n+10 ;
259         Element_ptr A = fflas_new (F, m, lda);
260         RandomMatrixWithRankandRandomRPM(F,m,n,r,A,lda,G);
261         fail |= test_LUdivine<Field,diag,trans>(F,A,lda,r,m,n);
262         RandomMatrixWithRankandRandomRPM(F,m,n,r,A,lda,G);
263         fail |= test_pluq<Field,diag>(F,A,r,m,n,lda,G);
264         if (fail) std::cout << "failed at big lda" << std::endl;
265         fflas_delete( A );
266     }
267     { /*  user given and lda bigger. Rank is max */
268         size_t lda = n+10 ;
269         size_t R = std::min(m,n);
270         Element_ptr A = fflas_new (F, m, lda);
271         RandomMatrixWithRankandRandomRPM(F,m,n,R,A,lda,G);
272         fail |= test_LUdivine<Field,diag,trans>(F,A,lda,R,m,n);
273         RandomMatrixWithRankandRandomRPM(F,m,n,R,A,lda,G);
274         fail |= test_pluq<Field,diag>(F,A,R,m,n,lda,G);
275         if (fail) std::cout << "failed at big lda max rank" << std::endl;
276         fflas_delete( A );
277     }
278     { /*  user given and lda bigger. Rank is min */
279         size_t lda = n+10 ;
280         size_t R = 0;
281         Element_ptr A = fflas_new (F, m, lda);
282         RandomMatrixWithRankandRandomRPM(F,m,n,R,A,lda,G);
283         fail |= test_LUdivine<Field,diag,trans>(F,A,lda,R,m,n);
284         RandomMatrixWithRankandRandomRPM(F,m,n,R,A,lda,G);
285         fail |= test_pluq<Field,diag>(F,A,R,m,n,lda,G);
286         if (fail) std::cout << "failed at big lda, rank 0" << std::endl;
287         fflas_delete( A );
288     }
289     { /*  square  */
290         size_t M = std::max(m,n);
291         size_t N = M ;
292         size_t R = M/2 ;
293         size_t lda = N+10 ;
294         Element_ptr A = fflas_new (F, M, lda);
295         RandomMatrixWithRankandRandomRPM(F,M,N,R,A,lda,G);
296         fail |= test_LUdivine<Field,diag,trans>(F,A,lda,R,M,N);
297         RandomMatrixWithRankandRandomRPM(F,M,N,R,A,lda,G);
298         fail |= test_pluq<Field,diag>(F,A,R,M,N,lda,G);
299         if (fail) std::cout << "failed at square" << std::endl;
300         fflas_delete( A );
301     }
302     { /*  wide  */
303         size_t M = std::max(m,n);
304         size_t N = 2*M ;
305         size_t R = 3*M/4 ;
306         size_t lda = N+5 ;
307         Element_ptr A = fflas_new (F, M, lda);
308         RandomMatrixWithRankandRandomRPM(F,M,N,R,A,lda,G);
309         fail |= test_LUdivine<Field,diag,trans>(F,A,lda,R,M,N);
310         RandomMatrixWithRankandRandomRPM(F,M,N,R,A,lda,G);
311         fail |= test_pluq<Field,diag>(F,A,R,M,N,lda,G);
312         if (fail) std::cout << "failed at wide" << std::endl;
313         fflas_delete( A );
314     }
315     { /*  narrow  */
316         size_t M = std::max(m,n);
317         size_t N = M/2 ;
318         size_t R = 3*M/8 ;
319         size_t lda = N+5 ;
320         Element_ptr A = fflas_new (F, M, lda);
321         RandomMatrixWithRankandRandomRPM(F,M,N,R,A,lda,G);
322         fail |= test_LUdivine<Field,diag,trans>(F,A,lda,R,M,N);
323         RandomMatrixWithRankandRandomRPM(F,M,N,R,A,lda,G);
324         fail |= test_pluq<Field,diag>(F,A,R,M,N,lda,G);
325         if (fail) std::cout << "failed at narrow" << std::endl;
326         fflas_delete( A );
327     }
328     return !fail;
329 }
330 
331 template<class Field>
run_with_field(Givaro::Integer q,uint64_t b,size_t m,size_t n,size_t r,size_t iters,uint64_t seed)332 bool run_with_field(Givaro::Integer q, uint64_t b, size_t m, size_t n, size_t r, size_t iters, uint64_t seed){
333     bool ok = true ;
334     int nbit=(int)iters;
335 
336     while (ok &&  nbit){
337         // choose Field
338         Field* F= chooseField<Field>(q,b,seed);
339         if (F==nullptr)
340             return true;
341         typename Field::RandIter G(*F,0,seed++);
342         std::ostringstream oss;
343         F->write(oss);
344 
345         std::cout.fill('.');
346         std::cout<<"Checking ";
347         std::cout.width(40);
348         std::cout<<oss.str();
349         std::cout<<" ... ";
350 
351 
352         ok = ok && launch_test<Field,FflasUnit,FflasNoTrans>    (*F,r,m,n,G);
353         ok = ok && launch_test<Field,FflasUnit,FflasTrans>      (*F,r,m,n,G);
354         ok = ok && launch_test<Field,FflasNonUnit,FflasNoTrans> (*F,r,m,n,G);
355         ok = ok && launch_test<Field,FflasNonUnit,FflasTrans>   (*F,r,m,n,G);
356 
357 #if 0 /*  may be bogus */
358         ok = ok && launch_test_append<Field,FflasUnit,FflasNoTrans>   (*F,r,m,n,G);
359         ok = ok && launch_test_append<Field,FflasNonUnit,FflasNoTrans>(*F,r,m,n,G);
360         ok = ok && launch_test_append<Field,FflasUnit,FflasTrans>     (*F,r,m,n,G);
361         ok = ok && launch_test_append<Field,FflasNonUnit,FflasTrans>  (*F,r,m,n,G);
362 #endif
363         nbit--;
364         if ( !ok )
365             //std::cout << "\033[1;31mFAILED\033[0m "<<std::endl;
366             std::cout << "FAILED "<<std::endl;
367         else
368             //std::cout << "\033[1;32mPASSED\033[0m "<<std::endl;
369             std::cout << "PASSED "<<std::endl;
370         delete F;
371     }
372     return ok;
373 }
374 
main(int argc,char ** argv)375 int main(int argc, char** argv)
376 {
377     cerr<<setprecision(20);
378     Givaro::Integer q=-1;
379     size_t b=0;
380     size_t m=90;
381     size_t n=93;
382     size_t r=50;
383     size_t iters=3;
384     bool loop=false;
385     uint64_t seed = getSeed();
386 
387     Argument as[] = {
388         { 'q', "-q Q", "Set the field characteristic (-1 for random).",         TYPE_INTEGER , &q },
389         { 'b', "-b B", "Set the bitsize of the field characteristic.",  TYPE_INT , &b },
390         { 'm', "-m M", "Set the row dimension of the matrix.",      TYPE_INT , &m },
391         { 'n', "-n N", "Set the column dimension of the matrix.", TYPE_INT , &n },
392         { 'r', "-r R", "Set the rank.", TYPE_INT , &r },
393         { 'i', "-i R", "Set number of repetitions.",            TYPE_INT , &iters },
394         { 'l', "-loop Y/N", "run the test in an infinite loop.", TYPE_BOOL , &loop },
395         { 's', "-s seed", "Set seed for the random generator", TYPE_UINT64, &seed },
396         END_OF_ARGUMENTS
397     };
398 
399     parseArguments(argc,argv,as);
400 
401     if (r > std::min (m,n))
402         r = std::min (m, n);
403 
404     srand(seed);
405 
406     bool ok=true;
407     do{
408         ok = ok &&run_with_field<Givaro::Modular<float> >           (q,b,m,n,r,iters,seed);
409         ok = ok &&run_with_field<Givaro::Modular<double> >          (q,b,m,n,r,iters,seed);
410         ok = ok &&run_with_field<Givaro::ModularBalanced<float> >   (q,b,m,n,r,iters,seed);
411         ok = ok &&run_with_field<Givaro::ModularBalanced<double> >  (q,b,m,n,r,iters,seed);
412         ok = ok &&run_with_field<Givaro::Modular<int32_t> >         (q,b,m,n,r,iters,seed);
413         ok = ok &&run_with_field<Givaro::ModularBalanced<int32_t> > (q,b,m,n,r,iters,seed);
414         ok = ok &&run_with_field<Givaro::Modular<int64_t> >         (q,b,m,n,r,iters,seed);
415         ok = ok &&run_with_field<Givaro::ModularBalanced<int64_t> > (q,b,m,n,r,iters,seed);
416         ok = ok &&run_with_field<Givaro::Modular<Givaro::Integer> > (q,5,m/6,n/6,r/6,iters,seed);
417         ok = ok &&run_with_field<Givaro::Modular<Givaro::Integer> > (q,(b?b:512),m/6,n/6,r/6,iters,seed);
418     } while (loop && ok);
419 
420     return !ok;
421 }
422 /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
423 // vim:sts=4:sw=4:ts=4:et:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s
424