Mathics icon indicating copy to clipboard operation
Mathics copied to clipboard

Pattern-matching with Flat Attribute: Different from Mathematica

Open rebcabin opened this issue 7 years ago • 6 comments

Please consider

SetAttributes[eqv, Flat]
eqv[p, q, q, p] /. eqv[x_, y_] :> {x, y}

which produces

{q, eqv[q, q, p]}

in mathics, and

{eqv[q], eqv[q, q, p]}

in Mathematica. This has ramifications in symbolic manipulations with custom associative operators because the missing head eqv is observable in semantics layers.

rebcabin avatar Aug 26 '18 13:08 rebcabin

I believe that the bigger issue, here, is correct modeling of idempotent functions with a combination of Flat and OneIdentiy

Idempotent functions have the behavior eqv[eqv[p]]===eqv[p]===p, e.g., Times[Times[x]]===Times[x]===x.

https://reference.wolfram.com/language/ref/OneIdentity.html?view=all .

This isn't completely clear to me, yet, but I will write some tests in Mathematica today and say more later. But the following crashes mathics now:

In[7]:= SetAttributes[or,{OneIdentity,Orderless}]
In[12]:= or[p,p,p]/.or[p_.,p_.]:>p
  File "mathics/core/expression.py", line 111, in mathics.core.expression.from_python (mathics/core/expression.c:6658)
raise NotImplementedError

rebcabin avatar Sep 07 '18 13:09 rebcabin

Looks like mathics assumes OneIdentity. Let's consider Mathematica's built-in Or, first. Its relevant attributes are just Flat, OneIdentity:

In[97]:= Attributes[Or]
Out[97]= {Flat, HoldAll, OneIdentity, Protected}

It also has no Default:

In[99]:= Default[Or]
Out[99]= Default[Or]

That produces the following behavior:

In[107]:= Or[p, p, p] /. Or[a_, b_] :> {a, b}
Or[p, p, p] /. Or[a_., b_.] :> {a, b}

Out[107]= {p, p || p}
Out[108]= {p, p} || p

In[109]:= Or[p, p, p]
Out[109]= p || p || p

In[112]:= Or[p, q] === Or[q, p]
Out[112]= False

This is similar to, but not exactly the same, as mathics; Pattern produces a nodef message by side effect when presented with Optional[Blank[]] patterns (syntax: _., underscore-dot) and reduces as if the patterns were not optional.

In[9]:= Or[p, p, p] /. Or[a_, b_] :> {a, b}
Out[9]= {p, p || p}

In[10]:= Or[p, p, p] /. Or[a_., b_.] :> {a, b}
Pattern::nodef: No default setting found for Or in position 1 when length is 2.
Out[10]= {p, p || p}

In[11]:= Or[p, p, p]
Out[11]= p || p || p

In[12]:= Or[p, q] === Or[q, p]
Out[12]= False

We want our or to be symmetric, so we will add Orderless. First, here is what Mathematica says, in stages, adding the attributes one at a time:

In[43]:= ClearAll[or]; SetAttributes[or, {Flat}]
or[p, p, p] /. or[a_, b_] :> {a, b}
or[p, p, p] /. or[a_., b_.] :> {a, b}

Out[44]= {or[p], or[p, p]}    <~~~ THIS WAS THE FIRST TOPIC OF THIS ISSUE
Out[45]= or[{p, p}, p]

In[40]:= ClearAll[or]; SetAttributes[or, {Flat, OneIdentity}]
or[p, p, p] /. or[a_, b_] :> {a, b}
or[p, p, p] /. or[a_., b_.] :> {a, b}

Out[41]= {p, or[p, p]}    <~~~ MATHEMATICA SAYS "ONLY IF ONEIDENTITY"
Out[42]= or[{p, p}, p]

In[46]:= ClearAll[or]; SetAttributes[or, {Flat, Orderless, OneIdentity}]
or[p, p, p] /. or[a_, b_] :> {a, b}
or[p, p, p] /. or[a_., b_.] :> {a, b}

Out[47]= {p, or[p, p]}    <~~~ THIS IS OK TOO
Out[48]= or[p, p, p]

Mathics disagrees right away:

In[13]:= ClearAll[or]; SetAttributes[or, {Flat}]
In[14]:= or[p, p, p] /. or[a_, b_] :> {a, b}
Out[14]= {p, or[p, p]}

In[15]:= or[p, p, p] /. or[a_., b_.] :> {a, b}
Pattern::nodef: No default setting found for or in position 1 when length is 2.
Out[15]= {p, or[p, p]}

In[16]:= ClearAll[or]; SetAttributes[or, {Flat, OneIdentity}]
In[17]:= or[p, p, p] /. or[a_, b_] :> {a, b}
Out[17]= {p, or[p, p]}

In[18]:= or[p, p, p] /. or[a_., b_.] :> {a, b}
Pattern::nodef: No default setting found for or in position 1 when length is 2.
Out[18]= {p, or[p, p]}

In[19]:= ClearAll[or]; SetAttributes[or, {Flat, Orderless, OneIdentity}]
In[20]:= or[p, p, p] /. or[a_, b_] :> {a, b}
Out[20]= {p, or[p, p]}

In[21]:= or[p, p, p] /. or[a_., b_.] :> {a, b}
Out[21]= {p, or[p, p]}

rebcabin avatar Sep 07 '18 15:09 rebcabin

Here is some more data about differences between Mathematica and mathics. Mathics has the correct pattern-matching behavior for Times, but doesn't let us mimic the behavior in our own symbol. I think mathics is confused about OneIdentity and implements the correct behavior for Times in some other way.

Here is what Mathematica says:

In[124]:= x /. Times[n_., x_] :> {n, x}
ClearAll[or]; SetAttributes[or, {Flat, Orderless, OneIdentity}]
Default[or] = False;
p /. or[n_., x_] :> {n, x}

Out[124]= {1, x}
Out[127]= {False, p}

and now mathics:

In[1]:= x /. Times[n_., x] :> {n, x}
Out[1]= {1, x}

In[2]:= SetAttributes[or, {Flat, Orderless, OneIdentity}]
In[3]:= Default[or] = False
In[4]:= p /. or[n_., x] :> {n, x}
Out[4]= p    <~~~ NO MATCH

rebcabin avatar Sep 07 '18 15:09 rebcabin

More data:

in both mathics
   and Mathematica,

       First[eqv[p, q, r]] === p
       Rest[eqv[p, q, r]] === eqv[q, r]]

   for all combinations of {Flat, Orderless, OneIdentity}. Proof:

   Generate all combinations as follows:

       In[66]:=
       Flatten[
         Table[
           Union[
             Sort/@Permutations[{Flat,Orderless,OneIdentity}, {i}]],
           {i,3}],
         1]
       Out[66]= {{Flat},
                 {OneIdentity},
                 {Orderless},
                 {Flat, OneIdentity},
                 {Flat, Orderless},
                 {OneIdentity, Orderless},
                 {Flat, OneIdentity, Orderless}}

   Then check as follows:

      In[67]:=
      Table[
        Module[{e = eqv[p,q,r]},    <~~~ HERE IS THE MONEY
          ClearAll[eqv];
          SetAttributes[eqv,j];
          {j, First@e, Rest@e}],
       {j, ... the above ...}]

      Out[67]= {{{Flat},                        p, eqv[q, r]},
               {{OneIdentity},                  p, eqv[q, r]},
               {{Orderless},                    p, eqv[q, r]},
               {{Flat, OneIdentity},            p, eqv[q, r]},
               {{Flat, Orderless},              p, eqv[q, r]},
               {{OneIdentity, Orderless},       p, eqv[q, r]},
               {{Flat, OneIdentity, Orderless}, p, eqv[q, r]}}

   We only get differences when we pattern-match:

       Table[
         Module[{ e = (eqv[p,q,r] /. {eqv[x_,y_] :> {x,y}}) },    <~~~ DIFF'T
           ClearAll[eqv];
           SetAttributes[eqv,j];
           {j, First@e, Rest@e}],
        {j, ... the above ...}]

   produces, in Mathematica

       {{{Flat},                        p, {eqv[q, r]}},
       {{OneIdentity},                  eqv[p], {eqv[q, r]}},
       {{Orderless},                    p, eqv[q, r]},
       {{Flat, OneIdentity},            p, eqv[q, r]},
       {{Flat, Orderless},              p, {eqv[q, r]}},
       {{OneIdentity, Orderless},       q, {eqv[p, r]}},
       {{Flat, OneIdentity, Orderless}, p, eqv[q, r]}}

   but, in mathics

       {{{Flat},                        p, {eqv[q, r]}},
       {{OneIdentity},                  p, {eqv[q, r]}},
       {{Orderless},                    p, eqv[q, r]},
       {{Flat, OneIdentity},            p, eqv[q, r]},
       {{Flat, Orderless},              p, {eqv[q, r]}},
       {{OneIdentity, Orderless},       p, {eqv[q, r]}},
       {{Flat, OneIdentity, Orderless}, p, eqv[q, r]}}

rebcabin avatar Sep 07 '18 23:09 rebcabin

I have some open questions on mathematica.stackexchange.com about this issue.

https://mathematica.stackexchange.com/questions/183322/subtle-order-of-evaluation-issues-when-pattern-matching-with-attributes

I regret that this became complicated, but I hope to get a much better understanding of the way pattern-matching is supposed to work if some people inside Wolfram look at the question. I know at least two employees (Lichtblau and Shifrin) watch that site.

EDIT

The issue has been resolved in stackexchange. Please see my answer on that site for suggested unit tests for mathics.

rebcabin avatar Oct 07 '18 19:10 rebcabin

I've started working on this again as of 11 Oct 2020. No ETA.

rebcabin avatar Oct 12 '20 03:10 rebcabin