how to code inverse of a 3 X 3 matrix in tcl scripting? - tcl

I'm finding difficulty in coding for inverse of a 3x3 matrix in tcl. do we really need packages to code for matrix , i need clear explanation for the following parts with minimal simpler coding
1.how to create matrix ?
2.how to find determinant?
3.how to find inverse of it ?

We don't need packages — they're just Tcl code after all — but they do simplify things quite a bit sometimes.
Matrices are represented in Tcl as lists of lists of numbers; it's the natural representation. Think of them being like this (which is row-major form):
set myMatrix {
{ 1.0 -2.0 3.0 }
{-4.0 5.0 -6.0 }
{ 7.0 -8.0 -9.0 }
}
But that can be rewritten like this (it's just a change of whitespace, which isn't significant):
set myMatrix {{1.0 -2.0 3.0} {-4.0 5.0 -6.0} {7.0 -8.0 -9.0}}
The code to invert such a matrix is on the Tcler's Wiki:
puts [Inverse3 $myMatrix]
# {-1.7222222222222223 -0.7777777777777778 -0.05555555555555555} {-1.4444444444444444 -0.5555555555555556 -0.1111111111111111} {-0.05555555555555555 -0.1111111111111111 -0.05555555555555555}
This is the code from the Wiki page, saved here in case the page gets edited away (and using the optimised _Cofactor3).
proc Inverse3 {matrix} {
if {[llength $matrix] != 3 ||
[llength [lindex $matrix 0]] != 3 ||
[llength [lindex $matrix 1]] != 3 ||
[llength [lindex $matrix 2]] != 3} {
error "wrong sized matrix"
}
set inv {{? ? ?} {? ? ?} {? ? ?}}
# Get adjoint matrix : transpose of cofactor matrix
for {set i 0} {$i < 3} {incr i} {
for {set j 0} {$j < 3} {incr j} {
lset inv $i $j [_Cofactor3 $matrix $i $j]
}
}
# Now divide by the determinant
set det [expr {double([lindex $matrix 0 0] * [lindex $inv 0 0]
+ [lindex $matrix 0 1] * [lindex $inv 1 0]
+ [lindex $matrix 0 2] * [lindex $inv 2 0])}]
if {$det == 0} {
error "non-invertable matrix"
}
for {set i 0} {$i < 3} {incr i} {
for {set j 0} {$j < 3} {incr j} {
lset inv $i $j [expr {[lindex $inv $i $j] / $det}]
}
}
return $inv
}
proc _Cofactor3 {matrix i j} {
set COLS {{1 2} {0 2} {0 1}}
lassign [lindex $COLS $j] row1 row2
lassign [lindex $COLS $i] col1 col2
set a [lindex $matrix $row1 $col1]
set b [lindex $matrix $row1 $col2]
set c [lindex $matrix $row2 $col1]
set d [lindex $matrix $row2 $col2]
set det [expr {$a*$d - $b*$c}]
if {($i+$j) & 1} { set det [expr {-$det}]}
return $det
}

Related

Reference Arrays and Variables after proc tcl

