At least Sympy, Maxima and Mathematica can treat boolean expressions as other expressions. Maxima : :
(%i1) display2d:false;
(%o1) false
(%i2) foo: a and b;
(%o2) a and b
(%i3) bar: subst([a=(%pi>3), b=(%pi<4)], foo);
(%o3) %pi > 3 and %pi < 4
(%i4) is(bar);
(%o4) unknown
(%i5) bar, ev;
(%o5) true
Sympy:
>>> a, b, x=symbols("a, b, x")
>>> foo.subs({a:pi>3, b:x<4})
x < 4
>>> from sympy import *
>>> a, b, x=symbols("a, b, x")
>>> foo=And(a, b) ; foo
a & b
>>> foo.subs({a:pi>3, b:x<4})
x < 4
>>> foo.subs({a:pi>3, b:pi<4})
True
Thanks to Sage’s and
and or
“lazy” operators and the interpretation on anything not zero as True
, Sage’s expressions containing logical operators gives surprising results :
sage: var("a, b")
(a, b)
sage: foo = a and b ; foo
b
sage: (x>3) and (x<4)
x > 3
sage: (pi>3) and (x<4)
x < 4
sage: (pi>3) and (pi<4)
pi < 4
bool((pi>3) and (pi<4))
True
As a consequence, we have no way to translate in sage the boolean expressions sometimes returned by Sympy or Maxima (e. g. Piecewise
functions returned by Sympy for some integrations. It ois easy to slap _sage_
methods to Sympy’s logical functions in terms of Python expressions, but :
- their interpretation won't be what us expected, and
- these expressions won't translate to Maxima in a useful way :
sage: "print(%s)"%(a and b)
'print(b)'
sage: maxima("print(%s)"%(a and b))
b
Another (better) solution is to wrap, for example, Sympy’s logical functions in symbolic function, and define suitanle conversion functions. This is easy for Sage<-> sympy translations ; Sage -> maxima translation is also manageable.
Where I’m stuck is the Maxima->Sage translation (necessary given the importance of Maxima for a lot of our symbolics) : the current interface isn’”t useable :
sage: maxima("a and b")
aandb
sage: maxima("a and b").sage()
aandb
sage: maxima("a and b").sage().parent()
Symbolic Ring
sage: maxima("a and b").sage().variables()
(aandb,)
sage: maxima("a and b").sage().operator() is None
True
sage: maxima("a and b").sage().operands()
[]
or worse :
sage: maxima("subst([a=(%pi>3), b=(%pi<4)], a and b)")
%pi>3and%pi<4
sage: maxima("subst([a=(%pi>3), b=(%pi<4)], a and b)").sage()
---------------------------------------------------------------------------
SyntaxError Traceback (most recent call last)
[ Snip... ]
TypeError: unable to make sense of Maxima expression 'pi>3andpi<4' in Sage
Maxima’s logical operators are not recognized as such and are “pasted” along their arguments.
What should be done would be to get them parsed, and translated a Sages logical function calls. An alternative would be to create “symbolic logical operators” at least for the classes relevant asossible Maxima returns, but, as far as I know, this is not possible in Python.
In both cases, this requires recognizing and treating specially Maxima logical operators. The code of the current Maxima interfaces stymied me : I have been unable to identify the relevant parts. Trace
ing test expressions failed, since the cricial parts probably written in Cython, aren’t accessible to pdb
.
Any hint or criticism well received.
"and" and "or" in python are not logical operators -- they are flow control for expressions; much like the " ... if ... else ..." expression is. Their behaviour cannot be overloaded. The bitwise operators "&" and "|" would be candidates for overloading.Concerning the translation from maxima to sage, the string parser may be difficult that way. I'm not so sure it can do smart things with infix.
sage: ME=maxima
sage: c=ME("a^b")
sage: c.parent()
Maxima
sage: c.sage()
a^b
sage: [maxima.part(c,u) for u in (0..len(c))]
["^", a, b]
The binary-based interface would be very easy to adapt, though:
sage: M=maxima_calculus
sage: c=M('a and b')
sage: c.ecl()
<ECL: ((MAND SIMP) $A $B)>Adapting the s-expr based translator would be very straightforward, since (as you can see) the and appears in exactly the same way other functions would. Example:sage: c=M('sin(a) + b')
sage: c.ecl()
<ECL: ((MPLUS SIMP) ((%SIN SIMP) $A) $B)>
The challenge would be that sage calculus never fully completed the transition to the s-expr translation.
It's much more efficient and robust, but the strings-based translation was "good enough" for anyone to bother completing the transition. A few operations (integrals and limits if I'm not mistaken) do use this layer, but other don't (I think simplifications mostly don't). Perhaps this is enough motivation to complete the transition, or at least to the degree needed for boolean expressions?
The relevant conversion functions aresage.interfaces.maxima_lib.sr_to_maxsage.interfaces.maxima_lib.max_to_srThey use some dictionaries that would need to be preseeded with appropriate translation values for MAND and MOR. You'd furthermore have to search for occurrences of them in the library to see where they are already used.
So maybe we can use whatever `maxima` does for "^" as a cookie-cutter for logical functions ?
The challenge would be that sage calculus never fully completed the transition to the s-expr translation.That is a different problem. If I understand you correctly, you would like to complete the transition to the library interface and essentially kill the pexpect one, thus dispensing with updating it for logical operators ?
This would leave in the dark all code previously written using pexpect-specific features of the Maxima interface. Not so nice...
Dear Nils,
Thanks for your answers.
Note : overloading &
and |
for SR
is probably not a good idea ; it would be way too easy to write problematic code such as :
sage: a < x < b # This "double comparison" is legal in Sage...
a < x # but gives a "flow control" interpretation... But the point is :
sage: a < 3 & 4 < b # Precedence for & and integers implies that the "&" is intermpreted first
a < 0 # Result probably unrelated to programer's intent...
sage: a < 3 | 4 < b # Ditto...
a < 7
imagine that 3 and 4 are replaced by function calls potentially returning either integers or symbolic expressions. The meaning of the expression above would depend of this type, with no warning from the interpreter.
Furthermore, there is no prefix operator available for overloading for not
implementation… As far as I know, while Sage has a decorator allowing the creation of new infix operators (with a somewhat strange name syntax...), there is nothing for the creatopn of prefix operators
And a question :
Le jeudi 6 mai 2021 à 20:17:57 UTC+2, Nils Bruin a écrit :
On Thursday, May 6, 2021 at 3:22:32 AM UTC-7 ... wrote:
[ Snip… ]
There is no pexpect involved there at all; you really need to make a separate maxima instance to get an interface based on that.
Huh ?
From maxima_calculus?
:
Only one instance of this interface can be instantiated, so the user should not try to instantiate another one, which is anyway set to raise an error:
What do you mean ?
There is no pexpect involved there at all; you really need to make a separate maxima instance to get an interface based on that.Huh ?
Frommaxima_calculus?
:Only one instance of this interface can be instantiated, so the user should not try to instantiate another one, which is anyway set to raise an error:
What do you mean ?
Both maxima interfaces mangle Maxima’s boolean operators printing.
The evaluation of a Maxima boolean expression gives nonsensical results in Sage. Using the pexpect
interface :
sage: ME=maxima
sage: ME("foo: a and (b or c)")
aand(borc)
This also happens in Maxima’s printing:
sage: ME("print(foo)")
aand(borc)
but the structure has correctly been interpreted :
sage: ME("untree(x) := append([part(x, 0)], maplist(lambda([u], if atom(u) then u else untree(u)), x))")
untree(x):=append([part(x,0)],maplist(lambda([u],ifatom(u)thenuelseuntree(u)),x))
sage: ME("untree(foo)")
["and",a,["or",b,c]]
This happens also in the maxima_calculus interface :
sage: MC=maxima_calculus
sage: MC("foo")
foo
sage: MC("foo: a and (b or c)")
aand(borc)
sage: MC("print(foo)")
aand(borc)
sage: MC("untree(x) := append([part(x, 0)], maplist(lambda([u], if atom(u) then u else untree(u)), x))")
untree(x):=append([part(x,0)],maplist(lambda([u],ifatom(u)thenuelseuntree(u)),x))
sage: MC("untree(foo)")
[\"and\",a,[\"or\",b,c]]
This suggests that the problem may exist in the common parent class Maxima_abstract. Whose code is (yet) impenetrable to me. nOne also notes that this problem also affects "normal" Maxima output (including code, whose whitespace is removed (Yuk !).
Is this problem worth a ticket by itself or should it be tackled in the implementation of Sage logical functions ? In the former case, how to file the "right" informative ticket ?
This also happens in the .interact()
manual interactions :
sage: ME.interact()
--> Switching to Maxima <--
maxima: foo
aand(borc)
maxima:
--> Exiting back to Sage <--
sage: MC.interact()
--> Switching to Maxima_lib <--
maxima_lib: foo
aand(borc)
maxima_lib:
--> Exiting back to Sage <--
HTH,
Both maxima interfaces mangle Maxima’s boolean operators printing.
The evaluation of a Maxima boolean expression gives nonsensical results in Sage. Using thepexpect
interface :sage: ME=maxima sage: ME("foo: a and (b or c)") aand(borc)
This also happens in Maxima’s printing:
sage: ME("print(foo)") aand(borc)
Le vendredi 7 mai 2021 à 17:54:51 UTC+2, Nils Bruin a écrit :
On Friday, May 7, 2021 at 6:07:30 AM UTC-7 emanuel wrote:
Both maxima interfaces mangle Maxima’s boolean operators printing.
The evaluation of a Maxima boolean expression gives nonsensical results in Sage. Using thepexpect
interface :sage: ME=maxima sage: ME("foo: a and (b or c)") aand(borc)
This also happens in Maxima’s printing:
sage: ME("print(foo)")kkkkkk aand(borc)
Yup, see:strings coming from the maxima interface are stripped of spaces.
Thus mangling the printed output, which becomes unparsable (by program or eyeball).
I guess there must be a benefit to doing so.
I guess that this is an instance of “hell is paved with good intents”. This stripping happens line 817 of maxima.py
(where any occurence of any number of consecutive whitespace is remplaced by a 0-length string) and line 461 of maxima_lib.py
(where a zero-length string joins the representation of each of the result’s components).
Wanna bet that the original intent was ' '
(i. e. a single space) in both cases ?
Clearly, it doesn't seem to be designed with alphanumerical infix operators in mind.
Nor with printed code output (see above). And it’s ugly as hell…
Unless you advise me not to do so (and explain why), I’ll file a ticket on this issue, propose a branch replacing those null strings with a single space, and try to break the damn thing.
Path to this code:sage: M=sage.interfaces.maxima.Maxima()
sage: C=M('a and b')
sage: C.str??
sage: C._check_valid??sage: M._eval_line??
Nice shortcut . I’ve learned something tonight…
Thank you very much.
Nor with printed code output (see above). And it’s ugly as hell…
Unless you advise me not to do so (and explain why), I’ll file a ticket on this issue, propose a branch replacing those null strings with a single space, and try to break the damn thing.
On Friday, May 7, 2021 at 12:37:41 PM UTC-7 emanuel wrote:
Unless you advise me not to do so (and explain why), I’ll file a ticket on this issue, propose a branch replacing those null strings with a single space, and try to break the damn thing.
I'm sure you'll succeed and discover why it was decided to strip out whitespace.
Or you could do some archaeology and see if you can find records of why this was done. It looks like the change goes back to 2007: