RosettaCodeData/Task/Deconvolution-2D+/Tcl/deconvolution-2d+-1.tcl
Ingy döt Net 764da6cbbb CDE
2013-04-10 16:57:12 -07:00

165 lines
4.4 KiB
Tcl

package require Tcl 8.5
namespace path {::tcl::mathfunc ::tcl::mathop}
# Utility to extract the number of dimensions of a matrix
proc rank m {
for {set rank 0} {[llength $m] > 1} {incr rank} {
set m [lindex $m 0]
}
return $rank
}
# Utility to get the size of a matrix, as a list of lengths
proc size m {
set r [rank $m]
set index {}
set size {}
for {set i 0} {$i<$r} {incr i} {
lappend size [llength [lindex $m $index]]
lappend index 0
}
return $size
}
# Utility that iterates over the space of coordinates within a matrix.
#
# Arguments:
# var The name of the variable (in the caller's context) to set to each
# coordinate.
# size The size of matrix whose coordinates are to be iterated over.
# body The script to evaluate (in the caller's context) for each coordinate,
# with the variable named by 'var' set to the coordinate for the particular
# iteration.
proc loopcoords {var size body} {
upvar 1 $var v
set count [* {*}$size]
for {set i 0} {$i < $count} {incr i} {
set coords {}
set j $i
for {set s $size} {[llength $s]} {set s [lrange $s 0 end-1]} {
set dimension [lindex $s end]
lappend coords [expr {$j % $dimension}]
set j [expr {$j / $dimension}]
}
set v [lreverse $coords]
uplevel 1 $body
}
}
# Assembles a row, which is one of the simultaneous equations that needs
# to be solved by reducing the whole set to reduced row echelon form. Note
# that each row describes the equation for a single cell of the 'g' function.
#
# Arguments:
# g The "result" matrix of the convolution being undone.
# h The known "input" matrix of the convolution being undone.
# gs The size descriptor of 'g', passed as argument for efficiency.
# gc The coordinate in 'g' that we are generating the equation for.
# fs The size descriptor of 'f', passed as argument for efficiency.
# hs The size descriptor of 'h' (the unknown "input" matrix), passed
# as argument for efficiency.
proc row {g f gs gc fs hs} {
loopcoords hc $hs {
set fc {}
set ok 1
foreach a $gc b $fs c $hc {
set d [expr {$a - $c}]
if {$d < 0 || $d >= $b} {
set ok 0
break
}
lappend fc $d
}
if {$ok} {
lappend row [lindex $f $fc]
} else {
lappend row 0
}
}
return [lappend row [lindex $g $gc]]
}
# Utility for converting a matrix to Reduced Row Echelon Form
# From http://rosettacode.org/wiki/Reduced_row_echelon_form#Tcl
proc toRREF {m} {
set lead 0
set rows [llength $m]
set cols [llength [lindex $m 0]]
for {set r 0} {$r < $rows} {incr r} {
if {$cols <= $lead} {
break
}
set i $r
while {[lindex $m $i $lead] == 0} {
incr i
if {$rows == $i} {
set i $r
incr lead
if {$cols == $lead} {
# Tcl can't break out of nested loops
return $m
}
}
}
# swap rows i and r
foreach j [list $i $r] row [list [lindex $m $r] [lindex $m $i]] {
lset m $j $row
}
# divide row r by m(r,lead)
set val [lindex $m $r $lead]
for {set j 0} {$j < $cols} {incr j} {
lset m $r $j [/ [double [lindex $m $r $j]] $val]
}
for {set i 0} {$i < $rows} {incr i} {
if {$i != $r} {
# subtract m(i,lead) multiplied by row r from row i
set val [lindex $m $i $lead]
for {set j 0} {$j < $cols} {incr j} {
lset m $i $j \
[- [lindex $m $i $j] [* $val [lindex $m $r $j]]]
}
}
}
incr lead
}
return $m
}
# Deconvolve a pair of matrixes. Solves for 'h' such that 'g = f convolve h'.
#
# Arguments:
# g The matrix of data to be deconvolved.
# f The matrix describing the convolution to be removed.
# type Optional description of the type of data expected. Defaults to 32-bit
# integer data; use 'double' for floating-point data.
proc deconvolve {g f {type int}} {
# Compute the sizes of the various matrixes involved.
set gsize [size $g]
set fsize [size $f]
foreach gs $gsize fs $fsize {
lappend hsize [expr {$gs - $fs + 1}]
}
# Prepare the set of simultaneous equations to solve
set toSolve {}
loopcoords coords $gsize {
lappend toSolve [row $g $f $gsize $coords $fsize $hsize]
}
# Solve the equations
set solved [toRREF $toSolve]
# Make a dummy result matrix of the right size
set h 0
foreach hs [lreverse $hsize] {set h [lrepeat $hs $h]}
# Fill the results from the equations into the result matrix
set idx 0
loopcoords coords $hsize {
lset h $coords [$type [lindex $solved $idx end]]
incr idx
}
return $h
}