Sanity check on objects, parents and elements

14 views
Skip to first unread message

Florent Hivert

unread,
Feb 26, 2010, 3:59:17 PM2/26/10
to Sage Devel
Hi there,

In order to sanitize the behavior of objects, parents and elements in sage,
I'm about to add some tests to the framework. I think they are all reasonable
but I may be asking to much. Please comment about the following:

1 - Any SageObject must have an equality methods such that
self == self and self != None

2 - Element construction should be idempotent. More precisely, for any
element e within parent P, the equality P(e) == e must hold.
This is for example needed by the constructor of many Parent with a base
ring, such as matrices.

3 - .zero() and .one() must never be mutable. I'd like to enforce this by
computing .__hash__(). Here I see two possibilities:
* decide that for any monoid M, if M.one() implement __hash__ it must
returns an int (not an Integer) and not raise an exception like in
sage: m = matrix([1]).__hash__()
...
TypeError: mutable matrices are unhashable

* decide that any element of a monoid must be hashable.

Do you think this is asking too much ?

By the way, if you have ideas of other basic sanity checks which must be added
please tell me.

Cheers,

Florent, back from Sage days 20.

PS: Sage days 20 were extremely fun and enjoyable ! Though we worked quite
hard (I typically went to bed at 3 and woke up at 8), we haven't been
producing that much code but I think we recruited a *lot* of new users among
which there may be many developers. A progress report is being written and
should be posted very soon. I need some rest now :-)

Florent Hivert

unread,
Feb 26, 2010, 4:13:20 PM2/26/10
to Sage Devel
Hi

Sorry for replying to myself.

> In order to sanitize the behavior of objects, parents and elements in sage,
> I'm about to add some tests to the framework. I think they are all reasonable
> but I may be asking to much. Please comment about the following:
>
> 1 - Any SageObject must have an equality methods such that
> self == self and self != None
>
> 2 - Element construction should be idempotent. More precisely, for any
> element e within parent P, the equality P(e) == e must hold.
> This is for example needed by the constructor of many Parent with a base
> ring, such as matrices.
>
> 3 - .zero() and .one() must never be mutable. I'd like to enforce this by
> computing .__hash__(). Here I see two possibilities:
> * decide that for any monoid M, if M.one() implement __hash__ it must
> returns an int (not an Integer) and not raise an exception like in
> sage: m = matrix([1]).__hash__()
> ...
> TypeError: mutable matrices are unhashable

The following sentence is ambiguous:


> * decide that any element of a monoid must be hashable.

Please replace it by:

* decide that element of any monoid must have a __hash__ method,
which may raise an error for mutable element but never on .one()
(and .zero() in a commutative monoid).

David Roe

unread,
Feb 26, 2010, 4:16:05 PM2/26/10
to sage-...@googlegroups.com
On Fri, Feb 26, 2010 at 3:59 PM, Florent Hivert <florent...@univ-rouen.fr> wrote:
      Hi there,

In order to sanitize the behavior of objects, parents and elements in sage,
I'm about to add some tests to the framework. I think they are all reasonable
but I may be asking to much. Please comment about the following:

 1 - Any SageObject must have an equality methods such that
     self == self and self != None

I don't think this should be true for an arbitrary SageObject.  There are some (eg subclasses of sage.factory.UniqueFactory) that are mainly used either internally or as object constructors.  I don't think we need to support GF == GF for example.

Requiring it for Parents and Elements seems completely reasonable on the other hand.  Perhaps CategoryObjects as well.
 
  2 - Element construction should be idempotent. More precisely, for any
    element e within parent P, the equality P(e) == e must hold.
    This is for example needed by the constructor of many Parent with a base
    ring, such as matrices.

+1
 
  3 - .zero() and .one() must never be mutable. I'd like to enforce this by
    computing .__hash__(). Here I see two possibilities:
       * decide that for any monoid M, if M.one() implement __hash__ it must
         returns an int (not an Integer) and not raise an exception like in
         sage: m = matrix([1]).__hash__()
         ...
         TypeError: mutable matrices are unhashable

I have no problem with requiring an int return value, though I'm not sure  how compliant the current sage library is with this condition.  I don't like the requirement that __hash__ never raise an error: we want to allow mutable matrices.
 
       * decide that element of any monoid must have a __hash__ method,
          which may raise an error for mutable element but never on .one()
          (and .zero() in a commutative monoid).

