Interact.py and Worksheet.py

Following the release of SAGE 3.1, I have began to work on published worksheets. The first thing I wanted to add was a "delete" button, but upon reading the code found no logical place to put the code for it - the HTML code in notebook.py is undocumented and very disorganized. So, to learn my way around I decided to first reogranize a bit.

Here is the header for a worksheet that I published.

Here is the same header on my development branch.

I'm not sure if anyone else will like this new header, but at least there is a logical place for the delete button, both in the UI and in the code.

Summerland

Four more panoramas that I forgot about. These are from near Summerland campground on Mt. Rainier And one from past Glacier Basin trail North of Emmons Glacier. More pictures here.

Sliders and Selectors - HTML vs Javascript

In my earlier enhancement of sliders, I created a Javascript list of values for each slider, which would update while the slider is being manipulated. It worked very well at first, but then resulted in a problem: SAGE truncated the array when it was too big which resulted in a Javascript error and nothing worked. In this I responded to 2 patches: pruning the array of values and removing truncation, since other people were complaining about it as well.

Since removing the truncation, I was able to create selectors with a lot of values, say, 10000.

def _(a=[1..10000]):
But they loaded very slowly because the SAGE server was rendering 10000 lines of
<option value='n' >n</option>
What if the server instead returned more Javascript? Then the code would look like this:
var values=[1, 2, ..., 10000];
for(var i in values)
    //code to generate a select option
Which means a lot less text being generated by the server. Of course, the optimal return is
for(var i=1; i<=10000; i+=1)
    //code to generate a select option
But this involves detecting the range which may or may not be possible depending on the underlying structure.

Unreadable code in Python

I finally figured out how to write unreadable code in Python. See if you can figure out what the following code does:

@interact
def _(h=(20,(1,36,1))):
    print (lambda f:f(0,f))(
        lambda n,f:'%s\n%s'%(
            ('*'*(2*n+1)).join([' '*(h-n-1)]*2),
            ((n<h-1 and f(n+1,f)) or '')
        )
    )

Apart from the interact function overhead, the code can be compressed into one line. Motivated by a discussion about Maxima in SAGE.

Range Sliders

Here is what I was working on in the last two days.

The range slider is allows selecting a pair a,b with a≤b using one control.

This is Trac #3679, awaiting review.

FC9, Part 3

I am back online and with Sage working after a fresh install of Ubuntu 8.04 x64. I will post pictures later.

FC9, Part 2

I am falling back to a fresh install.

After booting live from a spare laptop and copying the necessary libraries I had rpm working, but there were still a lot of Python problems. Eventually, I forced an install of Python (.fc9), and yum worked after that. I ran "yum upgrade -y --exclude=<pyvault file that ended in .fc4 and caused dependency problems>", yum updated 341 or so files, and updated the kernel. I thought I were done, so I rebooted the system.

I don't remember the exact numbers because I am writing this on Windows after FC9 broke again. This time it was not a partially-upgraded FC8, but an incomplete FC9 - the NVIDIA driver wasn't there, and there were Python problems everywhere. Yum could not find the module rpm. Great.

My advice to anyone who is considering upgrading from FC8 to FC9: preupgrade is most likely broken and you may have problems. At least backup your filesystem, so if anything goes wrong you can revert.

I think I'll get myself another pet tonight.

Upper Greider Lake

A long drive, but in the end nothing blocks the view of Greider Lake.

More pictures are in this album. Panorama was built with Hugin, a front end for Panorama Tools.

FC9, Part 1

For some reason I decided to upgrade to Fedora Core 9 today, thinking that the bugs have been fixed and the NVIDIA drivers are there. I ran Fedora's preupgrade, then rebooted, waited for 1062 packages to be installed. Finally, the installer finished (no errors reported) and asked me to reboot my machine again.

So, I started Fedora again, waited for it to boot, it displayed the graphical login screen, detected my graphics card - no problem there. I type my username, password, hit return: the login window disappears, then after a few seconds the screen goes black momentarily and thelogin window pops up again - something resembling a reboot of the x-server.

Eventually I login on terminal 1, run yum, intending to update, and yum gives the following error:

There was a problem importing one of the Python modules required to run yum. The error leading to this problem was:

libnss3.so: cannot open shared object file: No such file or directory

Please install a package which provides this module, or verify that the module is installed correctly.

It's possible that the above module doesn't match the current version of Python, which is:
2.5.1 (r251:64863, June 15 2008, 23:59:20)
[GCC 4.1.2 20070925 (Red Hat 4.1.2-33)]