I am in the process of creating a wireless sensor network using NS2. 20 Wireless Nodes are distributed randomly across an x-y plane, and using a distance calculation, the neighbors are found (the ones that a broadcast can reach). From the neighbors, I create clusters for each node starting with Node 0.
For example, starting with Node 0 as clusterhead, we would get an output like:
Cluster for Node 0 contains:
1
3
8
From there, move to the next node NOT contained in there so:
Cluster for Node 2 contains:
5
6
7
Using lists in TCL, I am having trouble referencing the cluster array changed in the proc neighbors. I am able to add elements to the list for Node 0 cluster. However, I can only do it inside the process and the loops create a 'reprinting' issue. I have tried different combinations of global cluster and upvar. How do I reference my cluster list AFTER the process?
Below is my current code with the output and reprint issue (the neighbors are correct, I am able to verify that in the simulator).
set cluster {}
set bool 0
proc neighbors {n1 n2 nd1 nd2} {
global bool cluster
set x1 [expr int([$n1 set X_])]
set y1 [expr int([$n1 set Y_])]
set x2 [expr int([$n2 set X_])]
set y2 [expr int([$n2 set Y_])]
set d [expr int(sqrt(pow(($x2-$x1),2)+pow(($y2-$y1),2)))]
if {$d<40} {
if {$nd1!=$nd2} {
set bool 1
if {$bool ==1} {
puts "$nd1 AND $nd2"
if {$nd1 == 0} {
lappend cluster $nd2
}
}
}
}
for {set i 0} {$i < [llength $::cluster]} {incr i} {
puts "${i}=[lindex $::cluster $i]"
}
}
for {set i 0} {$i < $val(nn)} {incr i} {
for {set j 0} {$j < $val(nn)} {incr j} {
$ns at 0.0 "neighbors $node($i) $node($j) $i $j"
}
}
Small section of Output (it repeats for every nd1 and nd2 combo) - For Node 0, the the 0-7 identifier is referencing the correct neighbor.
0 AND 19
0=2
1=4
2=5
3=6
4=10
5=13
6=15
7=19
0=2
1=4
2=5
3=6
4=10
5=13
6=15
7=19
0=2
1=4
2=5
3=6
4=10
5=13
6=15
7=19
Now, if I move the cluster index OUTSIDE the proc. I get no output referring to the cluster list.
set cluster {}
set bool 0
proc neighbors {n1 n2 nd1 nd2} {
global bool cluster
set x1 [expr int([$n1 set X_])]
set y1 [expr int([$n1 set Y_])]
set x2 [expr int([$n2 set X_])]
set y2 [expr int([$n2 set Y_])]
set d [expr int(sqrt(pow(($x2-$x1),2)+pow(($y2-$y1),2)))]
if {$d<40 && $nd1!=$nd2} {
puts "$nd1 AND $nd2"
if {$nd1 == 0} {
lappend cluster $nd2
}
}
}
for {set i 0} {$i < $val(nn)} {incr i} {
for {set j 0} {$j < $val(nn)} {incr j} {
puts "$i, $j"
$ns at 0.0 "neighbors $node($i) $node($j) $i $j"
}
}
for {set i 0} {$i < [llength $::cluster]} {incr i} {
puts "${i}=[lindex $::cluster $i]"
}
Output (just for Node 0):
0, 0
0, 1
0, 2
0, 3
...
0 AND 4
0 AND 6
0 AND 8
0 AND 9
0 AND 11
0 AND 12
0 AND 15
0 AND 16
0 AND 19
Although I would expect/want something like
...
0 AND 15
Cluster for Node 0:
0=8
1=12
2=15
How do I reference this array/list properly? Also, I feel like I will need to use the bool == 1 later as well. In simple tests, I cannot reference it's change outside the proc as well.
Thank you
I figured out the solution. The I THINK the problems lies in the fact that the simulator must run all process in the code - not the actual TCL compiler. Either way, adding a new proc printCluster gives you a single output of your cluster along with the neighbors.
set ::cluster {}
set bool 0
proc neighbors {n1 n2 nd1 nd2} {
global bool ::cluster {}
set x1 [expr int([$n1 set X_])]
set y1 [expr int([$n1 set Y_])]
set x2 [expr int([$n2 set X_])]
set y2 [expr int([$n2 set Y_])]
set d [expr int(sqrt(pow(($x2-$x1),2)+pow(($y2-$y1),2)))]
if {$d<40 && $nd1!=$nd2} {
puts "$nd1 AND $nd2"
if {$nd1 == 0} {
lappend ::cluster $nd2
}
}
proc printCluster {} {
puts "Node 0 Cluster:"
for {set i 0} {$i < [llength $::cluster]} {incr i} {
puts "[lindex $::cluster $i]"
}
}
}
for {set i 0} {$i < $val(nn)} {incr i} {
for {set j 0} {$j < $val(nn)} {incr j} {
$ns at 0.0 "neighbors $node($i) $node($j) $i $j"
}
}
$ns at 0.0 "printCluster"
Output:
...
Node 0 Cluster:
1
2
6
15
18

What is the reason of error "Floating point exception (core dumped)"

