The Mathematica Journal
Current Issue
Tricks of the Trade
In and Out
Riemann Surfaces IIc
The Mathematica Programmer
New Products
New Publications
News Bulletins
Editor's Pick
Write Us
About the Journal
Staff and Contributors
Back Issues
Download This Issue

The Architecture of the Leibniz System

The behavior described above is implemented through a combination of functions performed by the application and the kernel. Roughly speaking, the application contains the code necessary to translate expressions to and from a form that the kernel can understand, while code in the LZCompute package implements all of the necessary pattern matching and calculation tasks.

The application is responsible for parsing mathematical expressions and building an internal tree structure to reflect the structure of the expression. In addition, the tree structure also contains information about the location of each subexpression. As the user drags the mouse, the application uses this information to map the current mouse location to the portion of the expression the mouse is in. When results come back from the kernel, the application translates them from Mathematica form to a form suitable for display.

Most of the interesting behavior in this system is implemented in the LZCompute package. This is due to a conscious design decision. There were many manipulations and calculations that could have been performed either in the application itself or by code contained in the package. Whenever the option came up to either hard code a capability in C++ in the application or as Mathematica code in the package, I chose to implement the operation in the package. This provides two obvious benefits: the code in the package is easier to modify, and end users with a knowledge of Mathematica can modify the behavior of the system to suit their needs.

Implementing Drag-and-Drop Calculations

When the user clicks and drags inside an expression, Leibniz sends a request to the kernel to evaluate a potential drag-and-drop operation. It does this by calling the function LZEvaluateShuffle defined in the LZCompute package. For example, to evaluate the dragging operation shown in the illustration above, Leibniz would call the following.

Note the LZMover and LZTarget calls wrapped around parts of the expression. These function calls are used to indicate which part of the expression is being dragged (the mover) and which part of the expression is the target. LZEvaluateShuffle returns a list of code numbers corresponding to actions that are possible.

If LZEvaluateShuffle had to just return a single response code, life would be pretty simple. We could make a series of definitions like this.

To indicate how LZEvaluateShuffle should respond to a particular dragging request. In order to return a list of matches, we have to be a little more clever. This is how LZEvaluateShuffle is actually implemented.

LZEvaluateShuffle is implemented as a fixed point calculation starting with an empty list and repeatedly applying the EvalShuffle function. Rather than return a single code number, EvalShuffle appends its code number to the list only if the list does not already contain that particular code number. EvalShuffle is defined via a series of calls to the ES function: each of those calls defines EvalShuffle for a particular pattern. For example, the ES rule that matches the drag request shown above is the following.

How does Leibniz know which subexpression to mark as the target? After all, a given element in a mathematical expression is a member of multiple possible subexpressions. When the user moves the mouse to a new location in an expression, the application uses the information stored in the parse tree to map the location to a subexpression, typically a leaf in the parse tree. Leibniz then calls LZEvaluateShuffle with that subexpression identified as the target. If the result comes back empty, Leibniz moves up to the next largest subexpression containing the mouse location and calls LZEvaluateShuffle again with that subexpression identified as the target. This process continues until an action is found or there are no more possibilities for the target expression. Despite the fact that this process often requires multiple calls to LZEvaluateShuffle via MathLink, the process is fast enough to work comfortably in real time as the user drags the mouse over the expression.

Once Leibniz gets a list of code numbers back from the kernel, it calls the LZMessage function to retrieve descriptive strings for each of the potential actions. The descriptive string for the most likely option gets displayed immediately in the message panel. For example, the LZMessage entry that corresponds to the ES entry shown above is this.

As soon as the user releases the mouse button, Leibniz sends a request to evaluate the dragging operation. The LZShuffle function handles this request. The first parameter to LZShuffle is the code number for the desired action, and the second parameter is the expression being manipulated with mover and target identified. For example, the LZShuffle definition that matches the ES rule shown above is this.

Copyright © 2002 Wolfram Media, Inc. All rights reserved.

[Article Index][Prev Page][Next Page]