Note: the tips and tricks are being moved from the end of the README file to here.
How to define the field of expressions in factored form?
K := Dom::ExpressionField(expr@factor); K(x^2-1);
Note that it is safer to use expr@factor as normalizing function. Indeed, for efficiency reason, polynomials over fields of expressions use syntactical equality to decide for equality to zero; in some cases, the kernel may not detect, and delete, zero terms.
See
http://www.mupad.de/BUGS/?bug=1402 for details
Using MuPACS for editing mupad files and running MuPAD under emacs.
This Lisp mode can be download here:
http://sourceforge.net/projects/mupacs/.
In order to have a coherent appearance for mupad files in the project, it is possible to add these lines in your .emacs file (please, modify the following fields: mupad-directory, mupad-el-info, mupad-user-mail-address).
'(mupad-directory "the path of your MuPAD")
'(mupad-domain-indent 4
'(mupad-el-info "the path of the file mupad.el-info")
'(mupad-electric-p nil)
'(mupad-fixed-keys-alist nil)
'(mupad-fontification-level 0)
'(mupad-fontify-function-names nil)
'(mupad-fontify-global-var nil)
'(mupad-help-examples-comment "")
'(mupad-help-examples-execute t)
'(mupad-hilit-global-var nil)
'(mupad-indent-level 4)
'(mupad-javadoc-stylep t)
'(mupad-more-maidp t)
'(mupad-no-closed-brace nil)
'(mupad-no-hash-comment t)
'(mupad-run-arrow-behaviour "Usual-Search")
'(mupad-run-buffer-distinct-colorisation nil)
'(mupad-run-buffer-name 3)
'(mupad-run-commandline "mupad -g -E -U EMACS=TRUE")
'(mupad-run-input-terminator ";")
'(mupad-run-mode-hook (quote (mupad-bus-adapt-textwidth mupad-help-init)))
'(mupad-run-pgm "mupad")
'(mupad-run-pgm-opt (quote ("-R" "-U" "EMACS" "-g")))
'(mupad-run-recenter-behaviour (quote agressive))
'(mupad-run-recenter-bottom-margin 0)
'(mupad-run-recenter-on-history t)
'(mupad-run-system-trace 2)
'(mupad-show-sexpp nil)
'(mupad-user-mail-address "your email address")
'(sli-handles-sexp nil)
It is also possible to configure the entry MuPAD Indent Level to value 4 in the Mupad indentation group (menu M-x mupad-customize-mupad-group).
Adding a new domain / category to MuPAD-Combinat
You may want to use the script createDomain at the top level of the MuPAD-Combinat distribution. See the documentation inside the script for details. Basic usage:
createDomain combinat::myCombinatorialClass "the combinatorial class of ..."
Iteration versus recursion
I (Thomas) asked: Are recursive algorithms better, or loops within loops with loops faster?
and NicolasThiéry: replies:
This depends a lot on the context. In most cases you can just ignore the problem, unless the operations in the loop/calls are really trivial:
>> factorialRec := n -> n*factorialRec(n-1):
>> factorialRec(0) := 1:
>> factorialIterative :=
proc(n)
local result;
begin
result := 1;
for i from 1 to n do
result := result * i;
end_for;
end_proc:
For small n the overhead is non negligible:
>> n := 100: >> prog::timeThese(1000, ["rec" = factorialRec(n), >> "iter" = factorialIterative(n)]) rec: CPU: 0.392 ms/call Wallclock: 0.392 ms/call iter: CPU: 0.265 ms/call Wallclock: 0.265 ms/call
But for larger n, the arithmetic cost dominates:
>> n := 999: >> prog::timeThese(1000, ["rec" = factorialRec(n), >> "iter" = factorialIterative(n)]) rec: CPU: 5.973 ms/call Wallclock: 5.973 ms/call iter: CPU: 4.284 ms/call Wallclock: 4.284 ms/call
Now, recursive functions in MuPAD have a very serious shortcoming: the call stack size is very limited:
>> factorialRec(1001) Error: Recursive definition [See ?MAXDEPTH]; during evaluation of 'factorialRec'
Altogether, I usually go first recursive whenever the algorithmic calls for it for better readability. Later, if either the function really proves to be the critical section of a large computation, or if I actually bump into the stack size limit, then I switch to iterative.
Note: the upcoming MuPAD > 4.2 release will have a higher stack size limit. See ?MAXDEPTH
mult, mult2, _mult, ... I am confused; which one should I use?
All the variants of plus / mult in MuPAD-Combinat can become confusing, so here is a synthesis of the specifications for them. See also ?_mult, domainref.pdf, ?Cat::ModuleWithBasis?, ?Cat::AlgebraWithBasis?.
operators::_mult(a,b,c) The standard overloaded function for n-ary multiplication. Defined by associativity from mult2 above: it result in a call like
mult2(mult2(a,b),c)
Could become _mult at some point in the future.
dom::_mult What gets called by _mult when writing a*b*c with a in dom. Should take care of all the forms above, plus possibly others. Most often defined as operators::_mult.
There are no guarantee whatsoever on the type of the result (for example, the multiplication of two monomial symmetric functions with it may well yield back a schur function).
In particular, dom::_mult() will most likely return the integer 1.
There should hardly be any reason to use or define it directly.
Similarly, one has _plus, operators::plus2, operators::_plus, dom::plus2, dom::plus, dom::_plus.
Well, a good part of this mess would go away in a language with deeper overloading support; what we are doing here is essentially name mangling as in C vs C++. But I guess we have to live with it.
When you create a list with some elements from an other list, you have two possibilities:
>> proc() >> begin >> l := [ $ 1..1000 ]: >> prog::timeThis(100, [ l[i]+1 $ i=1..nops(l) ]): >> prog::timeThis(100, [ x +1 $ x in l ]): >> end_proc(); CPU: 5.7 ms/call Wallclock: 5.7 ms/call CPU: 4.87 ms/call Wallclock: 4.87 ms/call
The second case is slower than the first one because the call of the i-th element of l costs a lot of time when the size of l become huge
NicolasThiéry: the effect is correct, but not the explanation:
>> proc() >> begin >> l := [ $ 1..100 ]: >> prog::timeThis(100, [ l[i] $ i=1..100 ]): >> l := [ $ 1..10000 ]: >> prog::timeThis(100, [ l[i] $ i=1..100 ]): >> l := [ $ 1..1000000 ]: >> prog::timeThis(100, [ l[i] $ i=1..100 ]): >> end_proc(); CPU: 0.29 ms/call Wallclock: 0.29 ms/call CPU: 0.28 ms/call Wallclock: 0.28 ms/call CPU: 0.28 ms/call Wallclock: 0.28 ms/call
When you concatenate two lists you'd rather use append(l1, l2) instead of l1 :=l1,l2 because you don't force MuPAD to evaluate the first list l1.
NicolasThiéry: find a better explanation; this one does not explain well the timing below:
>> proc() >> begin >> l := [ $ 1..100000 ]: >> l2 := $ 1..100000; >> prog::timeThis(100, append(l, 1)); >> prog::timeThis(100, l.[1]); >> prog::timeThis(100, (l2,1)); >> end_proc(); CPU: 12.07 ms/call Wallclock: 12.07 ms/call CPU: 11.41 ms/call Wallclock: 11.41 ms/call CPU: 25.24 ms/call Wallclock: 25.24 ms/call
If you know the length p of a list you want to initialize, it is quicker to initialize it with [NIL $p] than with [0 $p]
>> prog::timeThis(1000, [ NIL $ 10000 ]): CPU: 0.785 ms/call Wallclock: 0.785 ms/call >> prog::timeThis(1000, [ 0 $ 10000 ]): CPU: 0.783 ms/call Wallclock: 0.783 ms/call
Writing tests
A very practical way to create new tests is to use prog::makeTest interactively, and to copy paste the result (after double checking it!) into the test file (say lib/COMBINAT/DOC/words.tst). Example:
>> prog::makeTest(combinat::partitions::count(4),
combinat::partitions::list(3)):
prog::test(combinat::partitions::count(4),
5):
prog::test(combinat::partitions::list(3),
[[3], [2, 1], [1, 1, 1]]):
Running the test suite of a particular package (say combinat::words)
cd test make COMBINAT/words.check
Running the test suite under a temporary different version of MuPAD
cd test make check MUPAD=mupad252 MUPAD_VERSION=2.5.2
It is also possible to run the tests from within MuPAD
export(prog::testUnit, runTests): // I have this in my mupadinit runTests(combinat::partitions):
One good thing about it is that you can then run the tests under the debugger:
debug(runTests(Dom::WeylGroup))
Caveat: this is far from failproof, because the tests are not run in the exact same context as from the command line and TrapError? tests are (currently) ignored; so always run the real tests from time to time.
Learn to use the MuPAD debugger. It is very powerful, quite easy to
debug(theFunctionToDebug(bla, bli))
And then you can go through the function step by step, continue until the next error, check and set variables, etc ... (like in any standard source code debugger like gdb). See ?debug for details.
For a nice graphical interface which shows you the source code while
you run through it, I recommend using the debugger inside emacs (see
http://mupacs.sf.net), or inside xmupad.
Breakpoints are not very practical to use with the MuPAD command line debugger. However, they can be conveniently emulated with the
f:=proc()
begin
bla:=1;
ble:=2;
warning("STOP HERE");
bli:=3;
end:
debug(f())
Now, just press c, and the execution will continue until the line with the warning is reached.
Learn option hold
twolists := proc(l1, l2)
option hold;
begin
[[context(l1)], [context(l2)]];
end:
twolists((1,2,3), 4); // returns [[1,2,3], 4]
Note that almost any access to the parameters in such a procedure requires a call to the function context.
Passing options when using option hold
context(hold(f)(x)) context(hold(a::b)(x))
If the function is not visible, or you need to use dom to name it, better use
context(subsop(hold(f)(x), 0 = dom::something))with any f -- it's just a placeholder anyway.
prog::getOptions
We strongly support the use of prog::getOptions for parsing procedure options. However, this function only appeared (undocumented) in MuPAD 2.5.0, and was changed incompatibly in MuPAD 3.0.0. To ensure compatibility with all versions of MuPAD, it should be called as combinat::getOptions, which is a patched backport of the prog::getOptions of MuPAD 3.0.0.
MuPAD 2.0.0's option remember bug for methods
Try this code with MuPAD 2.0.0:
domain bla
x:=
proc()
option remember;
begin
print(coucou);
end_proc;
end_proc:
bla::x();
bla::x();
bla::x();
The first time bla::x is called, the result is not inserted in the remember table. This is fixed in MuPAD >= 2.5.0. Standard workaround: force the evaluation of bla::x before calling it:
domain bla
x:=
proc()
option remember;
begin
print(coucou);
end_proc;
end_proc:
bla::x;
bla::x();
bla::x();
MuPAD 2.0.0's prog::check bugs
prog::check(
proc(typeOfObjects)
local rank;
begin
proc() : typeOfObjects
begin
end_proc
end_proc, 3):
prog::check(
proc(basSource)
begin
proc(b : basSource::basisIndices)
begin
end_proc
end):
operators <=>, ==>, <==
They simply do not exists in MuPAD 2.0.0, and would be somewhat painful to backport. So, don't use them!
Type::Boolean:
Beware that Type::Boolean is not defined in MuPAD < 2.5.0. Furthermore, if you just want to test for TRUE/FALSE/UNKNOWN, it's preferable to use DOM_BOOL (Type::Boolean is for boolean expressions in general).
Deleting list entries
Better use delete(l[3]) than l[3]:=null() to delete an element of a list (with MuPAD 2.0.0, the list needs to be evaluated for the element to be actually deleted).
min/max
Beware that min([1,2,3]) returns [1,2,3] with MuPAD <= 2.5 and 1 with MuPAD 3.0.0. So, always use the form min(op([1,2,3])) to get the minimal element of a list.
Strings indexed access
Since MuPAD 3.0.0 strings are indexed by 1..length(s) to be more coherent with other MuPAD objects (lists, ...); beforehand, they were indexed by 0..length(s)-1. This is a good thing in se, but this means that we need to be careful when writing code for all versions of MuPAD. We already encoutered this problem in several of the MuPAD-Combinat libraries (see e.g. combinat::dyckWords). The standard idiom for this is:
%if "12"[1]="1" then // work around a change in strings indexed
// access from MuPAD 3.0.0 on
// Code when strings are indexed by 1..length(s)
else
// Code when strings are indexed by 0..length(s)-1
end_if;
Mind the %if: it means that the test is resolved at parse-time. So there is no overhead during execution. Note also that the test does not rely on the MuPAD version, but on the feature itself. This makes it more robust (some old mupad devel versions before the string change already had version number 3.0.0, ...).
operands of an equation
For compatibility with MuPAD <= 2.5.3, use op(eq,1) instead of eq[1] for accessing the left hand side of an equation eq := bla=1
Compiling dynamic modules on MacOS 9
Note from Christopher: Compiling modules for this MuPAD version requires MetroWerks? CodeWarrior?, if I remember correctly. The "Modules" directory of the installation contains a directory "Dynamic Module SDK" in which you should find all that is needed.
Recompiling the dynamic modules for another MuPAD version
rm config.cache rm `\ls src/*/*.mcc | sed 's/\.mcc$/.cc/'` ./configure MUPAD=mupad2.5.1 make
Note: you may now also use 'make clean-module-sources' instead of the 2nd line above.
Compiling programs/dynamic modules with another compiler
rm config.cache cd src; make clean ./configure CXX=g++3 make
Using dynamic module functions when time is critical
(Christopher) Using:
f := external("module", "function")
f(a, b)
may be faster than
module::function(a, b)
There is still a non-negligible overhead compared to standard function calls in C.
Debugging dynamic modules with gdb
Procedure to analyze a kernel internal error in a dynamic module bla with gdb under emacs.
First, recompile the dynamic module with -g and without optimization:
make clean make bla.mdm CXXFLAGS=-g CFLAGS=-g
Then:
------------------ In MuPAD-------------------------------------
>> module(bla): module(util): util::kernelPid();
4556
------------------ In Emacs minibuffer -------------------------
M-x gdb
------------------ In gdb buffer -------------------------------
<gdb> attach 4556
<gdb> c
------------------ In MuPAD-------------------------------------
>> CommandThatCrashesMuPAD()
------------------ In gdb buffer -------------------------------
<gdb> bt
<gdb> ...
----------------------------------------------------------------
One can also put breakpoints in the C code instead of waiting for a crash To get the full C name of a dynamic module functions, one can look in the output of nm bla.mdm. Running the MuPAD debugger simultaneously can also be practical.