Great. Turns out I am not the only one having this problem. So, yum does not work and rpm does not work for the same reason. I've e-mailed a friend asking for help; he has yet to respond.

To summarize: Fedora is broken until further notice and if I don't get it fixed soon I'll be looking into those virtual machines to get SAGE going. Or maybe install SAGE on a spare laptop running Ubuntu.

Text Controls and More

A text control is just a block of text (really, HTML) that gets inserted among other controls. This can be useful in interactions that are not in context, for example by providing a brief description of what the interaction does. Unfortunately, the following generates a syntax error:

def f("This function does nothing."):
    pass

Doing nothing in Python is perfectly legal, but having a non-variable in the definition isn't. My current implementation isn't pretty:

@interact
def _(t1=text_control("This function does nothing.")):
    pass

In working on the control I have come to realize that the structure of interact is indeed limited. Currently, interact takes a function, whose variables are set with defaults as control objects. The design has several major limitations:

  1. All controls must be tied to a variable, which is not what we always want. For example, there is no reason to tie a block of text to a variable.
  2. Functions generated for interact cannot be used outside of interact. Similarly, functions generated outside of interact do not make good interactions. One has to write a wrapper function with the right controls.

Mathematica's Manipulate command uses a more logical design: pick any function and pass along a list of controls. For example:

Current

@interact
def f(t1=text_control("Plots a function."),
l=slider(-20, 0, default=-2, label='Lower bound:'),
u=slider(0,20, default=2, label='Upper bound:')):
    plot(lambda x:sin(x)/x, l, u).show()

Versus something like:

def f(l,u):
    plot(lambda x:sin(x)/x, l, u).show()

interact(f, "Plots a function",
('l', slider(-20, 0, default=-2, label='Lower bound:')),
('u', slider(0,20, default=2, label='Upper bound:')))

Benefits:

  1. We can put anything in the list: text, numbers (default values with no controls) without associating a variable.
  2. We can easily make high quality (that is, with appropriate controls, text, etc.) interactions for builtin SAGE functions or any other functions not designed for interact.
  3. We can nest interactions. Right now, it is possible to create a function, and then call interact on it, but this method is known to break because interactions are limited to one and nothing else per cell.
  4. I think it is easier to understand for someone who knows Python well but is not familiar with interact.

Well, those were my thoughts on interact right now, but perhaps I am looking at interact from the wrong angle today after messing with text controls. I will continue to perfect the text control, with another control in mind and more long-term plans that involve something completely different. To the reader I leave this mystery and hint only that the idea for the new control came to me yesterday while I was writing the example interaction.

Buttons and Sliders

Buttons, sliders, and text inputs are probably the most useful controls in everyday applications. There is also a checkbox control, but I have not yet seen that one used. And there are specialized controls, like the color selector. While I haven't done anything with text inputs, I have done a little with buttons and a little more with sliders.

Sunday

Who works on Sundays? Apparently some SAGE developers do because on Sunday I was on e-mail with William Stein trying to get to understand interact.py, the file that contains most of the Python interact code. Eventually I made a three-line change that satisfied one of the open boxes in the todo list.

The change is highlighting depressed buttons in button bars, which posed only a very little design challenge: somehow figuring out which button has been highlighted. But, JQuery comes to rescue with a very simple solution:

$("BUTTON", this.parentNode).css("border-style", "outset")

The first part tells JQuery to look for all buttons in the cell containing the button being pressed, the second part tells JQuery to make all those buttons look like they have popped up.

The patch: SAGE Trac #3578 is approved and merged with the next release of SAGE.

Monday

I started playing around with sliders, trying to dynamically assign slider width, and soon realized that is too much work for the result - for now. This is because SAGE uses visually-appealing sliders with rounded ends. So, I settled with a longer static-width slider but decided to also add a label. Like with buttons, the point is to make it easier to tell the position of the slider so the label would have to change as I move the slider, not afterwards. This, of course, meant that there must be a Javascript array containing the possible values on the slider, and they are not limited to numbers. And if you are experienced with Javascript, you'll know this trick:

(function(){
    var values = [...];
    ...
})();

This creates a block and executes it. Why? The scope of the variable sliders is limited to the block, so I can create several of these and not worry about confuscating the variable to avoid duplicates.

Here is an example of an interaction using sliders:

The patch: SAGE Trac #3599 is currently awaiting review.

SAGE - Interact

This and most of the following posts will be related to my development of SAGE's Interact feature. I will try to document my progress daily and provide useful links.