MuPAD and MuPAD-Combinat Programming Tips And Tricks

Note: the tips and tricks are being moved from the end of the README file to here.

Table Of Contents

Tips and tricks for users

  1. 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

Tips and tricks for developers

  1. 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).

  2. 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 ..."
  3. 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

  4. 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?.

    • _mult: the MuPAD function corresponding to the operator * a*b*c is equivalent to a*b*c
    • operators::mult2(x,y) The standard overloaded function for binary multiplication.
    • 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::mult2(x,y): binary multiplication of two elements of dom. Result guaranteed to be in dom.
    • dom::mult2Basis(t1,t2): binary multiplication of the two basis elements of dom indexed by t1 and t2. Result guaranteed to be in dom.
    • dom::mult(...): n-ary multiplication of elements of dom. The result guaranteed to be in dom. In particular, dom::mult() gives dom::one
    • dom::multcoeffs(x,c) Multiplication of x by the scalar c on the right. Results guaranteed to be in dom.
    • dom::multcoeffsLeft(c, x) Idem on the right. Obviously, can't be used as x::multcoeffsLeft(c).
    • 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.

  5. Optimization for creating, reading and modifying lists in MuPAD

Tests

  1. 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]]):
  2. Running the test suite of a particular package (say combinat::words)

    cd test
    make COMBINAT/words.check
  3. Running the test suite under a temporary different version of MuPAD

    cd test
    make check MUPAD=mupad252 MUPAD_VERSION=2.5.2
  4. 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.

Debbugging

Learn to use the MuPAD debugger. It is very powerful, quite easy to

use, and it will save you tons of time. In short
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

following little trick
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.

Evaluation voodoo

  1. Learn option hold

    Most functions want their arguments nicely evaluated. If for some reason yours is a function that does not (say, you really want "1+2" instead of "3" or the user should be able to give a sequence as one of the parameters), think again and if this is still the case, use option hold in the definition of the function, as in the following:
    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.

  2. Passing options when using option hold

    If your function has option hold and wants to call another function, which might have this option, too, the following are safe ways of doing that, if the function you want to call is visible to your caller:
    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.

Documentation

See DocumentationTipsAndTricks

Tips and tricks to ensure backward compatibility with former versions of MuPAD

  1. 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.

  2. 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();
  3. 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):
  4. operators <=>, ==>, <==

    They simply do not exists in MuPAD 2.0.0, and would be somewhat painful to backport. So, don't use them!

  5. 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).

  6. 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).

  7. 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.

  8. 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, ...).

  9. 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

Dynamic modules

  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.

  2. 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.

  3. Compiling programs/dynamic modules with another compiler

    rm config.cache
    cd src; make clean
    ./configure CXX=g++3
    make
  4. 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.

  5. 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.

Questions
  • How to tell gdb to reload the symbol table of bla.mdm everytime it is recompiled. For the moment, the simplest I have found is to restart gdb ... Hmm, that seems to work now ?!?