algebra.c
Go to the documentation of this file.
1 #include "algebra.h"
2 
16 #include <math.h>
17 #include <string.h>
18 
19 /* A generic header that must be re-implemented for each linear algebra package. */
20 
56 unsigned int AnalyzeKernel(unsigned int nr,unsigned int nc,double *mT,
57  unsigned int dof,double epsilon,
58  boolean computeRank,boolean checkRank,boolean getT,boolean getBase,
59  boolean *singular,unsigned int *rank,boolean **IR,double **T);
60 
61 
62 /* BEGIN GSL IMPLEMENTATION */
63 
64 #if _GSL
65 
76 void PrintGSLVector(FILE *f,char *label,gsl_vector *v);
77 
88 void PrintGSLPermutation(FILE *f,char *label,gsl_permutation *p);
89 
100 void PrintGSLMatrix(FILE *f,char *label,gsl_matrix *M);
101 
102 
103 /******************************************************************/
104 /* Private function definitions */
105 
106 unsigned int AnalyzeKernel(unsigned int nr,unsigned int nc,double *mT,
107  unsigned int dof,double epsilon,
108  boolean computeRank,boolean checkRank,boolean getT,boolean getBase,
109  boolean *singular,unsigned int *rank,boolean **IR,double **T)
110 {
111  unsigned int err;
112 
113  #if (_DEBUG<2)
114  gsl_set_error_handler_off();
115  #endif
116 
117  if (dof==nc)
118  {
119  /* Trivial case of a system without equations. We take this into account for completeness, but
120  this function should never be used in this case. */
121  *singular=FALSE;
122  *rank=0;
123 
124  if ((getT)&&(nc>0))
125  {
126  gsl_matrix_view vT;
127 
128  NEW(*T,nc*dof,double);
129  vT=gsl_matrix_view_array(*T,nc,dof);
130  gsl_matrix_set_identity (&(vT.matrix));
131  }
132  else
133  *T=NULL;
134 
135  if ((getBase)&&(nr>0))
136  {
137  unsigned int i;
138 
139  NEW(*IR,nr,boolean);
140  for(i=0;i<nr;i++)
141  (*IR)[i]=FALSE;
142  }
143  else
144  *IR=NULL;
145 
146  err=0;
147  }
148  else
149  {
150  gsl_matrix_view At;
151  gsl_vector *tau;
152  gsl_permutation *p;
153  int signum;
154  gsl_vector *norm;
155 
156  /* Define A^T */
157  At=gsl_matrix_view_array(mT,nc,nr);
158 
159  /* Extra space for the QR decomposition of At */
160  tau=gsl_vector_alloc((nr<nc?nr:nc));
161  p=gsl_permutation_alloc(nr);
162  norm=gsl_vector_alloc(nr);
163 
164  /* Decompose the matrix, with column pivoting to get the kernel. Note that
165  the permutation is not relevant for the kernel P*A*v=0 -> A*v=0 */
166  err=gsl_linalg_QRPT_decomp(&(At.matrix),tau,p,&signum,norm);
167 
168  if (err)
169  {
170  *singular=TRUE;
171  *rank=NO_UINT;
172  *T=NULL;
173  *IR=NULL;
174  err=3; /* Our error code for decomposition error */
175  }
176  else
177  {
178  unsigned int k,mrc;
179  double r;
180 
181  /***********************************************/
182  /* At is nc x nr */
183  /* Q is nc x nc */
184  /* R is nc x nr -> R^t is nr x nc */
185 
186  /* At=Q R P^t -> A= P R^t Q^t -> P^t A Q = R^t
187  Thus, the rows of R (columns of R^t) that are zero
188  indicate columns of Q that define part of the kernel.
189  Moreover, the norm of teh rows of R indicate how far
190  is from defining a new kernel basis vector. Sinc R
191  is triangular the norm of the row is the norm from
192  the diagonal to the end of the row (nr entries) */
193 
194  mrc=(nr<nc?nr:nc);
195 
196  if (computeRank)
197  {
198  /* Rank = number of not-null rows of R */
199  /* The rank can not be larger than the mrc=min(nr,nc) */
200  *rank=0;
201  for(k=0;k<mrc;k++)
202  {
203  /* Get the norm of row k of R from the diagonal to the end of the row */
204  r=NormWithStride(nr-k,(ROW_MAJOR?1:nc),&(mT[RC2INDEX(k,k,nc,nr)]));
205  if (r>epsilon)
206  (*rank)++;
207  }
208  if (dof==0)
209  {
210  *singular=FALSE;
211  /* temporary set 'dof' form '*rank' */
212  dof=nc-*rank;
213  }
214  else
215  *singular=((nc-dof)!=*rank);
216  }
217  else
218  {
219  if (dof==0)
220  Error("Null 'dof' parameter in AnalyzeKernel");
221  if (dof>nc)
222  Error("We can not have more 'dof' than variables");
223 
224  *rank=nc-dof;
225 
226  /* Check if row *rank-1 of R is actually not null. */
227  r=NormWithStride(nr-(*rank-1),(ROW_MAJOR?1:nc),&(mT[RC2INDEX(*rank-1,*rank-1,nc,nr)]));
228  *singular=(r<epsilon);
229 
230  if (checkRank)
231  {
232  if (*singular)
233  err=1; //Error("Singular point (increase N_DOF? Decrease EPSILON?)");
234 
235  /* Checking row '*rank' is actually null. */
236  r=NormWithStride(nr-*rank,(ROW_MAJOR?1:nc),&(mT[RC2INDEX(*rank,*rank,nc,nr)]));
237  if (r>epsilon)
238  err=2; //Error("Non-null kernel vector (decrease N_DOF? Increase EPSILON?)");
239  }
240  }
241 
242  #if (_DEBUG>0)
243  if ((dof>0)&&(!computeRank)&&(getT)&&(!getBase))
244  {
245  printf(" E=[ ");
246  for(k=*rank;k<mrc;k++)
247  {
248  r=NormWithStride(nr-k,(ROW_MAJOR?1:nc),&(mT[RC2INDEX(k,k,nc,nr)]));
249  printf("%.16f ",r);
250  }
251  printf("];\n");
252  }
253  #endif
254 
255  /* If requested, we define T and IR, even if the chart is singular. In this case
256  we return the most likely T and IR. */
257  if (getT)
258  {
259  unsigned int i;
260  gsl_vector_view vT;
261 
262  /* The kernel are the last 'dof' columns of Q */
263  /* Note that P^t A q_i = 0 <-> A q_i = 0 */
264  NEW(*T,nc*dof,double);
265  for(k=*rank,i=0;k<nc;k++,i++)
266  {
267  vT=gsl_vector_view_array_with_stride(&((*T)[RC2INDEX(0,i,nc,dof)]),(ROW_MAJOR?dof:1),nc);
268  gsl_vector_set_basis(&(vT.vector),k);
269  gsl_linalg_QR_Qvec(&(At.matrix),tau,&(vT.vector));
270  }
271  }
272  else
273  *T=NULL;
274 
275  if (getBase)
276  {
277  NEW(*IR,nr,boolean);
278  for(k=0;k<nr;k++)
279  (*IR)[k]=FALSE;
280  for(k=0;k<*rank;k++)
281  (*IR)[gsl_permutation_get(p,k)]=TRUE;
282  }
283  else
284  *IR=NULL;
285  }
286  gsl_vector_free(norm);
287  gsl_vector_free(tau);
288  gsl_permutation_free(p);
289  }
290  return(err);
291 }
292 
293 void PrintGSLVector(FILE *f,char *label,gsl_vector *v)
294 {
295  unsigned int i;
296 
297  if (label!=NULL)
298  fprintf(f,"%s=",label);
299  fprintf(f,"[");
300  for(i=0;i<v->size;i++)
301  fprintf(f,"%.16f ",gsl_vector_get(v,i));
302  fprintf(f,"];\n");
303 }
304 
305 void PrintGSLPermutation(FILE *f,char *label,gsl_permutation *p)
306 {
307  unsigned int i;
308 
309  if (label!=NULL)
310  fprintf(f,"%s=",label);
311  fprintf(f,"[");
312  for(i=0;i<p->size;i++)
313  fprintf(f,"%lu ",gsl_permutation_get(p,i));
314  fprintf(f,"];\n");
315 }
316 
317 void PrintGSLMatrix(FILE *f,char *label,gsl_matrix *M)
318 {
319  unsigned int i,j;
320 
321  if (label!=NULL)
322  fprintf(f,"%s=",label);
323  fprintf(f,"[");
324  for(i=0;i<M->size1;i++)
325  {
326  for(j=0;j<M->size2;j++)
327  fprintf(f,"%.16f ",gsl_matrix_get(M,i,j));
328  fprintf(f,"\n");
329  }
330  fprintf(f,"];\n");
331 }
332 
333 
334 /******************************************************************/
335 /* Public function definitions */
336 
337 int MatrixDeterminantSgn(double epsilon,unsigned int n,double *A)
338 {
339  int s;
340  gsl_matrix_view m;
341  gsl_permutation *p;
342 
343  #if (_DEBUG<2)
344  gsl_set_error_handler_off();
345  #endif
346 
347  /* We typically use this function for relatively large matrices
348  and, thus, we do not take into account the case where n<=3 */
349  p=gsl_permutation_alloc(n);
350  m=gsl_matrix_view_array(A,n,n);
351 
352  gsl_linalg_LU_decomp(&(m.matrix),p,&s);
353 
354  s=gsl_linalg_LU_sgndet(&(m.matrix),s);
355 
356  gsl_permutation_free(p);
357 
358  return(s);
359 }
360 
361 double MatrixDeterminant(unsigned int n,double *A)
362 {
363  double d;
364 
365  #if (_DEBUG<2)
366  gsl_set_error_handler_off();
367  #endif
368 
369  /* Observe that det(A)=det(A^t) and thus for the case 2 and 3
370  it does not matter if the matrix is stored ROW or COLUMN major */
371  switch(n)
372  {
373  case 1:
374  d=A[0];
375  break;
376  case 2:
377  d=A[0]*A[3]-A[1]*A[2];
378  break;
379  case 3:
380  d= A[0]*A[4]*A[8]+A[2]*A[3]*A[7]+A[1]*A[5]*A[6]
381  -A[2]*A[4]*A[6]-A[1]*A[3]*A[8]-A[0]*A[5]*A[7];
382  break;
383  default:
384  {
385  gsl_matrix_view m;
386  gsl_permutation *p;
387  int s;
388 
389  p=gsl_permutation_alloc(n);
390  m=gsl_matrix_view_array(A,n,n);
391 
392  gsl_linalg_LU_decomp(&(m.matrix),p,&s);
393 
394  d=gsl_linalg_LU_det(&(m.matrix),s);
395 
396  gsl_permutation_free(p);
397  }
398  }
399  return(d);
400 }
401 
402 void InitNewton(unsigned int nr,unsigned int nc,TNewton *n)
403 {
404 
405  #if (_DEBUG<2)
406  gsl_set_error_handler_off();
407  #endif
408 
409  n->nr=nr;
410  n->nc=nc;
411 
412  NEW(n->Ab,nr*nc,double);
413  NEW(n->bb,nr,double);
414 
415  n->A=gsl_matrix_view_array(n->Ab,n->nr,n->nc);
416  n->b=gsl_vector_view_array(n->bb,n->nr);
417 
418  n->w=gsl_vector_alloc(nc);
419 
420  /* V and S are initialized to zero since if nr<nc some parts
421  of them are actually not used. */
422  n->V=gsl_matrix_calloc(nc,nc);
423  n->S=gsl_vector_calloc(nc);
424  n->tmp=gsl_vector_alloc(nr<nc?nr:nc);
425 
426  if (nr<nc)
427  {
428  /* When doing the SVD of the transpose we actually
429  re-use memory for the non-transposed case. This
430  is done to safe memory but mainly to avoid copying
431  matrices. */
432  n->VT=gsl_matrix_submatrix(n->V,0,0,nc,nr);
433  n->UT=gsl_matrix_submatrix(&(n->A.matrix),0,0,nr,nr);
434  n->ST=gsl_vector_subvector(n->S,0,nr);
435  }
436 }
437 
438 int NewtonStep(double nullSingularValue,double *x,double *dif,TNewton *n)
439 {
440  int err;
441  unsigned int i,j;
442  double d;
443 
444  /* This is a replacement for tolerant_SV_decomp. See this function to
445  understand what we do here. */
446  if (n->nr<n->nc)
447  {
448  /* Store A^t in n->VT (which is actually a submatrix of n->V. Note that
449  the space for A will be over-write (to store U). However, only the
450  first (nr X nr) block of A is used. The rest is not touched. This
451  part can be set to zero, but it is not necessary since these
452  values are actually not used when solving. */
453  for(i=0;i<n->nr;i++)
454  {
455  for(j=0;j<n->nc;j++)
456  gsl_matrix_set(&(n->VT.matrix),j,i,n->Ab[RC2INDEX(i,j,n->nr,n->nc)]);
457  }
458 
459  err=gsl_linalg_SV_decomp(&(n->VT.matrix),&(n->UT.matrix),&(n->ST.vector),n->tmp);
460 
461  /* There is no need to copy VT in V since they share memory and the same
462  applies for UT (shares space with U) and ST (shares space with S). */
463  }
464  else
465  err=gsl_linalg_SV_decomp(&(n->A.matrix),n->V,n->S,n->tmp);
466 
467  /* Correct the estimation and compute the error */
468  if (!err)
469  {
470  /* Adjust the eigenvalues (never more than 'nc' eigenvalues) */
471  for(i=0;i<n->nc;i++)
472  {
473  if (gsl_vector_get(n->S,i)<nullSingularValue)
474  gsl_vector_set(n->S,i,0.0);
475  }
476 
477  /* This automatically solves least-square or the least-norm depending on
478  the problem size and the number of non-null eigenvalues. This
479  flexibility can not be achieved with QR typically because our matrices
480  are typically not full rank. */
481  err=gsl_linalg_SV_solve(&(n->A.matrix),n->V,n->S,&(n->b.vector),n->w);
482 
483  if (!err)
484  {
485  (*dif)=0.0;
486 
487  for(i=0;i<n->nc;i++)
488  {
489  d=gsl_vector_get(n->w,i);
490  x[i]-=d;
491  (*dif)+=(d*d);
492  }
493  (*dif)=sqrt(*dif);
494 
495  /* If the error is too large assume will never converge */
496  if ((*dif)>1e10)
497  err=1;
498  }
499  }
500 
501  return(err);
502 }
503 
504 void DeleteNewton(TNewton *n)
505 {
506  free(n->Ab);
507  free(n->bb);
508 
509  gsl_vector_free(n->w);
510 
511  gsl_matrix_free(n->V);
512  gsl_vector_free(n->S);
513  gsl_vector_free(n->tmp);
514 }
515 
516 void InitLS(unsigned int nr,unsigned int nc,TLinearSystem *ls)
517 {
518  #if (_DEBUG<2)
519  gsl_set_error_handler_off();
520  #endif
521 
522  if (nr<nc)
523  Error("The linear system must be well-constrained or over-constrained");
524 
525  ls->nr=nr;
526  ls->nc=nc;
527 
528  NEW(ls->Ab,nr*nc,double);
529  NEW(ls->bb,nr,double);
530  NEW(ls->xb,nc,double);
531 
532  ls->A=gsl_matrix_view_array(ls->Ab,nr,nc);
533  ls->b=gsl_vector_view_array(ls->bb,nr);
534  ls->x=gsl_vector_view_array(ls->xb,nc);
535 
536  if (nr==nc)
537  ls->p=gsl_permutation_alloc(nr);
538  else
539  {
540  /* nr>nc */
541  ls->tau=gsl_vector_alloc(nr);
542  ls->res=gsl_vector_alloc(nr);
543  }
544 }
545 
546 
547 int LSSolve(TLinearSystem *ls)
548 {
549  int err;
550 
551  if (ls->nr==ls->nc)
552  {
553  int s;
554 
555  /* Well constrained systems -> LU decomposition */
556  err=gsl_linalg_LU_decomp(&(ls->A.matrix),ls->p,&s);
557  if (!err)
558  err=gsl_linalg_LU_solve(&(ls->A.matrix),ls->p,&(ls->b.vector),&(ls->x.vector));
559  }
560  else
561  {
562  /* Over-constrained system -> QR decomposition and less-square solve */
563  err=gsl_linalg_QR_decomp(&(ls->A.matrix),ls->tau);
564 
565  if (!err)
566  err=gsl_linalg_QR_lssolve(&(ls->A.matrix),ls->tau,&(ls->b.vector),&(ls->x.vector),ls->res);
567  }
568 
569  return(err);
570 }
571 
572 void DeleteLS(TLinearSystem *ls)
573 {
574  free(ls->Ab);
575  free(ls->bb);
576  free(ls->xb);
577 
578  if (ls->nr==ls->nc)
579  gsl_permutation_free(ls->p);
580  else
581  {
582  gsl_vector_free(ls->tau);
583  gsl_vector_free(ls->res);
584  }
585 }
586 
587 #endif
588 
589 /* END GSL IMPLEMENTATION */
590 
591 
592 /* BEGIN LAPACK IMPLEMENTATION */
593 #ifdef _LAPACK
594 
595 unsigned int AnalyzeKernel(unsigned int nr,unsigned int nc,double *mT,
596  unsigned int dof,double epsilon,
597  boolean computeRank,boolean checkRank,boolean getT,boolean getBase,
598  boolean *singular,unsigned int *rank,boolean **IR,double **T)
599 {
600  unsigned int err;
601 
602  if (dof==nc)
603  {
604  /* Trivial case of a system without equations. We take this into account for completeness, but
605  this function should never be used in this case. */
606  *singular=FALSE;
607  *rank=0;
608 
609  if ((getT)&&(nc>0))
610  {
611  unsigned int i;
612 
613  NEWZ(*T,nc*dof,double);
614  for(i=0;i<nc;i++)
615  (*T)[RC2INDEX(i,i,nc,dof)]=1.0;
616  }
617  else
618  *T=NULL;
619 
620  if ((getBase)&&(nr>0))
621  {
622  unsigned int i;
623 
624  NEW(*IR,nr,boolean);
625  for(i=0;i<nr;i++)
626  (*IR)[i]=FALSE;
627  }
628  else
629  *IR=NULL;
630 
631  err=0;
632  }
633  else
634  {
635  int *p,lwork,info;
636  double *tau,wOpt,*work;
637 
638  /* Extra space for the QR decomposition of At */
639  NEW(tau,(nr<nc?nr:nc),double);
640  NEWZ(p,nr,int); /* This MUST be initialized to zero! */
641 
642  /* allocate the work space. We assume that the work space for the decomposition is larger
643  than that for the unpacking. */
644 
645  /* dgeqp3(M,N,A,LDA,PVT,TAU,WORK,LWORK,INFO) */
646  /* computes the QR factorization, with column pivoting */
647  lwork=-1;
648  #ifdef _LAPACKE
649  info=(int)LAPACKE_dgeqp3_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
650  (lapack_int)nc,(lapack_int)nr,mT,(lapack_int)nc,
651  (lapack_int*)p,tau,&wOpt,(lapack_int)lwork);
652  #else
653  dgeqp3_((int*)&nc,(int*)&nr,mT,(int*)&nc,p,tau,&wOpt,&lwork,&info);
654  #endif
655  lwork=(int)wOpt;
656  NEW(work,lwork,double);
657 
658  /* Decompose the matrix, with column pivoting to get the kernel. Note that
659  the permutation is not relevant for the kernel P*A*v=0 -> A*v=0 */
660  #ifdef _LAPACKE
661  info=(int)LAPACKE_dgeqp3_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
662  (lapack_int)nc,(lapack_int)nr,mT,(lapack_int)nc,
663  (lapack_int*)p,tau,work,(lapack_int)lwork);
664  #else
665  dgeqp3_((int*)&nc,(int*)&nr,mT,(int*)&nc,p,tau,work,&lwork,&info);
666  #endif
667 
668  err=(info!=0);
669 
670  if (err)
671  {
672  *singular=TRUE;
673  *rank=NO_UINT;
674  *T=NULL;
675  *IR=NULL;
676  err=3; /* Our error code for decomposition error */
677  }
678  else
679  {
680  unsigned int k,mrc;
681  double r;
682 
683  /***********************************************/
684  /* At is nc x nr */
685  /* Q is nc x nc */
686  /* R is nc x nr -> R^t is nr x nc */
687 
688  /* At=Q R P^t -> A= P R^t Q^t -> P^t A Q = R^t
689  Thus, the rows of R (columns of R^t) that are zero
690  indicate columns of Q that define part of the kernel.
691  Moreover, the norm of teh rows of R indicate how far
692  is from defining a new kernel basis vector. Sinc R
693  is triangular the norm of the row is the norm from
694  the diagonal to the end of the row (nr entries) */
695 
696  mrc=(nr<nc?nr:nc);
697 
698  if (computeRank)
699  {
700  /* Rank = number of not-null rows of R */
701  /* The rank can not be larger than the mrc=min(nr,nc) */
702  *rank=0;
703  for(k=0;k<mrc;k++)
704  {
705  /* Get the norm of row k of R from the diagonal to the end of the row */
706  r=NormWithStride(nr-k,(ROW_MAJOR?1:nc),&(mT[RC2INDEX(k,k,nc,nr)]));
707  if (r>epsilon)
708  (*rank)++;
709  }
710  if (dof==0)
711  {
712  *singular=FALSE;
713  /* temporary set 'dof' form '*rank' */
714  dof=nc-*rank;
715  }
716  else
717  *singular=((nc-dof)!=*rank);
718  }
719  else
720  {
721  if (dof==0)
722  Error("Null 'dof' parameter in AnalyzeKernel");
723  if (dof>nc)
724  Error("We can not have more 'dof' than variables");
725 
726  *rank=nc-dof;
727 
728  /* Check if row *rank-1 of R is actually not null. */
729  r=NormWithStride(nr-(*rank-1),(ROW_MAJOR?1:nc),&(mT[RC2INDEX(*rank-1,*rank-1,nc,nr)]));
730  *singular=(r<epsilon);
731 
732  if (checkRank)
733  {
734  if (*singular)
735  err=1; //Error("Singular point (increase N_DOF? Decrease EPSILON?)");
736 
737  /* Checking row '*rank' is actually null. */
738  r=NormWithStride(nr-*rank,(ROW_MAJOR?1:nc),&(mT[RC2INDEX(*rank,*rank,nc,nr)]));
739  if (r>epsilon)
740  err=2; //Error("Non-null kernel vector (decrease N_DOF? Increase EPSILON?)");
741  }
742  }
743 
744  #if (_DEBUG>0)
745  if ((dof>0)&&(!computeRank)&&(getT)&&(!getBase))
746  {
747  printf(" E=[ ");
748  for(k=*rank;k<mrc;k++)
749  {
750  r=NormWithStride(nr-k,(ROW_MAJOR?1:nc),&(mT[RC2INDEX(k,k,nc,nr)]));
751  printf("%.16f ",r);
752  }
753  printf("];\n");
754  }
755  #endif
756 
757  /* If requested, we define T and IR, even if the chart is singular. In this case
758  we return the most likely T and IR. */
759  if (getT)
760  {
761  unsigned int i;
762  char left='L';
763  char noTrans='N';
764 
765  /* The kernel are the last 'dof' columns of Q */
766  /* Note that P^t A q_i = 0 <-> A q_i = 0 */
767  NEWZ(*T,nc*dof,double); /* init to zero */
768  for(k=*rank,i=0;k<nc;k++,i++)
769  (*T)[RC2INDEX(k,i,nc,dof)]=1.0;
770  /* DORMQR(SIDE,TRANS,M,N,K,A,LDA,TAU,C,LDC,WORK,LWORK,INFO) */
771  /* (*T)=Q*(*T) (with Q encoded as elementary reflectors) */
772  #ifdef _LAPACKE
773  info=(int)LAPACKE_dormqr_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),left,noTrans,
774  (lapack_int)nc,(lapack_int)dof,(lapack_int)(nr<nc?nr:nc),
775  mT,(lapack_int)nc,tau,*T,(lapack_int)nc,work,(lapack_int)lwork);
776  #else
777  dormqr_(&left,&noTrans,(int*)&nc,(int*)&dof,(nr<nc?(int*)&nr:(int*)&nc),mT,(int*)&nc,tau,*T,(int*)&nc,work,&lwork,&info);
778  #endif
779  err=(info!=0);
780  }
781  else
782  *T=NULL;
783 
784  if (getBase)
785  {
786  NEW(*IR,nr,boolean);
787  for(k=0;k<nr;k++)
788  (*IR)[k]=FALSE;
789  for(k=0;k<*rank;k++)
790  (*IR)[p[k]-1]=TRUE; /* permutations numbered from 1 in lapack */
791  }
792  else
793  *IR=NULL;
794  }
795  free(work);
796  free(tau);
797  free(p);
798  }
799  return(err);
800 }
801 
802 int MatrixDeterminantSgn(double epsilon,unsigned int n,double *A)
803 {
804  int s,info,*p;
805  double v;
806 
807  NEW(p,n,int);
808 
809  /* LU decomposition */
810  /* DGETRF(M,N,A,LDA,IPIV,INFO) */
811  /* computes an LU factorization of a general M-by-N matrix using partial pivoting with row interchanges. */
812  #ifdef _LAPACKE
813  info=(int)LAPACKE_dgetrf_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
814  (lapack_int)n,(lapack_int)n,A,(lapack_int)n,(lapack_int*)p);
815  #else
816  dgetrf_((int*)&n,(int*)&n,A,(int*)&n,p,&info);
817  #endif
818 
819  if (info==0)
820  {
821  unsigned int i;
822 
823  /* Get the sign of the permutation */
824  s=1;
825  for(i=0;i<n;i++)
826  if ((p[i]-1)!=i) s=-s; /* permutations numbered from 1 in lapack */
827 
828  /* and now the sign of the diagonal of U */
829  for(i=0;((s!=0)&&(i<n));i++)
830  {
831  v=A[RC2INDEX(i,i,n,n)];
832  if (fabs(v)<epsilon) s=0;
833  else
834  {
835  if (v<0.0) s=-s;
836  }
837  }
838  }
839  else
840  s=0;
841 
842  free(p);
843 
844  return(s);
845 }
846 
847 double MatrixDeterminant(unsigned int n,double *A)
848 {
849  double d;
850 
851  /* Observe that det(A)=det(A^t) and thus for the case 2 and 3
852  it does not matter if the matrix is stored ROW or COLUMN major */
853  switch(n)
854  {
855  case 1:
856  d=A[0];
857  break;
858  case 2:
859  d=A[0]*A[3]-A[1]*A[2];
860  break;
861  case 3:
862  d= A[0]*A[4]*A[8]+A[2]*A[3]*A[7]+A[1]*A[5]*A[6]
863  -A[2]*A[4]*A[6]-A[1]*A[3]*A[8]-A[0]*A[5]*A[7];
864  break;
865  default:
866  {
867  int info,*p;
868 
869  NEW(p,n,int);
870 
871  /* LU decomposition */
872  /* DGETRF(M,N,A,LDA,IPIV,INFO) */
873  /* computes an LU factorization of a general M-by-N matrix using partial pivoting with row interchanges. */
874  #ifdef _LAPACKE
875  info=(int)LAPACKE_dgetrf_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
876  (lapack_int)n,(lapack_int)n,A,(lapack_int)n,(lapack_int*)p);
877  #else
878  dgetrf_((int*)&n,(int*)&n,A,(int*)&n,p,&info);
879  #endif
880  if (info==0)
881  {
882  unsigned int i;
883 
884  /* Get the sign of the permutation */
885  d=1.0;
886  for(i=0;i<n;i++)
887  if ((p[i]-1)!=i) d=-d; /* permutations numbered from 1 in lapack */
888 
889  /* and now the product of the diagonal of U */
890  for(i=0;i<n;i++)
891  d*=A[RC2INDEX(i,i,n,n)];
892  }
893  else
894  d=INF;
895 
896  free(p);
897  }
898  }
899  return(d);
900 }
901 
902 void InitNewton(unsigned int nr,unsigned int nc,TNewton *n)
903 {
904  int rank,one=1,info;
905  double w,e=1e-6;
906 
907  n->nr=(int)nr;
908  n->nc=(int)nc;
909 
910  NEW(n->Ab,nr*nc,double);
911  /* b is used for input and output */
912  NEW(n->bb,(nr>nc?nr:nc),double);
913 
914  NEW(n->s,(nr<nc?nr:nc),double);
915 
916  /* Request the optimal size for the work buffer */
917  /* DGELSS(M,N,NRHS,A,LDA,B,LDB,S,RCOND,RANK,WORK,LWORK,INFO) */
918  n->lwork=-1;
919  #ifdef _LAPACKE
920  info=(int)LAPACKE_dgelss_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
921  (lapack_int)n->nr,(lapack_int)n->nc,one,n->Ab,
922  (lapack_int)n->nr,n->bb,(lapack_int)(n->nr>n->nc?n->nr:n->nc),
923  n->s,e,(lapack_int*)&rank,&w,n->lwork);
924  #else
925  dgelss_(&n->nr,&n->nc,&one,n->Ab,&n->nr,n->bb,(n->nr>n->nc?&n->nr:&n->nc),n->s,&e,&rank,&w,&n->lwork,&info);
926  #endif
927 
928  if (info!=0)
929  Error("Clapack error in InitNewton");
930 
931  n->lwork=(int)w;
932  NEW(n->work,n->lwork,double);
933 }
934 
935 int NewtonStep(double nullSingularValue,double *x,double *dif,TNewton *n)
936 {
937  int rank,info,one=1;
938 
939  /* Minimum norm solution via SVD (works even if the matrix is not full rank) */
940 
941  /* DGELSS(M,N,NRHS,A,LDA,B,LDB,S,RCOND,RANK,WORK,LWORK,INFO) */
942  /* computes the minimum norm solution to a real linear least squares problem: */
943  /* singular values smaller than MaxSingularValue*Rcond are treated as zero */
944  /* This is a difference w.r.t the GSL implementation! */
945  #ifdef _LAPACKE
946  info=(int)LAPACKE_dgelss_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
947  (lapack_int)n->nr,(lapack_int)n->nc,one,n->Ab,
948  (lapack_int)n->nr,n->bb,(lapack_int)(n->nr>n->nc?n->nr:n->nc),
949  n->s,nullSingularValue,(lapack_int*)&rank,n->work,n->lwork);
950  #else
951  dgelss_(&n->nr,&n->nc,&one,n->Ab,&n->nr,n->bb,(n->nr>n->nc?&n->nr:&n->nc),n->s,&nullSingularValue,&rank,n->work,&n->lwork,&info);
952  #endif
953 
954  if (info==0)
955  {
956  SubtractVector(n->nc,x,n->bb);
957  *dif=Norm(n->nc,n->bb);
958 
959  /* If the error is too large assume will never converge */
960  if ((*dif)>1e10)
961  info=1;
962  }
963 
964  return(info!=0);
965 }
966 
967 void DeleteNewton(TNewton *n)
968 {
969  free(n->Ab);
970  free(n->bb);
971 
972  free(n->s);
973 
974  free(n->work);
975 }
976 
977 void InitLS(unsigned int nr,unsigned int nc,TLinearSystem *ls)
978 {
979  if (nr<nc)
980  Error("The linear system must be well-constrained or over-constrained");
981 
982  ls->nr=(int)nr;
983  ls->nc=(int)nc;
984 
985  NEW(ls->Ab,nr*nc,double);
986  NEW(ls->bb,nr,double);
987  ls->xb=ls->bb; /* vector re-used */
988 
989  if (nr==nc)
990  {
991  /* Solve via LU decompositoin */
992  NEW(ls->p,nr,int);
993  }
994  else
995  {
996  /* Solve via QR decomposition */
997  int one=1,info;
998  double w;
999  char noTrans='N';
1000 
1001  /* Request the optimal size for the work buffer */
1002  /* DGELS(TRANS,M,N,NRHS,A,LDA,B,LDB,WORK,LWORK,INFO) */
1003  /* DGELS solves overdetermined or underdetermined real linear systems */
1004  /* It uses QR or LQ decompositions. */
1005  /* It assumes the matrix to be full rank. */
1006  ls->lwork=-1;
1007  #ifdef _LAPACKE
1008  info=(int)LAPACKE_dgels_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),noTrans,
1009  (lapack_int)ls->nr,(lapack_int)ls->nc,one,ls->Ab,
1010  (lapack_int)ls->nr,ls->bb,(lapack_int)ls->nr,&w,ls->lwork);
1011  #else
1012  dgels_(&noTrans,&ls->nr,&ls->nc,&one,ls->Ab,&ls->nr,ls->bb,&ls->nr,&w,&ls->lwork,&info);
1013  #endif
1014 
1015  if (info!=0)
1016  Error("Clapack error in InitLS");
1017 
1018  ls->lwork=(int)w;
1019  NEW(ls->work,ls->lwork,double);
1020  }
1021 }
1022 
1023 
1024 int LSSolve(TLinearSystem *ls)
1025 {
1026  int one=1;
1027  int info;
1028  char noTrans='N';
1029 
1030  if (ls->nr==ls->nc)
1031  {
1032  /* DGESV(N,NRHS,A,LDA,IPIV,B,LDB,INFO) */
1033  /* Computes the solution to a real system of linear equations */
1034  /* The LU decomposition with partial pivoting and row interchanges is used */
1035  #ifdef _LAPACKE
1036  info=(int)LAPACKE_dgesv_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
1037  (lapack_int)ls->nr,one,ls->Ab,(lapack_int)ls->nr,
1038  (lapack_int*)ls->p,ls->bb,(lapack_int)ls->nr);
1039  #else
1040  dgesv_(&ls->nr,&one,ls->Ab,&ls->nr,ls->p,ls->bb,&ls->nr,&info);
1041  #endif
1042  }
1043  else
1044  /* Least square QR */
1045  /* DGELS(TRANS,M,N,NRHS,A,LDA,B,LDB,WORK,LWORK,INFO) */
1046  /* DGELS solves overdetermined or underdetermined real linear system using a QR or LQ factorization. */
1047  /* It is assumed that the matrix has full rank. */
1048  #ifdef _LAPACKE
1049  info=(int)LAPACKE_dgels_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),noTrans,
1050  (lapack_int)ls->nr,(lapack_int)ls->nc,one,ls->Ab,
1051  (lapack_int)ls->nr,ls->bb,(lapack_int)ls->nr,ls->work,ls->lwork);
1052  #else
1053  dgels_(&noTrans,&ls->nr,&ls->nc,&one,ls->Ab,&ls->nr,ls->bb,&ls->nr,ls->work,&ls->lwork,&info);
1054  #endif
1055 
1056  return(info!=0);
1057 }
1058 
1059 void DeleteLS(TLinearSystem *ls)
1060 {
1061  free(ls->Ab);
1062  free(ls->bb);
1063 
1064  if (ls->nr==ls->nc)
1065  free(ls->p);
1066  else
1067  free(ls->work);
1068 }
1069 
1070 
1071 #endif
1072 
1073 /* END LAPACK IMPLEMENTATION */
1074 
1075 
1076 /* GENERIC IMPLEMENTATION */
1077 /***************************************************************************/
1078 /***************************************************************************/
1079 /***************************************************************************/
1080 
1081 
1082 /* Some functions are independent of the underlying linear algebra package */
1083 
1084 unsigned int FindRank(double epsilon,unsigned int nr,unsigned int nc,double *mT)
1085 {
1086  boolean singular;
1087  boolean *IR;
1088  double *T;
1089  unsigned int rank;
1090 
1091  AnalyzeKernel(nr,nc,mT,0,epsilon,
1092  TRUE,FALSE,FALSE,FALSE, /* computeRank, checkRank, getT, getBase */
1093  &singular,&rank,&IR,&T);
1094 
1095  return(rank);
1096 }
1097 
1098 unsigned int FindKernel(unsigned int nr,unsigned int nc,double *mT,
1099  unsigned int dof,boolean check,double epsilon,double **T)
1100 {
1101  unsigned int err;
1102  boolean singular;
1103  boolean *IR;
1104  unsigned int rank;
1105 
1106  err=AnalyzeKernel(nr,nc,mT,dof,epsilon,
1107  FALSE,check,TRUE,FALSE, /* computeRank, checkRank, getT, getBase */
1108  &singular,&rank,&IR,T);
1109  return(err);
1110 }
1111 
1112 unsigned int FindKernelAndIndependentRows(unsigned int nr,unsigned int nc,double *mT,
1113  unsigned int dof,double epsilon,boolean *singular,
1114  boolean **IR,double **T)
1115 {
1116  unsigned int err;
1117  unsigned int rank;
1118 
1119  err=AnalyzeKernel(nr,nc,mT,dof,epsilon,
1120  FALSE,TRUE,TRUE,TRUE, /* computeRank, checkRank, getT, getBase */
1121  singular,&rank,IR,T);
1122 
1123  return(err);
1124 }
1125 
1126 inline double *GetNewtonMatrixBuffer(TNewton *n)
1127 {
1128  return(n->Ab);
1129 }
1130 
1131 inline double *GetNewtonRHBuffer(TNewton *n)
1132 {
1133  return(n->bb);
1134 }
1135 
1136 inline void NewtonSetMatrix(unsigned int i,unsigned int j,double v,TNewton *n)
1137 {
1138  n->Ab[RC2INDEX(i,j,n->nr,n->nc)]=v;
1139 }
1140 
1141 inline void NewtonSetRH(unsigned int i,double v,TNewton *n)
1142 {
1143  n->bb[i]=v;
1144 }
1145 
1146 inline double *GetLSMatrixBuffer(TLinearSystem *ls)
1147 {
1148  return(ls->Ab);
1149 }
1150 
1151 inline double *GetLSRHBuffer(TLinearSystem *ls)
1152 {
1153  return(ls->bb);
1154 }
1155 
1156 inline double *GetLSSolutionBuffer(TLinearSystem *ls)
1157 {
1158  return(ls->xb);
1159 }
1160 
1161 inline void LSSetMatrix(unsigned int i,unsigned int j,double v,TLinearSystem *ls)
1162 {
1163  ls->Ab[RC2INDEX(i,j,ls->nr,ls->nc)]=v;
1164 }
1165 
1166 inline void LSSetRH(unsigned int i,double v,TLinearSystem *ls)
1167 {
1168  ls->bb[i]=v;
1169 }
int MatrixDeterminantSgn(double epsilon, unsigned int n, double *A)
Sign of the determinant of a matrix.
unsigned int AnalyzeKernel(unsigned int nr, unsigned int nc, double *mT, unsigned int dof, double epsilon, boolean computeRank, boolean checkRank, boolean getT, boolean getBase, boolean *singular, unsigned int *rank, boolean **IR, double **T)
Analyzes the kernel of a matrix.
double * GetLSSolutionBuffer(TLinearSystem *ls)
Buffer to store the linear system solution.
Definition: algebra.c:1156
#define FALSE
FALSE.
Definition: boolean.h:30
unsigned int FindKernel(unsigned int nr, unsigned int nc, double *mT, unsigned int dof, boolean check, double epsilon, double **T)
Computes the kernel of a matrix.
Definition: algebra.c:1098
#define NEW(_var, _n, _type)
Allocates memory space.
Definition: defines.h:385
#define RC2INDEX(i, j, nr, nc)
Index in a vector of a matrix element.
Definition: basic_algebra.h:81
double MatrixDeterminant(unsigned int n, double *A)
Determinant of a matrix.
#define NEWZ(_var, _n, _type)
Allocates and cleans memory space.
Definition: defines.h:394
CBLAS_INLINE double Norm(unsigned int s, double *v)
Computes the norm of a vector.
void LSSetRH(unsigned int i, double v, TLinearSystem *ls)
Defines the vector being used in a linear system.
Definition: algebra.c:1166
#define TRUE
TRUE.
Definition: boolean.h:21
double * GetNewtonRHBuffer(TNewton *n)
Buffer to store the Newton right hand.
Definition: algebra.c:1131
void Error(const char *s)
General error function.
Definition: error.c:80
int NewtonStep(double nullSingularValue, double *x, double *dif, TNewton *n)
One step in a Newton iteration.
unsigned int FindRank(double epsilon, unsigned int nr, unsigned int nc, double *mT)
Determines the row-rank of a matrix.
Definition: algebra.c:1084
void DeleteNewton(TNewton *n)
Releases a Newton structure.
void InitLS(unsigned int nr, unsigned int nc, TLinearSystem *ls)
Defines a linear system structure.
void NewtonSetMatrix(unsigned int i, unsigned int j, double v, TNewton *n)
Defines the matrix being used in a Newton step.
Definition: algebra.c:1136
#define NO_UINT
Used to denote an identifier that has not been initialized.
Definition: defines.h:435
CBLAS_INLINE void SubtractVector(unsigned int s, double *v1, double *v2)
Substracts a vector from another vector.
unsigned int FindKernelAndIndependentRows(unsigned int nr, unsigned int nc, double *mT, unsigned int dof, double epsilon, boolean *singular, boolean **IR, double **T)
Computes the kernel of a matrix and determines the independent rows of this matrix.
Definition: algebra.c:1112
void DeleteLS(TLinearSystem *ls)
Releases a linear system structure.
double * GetLSRHBuffer(TLinearSystem *ls)
Buffer to store the linear system right hand (RH).
Definition: algebra.c:1151
int LSSolve(TLinearSystem *ls)
Solves the linear sytem.
void LSSetMatrix(unsigned int i, unsigned int j, double v, TLinearSystem *ls)
Defines the matrix being used in a linear system.
Definition: algebra.c:1161
double * GetLSMatrixBuffer(TLinearSystem *ls)
Buffer to store the A matrix.
Definition: algebra.c:1146
#define INF
Infinite.
Definition: defines.h:70
CBLAS_INLINE double NormWithStride(unsigned int s, unsigned int st, double *v)
Computes the norm of a vector.
void NewtonSetRH(unsigned int i, double v, TNewton *n)
Defines the vector being used in a Newton step.
Definition: algebra.c:1141
double * GetNewtonMatrixBuffer(TNewton *n)
Buffer to store the Newton matrix.
Definition: algebra.c:1126
void InitNewton(unsigned int nr, unsigned int nc, TNewton *n)
Defines a Newton structure.