MathXplorer/T - ver 1.0

This is version 1.0. Check the latest version for enhanced functionality.

Introduction Installation Tutorial Demo Announcement Download

Introduction

MathXplorer/T is a package that provides matrix-based numerical analysis capabilities within the Tcl environment. It extends the Tcl environment with two built-in commands: mexpr and m2t. mexpr, like its native Tcl cousin expr, can interpret mathematical expressions in standard form. However, mexpr does much more:

m2t can be used to format the variables in the MathViews variable-space as Tcl lists. For matrices, m2t creates a list of sub-lists; each sub-lists represents either a row or a column. m2t can also take a printf-style specification which it uses to format individual matrix elements.

MathXplorer/T performs all computations in the same process and thread as the Tcl process. In order to tightly integrate itself within the Tcl environment, it does not use pipes or sockets to communicate with an external numerical engine.

Installation, Start-up and Configuration

MathXplorer/T is currently available on Linux and Windows NT (Windows 95 should also work, but we haven't test it). On all platforms, MathXplorer/T requires two dynamically-loaded libraries. On Windows, the DLL's are called mathsrv1.dll and mxt.dll. On UNIX, the two shareable-object libraries are called libmv.so and libmxt.so. These should be placed in the standard system locations for dynamic libraries. Alternatively, Windows users can augment their PATH environment variable to include the location of these DLL's. Similarly, UNIX users can augment their LD_LIBRARY_PATH environment variable.

You load the MathXplorer/T package into Tcl by invoking Tcl's load command. Here are some example invocations of the load command:

Windows UNIX
load mxt.dll
load foo\\mxt.dll
load d:/mv/mxt.dll

load libmxt.so
load foo/mxt.dll
load ~/mv/libmxt.so

If the package is successfully loaded, you should see the following bit of shameless self-promotion:

MathViews Parsing Engine v2.1. (c) MathWizards 1998.

Should load complain with a file-not-found type of message, check that load's argument is a correct path, and that mathsrv1.dll (libmv.so under UNIX is accessible by virtue of the PATH environment variable (LD_LIBRARY_PATH under UNIX) or its standard system location.

If you want the MathXplorer/T package to be available whenever you start-up Tcl, place the load command in Tcl's start-up file. You can find out which file Tcl sources on start-up by inspecting the tcl_rcFileName variable.

Once you have loaded the MathXplorer/T package, the commands mexpr and m2t should be available. To test them, try the two commands at the Tcl prompt:

% mexpr x = 1:5
x =
1 2 3 4 5

% m2t -format %d x 1 2 3 4 5

These two commands produced the Tcl list "1 2 3 4 5."

Also, try out mexpr's in interactive mode: In this mode, the user can type MathViews commands directly without repeatedly using the mexpr command. For example, to create a 3-by-2 random matrix and compute the sine of every one of its elements, try the following:

% mexpr
>> x = rand(3,2)
x =   

0.8898 0.6656
0.1443 0.4328
0.7875 0.9344

>>
y = sin(x) y =
0.7769 0.6175
0.1438 0.4194
0.7086 0.8043

>>
exit exit
%
mexpr who Your variables are :
_MVPATH
_MVPWD
x
y
%

Note that we entered interactive mode by invoking Tcl command mexpr without arguments, and we left it by using the MathViews command exit. Also note the MathViews command who used to list the MathViews variables.

As mentioned in the introduction, the MathXplorer/T package supports user-defined script files. These files have the '.m' extension. For example, suppose you have defined a function sigmag in the file c:\m-files\sigmag.m containing the following:

function [ mag ] = sigmag( x )
mag = abs(fft(x));

Further suppose that you have m-files dispersed in several directories. In order to make MathViews recognize the functions in these m-files, use the MathViews internal variable _MVPATH. This variable is a string which is interpreted by MathViews as a list of directories. On start-up, MathViews looks in those directories for m-files and loads them for later use. At any time, you can clear MathViews' cache of loaded functions by invoking (in Windows):

% mexpr clear functions
% mexpr "_MVPATH='c:\\\\m-files;d:\\\\more-m-files\\\\foo'"
_MVPATH =
c:\m-files;d:\more-m-files\foo

(Admittedly obscene, the four back-slashes are needed under Windows to give _MVPATH the value c:\m-files;d:\more-m-files\foo. The double-quotes surrounding the argument to mexpr are needed to prevent Tcl from interpreting the Windows path-list separator, the semicolon, as a Tcl command separator. UNIX users don't have to suffer these complications.). It is a good idea to set the _MVPATH right after you have loaded the MathXplorer/T package, possibly in Tcl's start-up script.

The MathWizards web site contains a repository of m-files, found here. You may want to download some or all of these m-files.

Finally, we should mention that MathXplorer/T defines and uses a few Tcl procedures and variables:

The implementation of these two procedures is different, depending on whether the package was loaded into tclsh or wish. The bodies of these Tcl procedures can be inspected using info body procname, and can be given different implementations by the user. However, if mvget and mvput are deleted or renamed, mexpr will fail. The full script evaluated by the MathXplorer/T on startup can be found here.

Tutorial

MathXplorer/T augments Tcl's powerful capabilities. The hybrid of Tcl and MathViews makes it possible to write computationally efficient scripts, and cleanly separate and interface the two languages. In this tutorial, we will give an example of how the Tcl and Mathviews can work together.

Our goal in this tutorial is to write a script that will allow us to visualize the relative values of the elements of a matrix. We will need to decide on a colormap, a way of mapping an integer value in the interval [0 Imax) to a color value. Actually, since color is represented with three components representing red, green, and blue, we will need to produce three color values for each numeric value. Furthermore, we will need to transform the input matrix so that its elements (approximately) retain their relative magnitudes but are in the range [0 Imax) . Finally, we will use a Tk canvas widget to draw the matrix. For each element in the matrix, we will create a rectangle in the canvas with the computed background color. The pseudo-code for what we want to do then looks like:

  1. create a colormap
  2. get an input matrix (we will generate a random one)
  3. transform the matrix
  4. decompose the normalized matrix into three matrices corresponding to red, green, and blue components
  5. draw a Tk canvas

1) Create the Colormap

We've already done some of the hard work: We created a MathViews function called clrmap which will create three required colormap vectors. Here it is:

function [ r , g , b ] = clrmap( num_colors ) 
% black-red-yellow-white colormap
third_num_colors = floor( num_colors / 3 );
% create red component vector
r = ones(num_colors,1);
r(1:third_num_colors+1) = [ 0 : 1/(third_num_colors) : 1 ]';
r = round( r * 255 );
% use vector shifting operator to create green and blue 
% component vectors
g = r >> third_num_colors;
b = g >> third_num_colors;

Cut the text for the clrmap function and put it in a file called clrmap.m. If you've already started up MathXplorer/T, reset the _MVPATH variable so it contains the directory where clrmap.m lives (this is described in the section above). You should now be able to invoke the function as follows:

% mexpr
>> [ R, G, B ] = clrmap( 9 );
>> [ R G B ]
ans =   

0 0 0
85 0 0
170 0 0
255 0 0
255 85 0
255 170 0
255 255 0
255 255 85
255 255 170
>> exit %

What we've just done is create a color map containing 9 colors spanning black to white, with red and yellow in between. R(i), G(i), and B(i) contain the red, green, and blue components of ith color in the color map. Later, we will use m2t to cast the values in R, G, and B into their 16-bit hexadecimal representations, need by Tk to specify color. For example:

% mexpr B9 = B(9) 
B9 =   

170.0000

% m2t -format %x B9 aa

The problem with a color map of size nine is that it only lets us visually distinguish between nine different values. Let us instead use a color map of size 128:

% mexpr { [ R, G, B ] = clrmap( 128 ); }

Note how we used the semicolon after the MathViews command to suppress output.

2) Generating a Matrix

