Opened 2 years ago

Last modified 2 years ago

#24255 new defect

Warn with substitution of derived classes using equality

Reported by: rws Owned by:
Priority: major Milestone: sage-8.2
Component: symbolics Keywords:
Cc: Merged in:
Authors: Reviewers:
Report Upstream: N/A Work issues:
Branch: Commit:
Dependencies: Stopgaps:

Description (last modified by rws)

There is a long-standing problem with substitution in that some substitutions do not work when given as equality but do work when given as dictionary.

sage: (x*pi).subs(x*pi==e)
x*pi
sage: (x*pi).subs({x*pi:e})
e

sage: s = x.series(x,2); s
1*x + Order(x^2)
sage: (x*pi).subs(x*pi==s)
pi*x

It looks like that in some cases the conversion to dict switches lhs and rhs of the equation and this is because of such behaviour:

sage: x*pi==e
e == pi*x
sage: x == s
1*x + Order(x^2) == x

Change History (15)

comment:1 Changed 2 years ago by rws

  • Description modified (diff)

comment:2 Changed 2 years ago by rws

  • Description modified (diff)
  • Summary changed from Fix substitution with pattern given as equation to Fix constants coercion into SR

comment:3 Changed 2 years ago by rws

  • Description modified (diff)
  • Summary changed from Fix constants coercion into SR to Fix constant e coercion into SR

comment:4 Changed 2 years ago by rws

  • Description modified (diff)
  • Summary changed from Fix constant e coercion into SR to Fix Expression subclasses coercion into SR

comment:5 Changed 2 years ago by rws

  • Description modified (diff)

comment:6 Changed 2 years ago by rws

  • Description modified (diff)

comment:7 follow-up: Changed 2 years ago by rws

class A(SageObject):
    def __init__(self):
        from sage.symbolic.ring import SR
        self._parent = SR
    def __eq__(a, b):
        print(a, b)
        return True
    def __repr__(self):
        return "A"

class B(A):
    def __eq__(a, b):
        print(a, b)
        return True
    def __repr__(self):
        return "B"

sage: from sage.symbolic.tests import A,B
sage: A() == B()
(B, A)
True

versus

class A():
    def __init__(self):
        from sage.symbolic.ring import SR
        self._parent = SR
    def __eq__(a, b):
        print(a, b)
        return True
    def __repr__(self):
        return "A"

class B(A):
    def __eq__(a, b):
        print(a, b)
        return True
    def __repr__(self):
        return "B"

sage: from sage.symbolic.tests import A,B
sage: A() == B()
(A, B)
True

The first has A inherit from SageObject the second not.

Last edited 2 years ago by rws (previous) (diff)

comment:8 Changed 2 years ago by rws

Since we cannot use the @richcmp decorator on a cdef class we cannot work around it but must investigate the reason for the argument switching, which is possibly in Cython.

Last edited 2 years ago by rws (previous) (diff)

comment:9 Changed 2 years ago by rws

Adding __eq__ does not work I get a Cython compile error reported at https://github.com/cython/cython/issues/2019

comment:10 Changed 2 years ago by rws

  • Description modified (diff)

comment:11 in reply to: ↑ 7 Changed 2 years ago by jdemeyer

Replying to rws:

class A(SageObject):
    def __init__(self):
        from sage.symbolic.ring import SR
        self._parent = SR
    def __eq__(a, b):
        print(a, b)
        return True
    def __repr__(self):
        return "A"

class B(A):
    def __eq__(a, b):
        print(a, b)
        return True
    def __repr__(self):
        return "B"

sage: from sage.symbolic.tests import A,B
sage: A() == B()
(B, A)
True

versus

class A():
    def __init__(self):
        from sage.symbolic.ring import SR
        self._parent = SR
    def __eq__(a, b):
        print(a, b)
        return True
    def __repr__(self):
        return "A"

class B(A):
    def __eq__(a, b):
        print(a, b)
        return True
    def __repr__(self):
        return "B"

sage: from sage.symbolic.tests import A,B
sage: A() == B()
(A, B)
True

I think this is just a difference between new-style and old-style classes. This has nothing to do with Cython.

comment:12 Changed 2 years ago by jdemeyer

Without any Cython classes involved:

sage: class A(object):
....:     def __eq__(a, b):
....:         print(a, b)
....:         return True
....:     def __repr__(self):
....:         return "A"
....: 
....: class B(A):
....:     def __eq__(a, b):
....:         print(a, b)
....:         return True
....:     def __repr__(self):
....:         return "B"
sage: A() == B()
(B, A)
True

comment:13 follow-up: Changed 2 years ago by rws

Why then would class A(): not switch arguments? Anyway, this ticket either should warn if subclasses are encountered or at least add explanations to the documentation.

comment:14 in reply to: ↑ 13 Changed 2 years ago by rws

Replying to rws:

Why then would class A(): not switch arguments? Anyway, this ticket either should warn if subclasses are encountered or at least add explanations to the documentation.

Okay the answer is https://stackoverflow.com/questions/54867/what-is-the-difference-between-old-style-and-new-style-classes-in-python and Python is a PITA.

comment:15 Changed 2 years ago by rws

  • Summary changed from Fix Expression subclasses coercion into SR to Warn with substitution of derived classes using equality
Note: See TracTickets for help on using tickets.