00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 package require Tcl 8.4
00026
00027 namespace ::math::linearalgebra {
00028
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
00050
00051
00052
00053
00054
00055
00056 ret ::math::linearalgebra::dim ( type obj ) {
00057 return [llength [shape $obj]]
00058 }
00059
00060
00061
00062
00063
00064
00065
00066
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
00082
00083
00084
00085
00086
00087
00088
00089
00090
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
00110
00111
00112
00113
00114
00115
00116
00117
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
00137
00138
00139
00140
00141
00142
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
00158
00159
00160
00161
00162
00163
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
00180
00181
00182
00183
00184
00185
00186
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
00202
00203
00204
00205
00206
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
00217
00218
00219
00220
00221
00222
00223
00224
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
00235
00236
00237
00238
00239
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
00250
00251
00252
00253
00254
00255
00256
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
00269
00270
00271
00272
00273
00274
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
00293
00294
00295
00296
00297
00298
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
00312
00313
00314
00315
00316
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
00327
00328
00329
00330
00331
00332
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
00348
00349
00350
00351
00352
00353
00354
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
00384
00385
00386
00387
00388
00389
00390
00391
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
00402
00403
00404
00405
00406
00407
00408
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
00420
00421
00422
00423
00424
00425
00426
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
00437
00438
00439
00440
00441
00442
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
00453
00454
00455
00456
00457
00458
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
00469
00470
00471
00472
00473
00474
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
00485
00486
00487
00488
00489
00490
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
00501
00502
00503
00504
00505
00506
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
00517
00518
00519
00520
00521
00522
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
00533
00534
00535
00536
00537
00538
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
00549
00550
00551
00552
00553
00554
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
00565
00566
00567
00568
00569
00570
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
00581
00582
00583
00584
00585
00586
00587
00588
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
00601
00602
00603
00604
00605
00606
00607
00608
00609
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
00649
00650
00651
00652
00653
00654
00655
00656
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
00675
00676
00677
00678
00679
00680
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
00734
00735
00736
00737
00738
00739
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
00754
00755
00756
00757
00758
00759
00760
00761 ret ::math::linearalgebra::matmul_vm ( type vector , type matrix ) {
00762 return [transpose [matmul_mv [transpose $matrix] $vector]]
00763 }
00764
00765
00766
00767
00768
00769
00770
00771
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
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
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
00811
00812
00813
00814
00815
00816
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
00829
00830
00831
00832
00833
00834
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
00848
00849
00850
00851
00852
00853
00854
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
00867
00868
00869
00870
00871
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
00884
00885
00886
00887
00888
00889
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
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
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
00929
00930
00931
00932
00933
00934
00935
00936
00937
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
00952
00953
00954
00955
00956
00957
00958
00959 ret ::math::linearalgebra::mkOnes ( type size ) {
00960 return [mkMatrix $size $size 1.0]
00961 }
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
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
00992
00993
00994
00995
00996
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
01011
01012
01013
01014
01015
01016
01017
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
01048
01049
01050
01051
01052
01053
01054
01055
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
01078
01079
01080
01081
01082
01083
01084
01085
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
01107
01108
01109
01110
01111
01112
01113
01114
01115 ret ::math::linearalgebra::getrow ( type matrix , type row ) {
01116 lindex $matrix $row
01117 }
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129
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
01138
01139
01140
01141
01142
01143
01144
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
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
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
01175
01176
01177
01178
01179
01180
01181
01182
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
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
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
01216
01217
01218
01219
01220
01221
01222
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
01247
01248
01249
01250
01251
01252
01253
01254
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
01281
01282
01283
01284
01285
01286
01287
01288
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
01317
01318
01319
01320
01321
01322
01323
01324
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
01356
01357
01358
01359
01360
01361
01362
01363
01364
01365
01366
01367
01368
01369
01370
01371
01372
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
01448
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
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
01526
01527
01528
01529
01530
01531
01532
01533
01534
01535
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
01577
01578
01579
01580
01581
01582
01583
01584
01585
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
01633
01634
01635
01636
01637
01638
01639
01640
01641 ret ::math::linearalgebra::orthonormalizeColumns ( type matrix ) {
01642 transpose [orthonormalizeRows [transpose $matrix]]
01643 }
01644
01645
01646
01647
01648
01649
01650
01651
01652
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
01678
01679
01680
01681
01682
01683
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
01700
01701
01702
01703
01704
01705
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
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