Let us create a 40x40 random symmetric matrix using MathViews' rand function. rand returns a matrix whose values are drawn from a uniform probability distribution from zero to one.

% set n 40
% mexpr "x = rand($n);"
% mexpr "x = x' * x;"

3) Transforming the Matrix

Now, let us transform the random matrix x into another matrix y. We need to scale and translate all of x's values so they are in the range [1 128]. Recall that Imax=128 is the value we chose as the size of the color map. First, let us compute the range of values in x.

% mexpr
>> max_x = max(x(:))
max_x =    

16.7125
>> min_x = min(x(:)) min_x =
6.1320
>> range_x = max_x - min_x range_x =
10.5805
>> exit %

Of course, your particular numbers may vary, since the random matrix you generate is likely to be quite different than the one we are using for this example. Now, let us create a new matrix y which is the result of scaling and translating x:

% mexpr
>> y = x - min_x;
>> y = y / range_x;
>> y = y * ( 128 - 1 );
>> y = round( y ) + 1;
>> exit
%  

The elements of y should range between 1 and 128. To confirm this:

% mexpr
>> max(y(:))
ans =    

128
>> min(y(:)) ans =
1
>> exit %

4) Decomposing Into Colors

From y and our color map, we will now create three matrices (Ry, Gy, By) which contain the red, green, and blue components of each element of y. For example, suppose that y(3,29)=103. Then, we want Ry(3,29) to equal R(103). Clever use of MathViews' colon (:) operator allows us to do this mapping without needing nested for loops as follows:

% mexpr
>> Ry = y;
>> Gy = y;
>> By = y;
>> Ry(:) = R(y(:));
>> Gy(:) = G(y(:));
>> By(:) = B(y(:));
% exit

5) Drawing the Tk Canvas

From now-on, all our code will be in Tcl. First, let us convert Ry, Gy, and By into Tcl lists using m2t:

% set ry [ m2t -format %0.2x Ry ]
% set gy [ m2t -format %0.2x Gy ]
% set by [ m2t -format %0.2x By ]

Since Tk represents color in hexadecimal, we used the-format %0.2x option. Each command created a list of sub-lists. Each sub-list corresponds to a matrix column. (We did not show the output of the commands here because they are a few screens long.) Now, let us create the Tk canvas .c. Each element in the matrix will If we make each rectangle a three-pixel wide square, then our canvas needs be represented by a filled rectangle in .c. to have height and width of 3x40=120:

% canvas .c -width 120 -height 120
% pack .c

We create the filled rectangles by iterating over the Tcl lists ry, gy, and by. For each element, we compose a color by concatenating the elements extracted from these three lists. We use the variables xp and yp to denote the top-left corner position of each rectangle within the canvas. We also use variables j and i as column and row indices, respectively:

% for { set xp 0; set j 0} { $j < 40 } { incr xp 3; incr j } {
    set r_col [ lindex $ry $j ]
    set g_col [ lindex $gy $j ]
    set b_col [ lindex $by $j ]
    for { set yp 0; set i 0 } { $i < 40 } { incr yp 3; incr i } {
	set r_color [ lindex $r_col $i ]
	set g_color [ lindex $g_col $i ]
	set b_color [ lindex $b_col $i ]
	set color \#$r_color$g_color$b_color
	.c create rectangle $xp $yp [ expr $xp + 3 ] [ expr $yp + 3 ] \
		-fill $color -outline ""
    }
}

The resulting canvas should like like:

The inv.tcl demo script

The Tcl demo script inv.tcl extends the tutorial. It paints a random matrix along with its inverse, producing somewhat surprising results:

inv.tcl is listed here for reference:

