New Features in Maple 17: Language and Programming

Designed for mathematical computation, the Maple language combines the best principles from procedural, functional, and object-oriented programming. Maple 17 adds many useful features to your Maple programming toolkit. Maple 17 has added several changes to how you interact with variables including improving subscript handling, reclaiming the use of variable names like 'I', 'D', and 'Gamma', and adding support for thread-local variables.

Details and Examples

Reclaiming the Use of the Names 'I', 'D', 'gamma', and More

You can now reclaim variables such as 'D', 'I' or 'gamma' for use in your calculations.

 > local D := 5:

 > D+D;

By declaring 'D' local at the top-level, a new local variable has been created with the name 'D' and made the default in the current name-space in a way similar to that of using the short-name to access package exports after using the "with" command to load a package. The global version of this name, the differential operator, is still available by prefixing the name with colon-dash.

 > :-D(f)(0);

 > convert(:-D(f)(0), 'diff');

Any variable can be declared local, even ones that are not protected.

 > local x, y, min:

 > x := 4.4; y := 320.1; min := 0.001;

The global versions remain available via :-.

 > :-x, :-y, :-min(x,y);

The imaginary unit, I, also obeys a local declaration even though it is a special alias governed by interface(imaginaryunit). In this case the name of the global version becomes _I instead of :-I.

 > local I:

 > I := <1, 0, 0; 0, 1, 0; 0, 0, 1>;

 > I^2;

 > _I^2;

Selecting or removing items from an Array or Table

The select, remove, and selectremove commands are ideal for searching for data. The default behavior of these commands is to return a result object of exactly the same size and dimensions as the one you passed in, with NULL's in place of removed items.  Automatic flattening of NULLs makes the behavior different in lists compared to Arrays.

NULL is automatically removed from lists.

 > l := [1, NULL, 3, NULL, 5];

This is not the case for Arrays.

 > a := Array(1 .. 5, {1 = 1, 2 = NULL, 3 = 3, 4 = NULL, 5 = 5});

This paradigm was mirrored in selectremove.

 > select(isprime, [1, 2, 3, 4, 5, 6]);

 > select(isprime, <1, 2, 3, 4, 5, 6>);

Now, in Maple 17, you can use an option to force the removal of NULL from arrays and tables. The option, flatten, is noted in square brackets after the command name.

 > select[flatten](isprime, < 1, 2, 3, 4, 5, 6>);

 > remove[flatten](isprime, table({1 = 1, 2 = 2, 3 = 3, 4 = 4, 5 = 5, 6 = 6}));

Multi-dimensional data is flattened into a one-dimensional array.

 > M := <1, 2, 3; 4, 5, 6>;

 > select[flatten](isprime, M);

 > remove[flatten](isprime, M);

 > selectremove[flatten](isprime, M);

Sort with the output option

A new feature in Maple 17 is the output option for the sort command. It can be used by programmers to find out what reordering, or permutation, is applied to a list or Vector in order to sort it, rather than just the sorted result. This is often useful if there are multiple lists of corresponding elements that all need to be sorted according to the values of one of these lists.

For example, suppose we have corresponding lists of English and French words for numbers:

 > numbers := [2, 5, 3, 1, 8];

 > English := ["two", "five", "three", "one", "eight"];

 > French := ["deux", "cinq", "trois", "un", "huit"];

We would like all of these lists to be in numerical order. We can do this by sorting with the 'output' = 'permutation' option:

 > p := sort(numbers, 'output' = 'permutation');

This tells us that the first element of the sorted list is the fourth element of the original list (because the first entry of is four); the second element of the sorted list is the first element of the original list, and so on. Thus, we can obtain the sorted lists as follows:

 > numbers_sorted := numbers[p];

 > English_sorted := English[p];

 > French_sorted := French[p];

There is a slightly more efficient way of achieving this same result: In order to find the permutation , Maple already created the sorted version of . In above, we recreate that list as . We can do that in one step, using the option 'output' = ['permutation', 'sorted']:

 > p, numbers_sorted := sort(numbers, 'output' = ['permutation', 'sorted']);

 > English_sorted := English[p];

 > French_sorted := French[p];

