Menu

#155 Non-interactive interaction with Lisp

None
closed
nobody
Lisp (2)
5
2021-05-25
2021-05-17
No

Is there a way to interact with Lisp programmatically ?

I am aware of the (interactive) :lisp command. But I am not aware of any way to use Lisp code in a Maxima function.

An example of what I would like to do : both Sage and Mathematica allow to define piecewise functions (in slightly different ways (cases in Sage, Piecewise in Mathematica). A Maxima macro implementing (awkwardly) this functionality would be :

/* Port to Maxima of Sage's cases. */
cases(x) ::=block([L],
  L:maplist(lambda([cl],
      if length(cl) # 2
      then error("cases : clauses must be of length two.")
      else buildq([c:[first(cl)], a:[second(cl)]],
        if splice(c) then return(splice(a)))),
    x),
  buildq([L:L],ev(block(splice(L), false))));

This works:

(%i37) display2d:false;

(%o37) false
(%i38) maplist(foo, [-2, 2, a]);

(%o38) [1/2,4,unknown]
(%i37) display2d:false;

(%o37) false
(%i38) maplist(foo, [-2, 2, a]);

(%o38) [1/2,4,unknown]

But this has its limits : the block must be rebuilt at each evaluation, not once :

(%i40) define(foo(x), cases([(x<0, -1/x), (x>=0, x^2), (true, unknown)]));

(%o40) foo(x):=unknown
(%i41) maplist(foo, [-2, 2, a]);

(%o41) [unknown,unknown,unknown]

What I'd like to do is to call Lisp's cond, passing it a term-to-term translation of the argument.

Is there a way to do this without recompiling Maxima with this addition ? If not, this (calling Lisp from Maxima) should be considered as a highly desirable feature...

Sincerely yours,

Discussion

  • Tomio Arisaka

    Tomio Arisaka - 2021-05-21

    I think that you should see the "Lisp and Maxima" section of the Maxima manual.

    If you want to use the on-line manual, then as follows:
    When entering the "?? lisp" at the prompt of Maxima, all documented items which contain "lisp" in their titles are shown.
    So enter 0 in order to select the "Lisp and Maxima" title.

    (%i1) ?? lisp
    
     0: Lisp and Maxima
     1: file_search_lisp  (Functions and Variables for File Input and Output)
     2: file_type_lisp  (Functions and Variables for File Input and Output)
     3: lispdisp  (Functions and Variables for Display)
     4: to_lisp  (Functions and Variables for Command Line)
    Enter space-separated numbers, `all' or `none': 0
      ...
      ...
    

    Thanks,

     
  • Robert Dodier

    Robert Dodier - 2021-05-21

    Hi Emmanuel, I don't understand the expected output here. Can you give an example of a cases expression and what the expected translation of that in Maxima would be? Do you want to construct an expression like if -- then -- else ?

    There is a built-in function named charfun which might be related also.

     
  • Emmanuel Charpentier

    What I can do :

    • In Sagemath :
    sage: foo(x)=cases([(x<0, -1/x), (True, x^2)])
    sage: [foo(x) for x in (-2..2)]
    [1/2, 1, 0, 1, 4]
    
    • in Maxima :
    (%i1) display2d:false;
    
    (%o1) false
    (%i2) cases(x) ::=block([L],
      L:maplist(lambda([cl],
          if length(cl) # 2
          then error("cases : clauses must be of length two.")
          else buildq([c:[first(cl)], a:[second(cl)]],
            if splice(c) then return(splice(a)))),
        x),
      buildq([L:L],ev(block(splice(L), false))));
    
    (%o2) cases(x)::=block([L],
                L:maplist(lambda([cl],
                                 if length(cl) # 2
                                     then error(
                                     "cases : clauses must be of length two.")
                                     else buildq([c:[first(cl)],a:[second(cl)]],
                                                 if splice(c)
                                                     then return(splice(a)))),x),
                buildq([L:L],ev(block(splice(L),false))))
    (%i3) foo(x) := cases([[x<0, -1/x],[true, x^2]]);
    
    (%o3) foo(x):=cases([[x < 0,(-1)/x],[true,x^2]])
    (%i4) maplist(foo, [-2, -1, 0, 1, 2]);
    
    (%o4) [1/2,1,0,1,4]
    

    What I would like to do (warning : highly hypothetical pseudocode, written with vague Lisp macro memories 40 years old...) :

    define(cases(x), LispMacro((cond ,@MaximaToLispTranslation(x)));
    

    The point is that going to the Lisp code directly saves the building of the Maxima code at each execution ; This code generation is done once at define time, and the (cond ...) can be executed immediately after the translation of the argument to Lisp (necessary anyway...). BTW, you'll note that I used :=, not define, in order to obtain this translation.

    Mathematica and Sympy return some-to-many results of this kind (especially to express branches in integration problems...) ; an effort is made in Sagemath to translate these results back (cases exists but is perfectible, the difficultty of defining And, Or and Not is bilateral translation to Maxima... exactly for the same reasons as for cases.

    HTH,

    PS : as far as I understand it, charfun is only remotely related...

     
  • Robert Dodier

    Robert Dodier - 2021-05-22

    Thanks for the explanation. Here's a simple-minded implementation of cases using Maxima's if.

    cases ([L]) ::= buildq ([L1: apply (append, L)], "if" (splice (L1)));
    cases ([a,b],[c,d],[true, f]);
     => if a then b elseif c then d else f
     cases ([x<0, -1/x], [true, x^2]);
     => if x < 0 then -1/x else x^2
     define (foo(x), %);
      => foo(x) := if x < 0 then -1/x else x^2
    

    If that's not going in the right direction, you can say why and I'll try again.

    I don't think Common Lisp COND is going to help here, since Maxima logical expressions aren't required to evaluate to true or false; if has the necessary machinery to handle values which are not true or false.

     
  • Emmanuel Charpentier

    I don't think Common Lisp COND is going to help here, since Maxima logical expressions aren't required to evaluate to true or false; if has the necessary machinery to handle values which are not true or false.

    I see your point Your solution is very elegant.

    I ignored that applying if on L would insert the required ifelse and else, the documentation doesn't even hints it.. It might be useful to d an explicit example along the lines of:

    (%i6) apply("if", [a, b, c, d, e, f]);
    
    (%o6) if a then b elseif c then d elseif e then f
    

    to the documentaton of the (very) special if operator.

    As far as I remember, condtreats any non-nil value as true ; does if acts the same ?

    A possible snag, though : what happens if there is no default clause (i. e. no (true, ...) finat expression in L ? I'd like to rase an error,. What would if do ? As far as I understand the if documentation, it would return false...

    Notwithstanding the special case of cases, I still think that documenting how to extend Maxima in Lisp would be useful...

     
  • Robert Dodier

    Robert Dodier - 2021-05-25

    if requires that Boolean values be either true or false; non-null values are not handled the same as true. When the global flag prederror is true, a value other than true or false triggers an error; when prederror is false, the result is again a conditional expression.

    If you want if to not fall through to an implicit false value, you'll have to put something there. You could fix up cases to paste on two more elements, true and error("HEY") (or whatever). I guess cases would want to ensure the second-to-last value is not already true before pasting it on.

    About documentation, there is already some about Lisp and Maxima in "Minimal Maxima". There might be other resources on https://maxima.sourceforge.io/documentation.html . No doubt it could all be better organized.

     
  • Robert Dodier

    Robert Dodier - 2021-05-25
    • status: open --> closed
    • Group: -->
     
  • Robert Dodier

    Robert Dodier - 2021-05-25

    Closing this ticket since it appears the original request has been fulfilled.

     

Log in to post a comment.

MongoDB Logo MongoDB