This seems like the better option.  Though some have raised objections to the idea of zero() and one() returning immutable elements, it did seem to be the consensus in that thread.

David

John Cremona

unread,
Feb 26, 2010, 4:23:36 PM2/26/10
to sage-...@googlegroups.com
Quick question: many types have methods one_element() and
zero_element() which are used a lot. For example, ZZ.one() and
ZZ.zero() are aliases for ZZ.one_element() and ZZ.zero_element(). Is
your intention to deprecate these longer names?

John

> --
> To post to this group, send an email to sage-...@googlegroups.com
> To unsubscribe from this group, send an email to
> sage-devel+...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/sage-devel
> URL: http://www.sagemath.org
>

Nick Alexander

unread,
Feb 26, 2010, 4:24:44 PM2/26/10
to sage-...@googlegroups.com

On 26-Feb-10, at 12:59 PM, Florent Hivert wrote:

> Hi there,
>
> In order to sanitize the behavior of objects, parents and elements
> in sage,
> I'm about to add some tests to the framework. I think they are all
> reasonable
> but I may be asking to much.

I think your suggestions are reasonable, and am a strong +1 to these
automated testing frameworks.

Nick

Florent Hivert

unread,
Feb 26, 2010, 4:38:20 PM2/26/10
to sage-...@googlegroups.com
Hi David,

> > In order to sanitize the behavior of objects, parents and elements in sage,
> > I'm about to add some tests to the framework. I think they are all
> > reasonable
> > but I may be asking to much. Please comment about the following:
> >
> > 1 - Any SageObject must have an equality methods such that
> > self == self and self != None
> >
>
> I don't think this should be true for an arbitrary SageObject. There are
> some (eg subclasses of sage.factory.UniqueFactory) that are mainly used
> either internally or as object constructors. I don't think we need to
> support GF == GF for example.
>
> Requiring it for Parents and Elements seems completely reasonable on the
> other hand. Perhaps CategoryObjects as well.

Ok This is easy to change...

[...]

> > 3 - .zero() and .one() must never be mutable. I'd like to enforce this by
> > computing .__hash__(). Here I see two possibilities:


> > * decide that for any monoid M, if M.one() implement __hash__ it
> > must
> > returns an int (not an Integer) and not raise an exception like in
> > sage: m = matrix([1]).__hash__()
> > ...
> > TypeError: mutable matrices are unhashable
> >
>
> I have no problem with requiring an int return value, though I'm not sure
> how compliant the current sage library is with this condition.

The best way to know is to check (in progress). I'm expecting to catch some
bugs with it. I'll see how hard it is to fix the lib accordingly. The
framework allows to make case by case exception. Anyway, it's certainly good to
know.

> I don't like the requirement that __hash__ never raise an error: we want to
> allow mutable matrices.

Of course I'm not forgetting mutable matrices. Let me restate. Please choose
between (or suggest something else):

Strong requirement:
For any Monoid. __hash__ *must* be implemented and it don't raise an error for
.one() (Idem for CommutativeMonoids and zero())

Weak requirement:
For any monoid M, if M.one() implement __hash__ it must returns an int.

> This seems like the better option. Though some have raised objections to
> the idea of zero() and one() returning immutable elements, it did seem to be
> the consensus in that thread.

So I understand that you'll go for strong requirement.

I'm sorry for not making myself clear it the first mail. I'm not accustomed in
writing computer-law statement like SEP (Sage Enhancement Proposal :-)

Cheers,

Florent

Florent Hivert

unread,
Feb 26, 2010, 4:46:15 PM2/26/10
to sage-...@googlegroups.com
> Quick question: many types have methods one_element() and
> zero_element() which are used a lot. For example, ZZ.one() and
> ZZ.zero() are aliases for ZZ.one_element() and ZZ.zero_element(). Is
> your intention to deprecate these longer names?

I had the impression that this has been already decided see eg [1]:

def one_element(self):
r"""
Backward compatibility alias for :meth:`self.one()`.

TESTS::

sage: S = Monoids().example()
sage: S.one_element()
''

"""
return self.one()

Though I can't find the thread. Also, In the category roadmap [2]:

A.one() A.zero() a.is_one() a.is_zero() A(1) A(0) when it makes sense
A.one_element() A.zero_element() deprecated in the doc; fully deprecated
later.

Cheers,

Florent

[1] sage/categories/monoids.py

[2] http://trac.sagemath.org/sage_trac/wiki/CategoriesRoadMap

Robert Bradshaw

unread,
Feb 26, 2010, 7:46:25 PM2/26/10
to sage-...@googlegroups.com
On Feb 26, 2010, at 12:59 PM, Florent Hivert wrote:

> Hi there,
>
> In order to sanitize the behavior of objects, parents and elements
> in sage,
> I'm about to add some tests to the framework. I think they are all
> reasonable
> but I may be asking to much. Please comment about the following:
>
> 1 - Any SageObject must have an equality methods such that
> self == self and self != None
>
> 2 - Element construction should be idempotent. More precisely, for any
> element e within parent P, the equality P(e) == e must hold.
> This is for example needed by the constructor of many Parent
> with a base
> ring, such as matrices.

Elements of real interval fields don't satisfy the above two
constraints (the notion of equality for intervals being that every
element of the first interval is equal to every element in the second).

- Robert

Florent Hivert

unread,
Feb 27, 2010, 6:14:17 AM2/27/10
to sage-...@googlegroups.com
Hi Robert,

You get the point. As far as I understand a RIF only return True if the
interval are reduced to a single point. Is it right ? It would be better to
return a special value like Unknown than False. But that's another question...

Anyway, I'm strongly info favor of giving 1- and 2- a general rule with an
explicit exception for RIF. As I said, the test framework allows such
exceptions. Is there an agreement on

* Making 1- and 2- below a general rule

>> 1 - Any SageObject must have an equality methods such that
>> self == self and self != None
>>
>> 2 - Element construction should be idempotent. More precisely, for any
>> element e within parent P, the equality P(e) == e must hold.

* Making explicit exception for particular cases such as RIF.

Cheers,

Florent

David Roe

unread,
Feb 27, 2010, 12:00:03 PM2/27/10
to sage-...@googlegroups.com
I agree, subject to changing "SageObject" in 1 to "Element and CategoryObject".
David

Robert Bradshaw

unread,
Feb 27, 2010, 1:41:01 PM2/27/10
to sage-...@googlegroups.com

I certainly agree that 1-2 should be the general rule, I was just
pointing out an exception. I like the idea of returning an Unknown
object on RIF comparisons as well.

- Robert

Florent Hivert

unread,
Feb 28, 2010, 11:46:57 AM2/28/10
to sage-...@googlegroups.com
> I certainly agree that 1-2 should be the general rule, I was just pointing
> out an exception. I like the idea of returning an Unknown object on RIF
> comparisons as well.

This is now #8402 (work in progress).

Florent

Florent Hivert

unread,
Feb 27, 2010, 5:22:13 PM2/27/10
to sage-...@googlegroups.com
Hi Robert,

>> You get the point. As far as I understand a RIF only return True if the
>> interval are reduced to a single point. Is it right ? It would be better
>> to
>> return a special value like Unknown than False. But that's another
>> question...
>>

>> [...]


>
> I certainly agree that 1-2 should be the general rule, I was just pointing
> out an exception. I like the idea of returning an Unknown object on RIF
> comparisons as well.

Not my idea. This was the way it worked in MuPAD. There was a three state
boolean value, which was quite useful. Looking into python docs to see if we
can have "and" and "or" work with a 3-state booleans, I found:

" A rich comparison method may return the singleton NotImplemented if it does
not implement the operation for a given pair of arguments. By convention,
False and True are returned for a successful comparison. However, these
methods can return any value, so if the comparison operator is used in a
Boolean context (e.g., in the condition of an if statement), Python will call
bool() on the value to determine if the result is true or false. "

Wouldn't it be mor meaningful to return NotImplemented ?

Cheers,

Florent

Robert Bradshaw

unread,
Mar 1, 2010, 2:13:21 PM3/1/10
to sage-...@googlegroups.com

We do this to get symbolic equations.

> Wouldn't it be mor meaningful to return NotImplemented ?

No. If a.__richcmp__(b) returns NotImplemented then b.__richcmp__(a)
is attempted (basically). Also, the value is truly unkownable, so
Unknowns is the right thing to return here.

- Robert

Florent Hivert

unread,
Mar 1, 2010, 4:22:10 PM3/1/10
to sage-...@googlegroups.com
>> Not my idea. This was the way it worked in MuPAD. There was a three state
>> boolean value, which was quite useful. Looking into python docs to see if
>> we
>> can have "and" and "or" work with a 3-state booleans, I found:
>>
>> " A rich comparison method may return the singleton NotImplemented if it
>> does
>> not implement the operation for a given pair of arguments. By convention,
>> False and True are returned for a successful comparison. However, these
>> methods can return any value, so if the comparison operator is used in a
>> Boolean context (e.g., in the condition of an if statement), Python will
>> call
>> bool() on the value to determine if the result is true or false. "
>
> We do this to get symbolic equations.
>
>> Wouldn't it be mor meaningful to return NotImplemented ?
>
> No. If a.__richcmp__(b) returns NotImplemented then b.__richcmp__(a) is
> attempted (basically). Also, the value is truly unkownable, so Unknowns is
> the right thing to return here.

One of the main problem here is that PEP 335 "Overloadable Boolean Operators"
is not yet accepted. So right now there is no way to implement a three state
logic, is there one ? If not, Is there a way we can push on python dev to have
this PEP accepted ?

Cheers,

Florent

Robert Bradshaw

unread,
Mar 1, 2010, 5:20:04 PM3/1/10
to sage-...@googlegroups.com

It's unclear how this would work with the current short-circuit
semantics of and and or. However, if Unknown was returned such that
bool(Unknown) was False, then the information would be preserved at
least a little bit longer.

- Robert


Florent Hivert

unread,
Mar 1, 2010, 5:43:52 PM3/1/10
to sage-...@googlegroups.com
Hi Robert,

>> One of the main problem here is that PEP 335 "Overloadable Boolean
>> Operators"
>> is not yet accepted. So right now there is no way to implement a three
>> state
>> logic, is there one ? If not, Is there a way we can push on python dev to
>> have
>> this PEP accepted ?
>
> It's unclear how this would work with the current short-circuit semantics
> of and and or.


Here are the truth table in MuPAD, all computed in short-circuit semantics.

>> bool3 := [FALSE, UNKNOWN, TRUE];
>> print ([(a and b) $ b in bool3]) $ a in bool3

[FALSE, FALSE, FALSE]

[FALSE, UNKNOWN, UNKNOWN]

[FALSE, UNKNOWN, TRUE]

>> print ([(a or b) $ b in bool3]) $ a in bool3

[FALSE, UNKNOWN, TRUE]

[UNKNOWN, UNKNOWN, TRUE]

[TRUE, TRUE, TRUE]

To have a precise short-circuit semantic. Consider that
False < Unknown < True
and define "and" = "min" and "or" = "max". The natural short-circuit
evaluation for min and max works as expected.

in preudocode:

if a is False: return False
else if a <= b: return a
else: return b

in preudocode:

if a is True : return True
else if a >= b: return a
else: return b

> However, if Unknown was returned such that bool(Unknown) was False, then the
> information would be preserved at least a little bit longer.

This could indeed be a fallback.

Cheers,

Florent

Florent Hivert

unread,
Mar 2, 2010, 6:12:24 AM3/2/10
to sage-...@googlegroups.com
> It's unclear how this would work with the current short-circuit semantics
> of and and or. However, if Unknown was returned such that bool(Unknown) was
> False, then the information would be preserved at least a little bit
> longer.

Please review my proposal on #8413.

Cheers,

Florent

Nicolas M. Thiery

unread,
Mar 3, 2010, 8:20:50 AM3/3/10
to sage-...@googlegroups.com
On Fri, Feb 26, 2010 at 10:46:15PM +0100, Florent hivert wrote:
> > Quick question: many types have methods one_element() and
> > zero_element() which are used a lot. For example, ZZ.one() and
> > ZZ.zero() are aliases for ZZ.one_element() and ZZ.zero_element(). Is
> > your intention to deprecate these longer names?
>
> I had the impression that this has been already decided see eg [1]:
>
> def one_element(self):
> r"""
> Backward compatibility alias for :meth:`self.one()`.
>
> TESTS::
>
> sage: S = Monoids().example()
> sage: S.one_element()
> ''
>
> """
> return self.one()
>
> Though I can't find the thread. Also, In the category roadmap [2]:
>
> A.one() A.zero() a.is_one() a.is_zero() A(1) A(0) when it makes sense
> A.one_element() A.zero_element() deprecated in the doc; fully deprecated
> later.

Yup. This had been decided after a poll with the developers at Sage
days 15 in Seattle.

Cheers,
Nicolas
--
Nicolas M. Thi�ry "Isil" <nth...@users.sf.net>
http://Nicolas.Thiery.name/

Reply all
Reply to author
Forward
0 new messages