The output option is applicable when sorting a list, a Vector, or a one-dimensional Array. It can be used together with any predefined or custom sort order.

In this example, we sort a Vector and its element-wise derivative by length:

 > v := ;

 > w := diff~(v,x);

 > v_sorted, v_perm := sort(v, length, 'output' = ['sorted', 'permutation']);

 > w_sorted := w[v_perm];

Finally, in the following example, we take random points in the unit circle (using the rejection method). We then sort them by absolute value of the x-coordinate and color them in order.

 > n := 10^4:

 > x := Statistics:-Sample(Uniform(-1, 1), n);

 > y := Statistics:-Sample(Uniform(-1, 1), n);

 > inside := select( i -> x[i]^2+y[i]^2 <= 1, [seq(1 .. n)]):

 > k := numelems(inside);

 > x := x[inside];

 > y := y[inside];

 > x, p := sort(x, (x1,x2)-> abs(x1) < abs(x2), 'output' = ['sorted', 'permutation']):

 > y := y[p]:

 > plots:-pointplot(, color = ColorTools:-HueSpread('red', k, 1/(2*k)));

Thread Local Data

In Maple 17, module local variables can be declared thread local, meaning that they store a different value for each thread that access the variable. This allows for complex algorithms that need to maintain state to be written in a thread safe manner.

A module that wants to maintain internal state generally will not work correctly when executed in parallel. Consider the following example: the Mapper module maintains an internal state using the variables func and data.  The setMapper routine is used to set these variables. When ModuleApply is called, it maps func over the given data.

 > Mapper := module () local func, data; export setMapper, ModuleApply;  setMapper := proc (f::procedure, d::anything) func := f; data := d; NULL end proc; ModuleApply := proc (d) map(func, d, data) end proc end module:

 > adder := proc () Mapper:-setMapper(proc (x, y) options operator, arrow; x+y end proc, 1); Mapper([seq(i, i = 1 .. 10)]) end proc:

 > adder();

 > multer := proc () Mapper:-setMapper(proc (x, y) options operator, arrow; x*y end proc, 3); Mapper([seq(i, i = 1 .. 10)]) end proc:

 > multer();

If you execute the adder and multer commands in parallel, you can get incorrect results. Executing the following statement multiple times will show different results, some of which will be incorrect.

 > Threads:-Task:-Start(passed, Task = [adder], Task = [multer]);

 > Threads:-Task:-Start(passed, Task = [adder], Task = [multer]);

 > Threads:-Task:-Start(passed, Task = [adder], Task = [multer]);

 > Threads:-Task:-Start(passed, Task = [adder], Task = [multer]);

 > Threads:-Task:-Start(passed, Task = [adder], Task = [multer]);

Incorrect results are computed because the func and data state variables are shared between threads. Thus when one thread changes func's value, the other thread is also affected. In Maple 17, this can be fixed by declaring the state variables as thread_local . This means that each thread will maintain its own value for the state variables. Thus changing the value in one thread will not effect other threads.

 > MapperTL := module () local func::thread_local, data::thread_local; export setMapper, ModuleApply;   setMapper := proc (f::procedure, d::anything) func := f; data := d; NULL end proc; ModuleApply := proc (d) map(func, d, data) end proc end module;

 > adderTL := proc () MapperTL:-setMapper(proc (x, y) options operator, arrow; x+y end proc, 1); MapperTL([seq(i, i = 1 .. 10)]) end proc;

 > adderTL();

 > multerTL := proc () MapperTL:-setMapper(proc (x, y) options operator, arrow; x*y end proc, 3); MapperTL([seq(i, i = 1 .. 10)]) end proc;

 > multerTL();

Executing this version, using the thread local variables, will return the expected result.

 > Threads:-Task:-Start(passed, Task = [adderTL], Task = [multerTL]);

 > Threads:-Task:-Start(passed, Task = [adderTL], Task = [multerTL]);

 > Threads:-Task:-Start(passed, Task = [adderTL], Task = [multerTL]);

 > Threads:-Task:-Start(passed, Task = [adderTL], Task = [multerTL]);

 > Threads:-Task:-Start(passed, Task = [adderTL], Task = [multerTL]);