Mathics3 / mathics-core

An open-source Mathematica. This repository contains the Python modules for WL Built-in functions, variables, core primitives, e.g. Symbol, a parser to create Expressions, and an evaluator to execute them.
https://mathics.org
Other
786 stars 48 forks source link

SymPy inserts Piecewise by default #1138

Open davidar opened 1 month ago

davidar commented 1 month ago

Is your feature request related to a problem? Please describe. Not sure if this is a bug so much as a difference in default behaviour between SymPy and WMA. WMA tends to be a bit looser in making simplifying assumptions, whereas SymPy tries to enumerate all the possible cases. For example, for the expression:

Expand[Product[Sum[x^k, {k, 0, m}], {m, 1, 2}]]

WMA gives:

1 + 2*x + 2*x^2 + x^3

Whereas Mathics gives:

Piecewise[{{2, x == 1}, {1 + x, True}}]*Piecewise[{{3, x == 1}, {1 + x + x^2, True}}]

which causes some issues if e.g. you're trying to extract coefficients from the polynomial.

[!NOTE] In this case, the Piecewise aren't actually needed, and are just a remnant of the fact that the intermediate expression

Sum[x^k, {k, 0, m}]

evaluates to (note the singularity at $x=1$)

Piecewise[{{1 + m, x == 1}, {-1 / (-1 + x) + x ^ (1 + m) / (-1 + x), True}}]

So in this particular case Simplify will remove them, but in general SymPy does put them there for good reason.

Describe the solution you'd like For compatibility with WMA, stripping the Piecewise from Sum et al by default. The easiest way to do that is to apply

sympy_result.replace(lambda x: x.is_Piecewise, lambda x: x.args[-1][0])

to the intermediate SymPy results, though it's perhaps worth doing something a little more intelligent than that. I had a look to see if SymPy has any existing hinting for avoiding generating them in the first place but couldn't find any.

However, the behaviour of SymPy here is quite useful when you care about making sure the results are defined everywhere, so it's good to keep it as an option. As far as I know the closest analogy in WMA is GenerateConditions, though it doesn't quite work the same - according to the docs, setting it to All is supposed to have the same behaviour as SymPy here, but it doesn't in my tests, and setting it to True yields

ConditionalExpression[(-1 + x^(1 + m))/(-1 + x), Element[m, Integers] && m >= 0]

which isn't quite the same.

Not sure if there's a better solution to align the behaviours of the two?

Describe alternatives you've considered A possible workaround would be to implement PiecewiseExpand (I think the SymPy equivalent would be piecewise_fold), to at least allow the Piecewise expressions to be combined. But it's not exactly a full solution as it still results in existing WMA code that doesn't require that being broken.

mmatera commented 1 month ago

As in many other cases, the problem is that Sympy and WMA have different ideas about what is the standard form of a mathematical expression. One way to avoid these incompatibilities would be to write specific rules (in WL) to deal with the specific cases, like the one in the example. In this specific case, WMA does not evaluates the Sum before assign integer values to m:

In[1]:= TracePrint[Expand[Product[Sum[x^k, {k, 0, m}], {m, 1, 2}]], TraceAction:>(Print[Indent[TraceLevel[]-1],InputForm[#1]]&)]

 HoldForm[Expand[Product[Sum[x^k, {k, 0, m}], {m, 1, 2}]]]
  HoldForm[Expand]
  HoldForm[Product[Sum[x^k, {k, 0, m}], {m, 1, 2}]]
   HoldForm[Product]
   HoldForm[1]
   HoldForm[2]
   HoldForm[Sum[x^k, {k, 0, m}]]
    HoldForm[Sum]
    HoldForm[0]
    HoldForm[1]
    HoldForm[x^k]
     HoldForm[Power]
     HoldForm[x]
     HoldForm[k]
     HoldForm[0]
    HoldForm[x^0]
    HoldForm[1]
    HoldForm[x^k]
     HoldForm[Power]
     HoldForm[x]
     HoldForm[k]
     HoldForm[1]
    HoldForm[x^1]
    HoldForm[x]
   HoldForm[1 + x]
   HoldForm[Sum[x^k, {k, 0, m}]]
    HoldForm[Sum]
    HoldForm[0]
    HoldForm[2]
    HoldForm[x^k]
     HoldForm[Power]
     HoldForm[x]
     HoldForm[k]
     HoldForm[0]
    HoldForm[x^0]
    HoldForm[1]
    HoldForm[x^k]
     HoldForm[Power]
     HoldForm[x]
     HoldForm[k]
     HoldForm[1]
    HoldForm[x^1]
    HoldForm[x]
    HoldForm[x^k]
     HoldForm[Power]
     HoldForm[x]
     HoldForm[k]
     HoldForm[2]
    HoldForm[x^2]
   HoldForm[1 + x + x^2]
  HoldForm[(1 + x)*(1 + x + x^2)]
 HoldForm[Expand[(1 + x)*(1 + x + x^2)]]
 HoldForm[1 + 2*x + 2*x^2 + x^3]

                     2    3
Out[1]= 1 + 2 x + 2 x  + x

Notice that WMA is able to evaluate the sum as a function of m

In[14]:= Sum[x^k, {k, 0, m}]                                                                                                                                                  

               1 + m
         -1 + x
Out[14]= -----------
           -1 + x

but it doesn't it inside Product. Both Sum and Product have the attribute HoldAll, which allows to decide inside the eval_ method if the expression should be evaluated before or after setting the value of m, or to compile it before assigning values to m.