#!/usr/bin/wish
# assume that the MathXplorer/T package was loaded already, presumably
# in a start-up script automatically sourced by Tcl.  Otherwise, un-comment
# the appropriate load command below:
# on UNIX:
#   load libmxt.so
# on windows:
#    load mxt.dll
# transform the original matrix by scaling and translating it.  the
# elements of the resulting matrix should range between [ 0 max_int ].
# here, we use mexpr to create a MathViews function dynamically.
mexpr {
function [ r , g , b ] = clrmap( num_colors )
% black-red-yellow-white colormap
third_num_colors = floor( num_colors / 3 );
r = ones(num_colors,1);
r(1:third_num_colors+1) = [ 0 : 1/(third_num_colors) : 1 ]';
r = round( r * 255 );
g = r >> third_num_colors;
b = g >> third_num_colors;
}
mexpr {
function [ cx ] = normat( x, max_int )
  % determine range
  max_x = max(x(:));
  min_x = min(x(:));
  range_x = max_x - min_x;
  % offset and scale
  nx = ( x - min_x ) / range_x;
  % map the values of nx into integers in the range [0 max_int]
  cx = round( nx * ( max_int - 1 ) ) + 1;
end;
}
mexpr {
function [ Rx, Gx, Bx ] = color_mat( x, r , g , b )
  % transform the matrix
  tx = normat( x , length(r) );
  Rx = x;
  Gx = x;
  Bx = x;
  % decompose the transformed matrix into color components
  Rx(:) = r(tx(:));
  Gx(:) = g(tx(:));    
  Bx(:) = b(tx(:));
end;
}
proc compute_matrices { mat mat_size } {
    # generate a random matrix and associated color decompositions
    mexpr "$mat = rand($mat_size);"
    mexpr "\[ R$mat, G$mat, B$mat \] = color_mat( $mat, r, g, b );"
    # do the same for its inverse
    set inv ${mat}inv
    mexpr "$inv = inv($mat);"
    mexpr "\[ R$inv, G$inv, B$inv \] = color_mat( $inv, r, g, b );"
    # return the name of the inverse matrix 
    return $inv
}
proc draw_matrix { mat mat_size pixel_size cnvs } {
    # extract the r,g,b matrices in column order (the default)
    # and two character- wide hexidecimal format.
    # assume that the red, green, and blue matrices associated with 
    # the input matrix 'mat' are R$mat, G$mat, and B$mat
    set r [ m2t -format %0.2x R$mat ]
    set g [ m2t -format %0.2x G$mat ]
    set b [ m2t -format %0.2x B$mat ]
    set canv_size [ expr $pixel_size * $mat_size ]
    # create two canvases, one for the matrix x and another for its
    $cnvs configure -height $canv_size -width $canv_size
    for { set xp 0; set j 0} { $j < $mat_size } { 
	incr xp $pixel_size; incr j } {
	set r_j [ lindex $r $j ]
	set g_j [ lindex $g $j ]
	set b_j [ lindex $b $j ]
	for { set yp 0; set i 0 } { $i < $mat_size } { 
            incr yp $pixel_size; incr i } {
	    set r_color [ lindex $r_j $i ]
	    set g_color [ lindex $g_j $i ]
	    set b_color [ lindex $b_j $i ]
	    set color \#$r_color$g_color$b_color
	    $cnvs create rectangle $xp $yp [ expr $xp + $pixel_size ] \
		[ expr $yp + $pixel_size ] -fill $color -outline ""
	}
    }
}
set mat_size 40
set pix_size  4
set canv_size [ expr $mat_size * $pix_size ]
canvas .org -height $canv_size -width $canv_size
canvas .inv -height $canv_size -width $canv_size
pack .org .inv -side left
# create the colormap once
mexpr { [ r, g, b ] = clrmap( 128 ); }
proc doit {} {
    global mat_size
    global pix_size
    set mat_size [ expr abs($mat_size) ]
    puts -nonewline "computing matrices ... "
    flush stdout
    set etime [ clock seconds ]
    set invx [ compute_matrices x $mat_size ]
    set etime [ expr [ clock seconds ] - $etime ]
    puts "done ($etime seconds)."
    puts -nonewline "drawing matrices ..... "
    flush stdout
    set etime [ clock seconds ]
    draw_matrix x     $mat_size $pix_size .org 
    draw_matrix $invx $mat_size $pix_size .inv
    set etime [ expr [ clock seconds ] - $etime ]
    puts "done ($etime seconds)."
}
# build the gui widgets
toplevel .t 
entry  .t.eMatSize -textvariable mat_size -width 4
entry  .t.ePixSize -textvariable pix_size -width 5
label  .t.lMatSize -text "Matrix size:"
label  .t.lPixSize -text "Pixels per element:"
button .t.comp     -text "Compute" -command { doit }
button .t.exit     -text "Quit"    -command { exit }
# arrange them in a 3x2 grid
grid .t.lMatSize -row 0 -column 0 -sticky nsew
grid .t.eMatSize -row 0 -column 1 -sticky nsew
grid .t.lPixSize -row 1 -column 0 -sticky nsew
grid .t.ePixSize -row 1 -column 1 -sticky nsew
grid .t.comp     -row 2 -column 0 -sticky nsew
grid .t.exit     -row 2 -column 1 -sticky nsew