On 10/6/10 2:15 AM, Jeroen Demeyer wrote:
On 2010-10-06 02:04, François Bissey wrote:
I would like to see the source code of
sage.matrix.matrix_modn_sparse.Matrix_modn_sparse.__eq__(), but I have
no idea where to look.

sage/matrix/matrix_modn_sparse.pyx ?
Well, I obviously looked there, but didn't find it.

More precisely, I am looking for the location of the code which compares
a Matrix_modn_sparse with a Matrix_modn_dense.  I.e. I want to see the
code for

sage: L = matrix(GF(43), 3, 3, range(9), sparse=True)
sage: R = matrix(GF(43), 3, 3, range(9))
sage: type(L)
<type 'sage.matrix.matrix_modn_sparse.Matrix_modn_sparse'>
sage: type(R)
<type 'sage.matrix.matrix_modn_dense.Matrix_modn_dense'>
sage: L==R   # Where is this implemented?
True



You can use the coercion model code to understand more about what happens [1]:

sage: cm = sage.structure.element.get_coercion_model(); cm
<sage.structure.coerce.CoercionModel_cache_maps object at 0x101fb4770>
sage: sage: L = matrix(GF(43), 3, 3, range(9), sparse=True)
sage: sage: R = matrix(GF(43), 3, 3, range(9))
sage: cm.explain(L,R,operator.eq)
Equal but distinct parents.
Coercion on right operand via
    Call morphism:
From: Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 43 To: Full MatrixSpace of 3 by 3 sparse matrices over Finite Field of size 43
Arithmetic performed after coercions.
Result lives in Full MatrixSpace of 3 by 3 sparse matrices over Finite Field of size 43
Full MatrixSpace of 3 by 3 sparse matrices over Finite Field of size 43

This says that both are converted to sparse matrices before the comparison happens. Looking at the code, we see that there is a __richcmp__ (the cython equivalent of __cmp__ [2]) in the matrix_modn_sparse.pyx file:

def __richcmp__(matrix.Matrix self, right, int op): # always need for mysterious reasons.
        return self._richcmp(right, op)

This is not defined in the matrix/ directory:

~/sage/devel/sage/sage/matrix% grep "def _richcmp[^_]" *.py*
         ~/sage/devel/sage/sage/matrix%

So we go up the chain and look in structure/ directory, and find it is defined in devel/sage/sage/structure/element.pyx in the Element class, which ends with:

        return left._richcmp_c_impl(right, op)

and we also see a comment following the function:

    ####################################################################
    # For a derived Cython class, you **must** put the following in
    # your subclasses, in order for it to take advantage of the
    # above generic comparison code.  You must also define
    # either _cmp_c_impl (if your subclass is totally ordered),
    # _richcmp_c_impl (if your subclass is partially ordered), or both
    # (if your class has both a total order and a partial order;
    # then the total order will be available with cmp(), and the partial
    # order will be available with the relation operators; in this case
    # you must also define __cmp__ in your subclass).
    # This is simply how Python works.


The _richcmp_c_impl is not defined in the matrix/ directory, so it must also be inherited. Indeed, the _richcmp_c_impl method just below the comment above is:

    cdef _richcmp_c_impl(left, Element right, int op):
        if (<Element>left)._richcmp_c_impl == Element._richcmp_c_impl and \
               (<Element>left)._cmp_c_impl == Element._cmp_c_impl:
            # Not implemented, try some basic defaults
            if op == Py_EQ:
                 return left is right
            elif op == Py_NE:
                return left is not right
        return left._rich_to_bool(op, left._cmp_c_impl(right))

so the _cmp_c_impl is called. So we need to go back down and see where _cmp_c_impl. That's back in matrix/matrix_sparse.pyx:

    cdef int _cmp_c_impl(self, Element right) except -2:
        return cmp(self._dict(), right._dict())


So in the end, the dictionaries are compared, after the dense matrix is coerced to a sparse matrix.


As for the "mysteriously needed" reason? Cython calls __richcmp__, which needs to call the coercion framework _richcmp function. I guess the mysterious part is that __richcmp__ is not inherited. Well, according to the Python/C API docs [3], either all of __hash__, __cmp__, and __richcmp__ are inherited, or none are. So I guess we need the __richcmp__ function copied into the matrix/*.pyx files because __hash__ is redefined, so __richcmp__ isn't inherited.

At least, that's how I see it.

Thanks,

Jason



[1] http://www.sagemath.org/doc/reference/coercion.html, specifically http://www.sagemath.org/doc/reference/coercion.html#basic-arithmetic-rules

[2] http://docs.cython.org/src/userguide/special_methods.html#rich-comparisons

[3] see the thread http://www.mail-archive.com/[email protected]/msg07374.html or the docs http://docs.python.org/c-api/typeobj.html?highlight=__hash__#tp_richcompare

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

Reply via email to