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  #if (DAMPED_NEWTON)
438  n->alpha=DN_F;
439  #endif
440 }
441 
442 int NewtonStep(double nullSingularValue,double *x,double *dif,TNewton *n)
443 {
444  int err;
445  unsigned int i,j;
446  double d;
447 
448  /* This is a replacement for tolerant_SV_decomp. See this function to
449  understand what we do here. */
450  if (n->nr<n->nc)
451  {
452  /* Store A^t in n->VT (which is actually a submatrix of n->V. Note that
453  the space for A will be over-write (to store U). However, only the
454  first (nr X nr) block of A is used. The rest is not touched. This
455  part can be set to zero, but it is not necessary since these
456  values are actually not used when solving. */
457  for(i=0;i<n->nr;i++)
458  {
459  for(j=0;j<n->nc;j++)
460  gsl_matrix_set(&(n->VT.matrix),j,i,n->Ab[RC2INDEX(i,j,n->nr,n->nc)]);
461  }
462 
463  err=gsl_linalg_SV_decomp(&(n->VT.matrix),&(n->UT.matrix),&(n->ST.vector),n->tmp);
464 
465  /* There is no need to copy VT in V since they share memory and the same
466  applies for UT (shares space with U) and ST (shares space with S). */
467  }
468  else
469  err=gsl_linalg_SV_decomp(&(n->A.matrix),n->V,n->S,n->tmp);
470 
471  /* Correct the estimation and compute the error */
472  if (!err)
473  {
474  /* Adjust the eigenvalues (never more than 'nc' eigenvalues) */
475  for(i=0;i<n->nc;i++)
476  {
477  if (gsl_vector_get(n->S,i)<nullSingularValue)
478  gsl_vector_set(n->S,i,0.0);
479  }
480 
481  /* This automatically solves least-square or the least-norm depending on
482  the problem size and the number of non-null eigenvalues. This
483  flexibility can not be achieved with QR typically because our matrices
484  are typically not full rank. */
485  err=gsl_linalg_SV_solve(&(n->A.matrix),n->V,n->S,&(n->b.vector),n->w);
486 
487  if (!err)
488  {
489  (*dif)=0.0;
490 
491  for(i=0;i<n->nc;i++)
492  {
493  d=gsl_vector_get(n->w,i);
494  #if (DAMPED_NEWTON)
495  x[i]-=(n->alpha*d);
496  #else
497  x[i]-=d;
498  #endif
499  (*dif)+=(d*d);
500  }
501  (*dif)=sqrt(*dif);
502 
503  #if (DAMPED_NEWTON)
504  n->alpha+=DN_W*(DN_MF-n->alpha);
505  #endif
506 
507  /* If the error is too large assume will never converge */
508  if ((*dif)>1e10)
509  err=1;
510  }
511  }
512 
513  return(err);
514 }
515 
516 void DeleteNewton(TNewton *n)
517 {
518  free(n->Ab);
519  free(n->bb);
520 
521  gsl_vector_free(n->w);
522 
523  gsl_matrix_free(n->V);
524  gsl_vector_free(n->S);
525  gsl_vector_free(n->tmp);
526 }
527 
528 void InitLS(unsigned int nr,unsigned int nc,TLinearSystem *ls)
529 {
530  #if (_DEBUG<2)
531  gsl_set_error_handler_off();
532  #endif
533 
534  if (nr<nc)
535  Error("The linear system must be well-constrained or over-constrained");
536 
537  ls->nr=nr;
538  ls->nc=nc;
539 
540  NEW(ls->Ab,nr*nc,double);
541  NEW(ls->bb,nr,double);
542  NEW(ls->xb,nc,double);
543 
544  ls->A=gsl_matrix_view_array(ls->Ab,nr,nc);
545  ls->b=gsl_vector_view_array(ls->bb,nr);
546  ls->x=gsl_vector_view_array(ls->xb,nc);
547 
548  if (nr==nc)
549  ls->p=gsl_permutation_alloc(nr);
550  else
551  {
552  /* nr>nc */
553  ls->tau=gsl_vector_alloc(nr);
554  ls->res=gsl_vector_alloc(nr);
555  }
556 }
557 
558 
559 int LSSolve(TLinearSystem *ls)
560 {
561  int err;
562 
563  if (ls->nr==ls->nc)
564  {
565  int s;
566 
567  /* Well constrained systems -> LU decomposition */
568  err=gsl_linalg_LU_decomp(&(ls->A.matrix),ls->p,&s);
569  if (!err)
570  err=gsl_linalg_LU_solve(&(ls->A.matrix),ls->p,&(ls->b.vector),&(ls->x.vector));
571  }
572  else
573  {
574  /* Over-constrained system -> QR decomposition and less-square solve */
575  err=gsl_linalg_QR_decomp(&(ls->A.matrix),ls->tau);
576 
577  if (!err)
578  err=gsl_linalg_QR_lssolve(&(ls->A.matrix),ls->tau,&(ls->b.vector),&(ls->x.vector),ls->res);
579  }
580 
581  return(err);
582 }
583 
584 void DeleteLS(TLinearSystem *ls)
585 {
586  free(ls->Ab);
587  free(ls->bb);
588  free(ls->xb);
589 
590  if (ls->nr==ls->nc)
591  gsl_permutation_free(ls->p);
592  else
593  {
594  gsl_vector_free(ls->tau);
595  gsl_vector_free(ls->res);
596  }
597 }
598 
599 #endif
600 
601 /* END GSL IMPLEMENTATION */
602 
603 
604 /* BEGIN LAPACK IMPLEMENTATION */
605 #ifdef _LAPACK
606 
607 unsigned int AnalyzeKernel(unsigned int nr,unsigned int nc,double *mT,
608  unsigned int dof,double epsilon,
609  boolean computeRank,boolean checkRank,boolean getT,boolean getBase,
610  boolean *singular,unsigned int *rank,boolean **IR,double **T)
611 {
612  unsigned int err;
613 
614  if (dof==nc)
615  {
616  /* Trivial case of a system without equations. We take this into account for completeness, but
617  this function should never be used in this case. */
618  *singular=FALSE;
619  *rank=0;
620 
621  if ((getT)&&(nc>0))
622  {
623  unsigned int i;
624 
625  NEWZ(*T,nc*dof,double);
626  for(i=0;i<nc;i++)
627  (*T)[RC2INDEX(i,i,nc,dof)]=1.0;
628  }
629  else
630  *T=NULL;
631 
632  if ((getBase)&&(nr>0))
633  {
634  unsigned int i;
635 
636  NEW(*IR,nr,boolean);
637  for(i=0;i<nr;i++)
638  (*IR)[i]=FALSE;
639  }
640  else
641  *IR=NULL;
642 
643  err=0;
644  }
645  else
646  {
647  int *p,lwork,info;
648  double *tau,wOpt,*work;
649 
650  /* Extra space for the QR decomposition of At */
651  NEW(tau,(nr<nc?nr:nc),double);
652  NEWZ(p,nr,int); /* This MUST be initialized to zero! */
653 
654  /* allocate the work space. We assume that the work space for the decomposition is larger
655  than that for the unpacking. */
656 
657  /* dgeqp3(M,N,A,LDA,PVT,TAU,WORK,LWORK,INFO) */
658  /* computes the QR factorization, with column pivoting */
659  lwork=-1;
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,&wOpt,(lapack_int)lwork);
664  #else
665  dgeqp3_((int*)&nc,(int*)&nr,mT,(int*)&nc,p,tau,&wOpt,&lwork,&info);
666  #endif
667  lwork=(int)wOpt;
668  NEW(work,lwork,double);
669 
670  /* Decompose the matrix, with column pivoting to get the kernel. Note that
671  the permutation is not relevant for the kernel P*A*v=0 -> A*v=0 */
672  #ifdef _LAPACKE
673  info=(int)LAPACKE_dgeqp3_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
674  (lapack_int)nc,(lapack_int)nr,mT,(lapack_int)nc,
675  (lapack_int*)p,tau,work,(lapack_int)lwork);
676  #else
677  dgeqp3_((int*)&nc,(int*)&nr,mT,(int*)&nc,p,tau,work,&lwork,&info);
678  #endif
679 
680  err=(info!=0);
681 
682  if (err)
683  {
684  *singular=TRUE;
685  *rank=NO_UINT;
686  *T=NULL;
687  *IR=NULL;
688  err=3; /* Our error code for decomposition error */
689  }
690  else
691  {
692  unsigned int k,mrc;
693  double r;
694 
695  /***********************************************/
696  /* At is nc x nr */
697  /* Q is nc x nc */
698  /* R is nc x nr -> R^t is nr x nc */
699 
700  /* At=Q R P^t -> A= P R^t Q^t -> P^t A Q = R^t
701  Thus, the rows of R (columns of R^t) that are zero
702  indicate columns of Q that define part of the kernel.
703  Moreover, the norm of teh rows of R indicate how far
704  is from defining a new kernel basis vector. Sinc R
705  is triangular the norm of the row is the norm from
706  the diagonal to the end of the row (nr entries) */
707 
708  mrc=(nr<nc?nr:nc);
709 
710  if (computeRank)
711  {
712  /* Rank = number of not-null rows of R */
713  /* The rank can not be larger than the mrc=min(nr,nc) */
714  *rank=0;
715  for(k=0;k<mrc;k++)
716  {
717  /* Get the norm of row k of R from the diagonal to the end of the row */
718  r=NormWithStride(nr-k,(ROW_MAJOR?1:nc),&(mT[RC2INDEX(k,k,nc,nr)]));
719  if (r>epsilon)
720  (*rank)++;
721  }
722  if (dof==0)
723  {
724  *singular=FALSE;
725  /* temporary set 'dof' form '*rank' */
726  dof=nc-*rank;
727  }
728  else
729  *singular=((nc-dof)!=*rank);
730  }
731  else
732  {
733  if (dof==0)
734  Error("Null 'dof' parameter in AnalyzeKernel");
735  if (dof>nc)
736  Error("We can not have more 'dof' than variables");
737 
738  *rank=nc-dof;
739 
740  /* Check if row *rank-1 of R is actually not null. */
741  r=NormWithStride(nr-(*rank-1),(ROW_MAJOR?1:nc),&(mT[RC2INDEX(*rank-1,*rank-1,nc,nr)]));
742  *singular=(r<epsilon);
743 
744  if (checkRank)
745  {
746  if (*singular)
747  err=1; //Error("Singular point (increase N_DOF? Decrease EPSILON?)");
748 
749  /* Checking row '*rank' is actually null. */
750  r=NormWithStride(nr-*rank,(ROW_MAJOR?1:nc),&(mT[RC2INDEX(*rank,*rank,nc,nr)]));
751  if (r>epsilon)
752  err=2; //Error("Non-null kernel vector (decrease N_DOF? Increase EPSILON?)");
753  }
754  }
755 
756  #if (_DEBUG>0)
757  if ((dof>0)&&(!computeRank)&&(getT)&&(!getBase))
758  {
759  printf(" E=[ ");
760  for(k=*rank;k<mrc;k++)
761  {
762  r=NormWithStride(nr-k,(ROW_MAJOR?1:nc),&(mT[RC2INDEX(k,k,nc,nr)]));
763  printf("%.16f ",r);
764  }
765  printf("];\n");
766  }
767  #endif
768 
769  /* If requested, we define T and IR, even if the chart is singular. In this case
770  we return the most likely T and IR. */
771  if (getT)
772  {
773  unsigned int i;
774  char left='L';
775  char noTrans='N';
776 
777  /* The kernel are the last 'dof' columns of Q */
778  /* Note that P^t A q_i = 0 <-> A q_i = 0 */
779  NEWZ(*T,nc*dof,double); /* init to zero */
780  for(k=*rank,i=0;k<nc;k++,i++)
781  (*T)[RC2INDEX(k,i,nc,dof)]=1.0;
782  /* DORMQR(SIDE,TRANS,M,N,K,A,LDA,TAU,C,LDC,WORK,LWORK,INFO) */
783  /* (*T)=Q*(*T) (with Q encoded as elementary reflectors) */
784  #ifdef _LAPACKE
785  info=(int)LAPACKE_dormqr_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),left,noTrans,
786  (lapack_int)nc,(lapack_int)dof,(lapack_int)(nr<nc?nr:nc),
787  mT,(lapack_int)nc,tau,*T,(lapack_int)nc,work,(lapack_int)lwork);
788  #else
789  dormqr_(&left,&noTrans,(int*)&nc,(int*)&dof,(nr<nc?(int*)&nr:(int*)&nc),mT,(int*)&nc,tau,*T,(int*)&nc,work,&lwork,&info);
790  #endif
791  err=(info!=0);
792  }
793  else
794  *T=NULL;
795 
796  if (getBase)
797  {
798  NEW(*IR,nr,boolean);
799  for(k=0;k<nr;k++)
800  (*IR)[k]=FALSE;
801  for(k=0;k<*rank;k++)
802  (*IR)[p[k]-1]=TRUE; /* permutations numbered from 1 in lapack */
803  }
804  else
805  *IR=NULL;
806  }
807  free(work);
808  free(tau);
809  free(p);
810  }
811  return(err);
812 }
813 
814 int MatrixDeterminantSgn(double epsilon,unsigned int n,double *A)
815 {
816  int s,info,*p;
817  double v;
818 
819  NEW(p,n,int);
820 
821  /* LU decomposition */
822  /* DGETRF(M,N,A,LDA,IPIV,INFO) */
823  /* computes an LU factorization of a general M-by-N matrix using partial pivoting with row interchanges. */
824  #ifdef _LAPACKE
825  info=(int)LAPACKE_dgetrf_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
826  (lapack_int)n,(lapack_int)n,A,(lapack_int)n,(lapack_int*)p);
827  #else
828  dgetrf_((int*)&n,(int*)&n,A,(int*)&n,p,&info);
829  #endif
830 
831  if (info==0)
832  {
833  unsigned int i;
834 
835  /* Get the sign of the permutation */
836  s=1;
837  for(i=0;i<n;i++)
838  if ((p[i]-1)!=i) s=-s; /* permutations numbered from 1 in lapack */
839 
840  /* and now the sign of the diagonal of U */
841  for(i=0;((s!=0)&&(i<n));i++)
842  {
843  v=A[RC2INDEX(i,i,n,n)];
844  if (fabs(v)<epsilon) s=0;
845  else
846  {
847  if (v<0.0) s=-s;
848  }
849  }
850  }
851  else
852  s=0;
853 
854  free(p);
855 
856  return(s);
857 }
858 
859 double MatrixDeterminant(unsigned int n,double *A)
860 {
861  double d;
862 
863  /* Observe that det(A)=det(A^t) and thus for the case 2 and 3
864  it does not matter if the matrix is stored ROW or COLUMN major */
865  switch(n)
866  {
867  case 1:
868  d=A[0];
869  break;
870  case 2:
871  d=A[0]*A[3]-A[1]*A[2];
872  break;
873  case 3:
874  d= A[0]*A[4]*A[8]+A[2]*A[3]*A[7]+A[1]*A[5]*A[6]
875  -A[2]*A[4]*A[6]-A[1]*A[3]*A[8]-A[0]*A[5]*A[7];
876  break;
877  default:
878  {
879  int info,*p;
880 
881  NEW(p,n,int);
882 
883  /* LU decomposition */
884  /* DGETRF(M,N,A,LDA,IPIV,INFO) */
885  /* computes an LU factorization of a general M-by-N matrix using partial pivoting with row interchanges. */
886  #ifdef _LAPACKE
887  info=(int)LAPACKE_dgetrf_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
888  (lapack_int)n,(lapack_int)n,A,(lapack_int)n,(lapack_int*)p);
889  #else
890  dgetrf_((int*)&n,(int*)&n,A,(int*)&n,p,&info);
891  #endif
892  if (info==0)
893  {
894  unsigned int i;
895 
896  /* Get the sign of the permutation */
897  d=1.0;
898  for(i=0;i<n;i++)
899  if ((p[i]-1)!=i) d=-d; /* permutations numbered from 1 in lapack */
900 
901  /* and now the product of the diagonal of U */
902  for(i=0;i<n;i++)
903  d*=A[RC2INDEX(i,i,n,n)];
904  }
905  else
906  d=INF;
907 
908  free(p);
909  }
910  }
911  return(d);
912 }
913 
914 void InitNewton(unsigned int nr,unsigned int nc,TNewton *n)
915 {
916  int rank,one=1,info;
917  double w,e=1e-6;
918 
919  n->nr=(int)nr;
920  n->nc=(int)nc;
921 
922  NEW(n->Ab,nr*nc,double);
923  /* b is used for input and output */
924  NEW(n->bb,(nr>nc?nr:nc),double);
925 
926  NEW(n->s,(nr<nc?nr:nc),double);
927 
928  /* Request the optimal size for the work buffer */
929  /* DGELSS(M,N,NRHS,A,LDA,B,LDB,S,RCOND,RANK,WORK,LWORK,INFO) */
930  n->lwork=-1;
931  #ifdef _LAPACKE
932  info=(int)LAPACKE_dgelss_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
933  (lapack_int)n->nr,(lapack_int)n->nc,one,n->Ab,
934  (lapack_int)n->nr,n->bb,(lapack_int)(n->nr>n->nc?n->nr:n->nc),
935  n->s,e,(lapack_int*)&rank,&w,n->lwork);
936  #else
937  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);
938  #endif
939 
940  if (info!=0)
941  Error("Clapack error in InitNewton");
942 
943  n->lwork=(int)w;
944  NEW(n->work,n->lwork,double);
945 
946  #if (DAMPED_NEWTON)
947  n->alpha=DN_F;
948  #endif
949 }
950 
951 int NewtonStep(double nullSingularValue,double *x,double *dif,TNewton *n)
952 {
953  int rank,info,one=1;
954 
955  /* Minimum norm solution via SVD (works even if the matrix is not full rank) */
956 
957  /* DGELSS(M,N,NRHS,A,LDA,B,LDB,S,RCOND,RANK,WORK,LWORK,INFO) */
958  /* computes the minimum norm solution to a real linear least squares problem: */
959  /* singular values smaller than MaxSingularValue*Rcond are treated as zero */
960  /* This is a difference w.r.t the GSL implementation! */
961  #ifdef _LAPACKE
962  info=(int)LAPACKE_dgelss_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
963  (lapack_int)n->nr,(lapack_int)n->nc,one,n->Ab,
964  (lapack_int)n->nr,n->bb,(lapack_int)(n->nr>n->nc?n->nr:n->nc),
965  n->s,nullSingularValue,(lapack_int*)&rank,n->work,n->lwork);
966  #else
967  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);
968  #endif
969 
970  if (info==0)
971  {
972  #if (DAMPED_NEWTON)
973  SumVectorScale(n->nc,x,-n->alpha,n->bb,x);
974  n->alpha+=DN_W*(DN_MF-n->alpha);
975  #else
976  SubtractVector(n->nc,x,n->bb);
977  #endif
978  *dif=Norm(n->nc,n->bb);
979 
980  /* If the error is too large assume will never converge */
981  if ((*dif)>1e10)
982  info=1;
983  }
984 
985  return(info!=0);
986 }
987 
988 void DeleteNewton(TNewton *n)
989 {
990  free(n->Ab);
991  free(n->bb);
992 
993  free(n->s);
994 
995  free(n->work);
996 }
997 
998 void InitLS(unsigned int nr,unsigned int nc,TLinearSystem *ls)
999 {
1000  if (nr<nc)
1001  Error("The linear system must be well-constrained or over-constrained");
1002 
1003  ls->nr=(int)nr;
1004  ls->nc=(int)nc;
1005 
1006  NEW(ls->Ab,nr*nc,double);
1007  NEW(ls->bb,nr,double);
1008  ls->xb=ls->bb; /* vector re-used */
1009 
1010  if (nr==nc)
1011  {
1012  /* Solve via LU decompositoin */
1013  NEW(ls->p,nr,int);
1014  }
1015  else
1016  {
1017  /* Solve via QR decomposition */
1018  int one=1,info;
1019  double w;
1020  char noTrans='N';
1021 
1022  /* Request the optimal size for the work buffer */
1023  /* DGELS(TRANS,M,N,NRHS,A,LDA,B,LDB,WORK,LWORK,INFO) */
1024  /* DGELS solves overdetermined or underdetermined real linear systems */
1025  /* It uses QR or LQ decompositions. */
1026  /* It assumes the matrix to be full rank. */
1027  ls->lwork=-1;
1028  #ifdef _LAPACKE
1029  info=(int)LAPACKE_dgels_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),noTrans,
1030  (lapack_int)ls->nr,(lapack_int)ls->nc,one,ls->Ab,
1031  (lapack_int)ls->nr,ls->bb,(lapack_int)ls->nr,&w,ls->lwork);
1032  #else
1033  dgels_(&noTrans,&ls->nr,&ls->nc,&one,ls->Ab,&ls->nr,ls->bb,&ls->nr,&w,&ls->lwork,&info);
1034  #endif
1035 
1036  if (info!=0)
1037  Error("Clapack error in InitLS");
1038 
1039  ls->lwork=(int)w;
1040  NEW(ls->work,ls->lwork,double);
1041  }
1042 }
1043 
1044 
1045 int LSSolve(TLinearSystem *ls)
1046 {
1047  int one=1;
1048  int info;
1049  char noTrans='N';
1050 
1051  if (ls->nr==ls->nc)
1052  {
1053  /* DGESV(N,NRHS,A,LDA,IPIV,B,LDB,INFO) */
1054  /* Computes the solution to a real system of linear equations */
1055  /* The LU decomposition with partial pivoting and row interchanges is used */
1056  #ifdef _LAPACKE
1057  info=(int)LAPACKE_dgesv_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),
1058  (lapack_int)ls->nr,one,ls->Ab,(lapack_int)ls->nr,
1059  (lapack_int*)ls->p,ls->bb,(lapack_int)ls->nr);
1060  #else
1061  dgesv_(&ls->nr,&one,ls->Ab,&ls->nr,ls->p,ls->bb,&ls->nr,&info);
1062  #endif
1063  }
1064  else
1065  /* Least square QR */
1066  /* DGELS(TRANS,M,N,NRHS,A,LDA,B,LDB,WORK,LWORK,INFO) */
1067  /* DGELS solves overdetermined or underdetermined real linear system using a QR or LQ factorization. */
1068  /* It is assumed that the matrix has full rank. */
1069  #ifdef _LAPACKE
1070  info=(int)LAPACKE_dgels_work((ROW_MAJOR?LAPACK_ROW_MAJOR:LAPACK_COL_MAJOR),noTrans,
1071  (lapack_int)ls->nr,(lapack_int)ls->nc,one,ls->Ab,
1072  (lapack_int)ls->nr,ls->bb,(lapack_int)ls->nr,ls->work,ls->lwork);
1073  #else
1074  dgels_(&noTrans,&ls->nr,&ls->nc,&one,ls->Ab,&ls->nr,ls->bb,&ls->nr,ls->work,&ls->lwork,&info);
1075  #endif
1076 
1077  return(info!=0);
1078 }
1079 
1080 void DeleteLS(TLinearSystem *ls)
1081 {
1082  free(ls->Ab);
1083  free(ls->bb);
1084 
1085  if (ls->nr==ls->nc)
1086  free(ls->p);
1087  else
1088  free(ls->work);
1089 }
1090 
1091 
1092 #endif
1093 
1094 /* END LAPACK IMPLEMENTATION */
1095 
1096 
1097 /* GENERIC IMPLEMENTATION */
1098 /***************************************************************************/
1099 /***************************************************************************/
1100 /***************************************************************************/
1101 
1102 
1103 /* Some functions are independent of the underlying linear algebra package */
1104 
1105 unsigned int FindRank(double epsilon,unsigned int nr,unsigned int nc,double *mT)
1106 {
1107  boolean singular;
1108  boolean *IR;
1109  double *T;
1110  unsigned int rank;
1111 
1112  AnalyzeKernel(nr,nc,mT,0,epsilon,
1113  TRUE,FALSE,FALSE,FALSE, /* computeRank, checkRank, getT, getBase */
1114  &singular,&rank,&IR,&T);
1115 
1116  return(rank);
1117 }
1118 
1119 unsigned int FindKernel(unsigned int nr,unsigned int nc,double *mT,
1120  unsigned int dof,boolean check,double epsilon,double **T)
1121 {
1122  unsigned int err;
1123  boolean singular;
1124  boolean *IR;
1125  unsigned int rank;
1126 
1127  err=AnalyzeKernel(nr,nc,mT,dof,epsilon,
1128  FALSE,check,TRUE,FALSE, /* computeRank, checkRank, getT, getBase */
1129  &singular,&rank,&IR,T);
1130  return(err);
1131 }
1132 
1133 unsigned int FindKernelAndIndependentRows(unsigned int nr,unsigned int nc,double *mT,
1134  unsigned int dof,double epsilon,boolean *singular,
1135  boolean **IR,double **T)
1136 {
1137  unsigned int err;
1138  unsigned int rank;
1139 
1140  err=AnalyzeKernel(nr,nc,mT,dof,epsilon,
1141  FALSE,TRUE,TRUE,TRUE, /* computeRank, checkRank, getT, getBase */
1142  singular,&rank,IR,T);
1143 
1144  return(err);
1145 }
1146 
1147 inline double *GetNewtonMatrixBuffer(TNewton *n)
1148 {
1149  return(n->Ab);
1150 }
1151 
1152 inline double *GetNewtonRHBuffer(TNewton *n)
1153 {
1154  return(n->bb);
1155 }
1156 
1157 inline void NewtonSetMatrix(unsigned int i,unsigned int j,double v,TNewton *n)
1158 {
1159  n->Ab[RC2INDEX(i,j,n->nr,n->nc)]=v;
1160 }
1161 
1162 inline void NewtonSetRH(unsigned int i,double v,TNewton *n)
1163 {
1164  n->bb[i]=v;
1165 }
1166 
1167 inline double *GetLSMatrixBuffer(TLinearSystem *ls)
1168 {
1169  return(ls->Ab);
1170 }
1171 
1172 inline double *GetLSRHBuffer(TLinearSystem *ls)
1173 {
1174  return(ls->bb);
1175 }
1176 
1177 inline double *GetLSSolutionBuffer(TLinearSystem *ls)
1178 {
1179  return(ls->xb);
1180 }
1181 
1182 inline void LSSetMatrix(unsigned int i,unsigned int j,double v,TLinearSystem *ls)
1183 {
1184  ls->Ab[RC2INDEX(i,j,ls->nr,ls->nc)]=v;
1185 }
1186 
1187 inline void LSSetRH(unsigned int i,double v,TLinearSystem *ls)
1188 {
1189  ls->bb[i]=v;
1190 }
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.
#define DN_W
Damping Newton update ratio.
Definition: algebra.h:55
double * GetLSSolutionBuffer(TLinearSystem *ls)
Buffer to store the linear system solution.
Definition: algebra.c:1177
#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:1119
#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 DN_MF
Damping Newton maximum factor.
Definition: algebra.h:45
#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:1187
#define TRUE
TRUE.
Definition: boolean.h:21
double * GetNewtonRHBuffer(TNewton *n)
Buffer to store the Newton right hand.
Definition: algebra.c:1152
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:1105
#define DN_F
Damping Newton initial factor.
Definition: algebra.h:38
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:1157
#define NO_UINT
Used to denote an identifier that has not been initialized.
Definition: defines.h:435
CBLAS_INLINE void SumVectorScale(unsigned int s, double *v1, double w, double *v2, double *v)
Adds two vectors with a scale.
Definition: basic_algebra.c:86
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:1133
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:1172
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:1182
double * GetLSMatrixBuffer(TLinearSystem *ls)
Buffer to store the A matrix.
Definition: algebra.c:1167
#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:1162
double * GetNewtonMatrixBuffer(TNewton *n)
Buffer to store the Newton matrix.
Definition: algebra.c:1147
void InitNewton(unsigned int nr, unsigned int nc, TNewton *n)
Defines a Newton structure.