linalg.tcl

Go to the documentation of this file.
00001 /*  linalg.tcl --*/
00002 /*     Linear algebra package, based partly on Hume's LA package,*/
00003 /*     partly on experiments with various representations of*/
00004 /*     matrices. Also the functionality of the BLAS library has*/
00005 /*     been taken into account.*/
00006 /* */
00007 /*     General information:*/
00008 /*     - The package provides both a high-level general interface and*/
00009 /*       a lower-level specific interface for various LA functions*/
00010 /*       and tasks.*/
00011 /*     - The general procedures perform some checks and then call*/
00012 /*       the various specific procedures. The general procedures are*/
00013 /*       aimed at robustness and ease of use.*/
00014 /*     - The specific procedures do not check anything, they are*/
00015 /*       designed for speed. Failure to comply to the interface*/
00016 /*       requirements will presumably lead to [expr] errors.*/
00017 /*     - Vectors are represented as lists, matrices as lists of*/
00018 /*       lists, where the rows are the innermost lists:*/
00019 /* */
00020 /*       / a11 a12 a13 \*/
00021 /*       | a21 a22 a23 | == { {a11 a12 a13} {a21 a22 a23} {a31 a32 a33} }*/
00022 /*       \ a31 a32 a33 /*/
00023 /* */
00024 
00025 package require Tcl 8.4
00026 
00027 namespace ::math::linearalgebra {
00028     /*  Define the namespace*/
00029     namespace export dim shape conforming symmetric
00030     namespace export norm norm_one norm_two norm_max normMatrix
00031     namespace export dotproduct unitLengthVector normalizeStat
00032     namespace export axpy axpy_vect axpy_mat crossproduct
00033     namespace export add add_vect add_mat
00034     namespace export sub sub_vect sub_mat
00035     namespace export scale scale_vect scale_mat matmul transpose
00036     namespace export rotate angle choleski
00037     namespace export getrow getcol getelem row =  col =  elem = 
00038     namespace export mkVector mkMatrix mkIdentity mkDiagonal
00039     namespace export mkHilbert mkDingdong mkBorder mkFrank
00040     namespace export mkMoler mkWilkinsonW+ mkWilkinsonW-
00041     namespace export solveGauss solveTriangular
00042     namespace export solveGaussBand solveTriangularBand
00043     namespace export determineSVD eigenvectorsSVD
00044     namespace export leastSquaresSVD
00045     namespace export orthonormalizeColumns orthonormalizeRows
00046     namespace export show to_LA from_LA
00047 }
00048 
00049 /*  dim --*/
00050 /*      Return the dimension of an object (scalar, vector or matrix)*/
00051 /*  Arguments:*/
00052 /*      obj        Object like a scalar, vector or matrix*/
00053 /*  Result:*/
00054 /*      Dimension: 0 for a scalar, 1 for a vector, 2 for a matrix*/
00055 /* */
00056 ret  ::math::linearalgebra::dim ( type obj ) {
00057     return [llength [shape $obj]]
00058 }
00059 
00060 /*  shape --*/
00061 /*      Return the shape of an object (scalar, vector or matrix)*/
00062 /*  Arguments:*/
00063 /*      obj        Object like a scalar, vector or matrix*/
00064 /*  Result:*/
00065 /*      List of the sizes: empty list for a scalar, number of components*/
00066 /*      for a vector, number of rows and columns for a matrix*/
00067 /* */
00068 ret  ::math::linearalgebra::shape ( type obj ) {
00069     if { [llength $obj] <= 1 } {
00070        return {}
00071     }
00072     set result [llength $obj]
00073     if { [llength [lindex $obj 0]] <= 1 } {
00074        return $result
00075     } else {
00076        lappend result [llength [lindex $obj 0]]
00077     }
00078     return $result
00079 }
00080 
00081 /*  show --*/
00082 /*      Return a string representing the vector or matrix,*/
00083 /*      for easy printing*/
00084 /*  Arguments:*/
00085 /*      obj        Object like a scalar, vector or matrix*/
00086 /*      format     Format to be used (defaults to %6.4f)*/
00087 /*      rowsep     Separator for rows (defaults to \n)*/
00088 /*      colsep     Separator for columns (defaults to " ")*/
00089 /*  Result:*/
00090 /*      String representing the vector or matrix*/
00091 /* */
00092 ret  ::math::linearalgebra::show ( type obj , optional format =%6.4f , optional rowsep =\n , optional colsep =" " ) {
00093     set result ""
00094     if { [llength [lindex $obj 0]] == 1 } {
00095         foreach v $obj {
00096             append result "[format $format $v]$rowsep"
00097         }
00098     } else {
00099         foreach row $obj {
00100             foreach v $row {
00101                 append result "[format $format $v]$colsep"
00102             }
00103             append result $rowsep
00104         }
00105     }
00106     return $result
00107 }
00108 
00109 /*  conforming --*/
00110 /*      Determine if two objects (vector or matrix) are conforming*/
00111 /*      in shape, rows or for a matrix multiplication*/
00112 /*  Arguments:*/
00113 /*      type       Type of conforming: shape, rows or matmul*/
00114 /*      obj1       First object (vector or matrix)*/
00115 /*      obj2       Second object (vector or matrix)*/
00116 /*  Result:*/
00117 /*      1 if they conform, 0 if not*/
00118 /* */
00119 ret  ::math::linearalgebra::conforming ( type type , type obj1 , type obj2 ) {
00120     set shape1 [shape $obj1]
00121     set shape2 [shape $obj2]
00122     set result 0
00123     if { $type == "shape" } {
00124         set result [expr {[lindex $shape1 0] == [lindex $shape2 0] &&
00125                           [lindex $shape1 1] == [lindex $shape2 1]}]
00126     }
00127     if { $type == "rows" } {
00128         set result [expr {[lindex $shape1 0] == [lindex $shape2 0]}]
00129     }
00130     if { $type == "matmul" } {
00131         set result [expr {[lindex $shape1 1] == [lindex $shape2 0]}]
00132     }
00133     return $result
00134 }
00135 
00136 /*  crossproduct --*/
00137 /*      Return the "cross product" of two 3D vectors*/
00138 /*  Arguments:*/
00139 /*      vect1      First vector*/
00140 /*      vect2      Second vector*/
00141 /*  Result:*/
00142 /*      Cross product*/
00143 /* */
00144 ret  ::math::linearalgebra::crossproduct ( type vect1 , type vect2 ) {
00145 
00146     if { [llength $vect1] == 3 && [llength $vect2] == 3 } {
00147         foreach {v11 v12 v13} $vect1 {v21 v22 v23} $vect2 {break}
00148         return [list \
00149             [expr {$v12*$v23 - $v13*$v22}] \
00150             [expr {$v13*$v21 - $v11*$v23}] \
00151             [expr {$v11*$v22 - $v12*$v21}] ]
00152     } else {
00153         return -code error "Cross-product only defined for 3D vectors"
00154     }
00155 }
00156 
00157 /*  angle --*/
00158 /*      Return the "angle" between two vectors (in radians)*/
00159 /*  Arguments:*/
00160 /*      vect1      First vector*/
00161 /*      vect2      Second vector*/
00162 /*  Result:*/
00163 /*      Angle between the two vectors*/
00164 /* */
00165 ret  ::math::linearalgebra::angle ( type vect1 , type vect2 ) {
00166 
00167     set dp [dotproduct $vect1 $vect2]
00168     set n1 [norm_two $vect1]
00169     set n2 [norm_two $vect2]
00170 
00171     if { $n1 == 0.0 || $n2 == 0.0 } {
00172         return -code error "Angle not defined for null vector"
00173     }
00174 
00175     return [expr {acos($dp/$n1/$n2)}]
00176 }
00177 
00178 
00179 /*  norm --*/
00180 /*      Compute the (1-, 2- or Inf-) norm of a vector*/
00181 /*  Arguments:*/
00182 /*      vector     Vector (list of numbers)*/
00183 /*      type       Either 1, 2 or max/inf to indicate the type of*/
00184 /*                 norm (default: 2, the euclidean norm)*/
00185 /*  Result:*/
00186 /*      The (1-, 2- or Inf-) norm of a vector*/
00187 /* */
00188 ret  ::math::linearalgebra::norm ( type vector , optional type =2 ) {
00189     if { $type == 2 } {
00190        return [norm_two $vector]
00191     }
00192     if { $type == 1 } {
00193        return [norm_one $vector]
00194     }
00195     if { $type == "max" || $type == "inf" } {
00196        return [norm_max $vector]
00197     }
00198     return -code error "Unknown norm: $type"
00199 }
00200 
00201 /*  norm_one --*/
00202 /*      Compute the 1-norm of a vector*/
00203 /*  Arguments:*/
00204 /*      vector     Vector*/
00205 /*  Result:*/
00206 /*      The 1-norm of a vector*/
00207 /* */
00208 ret  ::math::linearalgebra::norm_one ( type vector ) {
00209     set sum 0.0
00210     foreach c $vector {
00211         set sum [expr {$sum+abs($c)}]
00212     }
00213     return $sum
00214 }
00215 
00216 /*  norm_two --*/
00217 /*      Compute the 2-norm of a vector (euclidean norm)*/
00218 /*  Arguments:*/
00219 /*      vector     Vector*/
00220 /*  Result:*/
00221 /*      The 2-norm of a vector*/
00222 /*  Note:*/
00223 /*      Rely on the function hypot() to make this robust*/
00224 /*      against overflow and underflow*/
00225 /* */
00226 ret  ::math::linearalgebra::norm_two ( type vector ) {
00227     set sum 0.0
00228     foreach c $vector {
00229         set sum [expr {hypot($c,$sum)}]
00230     }
00231     return $sum
00232 }
00233 
00234 /*  norm_max --*/
00235 /*      Compute the inf-norm of a vector (maximum of its components)*/
00236 /*  Arguments:*/
00237 /*      vector     Vector*/
00238 /*  Result:*/
00239 /*      The inf-norm of a vector*/
00240 /* */
00241 ret  ::math::linearalgebra::norm_max ( type vector ) {
00242     set max [lindex $vector 0]
00243     foreach c $vector {
00244         set max [expr {abs($c)>$max? abs($c) : $max}]
00245     }
00246     return $max
00247 }
00248 
00249 /*  normMatrix --*/
00250 /*      Compute the (1-, 2- or Inf-) norm of a matrix*/
00251 /*  Arguments:*/
00252 /*      matrix     Matrix (list of row vectors)*/
00253 /*      type       Either 1, 2 or max/inf to indicate the type of*/
00254 /*                 norm (default: 2, the euclidean norm)*/
00255 /*  Result:*/
00256 /*      The (1-, 2- or Inf-) norm of the matrix*/
00257 /* */
00258 ret  ::math::linearalgebra::normMatrix ( type matrix , optional type =2 ) {
00259     set v {}
00260 
00261     foreach row $matrix {
00262         lappend v [norm $row $type]
00263     }
00264 
00265     return [norm $v $type]
00266 }
00267 
00268 /*  symmetric --*/
00269 /*      Determine if the matrix is symmetric or not*/
00270 /*  Arguments:*/
00271 /*      matrix     Matrix (list of row vectors)*/
00272 /*      eps        Tolerance (defaults to 1.0e-8)*/
00273 /*  Result:*/
00274 /*      1 if symmetric (within the tolerance), 0 if not*/
00275 /* */
00276 ret  ::math::linearalgebra::symmetric ( type matrix , optional eps =1.0e-8 ) {
00277     set shape [shape $matrix]
00278     if { [lindex $shape 0] != [lindex $shape 1] } {
00279        return 0
00280     }
00281 
00282     set norm_org   [normMatrix $matrix]
00283     set norm_asymm [normMatrix [sub $matrix [transpose $matrix]]]
00284 
00285     if { $norm_asymm <= $eps*$norm_org } {
00286         return 1
00287     } else {
00288         return 0
00289     }
00290 }
00291 
00292 /*  dotproduct --*/
00293 /*      Compute the dot product of two vectors*/
00294 /*  Arguments:*/
00295 /*      vect1      First vector*/
00296 /*      vect2      Second vector*/
00297 /*  Result:*/
00298 /*      The dot product of the two vectors*/
00299 /* */
00300 ret  ::math::linearalgebra::dotproduct ( type vect1 , type vect2 ) {
00301     if { [llength $vect1] != [llength $vect2] } {
00302        return -code error "Vectors must be of equal length"
00303     }
00304     set sum 0.0
00305     foreach c1 $vect1 c2 $vect2 {
00306         set sum [expr {$sum + $c1*$c2}]
00307     }
00308     return $sum
00309 }
00310 
00311 /*  unitLengthVector --*/
00312 /*      Normalize a vector so that a length 1 results and return the new vector*/
00313 /*  Arguments:*/
00314 /*      vector     Vector to be normalized*/
00315 /*  Result:*/
00316 /*      A vector of length 1*/
00317 /* */
00318 ret  ::math::linearalgebra::unitLengthVector ( type vector ) {
00319     set scale [norm_two $vector]
00320     if { $scale == 0.0 } {
00321         return -code error "Can not normalize a null-vector"
00322     }
00323     return [scale [expr {1.0/$scale}] $vector]
00324 }
00325 
00326 /*  normalizeStat --*/
00327 /*      Normalize a matrix or vector in a statistical sense and return the result*/
00328 /*  Arguments:*/
00329 /*      mv        Matrix or vector to be normalized*/
00330 /*  Result:*/
00331 /*      A matrix or vector whose columns are normalised to have a mean of*/
00332 /*      0 and a standard deviation of 1.*/
00333 /* */
00334 ret  ::math::linearalgebra::normalizeStat ( type mv ) {
00335 
00336     if { [llength [lindex $mv 0]] > 1 } {
00337         set result {}
00338         foreach vector [transpose $mv] {
00339             lappend result [NormalizeStat_vect $vector]
00340         }
00341         return [transpose $result]
00342     } else {
00343         return [NormalizeStat_vect $mv]
00344     }
00345 }
00346 
00347 /*  NormalizeStat_vect --*/
00348 /*      Normalize a vector in a statistical sense and return the result*/
00349 /*  Arguments:*/
00350 /*      v        Vector to be normalized*/
00351 /*  Result:*/
00352 /*      A vector whose elements are normalised to have a mean of*/
00353 /*      0 and a standard deviation of 1. If all coefficients are equal,*/
00354 /*      a null-vector is returned.*/
00355 /* */
00356 ret  ::math::linearalgebra::NormalizeStat_vect ( type v ) {
00357     if { [llength $v] <= 1 } {
00358         return -code error "Vector can not be normalised - too few coefficients"
00359     }
00360 
00361     set sum   0.0
00362     set sum2  0.0
00363     set count 0.0
00364     foreach c $v {
00365         set sum   [expr {$sum   + $c}]
00366         set sum2  [expr {$sum2  + $c*$c}]
00367         set count [expr {$count + 1.0}]
00368     }
00369     set corr   [expr {$sum/$count}]
00370     set factor [expr {($sum2-$sum*$sum/$count)/($count-1)}]
00371     if { $factor > 0.0 } {
00372         set factor [expr {1.0/sqrt($factor)}]
00373     } else {
00374         set factor 0.0
00375     }
00376     set result {}
00377     foreach c $v {
00378         lappend result [expr {$factor*($c-$corr)}]
00379     }
00380     return $result
00381 }
00382 
00383 /*  axpy --*/
00384 /*      Compute the sum of a scaled vector/matrix and another*/
00385 /*      vector/matrix: a*x + y*/
00386 /*  Arguments:*/
00387 /*      scale      Scale factor (a) for the first vector/matrix*/
00388 /*      mv1        First vector/matrix (x)*/
00389 /*      mv2        Second vector/matrix (y)*/
00390 /*  Result:*/
00391 /*      The result of a*x+y*/
00392 /* */
00393 ret  ::math::linearalgebra::axpy ( type scale , type mv1 , type mv2 ) {
00394     if { [llength [lindex $mv1 0]] > 1 } {
00395         return [axpy_mat $scale $mv1 $mv2]
00396     } else {
00397         return [axpy_vect $scale $mv1 $mv2]
00398     }
00399 }
00400 
00401 /*  axpy_vect --*/
00402 /*      Compute the sum of a scaled vector and another vector: a*x + y*/
00403 /*  Arguments:*/
00404 /*      scale      Scale factor (a) for the first vector*/
00405 /*      vect1      First vector (x)*/
00406 /*      vect2      Second vector (y)*/
00407 /*  Result:*/
00408 /*      The result of a*x+y*/
00409 /* */
00410 ret  ::math::linearalgebra::axpy_vect ( type scale , type vect1 , type vect2 ) {
00411     set result {}
00412 
00413     foreach c1 $vect1 c2 $vect2 {
00414         lappend result [expr {$scale*$c1+$c2}]
00415     }
00416     return $result
00417 }
00418 
00419 /*  axpy_mat --*/
00420 /*      Compute the sum of a scaled matrix and another matrix: a*x + y*/
00421 /*  Arguments:*/
00422 /*      scale      Scale factor (a) for the first matrix*/
00423 /*      mat1       First matrix (x)*/
00424 /*      mat2       Second matrix (y)*/
00425 /*  Result:*/
00426 /*      The result of a*x+y*/
00427 /* */
00428 ret  ::math::linearalgebra::axpy_mat ( type scale , type mat1 , type mat2 ) {
00429     set result {}
00430     foreach row1 $mat1 row2 $mat2 {
00431         lappend result [axpy_vect $scale $row1 $row2]
00432     }
00433     return $result
00434 }
00435 
00436 /*  add --*/
00437 /*      Compute the sum of two vectors/matrices*/
00438 /*  Arguments:*/
00439 /*      mv1        First vector/matrix (x)*/
00440 /*      mv2        Second vector/matrix (y)*/
00441 /*  Result:*/
00442 /*      The result of x+y*/
00443 /* */
00444 ret  ::math::linearalgebra::add ( type mv1 , type mv2 ) {
00445     if { [llength [lindex $mv1 0]] > 1 } {
00446         return [add_mat $mv1 $mv2]
00447     } else {
00448         return [add_vect $mv1 $mv2]
00449     }
00450 }
00451 
00452 /*  add_vect --*/
00453 /*      Compute the sum of two vectors*/
00454 /*  Arguments:*/
00455 /*      vect1      First vector (x)*/
00456 /*      vect2      Second vector (y)*/
00457 /*  Result:*/
00458 /*      The result of x+y*/
00459 /* */
00460 ret  ::math::linearalgebra::add_vect ( type vect1 , type vect2 ) {
00461     set result {}
00462     foreach c1 $vect1 c2 $vect2 {
00463         lappend result [expr {$c1+$c2}]
00464     }
00465     return $result
00466 }
00467 
00468 /*  add_mat --*/
00469 /*      Compute the sum of two matrices*/
00470 /*  Arguments:*/
00471 /*      mat1       First matrix (x)*/
00472 /*      mat2       Second matrix (y)*/
00473 /*  Result:*/
00474 /*      The result of x+y*/
00475 /* */
00476 ret  ::math::linearalgebra::add_mat ( type mat1 , type mat2 ) {
00477     set result {}
00478     foreach row1 $mat1 row2 $mat2 {
00479         lappend result [add_vect $row1 $row2]
00480     }
00481     return $result
00482 }
00483 
00484 /*  sub --*/
00485 /*      Compute the difference of two vectors/matrices*/
00486 /*  Arguments:*/
00487 /*      mv1        First vector/matrix (x)*/
00488 /*      mv2        Second vector/matrix (y)*/
00489 /*  Result:*/
00490 /*      The result of x-y*/
00491 /* */
00492 ret  ::math::linearalgebra::sub ( type mv1 , type mv2 ) {
00493     if { [llength [lindex $mv1 0]] > 0 } {
00494         return [sub_mat $mv1 $mv2]
00495     } else {
00496         return [sub_vect $mv1 $mv2]
00497     }
00498 }
00499 
00500 /*  sub_vect --*/
00501 /*      Compute the difference of two vectors*/
00502 /*  Arguments:*/
00503 /*      vect1      First vector (x)*/
00504 /*      vect2      Second vector (y)*/
00505 /*  Result:*/
00506 /*      The result of x-y*/
00507 /* */
00508 ret  ::math::linearalgebra::sub_vect ( type vect1 , type vect2 ) {
00509     set result {}
00510     foreach c1 $vect1 c2 $vect2 {
00511         lappend result [expr {$c1-$c2}]
00512     }
00513     return $result
00514 }
00515 
00516 /*  sub_mat --*/
00517 /*      Compute the difference of two matrices*/
00518 /*  Arguments:*/
00519 /*      mat1       First matrix (x)*/
00520 /*      mat2       Second matrix (y)*/
00521 /*  Result:*/
00522 /*      The result of x-y*/
00523 /* */
00524 ret  ::math::linearalgebra::sub_mat ( type mat1 , type mat2 ) {
00525     set result {}
00526     foreach row1 $mat1 row2 $mat2 {
00527         lappend result [sub_vect $row1 $row2]
00528     }
00529     return $result
00530 }
00531 
00532 /*  scale --*/
00533 /*      Scale a vector or a matrix*/
00534 /*  Arguments:*/
00535 /*      scale      Scale factor (scalar; a)*/
00536 /*      mv         Vector/matrix (x)*/
00537 /*  Result:*/
00538 /*      The result of a*x*/
00539 /* */
00540 ret  ::math::linearalgebra::scale ( type scale , type mv ) {
00541     if { [llength [lindex $mv 0]] > 1 } {
00542         return [scale_mat $scale $mv]
00543     } else {
00544         return [scale_vect $scale $mv]
00545     }
00546 }
00547 
00548 /*  scale_vect --*/
00549 /*      Scale a vector*/
00550 /*  Arguments:*/
00551 /*      scale      Scale factor to apply (a)*/
00552 /*      vect       Vector to be scaled (x)*/
00553 /*  Result:*/
00554 /*      The result of a*x*/
00555 /* */
00556 ret  ::math::linearalgebra::scale_vect ( type scale , type vect ) {
00557     set result {}
00558     foreach c $vect {
00559         lappend result [expr {$scale*$c}]
00560     }
00561     return $result
00562 }
00563 
00564 /*  scale_mat --*/
00565 /*      Scale a matrix*/
00566 /*  Arguments:*/
00567 /*      scale      Scale factor to apply*/
00568 /*      mat        Matrix to be scaled*/
00569 /*  Result:*/
00570 /*      The result of x+y*/
00571 /* */
00572 ret  ::math::linearalgebra::scale_mat ( type scale , type mat ) {
00573     set result {}
00574     foreach row $mat {
00575         lappend result [scale_vect $scale $row]
00576     }
00577     return $result
00578 }
00579 
00580 /*  rotate --*/
00581 /*      Apply a planar rotation to two vectors*/
00582 /*  Arguments:*/
00583 /*      c          Cosine of the angle*/
00584 /*      s          Sine of the angle*/
00585 /*      vect1      First vector (x)*/
00586 /*      vect2      Second vector (y)*/
00587 /*  Result:*/
00588 /*      A list of two elements: c*x-s*y and s*x+c*y*/
00589 /* */
00590 ret  ::math::linearalgebra::rotate ( type c , type s , type vect1 , type vect2 ) {
00591     set result1 {}
00592     set result2 {}
00593     foreach v1 $vect1 v2 $vect2 {
00594         lappend result1 [expr {$c*$v1-$s*$v2}]
00595         lappend result2 [expr {$s*$v1+$c*$v2}]
00596     }
00597     return [list $result1 $result2]
00598 }
00599 
00600 /*  transpose --*/
00601 /*      Transpose a matrix*/
00602 /*  Arguments:*/
00603 /*      matrix     Matrix to be transposed*/
00604 /*  Result:*/
00605 /*      The transposed matrix*/
00606 /*  Note:*/
00607 /*      The second transpose implementation is faster on large*/
00608 /*      matrices (100x100 say), there is no significant difference*/
00609 /*      on small ones (10x10 say).*/
00610 /* */
00611 /* */
00612 ret  ::math::linearalgebra::transpose_old ( type matrix ) {
00613    set row {}
00614    set transpose {}
00615    foreach c [lindex $matrix 0] {
00616       lappend row 0.0
00617    }
00618    foreach r $matrix {
00619       lappend transpose $row
00620    }
00621 
00622    set nr 0
00623    foreach r $matrix {
00624       set nc 0
00625       foreach c $r {
00626          lset transpose $nc $nr $c
00627          incr nc
00628       }
00629       incr nr
00630    }
00631    return $transpose
00632 }
00633 
00634 ret  ::math::linearalgebra::transpose ( type matrix ) {
00635    set transpose {}
00636    set c 0
00637    foreach col [lindex $matrix 0] {
00638        set newrow {}
00639        foreach row $matrix {
00640            lappend newrow [lindex $row $c]
00641        }
00642        lappend transpose $newrow
00643        incr c
00644    }
00645    return $transpose
00646 }
00647 
00648 /*  MorV --*/
00649 /*      Identify if the object is a row/column vector or a matrix*/
00650 /*  Arguments:*/
00651 /*      obj        Object to be examined*/
00652 /*  Result:*/
00653 /*      The letter R, C or M depending on the shape*/
00654 /*      (just to make it all work fine: S for scalar)*/
00655 /*  Note:*/
00656 /*      Private procedure to fix a bug in matmul*/
00657 /* */
00658 ret  ::math::linearalgebra::MorV ( type obj ) {
00659     if { [llength $obj] > 1 } {
00660         if { [llength [lindex $obj 0]] > 1 } {
00661             return "M"
00662         } else {
00663             return "C"
00664         }
00665     } else {
00666         if { [llength [lindex $obj 0]] > 1 } {
00667             return "R"
00668         } else {
00669             return "S"
00670         }
00671     }
00672 }
00673 
00674 /*  matmul --*/
00675 /*      Multiply a vector/matrix with another vector/matrix*/
00676 /*  Arguments:*/
00677 /*      mv1        First vector/matrix (x)*/
00678 /*      mv2        Second vector/matrix (y)*/
00679 /*  Result:*/
00680 /*      The result of x*y*/
00681 /* */
00682 ret  ::math::linearalgebra::matmul_org ( type mv1 , type mv2 ) {
00683     if { [llength [lindex $mv1 0]] > 1 } {
00684         if { [llength [lindex $mv2 0]] > 1 } {
00685             return [matmul_mm $mv1 $mv2]
00686         } else {
00687             return [matmul_mv $mv1 $mv2]
00688         }
00689     } else {
00690         if { [llength [lindex $mv2 0]] > 1 } {
00691             return [matmul_vm $mv1 $mv2]
00692         } else {
00693             return [matmul_vv $mv1 $mv2]
00694         }
00695     }
00696 }
00697 
00698 ret  ::math::linearalgebra::matmul ( type mv1 , type mv2 ) {
00699     switch -exact -- "[MorV $mv1][MorV $mv2]" {
00700     "MM" {
00701          return [matmul_mm $mv1 $mv2]
00702     }
00703     "MC" {
00704          return [matmul_mv $mv1 $mv2]
00705     }
00706     "MR" {
00707          return -code error "Can not multiply a matrix with a row vector - wrong order"
00708     }
00709     "RM" {
00710          return [matmul_vm [transpose $mv1] $mv2]
00711     }
00712     "RC" {
00713          return [dotproduct [transpose $mv1] $mv2]
00714     }
00715     "RR" {
00716          return -code error "Can not multiply a matrix with a row vector - wrong order"
00717     }
00718     "CM" {
00719          return [transpose [matmul_vm $mv1 $mv2]]
00720     }
00721     "CR" {
00722          return [matmul_vv $mv1 [transpose $mv2]]
00723     }
00724     "CC" {
00725          return [matmul_vv $mv1 $mv2]
00726     }
00727     default {
00728          return -code error "Can not use a scalar object"
00729     }
00730     }
00731 }
00732 
00733 /*  matmul_mv --*/
00734 /*      Multiply a matrix and a column vector*/
00735 /*  Arguments:*/
00736 /*      matrix     Matrix (applied left: A)*/
00737 /*      vector     Vector (interpreted as column vector: x)*/
00738 /*  Result:*/
00739 /*      The vector A*x*/
00740 /* */
00741 ret  ::math::linearalgebra::matmul_mv ( type matrix , type vector ) {
00742    set newvect {}
00743    foreach row $matrix {
00744       set sum 0.0
00745       foreach v $vector c $row {
00746          set sum [expr {$sum+$v*$c}]
00747       }
00748       lappend newvect $sum
00749    }
00750    return $newvect
00751 }
00752 
00753 /*  matmul_vm --*/
00754 /*      Multiply a row vector with a matrix*/
00755 /*  Arguments:*/
00756 /*      vector     Vector (interpreted as row vector: x)*/
00757 /*      matrix     Matrix (applied right: A)*/
00758 /*  Result:*/
00759 /*      The vector xtrans*A = Atrans*x*/
00760 /* */
00761 ret  ::math::linearalgebra::matmul_vm ( type vector , type matrix ) {
00762    return [transpose [matmul_mv [transpose $matrix] $vector]]
00763 }
00764 
00765 /*  matmul_vv --*/
00766 /*      Multiply two vectors to obtain a matrix*/
00767 /*  Arguments:*/
00768 /*      vect1      First vector (column vector, x)*/
00769 /*      vect2      Second vector (row vector, y)*/
00770 /*  Result:*/
00771 /*      The "outer product" x*ytrans*/
00772 /* */
00773 ret  ::math::linearalgebra::matmul_vv ( type vect1 , type vect2 ) {
00774    set newmat {}
00775    foreach v1 $vect1 {
00776       set newrow {}
00777       foreach v2 $vect2 {
00778          lappend newrow [expr {$v1*$v2}]
00779       }
00780       lappend newmat $newrow
00781    }
00782    return $newmat
00783 }
00784 
00785 /*  matmul_mm --*/
00786 /*      Multiply two matrices*/
00787 /*  Arguments:*/
00788 /*      mat1      First matrix (A)*/
00789 /*      mat2      Second matrix (B)*/
00790 /*  Result:*/
00791 /*      The matrix product A*B*/
00792 /*  Note:*/
00793 /*      By transposing matrix B we can access the columns*/
00794 /*      as rows - much easier and quicker, as they are*/
00795 /*      the elements of the outermost list.*/
00796 /* */
00797 ret  ::math::linearalgebra::matmul_mm ( type mat1 , type mat2 ) {
00798    set newmat {}
00799    set tmat [transpose $mat2]
00800    foreach row1 $mat1 {
00801       set newrow {}
00802       foreach row2 $tmat {
00803          lappend newrow [dotproduct $row1 $row2]
00804       }
00805       lappend newmat $newrow
00806    }
00807    return $newmat
00808 }
00809 
00810 /*  mkVector --*/
00811 /*      Make a vector of a given size*/
00812 /*  Arguments:*/
00813 /*      ndim       Dimension of the vector*/
00814 /*      value      Default value for all elements (default: 0.0)*/
00815 /*  Result:*/
00816 /*      A list with ndim elements, representing a vector*/
00817 /* */
00818 ret  ::math::linearalgebra::mkVector ( type ndim , optional value =0.0 ) {
00819     set result {}
00820 
00821     while { $ndim > 0 } {
00822         lappend result $value
00823         incr ndim -1
00824     }
00825     return $result
00826 }
00827 
00828 /*  mkUnitVector --*/
00829 /*      Make a unit vector in a given direction*/
00830 /*  Arguments:*/
00831 /*      ndim       Dimension of the vector*/
00832 /*      dir        The direction (0, ... ndim-1)*/
00833 /*  Result:*/
00834 /*      A list with ndim elements, representing a unit vector*/
00835 /* */
00836 ret  ::math::linearalgebra::mkUnitVector ( type ndim , type dir ) {
00837 
00838     if { $dir < 0 || $dir >= $ndim } {
00839         return -code error "Invalid direction for unit vector - $dir"
00840     } else {
00841         set result [mkVector $ndim]
00842         lset result $dir 1.0
00843     }
00844     return $result
00845 }
00846 
00847 /*  mkMatrix --*/
00848 /*      Make a matrix of a given size*/
00849 /*  Arguments:*/
00850 /*      nrows      Number of rows*/
00851 /*      ncols      Number of columns*/
00852 /*      value      Default value for all elements (default: 0.0)*/
00853 /*  Result:*/
00854 /*      A nested list, representing an nrows x ncols matrix*/
00855 /* */
00856 ret  ::math::linearalgebra::mkMatrix ( type nrows , type ncols , optional value =0.0 ) {
00857     set result {}
00858 
00859     while { $nrows > 0 } {
00860         lappend result [mkVector $ncols $value]
00861         incr nrows -1
00862     }
00863     return $result
00864 }
00865 
00866 /*  mkIdent --*/
00867 /*      Make an identity matrix of a given size*/
00868 /*  Arguments:*/
00869 /*      size       Number of rows/columns*/
00870 /*  Result:*/
00871 /*      A nested list, representing an size x size identity matrix*/
00872 /* */
00873 ret  ::math::linearalgebra::mkIdentity ( type size ) {
00874     set result [mkMatrix $size $size 0.0]
00875 
00876     while { $size > 0 } {
00877         incr size -1
00878         lset result $size $size 1.0
00879     }
00880     return $result
00881 }
00882 
00883 /*  mkDiagonal --*/
00884 /*      Make a diagonal matrix of a given size*/
00885 /*  Arguments:*/
00886 /*      diag       List of values to appear on the diagonal*/
00887 /* */
00888 /*  Result:*/
00889 /*      A nested list, representing a diagonal matrix*/
00890 /* */
00891 ret  ::math::linearalgebra::mkDiagonal ( type diag ) {
00892     set size   [llength $diag]
00893     set result [mkMatrix $size $size 0.0]
00894 
00895     while { $size > 0 } {
00896         incr size -1
00897         lset result $size $size [lindex $diag $size]
00898     }
00899     return $result
00900 }
00901 
00902 /*  mkHilbert --*/
00903 /*      Make a Hilbert matrix of a given size*/
00904 /*  Arguments:*/
00905 /*      size       Size of the matrix*/
00906 /*  Result:*/
00907 /*      A nested list, representing a Hilbert matrix*/
00908 /*  Notes:*/
00909 /*      Hilbert matrices are very ill-conditioned wrt*/
00910 /*      eigenvalue/eigenvector problems. Therefore they*/
00911 /*      are good candidates for testing the accuracy*/
00912 /*      of algorithms and implementations.*/
00913 /* */
00914 ret  ::math::linearalgebra::mkHilbert ( type size ) {
00915     set size   [llength $diag]
00916 
00917     set result {}
00918     for { set j 0 } { $j < $size } { incr j } {
00919         set row {}
00920         for { set i 0 } { $i < $size } { incr i } {
00921             lappend row [expr {1.0/($i+$j+1.0)}]
00922         }
00923         lappend result $row
00924     }
00925     return $result
00926 }
00927 
00928 /*  mkDingdong --*/
00929 /*      Make a Dingdong matrix of a given size*/
00930 /*  Arguments:*/
00931 /*      size       Size of the matrix*/
00932 /*  Result:*/
00933 /*      A nested list, representing a Dingdong matrix*/
00934 /*  Notes:*/
00935 /*      Dingdong matrices are imprecisely represented,*/
00936 /*      but have the property of being very stable in*/
00937 /*      such algorithms as Gauss elimination.*/
00938 /* */
00939 ret  ::math::linearalgebra::mkDingdong ( type size ) {
00940     set result {}
00941     for { set j 0 } { $j < $size } { incr j } {
00942         set row {}
00943         for { set i 0 } { $i < $size } { incr i } {
00944             lappend row [expr {0.5/($size-$i-$j-0.5)}]
00945         }
00946         lappend result $row
00947     }
00948     return $result
00949 }
00950 
00951 /*  mkOnes --*/
00952 /*      Make a square matrix consisting of ones*/
00953 /*  Arguments:*/
00954 /*      size       Number of rows/columns*/
00955 /*  Result:*/
00956 /*      A nested list, representing a size x size matrix,*/
00957 /*      filled with 1.0*/
00958 /* */
00959 ret  ::math::linearalgebra::mkOnes ( type size ) {
00960     return [mkMatrix $size $size 1.0]
00961 }
00962 
00963 /*  mkMoler --*/
00964 /*      Make a Moler matrix*/
00965 /*  Arguments:*/
00966 /*      size       Size of the matrix*/
00967 /*  Result:*/
00968 /*      A nested list, representing a Moler matrix*/
00969 /*  Notes:*/
00970 /*      Moler matrices have a very simple Choleski*/
00971 /*      decomposition. It has one small eigenvalue*/
00972 /*      and it can easily upset elimination methods*/
00973 /*      for systems of linear equations*/
00974 /* */
00975 ret  ::math::linearalgebra::mkMoler ( type size ) {
00976     set result {}
00977     for { set j 0 } { $j < $size } { incr j } {
00978         set row {}
00979         for { set i 0 } { $i < $size } { incr i } {
00980             if { $i == $j } {
00981                 lappend row [expr {$i+1}]
00982             } else {
00983                 lappend row [expr {($i>$j?$j:$i)-1.0}]
00984             }
00985         }
00986         lappend result $row
00987     }
00988     return $result
00989 }
00990 
00991 /*  mkFrank --*/
00992 /*      Make a Frank matrix*/
00993 /*  Arguments:*/
00994 /*      size       Size of the matrix*/
00995 /*  Result:*/
00996 /*      A nested list, representing a Frank matrix*/
00997 /* */
00998 ret  ::math::linearalgebra::mkFrank ( type size ) {
00999     set result {}
01000     for { set j 0 } { $j < $size } { incr j } {
01001         set row {}
01002         for { set i 0 } { $i < $size } { incr i } {
01003             lappend row [expr {($i>$j?$j:$i)-2.0}]
01004         }
01005         lappend result $row
01006     }
01007     return $result
01008 }
01009 
01010 /*  mkBorder --*/
01011 /*      Make a bordered matrix*/
01012 /*  Arguments:*/
01013 /*      size       Size of the matrix*/
01014 /*  Result:*/
01015 /*      A nested list, representing a bordered matrix*/
01016 /*  Note:*/
01017 /*      This matrix has size-2 eigenvalues at 1.*/
01018 /* */
01019 ret  ::math::linearalgebra::mkBorder ( type size ) {
01020     set result {}
01021     for { set j 0 } { $j < $size } { incr j } {
01022         set row {}
01023         for { set i 0 } { $i < $size } { incr i } {
01024             set entry 0.0
01025             if { $i == $j } {
01026                 set entry 1.0
01027             }
01028             if { $i == $size-1 } {
01029                 set entry [expr {pow(2.0,-$j)}]
01030                 if { $j == $size-1 } {
01031                    set entry 0.0
01032                 }
01033             }
01034             if { $j == $size-1 } {
01035                 set entry [expr {pow(2.0,-$i)}]
01036                 if { $i == $size-1 } {
01037                    set entry 0.0
01038                 }
01039             }
01040             lappend row $entry
01041         }
01042         lappend result $row
01043     }
01044     return $result
01045 }
01046 
01047 /*  mkWilkinsonW+ --*/
01048 /*      Make a Wilkinson W+ matrix*/
01049 /*  Arguments:*/
01050 /*      size       Size of the matrix*/
01051 /*  Result:*/
01052 /*      A nested list, representing a Wilkinson W+ matrix*/
01053 /*  Note:*/
01054 /*      This kind of matrix has pairs of eigenvalues that*/
01055 /*      are very close together. Usually the order is odd.*/
01056 /* */
01057 ret  ::math::linearalgebra::mkWilkinsonW+ ( type size ) {
01058     set result {}
01059     for { set j 0 } { $j < $size } { incr j } {
01060         set row {}
01061         for { set i 0 } { $i < $size } { incr i } {
01062             set entry 0.0
01063             if { $i == $j } {
01064                 set entry [expr {$size/2 + 1 -
01065                                    ($i>$size-$i+1? $size-$i : $i+1)}]
01066             }
01067             if { $i == $j+1 || $i+1 == $j } {
01068                 set entry 1
01069             }
01070             lappend row $entry
01071         }
01072         lappend result $row
01073     }
01074     return $result
01075 }
01076 
01077 /*  mkWilkinsonW+ --*/
01078 /*      Make a Wilkinson W+ matrix*/
01079 /*  Arguments:*/
01080 /*      size       Size of the matrix*/
01081 /*  Result:*/
01082 /*      A nested list, representing a Wilkinson W+ matrix*/
01083 /*  Note:*/
01084 /*      This kind of matrix has pairs of eigenvalues with*/
01085 /*      opposite signs (if the order is odd).*/
01086 /* */
01087 ret  ::math::linearalgebra::mkWilkinsonW- ( type size ) {
01088     set result {}
01089     for { set j 0 } { $j < $size } { incr j } {
01090         set row {}
01091         for { set i 0 } { $i < $size } { incr i } {
01092             set entry 0.0
01093             if { $i == $j } {
01094                 set entry [expr {$size/2 + $i}]
01095             }
01096             if { $i == $j+1 || $i+1 == $j } {
01097                 set entry 1
01098             }
01099             lappend row $entry
01100         }
01101         lappend result $row
01102     }
01103     return $result
01104 }
01105 
01106 /*  getrow --*/
01107 /*      Get the specified row from a matrix*/
01108 /*  Arguments:*/
01109 /*      matrix     Matrix in question*/
01110 /*      row        Index of the row*/
01111 /* */
01112 /*  Result:*/
01113 /*      A list with the values on the requested row*/
01114 /* */
01115 ret  ::math::linearalgebra::getrow ( type matrix , type row ) {
01116     lindex $matrix $row
01117 }
01118 
01119 /*  setrow --*/
01120 /*      Set the specified row in a matrix*/
01121 /*  Arguments:*/
01122 /*      matrix     _Name_ of matrix in question*/
01123 /*      row        Index of the row*/
01124 /*      newvalues  New values for the row*/
01125 /* */
01126 /*  Result:*/
01127 /*      Updated matrix*/
01128 /*  Side effect:*/
01129 /*      The matrix is updated*/
01130 /* */
01131 ret  ::math::linearalgebra::setrow ( type matrix , type row , type newvalues ) {
01132     upvar $matrix mat
01133     lset mat $row $newvalues
01134     return $mat
01135 }
01136 
01137 /*  getcol --*/
01138 /*      Get the specified column from a matrix*/
01139 /*  Arguments:*/
01140 /*      matrix     Matrix in question*/
01141 /*      col        Index of the column*/
01142 /* */
01143 /*  Result:*/
01144 /*      A list with the values on the requested column*/
01145 /* */
01146 ret  ::math::linearalgebra::getcol ( type matrix , type col ) {
01147     set result {}
01148     foreach r $matrix {
01149         lappend result [lindex $r $col]
01150     }
01151     return $result
01152 }
01153 
01154 /*  setcol --*/
01155 /*      Set the specified column in a matrix*/
01156 /*  Arguments:*/
01157 /*      matrix     _Name_ of matrix in question*/
01158 /*      col        Index of the column*/
01159 /*      newvalues  New values for the column*/
01160 /* */
01161 /*  Result:*/
01162 /*      Updated matrix*/
01163 /*  Side effect:*/
01164 /*      The matrix is updated*/
01165 /* */
01166 ret  ::math::linearalgebra::setcol ( type matrix , type col , type newvalues ) {
01167     upvar $matrix mat
01168     for { set i 0 } { $i < [llength $mat] } { incr i } {
01169         lset mat $i $col [lindex $newvalues $i]
01170     }
01171     return $mat
01172 }
01173 
01174 /*  getelem --*/
01175 /*      Get the specified element (row,column) from a matrix/vector*/
01176 /*  Arguments:*/
01177 /*      matrix     Matrix in question*/
01178 /*      row        Index of the row*/
01179 /*      col        Index of the column (not present for vectors)*/
01180 /* */
01181 /*  Result:*/
01182 /*      The matrix element (row,column)*/
01183 /* */
01184 ret  ::math::linearalgebra::getelem ( type matrix , type row , optional col ={) } {
01185     if { $col != {} } {
01186         lindex $matrix $row $col
01187     } else {
01188         lindex $matrix $row
01189     }
01190 }
01191 
01192 /*  setelem --*/
01193 /*      Set the specified element (row,column) in a matrix or vector*/
01194 /*  Arguments:*/
01195 /*      matrix     _Name_ of matrix/vector in question*/
01196 /*      row        Index of the row*/
01197 /*      col        Index of the column/new value*/
01198 /*      newvalue   New value  for the element (not present for vectors)*/
01199 /* */
01200 /*  Result:*/
01201 /*      Updated matrix*/
01202 /*  Side effect:*/
01203 /*      The matrix is updated*/
01204 /* */
01205 ret  ::math::linearalgebra::setelem ( type matrix , type row , type col , optional newvalue ={) } {
01206     upvar $matrix mat
01207     if { $newvalue != {} } {
01208         l mat =  $row $col $newvalue
01209     } else {
01210         l mat =  $row $col
01211     }
01212     return $mat
01213 }
01214 
01215 /*  solveGauss --*/
01216 /*      Solve a system of linear equations using Gauss elimination*/
01217 /*  Arguments:*/
01218 /*      matrix     Matrix defining the coefficients*/
01219 /*      bvect      Right-hand side (may be several columns)*/
01220 /* */
01221 /*  Result:*/
01222 /*      Solution of the system or an error in case of singularity*/
01223 /* */
01224 ret  ::math::linearalgebra::solveGauss ( type matrix , type bvect ) {
01225     set norows [llength $matrix]
01226     set nocols $norows
01227 
01228     for { set i 0 } { $i < $nocols } { incr i } {
01229         set sweep_row   [getrow $matrix $i]
01230         set bvect_sweep [getrow $bvect  $i]
01231         # No pivoting yet
01232         set sweep_fact  [expr {double([lindex $sweep_row $i])}]
01233         for { set j [expr {$i+1}] } { $j < $norows } { incr j } {
01234             set current_row   [getrow $matrix $j]
01235             set bvect_current [getrow $bvect  $j]
01236             set factor      [expr {-[lindex $current_row $i]/$sweep_fact}]
01237 
01238             lset matrix $j [axpy_vect $factor $sweep_row   $current_row]
01239             lset bvect  $j [axpy_vect $factor $bvect_sweep $bvect_current]
01240         }
01241     }
01242 
01243     return [solveTriangular $matrix $bvect]
01244 }
01245 
01246 /*  solveTriangular --*/
01247 /*      Solve a system of linear equations where the matrix is*/
01248 /*      upper-triangular*/
01249 /*  Arguments:*/
01250 /*      matrix     Matrix defining the coefficients*/
01251 /*      bvect      Right-hand side (may be several columns)*/
01252 /* */
01253 /*  Result:*/
01254 /*      Solution of the system or an error in case of singularity*/
01255 /* */
01256 ret  ::math::linearalgebra::solveTriangular ( type matrix , type bvect ) {
01257     set norows [llength $matrix]
01258     set nocols $norows
01259 
01260     for { set i [expr {$norows-1}] } { $i >= 0 } { incr i -1 } {
01261         set sweep_row   [getrow $matrix $i]
01262         set bvect_sweep [getrow $bvect  $i]
01263         set sweep_fact  [expr {double([lindex $sweep_row $i])}]
01264         set norm_fact   [expr {1.0/$sweep_fact}]
01265 
01266         lset bvect $i [scale $norm_fact $bvect_sweep]
01267 
01268         for { set j [expr {$i-1}] } { $j >= 0 } { incr j -1 } {
01269             set current_row   [getrow $matrix $j]
01270             set bvect_current [getrow $bvect  $j]
01271             set factor     [expr {-[lindex $current_row $i]/$sweep_fact}]
01272 
01273             lset bvect  $j [axpy_vect $factor $bvect_sweep $bvect_current]
01274         }
01275     }
01276 
01277     return $bvect
01278 }
01279 
01280 /*  solveGaussBand --*/
01281 /*      Solve a system of linear equations using Gauss elimination,*/
01282 /*      where the matrix is stored as a band matrix.*/
01283 /*  Arguments:*/
01284 /*      matrix     Matrix defining the coefficients (in band form)*/
01285 /*      bvect      Right-hand side (may be several columns)*/
01286 /* */
01287 /*  Result:*/
01288 /*      Solution of the system or an error in case of singularity*/
01289 /* */
01290 ret  ::math::linearalgebra::solveGaussBand ( type matrix , type bvect ) {
01291     set norows   [llength $matrix]
01292     set nocols   $norows
01293     set nodiags  [llength [lindex $matrix 0]]
01294     set lowdiags [expr {($nodiags-1)/2}]
01295 
01296     for { set i 0 } { $i < $nocols } { incr i } {
01297         set sweep_row   [getrow $matrix $i]
01298         set bvect_sweep [getrow $bvect  $i]
01299 
01300         set sweep_fact [lindex $sweep_row [expr {$lowdiags-$i}]]
01301 
01302         for { set j [expr {$i+1}] } { $j <= $lowdiags } { incr j } {
01303             set sweep_row     [concat [lrange $sweep_row 1 end] 0.0]
01304             set current_row   [getrow $matrix $j]
01305             set bvect_current [getrow $bvect  $j]
01306             set factor      [expr {-[lindex $current_row $i]/$sweep_fact}]
01307 
01308             lset matrix $j [axpy_vect $factor $sweep_row   $current_row]
01309             lset bvect  $j [axpy_vect $factor $bvect_sweep $bvect_current]
01310         }
01311     }
01312 
01313     return [solveTriangularBand $matrix $bvect]
01314 }
01315 
01316 /*  solveTriangularBand --*/
01317 /*      Solve a system of linear equations where the matrix is*/
01318 /*      upper-triangular (stored as a band matrix)*/
01319 /*  Arguments:*/
01320 /*      matrix     Matrix defining the coefficients (in band form)*/
01321 /*      bvect      Right-hand side (may be several columns)*/
01322 /* */
01323 /*  Result:*/
01324 /*      Solution of the system or an error in case of singularity*/
01325 /* */
01326 ret  ::math::linearalgebra::solveTriangularBand ( type matrix , type bvect ) {
01327     set norows   [llength $matrix]
01328     set nocols   $norows
01329     set nodiags  [llength [lindex $matrix 0]]
01330     set uppdiags [expr {($nodiags-1)/2}]
01331     set middle   [expr {($nodiags-1)/2}]
01332 
01333     for { set i [expr {$norows-1}] } { $i >= 0 } { incr i -1 } {
01334         set sweep_row   [getrow $matrix $i]
01335         set bvect_sweep [getrow $bvect  $i]
01336         set sweep_fact  [lindex $sweep_row $middle]
01337         set norm_fact   [expr {1.0/$sweep_fact}]
01338 
01339         lset bvect $i [scale $norm_fact $bvect_sweep]
01340 
01341         for { set j [expr {$i-1}] } { $j >= $i-$middle && $j >= 0 } \
01342                 { incr j -1 } {
01343             set current_row   [getrow $matrix $j]
01344             set bvect_current [getrow $bvect  $j]
01345             set k             [expr {$i-$middle}]
01346             set factor     [expr {-[lindex $current_row $k]/$sweep_fact}]
01347 
01348             lset bvect  $j [axpy_vect $factor $bvect_sweep $bvect_current]
01349         }
01350     }
01351 
01352     return $bvect
01353 }
01354 
01355 /*  determineSVD --*/
01356 /*      Determine the singular value decomposition of a matrix*/
01357 /*  Arguments:*/
01358 /*      A          Matrix to be examined*/
01359 /*      epsilon    Tolerance for the procedure (defaults to 2.3e-16)*/
01360 /* */
01361 /*  Result:*/
01362 /*      List of the three elements U, S and V, where:*/
01363 /*      U, V orthogonal matrices, S a diagonal matrix (here a vector)*/
01364 /*      such that A = USVt*/
01365 /*  Note:*/
01366 /*      This is taken directly from Hume's LA package, and adjusted*/
01367 /*      to fit the different matrix format. Also changes are applied*/
01368 /*      that can be found in the second edition of Nash's book*/
01369 /*      "Compact numerical methods for computers"*/
01370 /* */
01371 /*      To be done: transpose the algorithm so that we can work*/
01372 /*      on rows, rather than columns*/
01373 /* */
01374 ret  ::math::linearalgebra::determineSVD ( type A , optional epsilon =2.3e-16 ) {
01375     foreach {m n} [shape $A] {break}
01376     set tolerance [expr {$epsilon * $epsilon* $m * $n}]
01377     set V [mkIdentity $n]
01378 
01379     #
01380     # Top of the iteration
01381     #
01382     set count 1
01383     for {set isweep 0} {$isweep < 30 && $count > 0} {incr isweep} {
01384         set count [expr {$n*($n-1)/2}] ;# count of rotations in a sweep
01385         for {set j 0} {$j < [expr {$n-1}]} {incr j} {
01386             for {set k [expr {$j+1}]} {$k < $n} {incr k} {
01387                 set p [set q [set r 0.0]]
01388                 for {set i 0} {$i < $m} {incr i} {
01389                     set Aij [lindex $A $i $j]
01390                     set Aik [lindex $A $i $k]
01391                     set p [expr {$p + $Aij*$Aik}]
01392                     set q [expr {$q + $Aij*$Aij}]
01393                     set r [expr {$r + $Aik*$Aik}]
01394                 }
01395                 if { $q < $r } {
01396                     set c 0.0
01397                     set s 1.0
01398                 } elseif { $q * $r == 0.0 } {
01399                     # Underflow of small elements
01400                     incr count -1
01401                     continue
01402                 } elseif { ($p*$p)/($q*$r) < $tolerance } {
01403                     # Cols j,k are orthogonal
01404                     incr count -1
01405                     continue
01406                 } else {
01407                     set q [expr {$q-$r}]
01408                     set v [expr {sqrt(4.0*$p*$p + $q*$q)}]
01409                     set c [expr {sqrt(($v+$q)/(2.0*$v))}]
01410                     set s [expr {-$p/($v*$c)}]
01411                     # s == sine of rotation angle, c == cosine
01412                     # Note: -s in comparison with original LA!
01413                 }
01414                 #
01415                 # Rotation of A
01416                 #
01417                 set colj [getcol $A $j]
01418                 set colk [getcol $A $k]
01419                 foreach {colj colk} [rotate $c $s $colj $colk] {break}
01420                 setcol A $j $colj
01421                 setcol A $k $colk
01422                 #
01423                 # Rotation of V
01424                 #
01425                 set colj [getcol $V $j]
01426                 set colk [getcol $V $k]
01427                 foreach {colj colk} [rotate $c $s $colj $colk] {break}
01428                 setcol V $j $colj
01429                 setcol V $k $colk
01430             } ;#k
01431         } ;# j
01432         #puts "pass=$isweep skipped rotations=$count"
01433     } ;# isweep
01434 
01435     set S {}
01436     for {set j 0} {$j < $n} {incr j} {
01437         set q [norm_two [getcol $A $j]]
01438         lappend S $q
01439         if { $q >= $tolerance } {
01440             set newcol [scale [expr {1.0/$q}] [getcol $A $j]]
01441             setcol A $j $newcol
01442         }
01443     } ;# j
01444     return [list $A $S $V]
01445 }
01446 
01447 /*  eigenvectorsSVD --*/
01448 /*      Determine the eigenvectors and eigenvalues of a real*/
01449 /*      symmetric matrix via the SVD*/
01450 /*  Arguments:*/
01451 /*      A          Matrix to be examined*/
01452 /*      epsilon    Tolerance for the procedure (defaults to 2.3e-16)*/
01453 /* */
01454 /*  Result:*/
01455 /*      List of the matrix of eigenvectors and the vector of corresponding*/
01456 /*      eigenvalues*/
01457 /*  Note:*/
01458 /*      This is taken directly from Hume's LA package, and adjusted*/
01459 /*      to fit the different matrix format. Also changes are applied*/
01460 /*      that can be found in the second edition of Nash's book*/
01461 /*      "Compact numerical methods for computers"*/
01462 /* */
01463 ret  ::math::linearalgebra::eigenvectorsSVD ( type A , optional epsilon =2.3e-16 ) {
01464     foreach {m n} [shape $A] {break}
01465     if { $m != $n } {
01466        return -code error "Expected a square matrix"
01467     }
01468 
01469     #
01470     # Determine the shift h so that the matrix A+hI is positive
01471     # definite (the Gershgorin region)
01472     #
01473     set h {}
01474     set i 0
01475     foreach row $matrix {
01476         set aii [lindex $row $i]
01477         set sum [expr {2.0*abs($aii) - [norm_one $row]}]
01478         incr i
01479 
01480         if { $h == {} || $sum < $h } {
01481             set h $sum
01482         }
01483     }
01484     if { $h <= $eps } {
01485         set h [expr {$h - sqrt($eps)}]
01486         # try to make smallest eigenvalue positive and not too small
01487         set A [sub $A [mkIdentity $m $h]]
01488     } else {
01489         set h 0.0
01490     }
01491 
01492     #
01493     # Determine the SVD decomposition: this holds the
01494     # eigenvectors and eigenvalues
01495     #
01496     foreach {U S V} [determineSVD $A $eps] {break}
01497 
01498     #
01499     # Rescale and flip signs if all negative or zero
01500     #
01501     set evals {}
01502     for {set j 0} {$j < $n} {incr j} {
01503         set s 0.0
01504         set notpositive 0
01505         for {set i 0} {$i < $n} {incr i} {
01506             set Aij [lindex $A $i $j]
01507             if { $Aij <= 0.0 } {
01508                incr notpositive
01509             }
01510             set s [expr {$s + $Aij*$Aij}]
01511         }
01512         set s [expr {sqrt($s)}]
01513         if { $notpositive == $n } {
01514             set sf [expr {0.0-$s}]
01515         } else {
01516             set sf $s
01517         }
01518         set colv [getcol $A $j]
01519         setcol A [scale [expr {1.0/$sf}] $colv]
01520         lappend evals [expr {$s+$h}]
01521     }
01522     return [list $A $evals]
01523 }
01524 
01525 /*  leastSquaresSVD --*/
01526 /*      Determine the solution to the least-squares problem Ax ~ y*/
01527 /*      via the singular value decomposition*/
01528 /*  Arguments:*/
01529 /*      A          Matrix to be examined*/
01530 /*      y          Dependent variable*/
01531 /*      qmin       Minimum singular value to be considered (defaults to 0)*/
01532 /*      epsilon    Tolerance for the procedure (defaults to 2.3e-16)*/
01533 /* */
01534 /*  Result:*/
01535 /*      Vector x as the solution of the least-squares problem*/
01536 /* */
01537 ret  ::math::linearalgebra::leastSquaresSVD ( type A , type y , optional qmin =0.0 , optional epsilon =2.3e-16 ) {
01538 
01539     foreach {m n} [shape $A] {break}
01540     foreach {U S V} [determineSVD $A $epsilon] {break}
01541 
01542     set tol [expr {$epsilon * $epsilon * $n * $n}]
01543     #
01544     # form Utrans*y into g
01545     #
01546     set g {}
01547     for {set j 0} {$j < $n} {incr j} {
01548         set s 0.0
01549         for {set i 0} {$i < $m} {incr i} {
01550             set Uij [lindex $U $i $j]
01551             set yi  [lindex $y $i]
01552             set s [expr {$s + $Uij*$yi}]
01553         }
01554         lappend g $s ;# g[j] = $s
01555     }
01556 
01557     #
01558     # form VS+g = VS+Utrans*g
01559     #
01560     set x {}
01561     for {set j 0} {$j < $n} {incr j} {
01562         set s 0.0
01563         for {set i 0} {$i < $n} {incr i} {
01564             set zi [lindex $S $i]
01565             if { $zi > $qmin } {
01566                 set Vji [lindex $V $j $i]
01567                 set gi  [lindex $g $i]
01568                 set s   [expr {$s + $Vji*$gi/$zi}]
01569             }
01570         }
01571         lappend x $s
01572     }
01573     return $x
01574 }
01575 
01576 /*  choleski --*/
01577 /*      Determine the Choleski decomposition of a symmetric,*/
01578 /*      positive-semidefinite matrix (this condition is not checked!)*/
01579 /* */
01580 /*  Arguments:*/
01581 /*      matrix     Matrix to be treated*/
01582 /* */
01583 /*  Result:*/
01584 /*      Lower-triangular matrix (L) representing the Choleski decomposition:*/
01585 /*         L Lt = matrix*/
01586 /* */
01587 ret  ::math::linearalgebra::choleski ( type matrix ) {
01588     foreach {rows cols} [shape $matrix] {break}
01589 
01590     set result $matrix
01591 
01592     for { set j 0 } { $j < $cols } { incr j } {
01593         if { $j > 0 } {
01594             for { set i $j } { $i < $cols } { incr i } {
01595                 set sum [lindex $result $i $j]
01596                 for { set k 0 } { $k <= $j-1 } { incr k } {
01597                     set Aki [lindex $result $i $k]
01598                     set Akj [lindex $result $j $k]
01599                     set sum [expr {$sum-$Aki*$Akj}]
01600                 }
01601                 lset result $i $j $sum
01602             }
01603         }
01604 
01605         #
01606         # Take care of a singular matrix
01607         #
01608         if { [lindex $result $j $j] <= 0.0 } {
01609             lset result $j $j 0.0
01610         }
01611 
01612         #
01613         # Scale the column
01614         #
01615         set s [expr {sqrt([lindex $result $j $j])}]
01616         for { set i 0 } { $i < $cols } { incr i } {
01617             if { $i >= $j } {
01618                 if { $s == 0.0 } {
01619                     lset result $i $j 0.0
01620                 } else {
01621                     lset result $i $j [expr {[lindex $result $i $j]/$s}]
01622                 }
01623             } else {
01624                 lset result $i $j 0.0
01625             }
01626         }
01627     }
01628 
01629     return $result
01630 }
01631 
01632 /*  orthonormalizeColumns --*/
01633 /*      Orthonormalize the columns of a matrix, using the modified*/
01634 /*      Gram-Schmidt method*/
01635 /*  Arguments:*/
01636 /*      matrix     Matrix to be treated*/
01637 /* */
01638 /*  Result:*/
01639 /*      Matrix with pairwise orthogonal columns, each having length 1*/
01640 /* */
01641 ret  ::math::linearalgebra::orthonormalizeColumns ( type matrix ) {
01642     transpose [orthonormalizeRows [transpose $matrix]]
01643 }
01644 
01645 /*  orthonormalizeRows --*/
01646 /*      Orthonormalize the rows of a matrix, using the modified*/
01647 /*      Gram-Schmidt method*/
01648 /*  Arguments:*/
01649 /*      matrix     Matrix to be treated*/
01650 /* */
01651 /*  Result:*/
01652 /*      Matrix with pairwise orthogonal rows, each having length 1*/
01653 /* */
01654 ret  ::math::linearalgebra::orthonormalizeRows ( type matrix ) {
01655     set result $matrix
01656     set rowno  0
01657     foreach r $matrix {
01658         set newrow [unitLengthVector [getrow $result $rowno]]
01659         setrow result $rowno $newrow
01660         incr rowno
01661         set  rowno2 $rowno
01662 
01663         #
01664         # Update the matrix immediately: this is numerically
01665         # more stable
01666         #
01667         foreach nextrow [lrange $result $rowno end] {
01668             set factor  [dotproduct $newrow $nextrow]
01669             set nextrow [sub_vect $nextrow [scale_vect $factor $newrow]]
01670             setrow result $rowno2 $nextrow
01671             incr rowno2
01672         }
01673     }
01674     return $result
01675 }
01676 
01677 /*  to_LA --*/
01678 /*      Convert a matrix or vector to the LA format*/
01679 /*  Arguments:*/
01680 /*      mv         Matrix or vector to be converted*/
01681 /* */
01682 /*  Result:*/
01683 /*      List according to LA conventions*/
01684 /* */
01685 ret  ::math::linearalgebra::to_LA ( type mv ) {
01686     foreach {rows cols} [shape $mv] {
01687         if { $cols == {} } {
01688             set cols 0
01689         }
01690     }
01691 
01692     set result [list 2 $rows $cols]
01693     foreach row $mv {
01694         set result [concat $result $row]
01695     }
01696     return $result
01697 }
01698 
01699 /*  from_LA --*/
01700 /*      Convert a matrix or vector from the LA format*/
01701 /*  Arguments:*/
01702 /*      mv         Matrix or vector to be converted*/
01703 /* */
01704 /*  Result:*/
01705 /*      List according to current conventions*/
01706 /* */
01707 ret  ::math::linearalgebra::from_LA ( type mv ) {
01708     foreach {rows cols} [lrange $mv 1 2] {break}
01709 
01710     if { $cols != 0 } {
01711         set result {}
01712         set elem2  2
01713         for { set i 0 } { $i < $rows } { incr i } {
01714             set  elem1 [expr {$elem2+1}]
01715             incr elem2 $cols
01716             lappend result [lrange $mv $elem1 $elem2]
01717         }
01718     } else {
01719         set result [lrange $mv 3 end]
01720     }
01721 
01722     return $result
01723 }
01724 
01725 /* */
01726 /*  Announce the package's presence*/
01727 /* */
01728 package provide math::linearalgebra 1.0.2
01729 
01730 if { 0 } {
01731 Te doen:
01732 behoorlijke testen!
01733 matmul
01734 solveGauss_band
01735 join_col, join_row
01736 kleinste-kwadraten met SVD en met Gauss
01737 PCA
01738 }
01739 
01740 if { 0 } {
01741  matrix =  {{1.0  2.0 -1.0}
01742             {3.0  1.1  0.5}
01743             {1.0 -2.0  3.0}}
01744  bvect =   {{1.0  2.0 -1.0}
01745             {3.0  1.1  0.5}
01746             {1.0 -2.0  3.0}}
01747 puts [join [::math::linearalgebra::solveGauss $matrix $bvect] \n]
01748  bvect =   {{4.0   2.0}
01749             {12.0  1.2}
01750             {4.0  -2.0}}
01751 puts [join [::math::linearalgebra::solveGauss $matrix $bvect] \n]
01752 }
01753 
01754 if { 0 } {
01755 
01756     vect1 =  {1.0 2.0}
01757     vect2 =  {3.0 4.0}
01758    ::math::linearalgebra::axpy_vect 1.0 $vect1 $vect2
01759    ::math::linearalgebra::add_vect      $vect1 $vect2
01760    puts [time {::math::linearalgebra::axpy_vect 1.0 $vect1 $vect2} 50000]
01761    puts [time {::math::linearalgebra::axpy_vect 2.0 $vect1 $vect2} 50000]
01762    puts [time {::math::linearalgebra::axpy_vect 1.0 $vect1 $vect2} 50000]
01763    puts [time {::math::linearalgebra::axpy_vect 1.1 $vect1 $vect2} 50000]
01764    puts [time {::math::linearalgebra::add_vect      $vect1 $vect2} 50000]
01765 }
01766 
01767 if { 0 } {
01768  M =  {{1 2} {2 1}}
01769 puts "[::math::linearalgebra::determineSVD $M]"
01770 }
01771 if { 0 } {
01772  M =  {{1 2} {2 1}}
01773 puts "[::math::linearalgebra::normMatrix $M]"
01774 }
01775 if { 0 } {
01776  M =  {{1.3 2.3} {2.123 1}}
01777 puts "[::math::linearalgebra::show $M]"
01778  M =  {{1.3 2.3 45 3.} {2.123 1 5.6 0.01}}
01779 puts "[::math::linearalgebra::show $M]"
01780 puts "[::math::linearalgebra::show $M %12.4f]"
01781 }
01782 if { 0 } {
01783      M =  {{1 0 0}
01784            {1 1 0}
01785            {1 1 1}}
01786     puts [::math::linearalgebra::orthonormalizeRows $M]
01787 }
01788 if { 0 } {
01789      M =  [::math::linearalgebra::mkMoler 5]
01790     puts [::math::linearalgebra::choleski $M]
01791 }
01792 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1