I'm trying to save output $result of proc Inverse2 which get scheduled after every one second (it is called inside another procedure,that procedure is rescheduled for 1s that is why Inverse2 procedure)
I want to get output which is {x y now} and assign variable to it for latest two instances
x1-> x location at current time (for example at 8.0)
y1-> y location at current time
x2-> x location at (current time+1) (for example at 9.0)
y2-> y location at (current time+1)
and use for further calculations.
Below is a code I have tried but error I got after two iterations is Floating point exception (core dumped). Where I'm doing wrong?
code:
set result {}
proc Inverse2 {m} {
set op [open output.tr w]
global result
global ns
set now [$ns now]
lassign [lindex $m 0 2] x1
lassign [lindex $m 0 3] y1
lassign [lindex $m 0 6] d1
lassign [lindex $m 1 2] x2
lassign [lindex $m 1 3] y2
lassign [lindex $m 1 6] d2
lassign [lindex $m 2 2] x3
lassign [lindex $m 2 3] y3
lassign [lindex $m 2 6] d3
set mt {{? ?} {? ?}}
lset mt 0 0 [expr 2*($x1-$x2)]
lset mt 0 1 [expr 2*($y1-$y2)]
lset mt 1 0 [expr 2*($x1-$x3)]
lset mt 1 1 [expr 2*($y1-$y3)]
set const {{?} {?}}
lset const 0 [expr {(pow($x1,2)+pow($y1,2)-pow($d1,2))-(pow($x2,2)+pow($y2,2)-pow($d2,2))}]
lset const 1 [expr {(pow($x1,2)+pow($y1,2)-pow($d1,2))-(pow($x3,2)+pow($y3,2)-pow($d3,2))}]
#puts $result "$const"
# puts $result "$mt"
set x [expr {double([lindex [Inverse3 $mt] 0 0] * [lindex $const 0]
+ [lindex [Inverse3 $mt] 0 1] * [lindex $const 1])}]
set y [expr {double([lindex [Inverse3 $mt] 1 0] * [lindex $const 0]
+ [lindex [Inverse3 $mt] 1 1] * [lindex $const 1])}]
lappend result "$x $y $now"
puts $result
for {set i 0} {$i< [llength $result]} {incr i} { #for latest two instances
for {set j 1} {$i< [llength $result]} {incr j} {
set X1 [lindex $result $i 0]
set Y1 [lindex $result $i 1]
if {[llength $result] >1} { #to ensure length of list is greater than 1
set X2 [lindex $result $j 0]
set Y2 [lindex $result $j 1]
set v [expr hypot($X2-$X1,$Y2-$Y1)/ ($now-($now-1))]
set theta [expr acos(($X2-$X1)/(hypot($X2-$X1,$Y2-$Y1)))]
set Xp [expr ($X2+($v*$now*cos($theta)))]
set Yp [expr ($Y2+($v*$now*sin($theta)))]
puts "$Xp $Yp"
}
break
}
}
}
Floating point exceptions can come from a few different things. In general, the main culprit is doing something awful like dividing zero by zero. However, Tcl is usually pretty good at ensuring that such things don't crash your program entirely, and instead just generate errors you can catch. Whatever is going on is therefore either one of the trickier cases, or due to running in ns2 and that turning signalling floating point errors on (Tcl's standard implementation disables them precisely to avoid probably-unwarranted fatal crashes).
If it is the latter, moving the processing out of the process into a standard tclsh is the easiest way forward. We can make stronger guarantees about the correctness of behaviour there as we have more control of tricky things like FPU flags.
But if it is the former… the problem should lie in these lines:
set v [expr hypot($X2-$X1,$Y2-$Y1)/ ($now-($now-1))]
set theta [expr acos(($X2-$X1)/(hypot($X2-$X1,$Y2-$Y1)))]
set Xp [expr ($X2+($v*$now*cos($theta)))]
set Yp [expr ($Y2+($v*$now*sin($theta)))]
Of the lines there, the one that looks most suspicious is the calculation of theta. There's several problems with what you're doing (e.g., it won't handle some quadrants correctly due to trigonometric periodicities) but the big nasty is that you've got a division in there that will be by zero if two successive positions are the same. Given that you're able to use hypot(), computing the angle is by far best computed with atan2(), as that deals with tricky edge cases much better (e.g., it has no problems with awful infinities). Try this:
set theta [expr { atan2($Y2-$Y1, $X2-$X1) }]
Also put your expressions in {braces} as I've done above. It permits Tcl to bytecode-compile the expression and makes your code quite a bit faster. It also lets you put spaces in the expression safely, which aids readability a lot even when you're not splitting over multiple lines, and ensures you get (much!) better error messages if you ever happen to use a variable holding a non-numeric value in your expression. In short, it's easy to do and makes your code much better.
Other minor issues
Do you expect ($now-($now-1)) to ever compute anything other than 1? Or at least a value very close to 1.0, given that you're dealing with floating point numbers for simulation time? I think your calculation of v can be safely simplified down to straight use of hypot().
These two nested loops look odd:
for {set i 0} {$i< [llength $result]} {incr i} {
for {set j 1} {$i< [llength $result]} {incr j} {
I think you either mean to do this:
for {set i 0} {$i< [llength $result]} {incr i} {
for {set j 0} {$j< [llength $result]} {incr j} {
if {$i == $j} continue; # Skip the diagonal in the comparison matrix
or this:
for {set i 0} {$i< [llength $result]} {incr i} {
for {set j [expr {$i + 1}]} {$j< [llength $result]} {incr j} {
# Just the upper triangle of the comparison matrix
depending on whether the rest of the code should compare values from both ways round (but never with itself), or just one way round. The latter does less work, but might be wrong if comparisons aren't symmetric (which depends on the details of what you're up to).

How to extract the required lines from a file using TCL?

My file looks like this below. I want to extract only id, pos and type from file so that I can use it further. Should I need to treat this data as a list and use lindex syntax to retrieve.
{particles {id pos type v f}
{0 442.3601602032813 775.8494355067412 339.7428247245854 0 -1.0649468656691174 0.3118359585805807 0.7974629587243917 -7.856415738784473 120.82920096524781 80.7680149353967}
{1 75.78431491144367 187.28007812237516 279.3569202413006 0 0.3317344469183915 3.0716559473604916 -1.679965732986453 2.573367640795655 -11.46026754809828 125.75306472245369}
{2 44.167251258358164 371.8839725825084 80.32709197838003 0 -0.6260768510694417 0.9493261445672099 0.9445444874655268 -98.8132600015945 -80.10617403827258 43.578514821777155}
{3 289.0168944249348 193.4364952458922 96.30251497465443 0 -0.5327035586676473 1.028492567403681 4.364969924730662 139.09290151549465 75.46717320097427 -29.955066397359094}
{4 324.94257366360085 404.9215380451672 799.3481016151578 0 -1.2801842708841038 -4.320355658821216 2.9394195915181567 -109.72971491904342 -44.06068452005151 118.2802261191011}
{5 598.4521733790556 447.74320547029174 750.4399422142899 0 1.740414834859398 -0.5926143788565617 1.5937397085063156 -155.08309023301794 186.08101953841978 177.1804659692657}
}
This is the code I have used below. Can anyone tel me the code which I used is correct or not.
set num_part 6
set mol1 0.1666
set mol2 0.8333
set num_conf 2
for {set i 0} {$i < $num_conf} {incr i} {
set f [open "config_$i" "r"]
set part [while { [blockfile $f read auto] != "eof" } {} ]
set g [open "positions" "w"]
blockfile $g write particles {id pos type}
close $f
close $g
set g [open "positions" "r"]
set data [read $g]
close $g
set num0 0
for {set j 0} {$j < [expr { $num_part + 1 }]} {incr j} {
set type [lindex $data 0 $j 4]
if { $type == 0 } {
set tlist [expr $i]
set x0 [lindex $data 0 $j 1]
set y0 [lindex $data 0 $j 2]
set z0 [lindex $data 0 $j 3]
set total1 [ expr { sqrt(($x0 * $x0) + ($y0 * $y0)+ ($z0 * $z0)) }]+0]
incr num0
puts " $i :: $num0 "
set dum 0
for {set k 0} {$k < [expr { $num_part + 1 }]} {incr k} {
set type [lindex $data 0 $k 4]
if { $type == 1 } {
set tlist [expr $i]
set x1 [lindex $data 0 $k 1]
set y1 [lindex $data 0 $k 2]
set z1 [lindex $data 0 $k 3]
set total2 [ expr { sqrt(($x1 * $x1) + ($y1 * $y1)+ ($z1 * $z1)) }]+0]
incr dum
puts " $i :: $dum "
}
}
}
}
}
set h [open "dist12" "w"]
set dist12 [ expr {($mol1 * $total1)-($mol2 * $total2)}]
puts "Distance between cross particles $dist12"
puts $h "\# t $dist12 "
foreach t $tlist dist" $dist12 { puts $h "$t $dist_12" }
close $h
You've a few lines that look suspicious.
1:
set part [while { [blockfile $f read auto] != "eof" } {} ]
The result of while is an empty string, so the above code probably isn't doing what you hope. Not quite sure how to fix it though; blockfile isn't a standard Tcl command.
2:
for {set j 0} {$j < [expr { $num_part + 1 }]} {incr j} {
Not really a correctness issue, but that could be written as:
for {set j 0} {$j < $num_part + 1} {incr j} {
The bytecode generated will be virtually identical, but it's shorter and easier to read.
3:
set tlist [expr $i]
This looks unnecessary and suspcious. We know i is a numeric variable (in fact it contains an integer), so there's no need to pretend it is an expression. It slows things down for no benefit.
You've two occurrences of this.
4:
set total1 [ expr { sqrt(($x0 * $x0) + ($y0 * $y0)+ ($z0 * $z0)) }]+0]
This line is definitely wrong. The number of ] characters doesn't match the number of [ characters, so what you get will be “unexpected”, and that +0 is either useless or harmful. It's probably best to write a procedure to help you with this. Put the procedure at the top of the script.
proc length {x y z} {
expr { sqrt($x*$x + $y*$y + $z*$z) }
}
Then just call it later on:
set total1 [length $x0 $y0 $z0]
The same applies to the calculation of total2 later.
5:
foreach t $tlist dist" $dist12 { puts $h "$t $dist_12" }
Looks like this has a typo: dist" instead of dist. The failure to use dist inside the loop also looks odd; I think you're going wrong here, and should take another look and think about what you actually want to do.

Colon (:) meaning in Tcl script

I am new in Tcl and can't understand meaning of colon (:) in Tcl, and I have not found any answer to my question in web. Especially, I want to understand role of ":" in tcl script brought here, for example "$c:data", or "$c:row".
…
for {set y 0} {$y < $height} {incr y} \
{
set r:row {}
set g:row {}
set b:row {}
for {set x 0} {$x < $width} {incr x} \
{
foreach {r g b} [$image get $x $y] break
foreach c {r g b} { lappend $c:row [set $c] }
}
foreach c {r g b} { lappend $c:data [set $c:row] }
}
…
foreach c {r g b} \
{
set c00 [lindex [set $c:data] [expr {$y - 2}] [expr {$x - 2}]]
set c01 [lindex [set $c:data] [expr {$y - 1}] [expr {$x - 0}]]
set c02 [lindex [set $c:data] [expr {$y - 2}] [expr {$x + 2}]]
set c10 [lindex [set $c:data] [expr {$y + 0}] [expr {$x - 1}]]
set c11 [lindex [set $c:data] [expr {$y + 0}] [expr {$x - 0}]]
set c12 [lindex [set $c:data] [expr {$y + 0}] [expr {$x + 1}]]
set c20 [lindex [set $c:data] [expr {$y + 2}] [expr {$x - 2}]]
set c21 [lindex [set $c:data] [expr {$y + 1}] [expr {$x - 0}]]
…
Colon in this context is just a symbol in the identifier of the Tcl variable. You can think of it as the underscore symbol that makes identifier more readable.

Any filter we can use in tcl script?

I have a question about the for loop,
for {{set loop 0} {$loop < 100} {incr loop}} {
#do someting here
}
loop goes from 0 to 99, and I do something for each value of loop, but if the loop is 3, I will skip it, so, is there any filter in tcl to achieve it or we should write it as:
for {{set loop 0} {$loop < 100} {incr loop}} {
if {loop != 3} {
#do someting here
}
}
You can use the "continue" command. For example:
for {set loop 0} {$loop < 100} {incr loop} {
if {$loop == 3} continue
# do something here
}
% proc xiter {varName "over" a z "excluding" filter body} {
upvar 1 $varName i
set excl [lsort $filter]
for {set i $a} {$i < $z} {incr i} {
if {[lsearch -exact -sorted $excl $i] < 0} {
uplevel 1 $body
}
}
}
% xiter loop over 0 10 excluding {5 3 8} {
puts $loop
}
0
1
2
4
6
7
9
The first, third and fourth arguments to for can be arbitrary scripts, so you could do this:
for {set i 0} {$i < 100} {incr i [expr {$i == 2 ? 2 : 1}]} {
do stuff with $i ...
}