How to test that something is a morphism in a category

33 views
Skip to first unread message

Simon King

unread,
Dec 29, 2010, 10:53:25 AM12/29/10
to sage-algebra
Hi!

Let C be a category. If I understand correctly, "A in C" tells whether
A is an object of C. But how does one test whether A is a morphism in
C?

Currently, we have
sage: R.<x,y,z> = ZZ[]
sage: f = R.hom([x^2,y^2,z^2])
sage: C = R.category()
sage: f in C
False
sage: f in C.hom_category()
True

It seems to me that the last line is not correct (and perhaps I have
to apologize, because it is possible that I was responsible for it -
but I'm not sure). Namely, the objects of C.hom_category() are homsets
of C - f is *contained* in a homset of C, but it is of course not a
homset, itself.

What should be the "official" way to test whether f is a morphism in
C? Of course, in that particular example, one has f.category_for(),
but I think in general one should better test
parent(f) in C.hom_category()
, right?

I ask for the following reason:

An group action of G on a set S can be thought of as a morphism from
Groupoid(G) to Sets(). An element g of G is a morphism in the category
Groupoid(G), but of course there is no attribute g.category_for(). But
"parent(g) in Groupoid(G).hom_category()" (which is not implemented,
yet) would make sense, wouldn't it?

Cheers,
Simon

Nicolas M. Thiery

unread,
Dec 29, 2010, 4:59:17 PM12/29/10
to sage-a...@googlegroups.com
Hi Simon!

On Wed, Dec 29, 2010 at 07:53:25AM -0800, Simon King wrote:
> Let C be a category. If I understand correctly, "A in C" tells
> whether A is an object of C.

Yep.

> But how does one test whether A is a morphism in C?

As far as I know, no idiom has been yet chosen for this feature. There
should be one.

> Currently, we have
> sage: R.<x,y,z> = ZZ[]
> sage: f = R.hom([x^2,y^2,z^2])
> sage: C = R.category()
> sage: f in C
> False
> sage: f in C.hom_category()
> True
>
> It seems to me that the last line is not correct (and perhaps I have
> to apologize, because it is possible that I was responsible for it -
> but I'm not sure). Namely, the objects of C.hom_category() are homsets
> of C - f is *contained* in a homset of C, but it is of course not a
> homset, itself.

Agreed.

> What should be the "official" way to test whether f is a morphism in
> C?

Maybe ``f in C.morphisms()'' ? I'll use it in the examples below, but
that's just a suggestion among others.


> Of course, in that particular example, one has f.category_for(),
> but I think in general one should better test
> parent(f) in C.hom_category()
> , right?

I am not sure. Assume f is a QQ-algebra morphism. Then, we should have:

sage: f in Algebras(QQ).morphisms()
True

We could also choose to have:

sage: f in VectorSpaces(QQ).morphisms()
True

which is both natural and possibly practical (though this means
implicitly applying a forgetful functor).

But we can't go this way using ``parent(f)``:

sage: parent(f)
Homsets from A to B in category of Algebras(QQ)

since the later is certainly not a homset in the category of
VectorSpaces(QQ) (e.g. the sum of two algebras morphisms is not an
algebra morphism).

By the way: recall that, as advertised in the category roadmap, the
hierarchy of categories for homsets is currently mathematically
incorrect (Hom is not a covariant functionrial construction as is
currently implemented). A typical consequence is:

sage: V = SymmetricFunctions(QQ) # An algebra(QQ)
sage: H = Hom(V,V)
sage: H.category()
Join of Category of hom sets in Category of modules over Rational Field and Category of hom sets in Category of rings

I take the blame for that. And this probably needs to be fixed before
further serious cleanup around homsets.


> I ask for the following reason:
>
> An group action of G on a set S can be thought of as a morphism from
> Groupoid(G) to Sets(). An element g of G is a morphism in the category
> Groupoid(G), but of course there is no attribute g.category_for(). But
> "parent(g) in Groupoid(G).hom_category()" (which is not implemented,
> yet) would make sense, wouldn't it?

Hmm, it's too late for me to have a clear idea right now :-)

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

Simon King

unread,
Dec 29, 2010, 8:49:07 PM12/29/10
to sage-algebra
Hi Nicolas!

On 29 Dez., 22:59, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> > What should be the "official" way to test whether f is a morphism in
> > C?
>
> Maybe ``f in C.morphisms()'' ? I'll use it in the examples below, but
> that's just a suggestion among others.

The disadvantage would be that one needed to implement yet another
parent structure for C.morphisms(), namely the union of all homsets of
C.

What about ``C.is_morphism(f)", and for the sake of symmetry also
``C.is_object(A)" as a synonyme for "A in C"?

> By the way: recall that, as advertised in the category roadmap, the
> hierarchy of categories for homsets is currently mathematically
> incorrect ... And this probably needs to be fixed before
> further serious cleanup around homsets.

I do think that implementing a framework for "containment in a
homset" (aka "is f a morphism in C") is possible and worth-while even
without a thorough cleanup: For "proper" morphisms, we have the
method "f.category_for()", so that the functionality
"C.is_morphism(f)" (or whatever syntax) is easy to implement on top of
it - and it should then also be easy to treat the special case
"C=Groupoid(G)", like
{{{
def is_morphism(self, f):
return f in self.__G
}}}
for Groupoids, and generically
{{{
def is_morphism(self, f):
try:
C = f.category_for()
except AttributeError:
return False
return C.is_subcategory(self)
}}}

Cheers,
Simon

Niles

unread,
Dec 30, 2010, 10:33:49 AM12/30/10
to sage-algebra
my 2 cents:

On Dec 29, 8:49 pm, Simon King <simon.k...@uni-jena.de> wrote:

> > > What should be the "official" way to test whether f is a morphism in
> > > C?

I think "f in C" should return True, and that this should be the
"official" way of testing: it has the advantages of being compatible
with the way sage tests membership of other things, and being
mathematically accurate.

> What about ``C.is_morphism(f)", and for the sake of symmetry also
> ``C.is_object(A)" as a synonyme for "A in C"?

This was my second thought, and I think both is_morphism and is_object
should be added, they should test for membership, and test
additionally whether the input is an object or a morphism (e.g., for
morphisms, test for existence of domain & codomain methods, and
whether they return objects of the category). Perhaps the cleanest
way to code would be to move the bulk of the code for "A in C" to a
new "is_object" method, write an "is_morphism" method, and then have
"x in C" return the truth value of "C.is_object(x) or
C.is_morphism(x)".

Also, should "C.is_morphism" be "C.has_morphism" (and similarly for
"is_object") instead? In most of the rest of sage, the .is_ methods
take no argument and apply to "self" (e.g. is_finite, is_commutative,
is_noetherian, etc.). Perhaps "C.contains_morphism" is clearest of
all . . . other ideas?

Although I'm in favor of the categories cleanup, this could, I think,
be independent of it.

best,
Niles

Simon King

unread,
Dec 30, 2010, 11:30:43 AM12/30/10
to sage-algebra
Hi Niles!

On 30 Dez., 16:33, Niles <nil...@gmail.com> wrote:
> my 2 cents:
>
> On Dec 29, 8:49 pm, Simon King <simon.k...@uni-jena.de> wrote:
>
> > > > What should be the "official" way to test whether f is a morphism in
> > > > C?
>
> I think "f in C" should return True, and that this should be the
> "official" way of testing:  it has the advantages of being compatible
> with the way sage tests membership of other things, and being
> mathematically accurate.

I wouldn't agree here.

For example, 1 is an element of ZZ and ZZ is an object in Rings(), but
certainly we only want "ZZ in Rings()", not "1 in Rings()". And
similarly, a ring homomorphism f from ZZ to QQ['x'] is contained in
Hom(ZZ,QQ['x']), which is a homset in Rings() (or an object in
Rings().hom_category()) -- so, we may consider to have
"Hom(ZZ,QQ['x']) in Rings()" (however, I wouldn't like that!), but
certainly we don't want f in Rings().

> Also, should "C.is_morphism" be "C.has_morphism" (and similarly for
> "is_object") instead?  In most of the rest of sage, the .is_ methods
> take no argument and apply to "self" (e.g. is_finite, is_commutative,
> is_noetherian, etc.).  Perhaps "C.contains_morphism" is clearest of
> all . . . other ideas?

Yes, C.has_morphism(f) sounds better than C.is_morphism(f) (also from
a grammatical point of view). And it is shorter than
C.contains_morphism(f).

So, I am "+1" to C.has_morphism(f)

> Although I'm in favor of the categories cleanup, this could, I think,
> be independent of it.

Yes, I think the functionality should be fairly straight forward to
implement, regardless of category cleanup.

I am already doing doctests, so, probably I'll soon open a ticket.

Cheers,
Simon

Simon King

unread,
Dec 30, 2010, 1:42:33 PM12/30/10
to sage-algebra
On 30 Dez., 17:30, Simon King <simon.k...@uni-jena.de> wrote:
> I am already doing doctests, so, probably I'll soon open a ticket.

Before I do so, I have some questions.

Currently, it is sage.categories.map.Map that implements the method
"category_for" -- which is rather strange for a *map* that is not
necesserily a morphism.

In sage.categories.morphism, I read
def category(self):
return self.parent().category() # Shouln't it be Category of
elements of ...?

I ask:
* Shouldn't Morphism.category() do what Map.category_for() currently
does? Shouldn't Morphism.category() be replaced by the current
Map.category_for()?

* Should Map really be derived from Element, should it really be an
element of a Homset? Or should this all be moved to Morphism? It seems
to me that Map should only inherit from object, whereas Morphism
should inherit from both Map and Element The problem is that, as far
as I know, there is no double inheritance for a "cdef class".

However, I think that (if you answer the above affirmatively) it would
not be good to have one huge ticket doing all of that simultaneously.
I'd prefer to have a small ticket providing "containment test for
morphisms", a medium sized ticket for moving category()/
category_for(), and a big ticket that changes the inheritance of Map
and Morphism.

Or do you think that one huge ticket is better than three not so huge?

Best regards,
Simon

Nicolas M. Thiery

unread,
Dec 31, 2010, 7:36:55 AM12/31/10
to sage-a...@googlegroups.com
Hi Simon!

On Thu, Dec 30, 2010 at 10:42:33AM -0800, Simon King wrote:
> Currently, it is sage.categories.map.Map that implements the method
> "category_for" -- which is rather strange for a *map* that is not
> necesserily a morphism.
>
> In sage.categories.morphism, I read
> def category(self):
> return self.parent().category() # Shouln't it be Category of
> elements of ...?
>
> I ask:
> * Shouldn't Morphism.category() do what Map.category_for() currently
> does? Shouldn't Morphism.category() be replaced by the current
> Map.category_for()?

I wrote that comment :-)

I don't know if Morphism.category is actually used anywhere, so we
probably can change its semantic. I don't like the current semantic of
Morphism.category because it is incompatible with the semantic of
.category() for elements. I think it should be something like:

sage: f = Hom(QQ,QQ)(1)
sage: f.category()
Category of elements of Set of Homomorphisms from Rational Field to Rational Field

or maybe something more specific:

sage: f.category()
Category of morphisms in Category of rings

Note that:

sage: f.category()
Category of rings

would also be incompatible with the semantic of .category() for
elements. This is the reason why I added an extra ``category_for''
method. And I take the blame for not finding some more suitable name;
better suggestions welcome!


A bit of fuel for the discussion: the following assertion should
always be verified, for any Sage object:

sage: f in f.category()
True

(see the last assert in sage.structure.sage_object.SageObject._test_category)


> * Should Map really be derived from Element, should it really be an
> element of a Homset? Or should this all be moved to Morphism? It seems
> to me that Map should only inherit from object, whereas Morphism
> should inherit from both Map and Element The problem is that, as far
> as I know, there is no double inheritance for a "cdef class".

I am not sure what the exact intention was for having two distinct
concepts of Maps and Morphisms, so I can't really help. But what you
say sounds right. We should get feedback from the original authors
(Robert, ...).

> However, I think that (if you answer the above affirmatively) it would
> not be good to have one huge ticket doing all of that simultaneously.
> I'd prefer to have a small ticket providing "containment test for
> morphisms", a medium sized ticket for moving category()/
> category_for(), and a big ticket that changes the inheritance of Map
> and Morphism.

Yes, if you can naturally split the work in several independent
tickets, go for it!

Nicolas M. Thiery

unread,
Dec 31, 2010, 7:39:41 AM12/31/10
to sage-a...@googlegroups.com
On Thu, Dec 30, 2010 at 08:30:43AM -0800, Simon King wrote:
> > I think "f in C" should return True, and that this should be the
> > "official" way of testing: �it has the advantages of being compatible
> > with the way sage tests membership of other things, and being
> > mathematically accurate.
>
> I wouldn't agree here.

+1

> Yes, C.has_morphism(f) sounds better than C.is_morphism(f) (also from
> a grammatical point of view). And it is shorter than
> C.contains_morphism(f).
>
> So, I am "+1" to C.has_morphism(f)

+1.

What about the following variant:

sage: f.is_morphism(category = C)

I guess it depends whether the actual code doing the test depends more
on the kind of morphism or on the category.

> > Although I'm in favor of the categories cleanup, this could, I think,
> > be independent of it.
>
> Yes, I think the functionality should be fairly straight forward to
> implement, regardless of category cleanup.

Good news!

Happy new year!

Simon King

unread,
Dec 31, 2010, 8:57:25 AM12/31/10
to sage-algebra
Hi Nicolas,

On 31 Dez., 13:36, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> I wrote that comment :-)
>
> I don't know if Morphism.category is actually used anywhere, so we
> probably can change its semantic.

I know that it would be used once #8807 (which has a positive review)
was merged. But when I open a new ticket for adding has_morphism then
I would of course build on top of #8807.

> I don't like the current semantic of
> Morphism.category because it is incompatible with the semantic of
> .category() for elements. I think it should be something like:
>
>         sage: f = Hom(QQ,QQ)(1)
>         sage: f.category()
>         Category of elements of Set of Homomorphisms from Rational Field to Rational Field

What is meant by a "category of elements"? I just saw that it already
exists:
sage: 1.category()
Category of elements of Integer Ring

But I don't understand: What are the morphisms of a "category of
elements"?

*If* we agree that there should be a "category" of elements then there
should of course be a category of morphisms as well.

> Note that:
>
>         sage: f.category()
>         Category of rings
>
> would also be incompatible with the semantic of .category() for
> elements.

Here I am not so sure if I agree. It seems to me that the common
syntax in textbooks is different for elements and for morphisms:

One would say "x is an element of an object P of a category C" -
nobody would say "x is an element of a category C".
In the case of a morphism, one *could* say "f is an element of the
Homset from P to Q in a category C" or "f is an element of an object
of the category of homsets of C". But it seems common to say "f is a
morphism in C".

Therefore, I would strongly oppose against "1 in Rings()", but I would
only weakly oppose against "ZZ.hom([1]) in Rings()".

> A bit of fuel for the discussion: the following assertion should
> always be verified, for any Sage object:
>
>         sage: f in f.category()
>         True

Hence, either one removes f.category() for morphisms, or one makes it
a synonyme for f.category_for() and accepts "ZZ.hom([1]) in Rings()"
- but please not ZZ.hom([1]) in Rings().hom_category()", which is
currently the case!!

> Yes, if you can naturally split the work in several independent
> tickets, go for it!

OK.

Cheers,
Simon

Simon King

unread,
Dec 31, 2010, 9:18:37 AM12/31/10
to sage-algebra
On 31 Dez., 13:39, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> What about the following variant:
>
>         sage: f.is_morphism(category = C)
>
> I guess it depends whether the actual code doing the test depends more
> on the kind of morphism or on the category.

If one writes code then, I guess, one typically knows that something
(C) is a category and wants to know whether some argument (f) is a
morphism in C. Hence, typically it is guaranteed that C is a category,
but there is nothing known about f. Hence, one always needed to code
"if hasattr(f,'is_morphism') and f.is_morphism(C):" -- which is
awkward.

Therefore, I prefer C.has_morphism(f) over f.is_morphism(C).

Let's make a little poll:

1) Should we have "1 in Rings()"?
I am strongly -1 on this question.

2) Should we have "ZZ.hom([1]) in Rings()"?
I am very weakly +1 on this question.

3) If x is an element of G: Should x be a morphism of Groupoid(G)
(hence, "Groupoid(G).is_morphism(x)" returns True)? Or should there be
a difference between an element of G and a morphism of Groupoid(G)?
I am undecided on these questions. But if x is not a morphism of
Groupoid(G), then at least Groupoid(G)(x) should return a morphism of
Groupoid(G).

Best regards,
Simon

Nicolas M. Thiery

unread,
Dec 31, 2010, 9:29:34 AM12/31/10
to sage-a...@googlegroups.com
On Fri, Dec 31, 2010 at 05:57:25AM -0800, Simon King wrote:
> What is meant by a "category of elements"? I just saw that it already
> exists:
> sage: 1.category()
> Category of elements of Integer Ring
>
> But I don't understand: What are the morphisms of a "category of
> elements"?

Good question. Ask whoever implemented this concept :-)

Another funny one:

sage: gap(1).category()
Category of elements of Gap

> *If* we agree that there should be a "category" of elements then there
> should of course be a category of morphisms as well.

Possibly; can you be more specific though?

What I care about is that morphisms shall be normal elements (of their
homsets). So *if* we keep that notion of "Category of elements of a
parent", then f.category() should be a subcategory of the "Category of
the elements of f.parent()".


> > Note that:
> >
> > � � � � sage: f.category()
> > � � � � Category of rings
> >
> > would also be incompatible with the semantic of .category() for
> > elements.
>
> Here I am not so sure if I agree. It seems to me that the common
> syntax in textbooks is different for elements and for morphisms:
>
> One would say "x is an element of an object P of a category C" -
> nobody would say "x is an element of a category C".
> In the case of a morphism, one *could* say "f is an element of the
> Homset from P to Q in a category C" or "f is an element of an object
> of the category of homsets of C". But it seems common to say "f is a
> morphism in C".
>
> Therefore, I would strongly oppose against "1 in Rings()",

Yes.

> but I would only weakly oppose against "ZZ.hom([1]) in Rings()".

Would you say: "the morphism f is in C"? It seems to me that "is a
morphism in C" is a single block that can't be reduced to "is a
morphism" and "is in C".

> > A bit of fuel for the discussion: the following assertion should
> > always be verified, for any Sage object:
> >
> > � � � � sage: f in f.category()
> > � � � � True
>
> Hence, either one removes f.category() for morphisms, or one makes it
> a synonyme for f.category_for() and accepts "ZZ.hom([1]) in Rings()"

I vote -1 for the latter. But there might be other options than the
former. The question comes back to whether elements / morphisms should
have a category.

> - but please not ZZ.hom([1]) in Rings().hom_category()", which is
> currently the case!!

Definitely.

Simon King

unread,
Dec 31, 2010, 10:16:53 AM12/31/10
to sage-algebra
Hi Nicolas,

On 31 Dez., 15:29, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> On Fri, Dec 31, 2010 at 05:57:25AM -0800, Simon King wrote:
> > ...
> > *If* we agree that there should be a "category" of elements then there
> > should of course be a category of morphisms as well.
>
> Possibly; can you be more specific though?

*If* we agree that the "category of elements of a parent P" is not
nonsense then the "category of morphisms of a category C" is not
nonsense either.

> What I care about is that morphisms shall be normal elements (of their
> homsets). So *if* we keep that notion of "Category of elements of a
> parent", then f.category() should be a subcategory of the "Category of
> the elements of f.parent()".

Yes. *If* we agree that Element should have an attribute .category().

It seems to me that Element.category() is redundant: There is no
information in x.category() that is not already in x.parent(). Should
it perhaps be removed?

> > but I would only weakly oppose against "ZZ.hom([1]) in Rings()".
>
> Would you say: "the morphism f is in C"? It seems to me that "is a
> morphism in C" is a single block that can't be reduced to "is a
> morphism" and "is in C".

Good point.

> > Hence, either one removes f.category() for morphisms, or one makes it
> > a synonyme for f.category_for() and accepts "ZZ.hom([1]) in Rings()"
>
> I vote -1 for the latter. But there might be other options than the
> former.

If one does not remove f.category() then one will have "f in
f.category()", as one should.

Then I see three options:

1) (Current behaviour) f.category() returns
"f.category_for().hom_category()" - but we certainly don't want that
"ZZ.hom([1]) in Rings().hom_category()".

2) ``Map`` inherits the method category() from ``Element`` - then,
f.category() is the "category of elements of f.parent()". We could
probably live with f being contained in that category, but the
question is whether we like to think of that as a category.

3) f.category() returns f.category_for(). But you don't like
"ZZ.hom([1]) in Rings()".

If there is no fourth option, it seems to me we should better
remove .category() from all classes deriving from Element.

> The question comes back to whether elements / morphisms should
> have a category.

Indeed.

Cheers,
Simon

Niles

unread,
Dec 31, 2010, 5:53:17 PM12/31/10
to sage-algebra
Hello all,

On Dec 30, 11:30 am, Simon King <simon.k...@uni-jena.de> wrote:

> On 30 Dez., 16:33, Niles <nil...@gmail.com> wrote:
> >
> > I think "f in C" should return True, and that this should be the
> > "official" way of testing: it has the advantages of being compatible
> > with the way sage tests membership of other things, and being
> > mathematically accurate.
>
> I wouldn't agree here.
>

I think I didn't mean to say "mathematically accurate", but rather "in
current usage in mathematical writing" -- that seems to be the
conclusion of some of the other discussion here too. But the comment
I want to add now is that I *do* think it's mathematically accurate,
but maybe whether you agree or not depends on what you think the
definition of a category is. Often, people write things like "A
category is a class of objects such that for every pair of objects X,
Y there is a class (or set) Hom(X,Y) satisfying ..." -- this kind of
definition would lead to things like "f is an element in the homset
Hom(X,Y) of the category C". However, one could also make the
definition "A category consists of a class of objects and a class of
morphisms satisfying the following properties: 1) every morphism has
a source and target, and these are objects in the category, 2) ...".

Wikipedia's page gives the latter definition [1], as does Mac Lane's
"Categories for the working mathematician" (around page 10 -- it's
somewhat longer than what I've written here, but carries the same
idea). The nLab (wiki used by many people active in higher category
theory) gives both definitions, and some discussion of language
relating to the different definitions [2].

... oh dear, I think this may be turning into a rant -- I didn't want
that :) In any case, I guess you can see why I'm in favor of
"ZZ.hom([1]) in Rings()" being True, while *not* in favor of "ZZ(1) in
Rings()" being True.

[1]: http://en.wikipedia.org/wiki/Category_%28mathematics%29#Definition
[2]: http://ncatlab.org/nlab/show/category


On Dec 31, 10:16 am, Simon King <simon.k...@uni-jena.de> wrote:

> *If* we agree that the "category of elements of a parent P" is not
> nonsense then the "category of morphisms of a category C" is not
> nonsense either.

I agree -- the morphisms in both of these categories should all be
identity maps. With this understanding, "category of elements" and
"category of morphisms" are good stand-in's for "class of elements"
and "class of morphisms" (in the set-theoretic sense of the word
class, not the object-oriented sense) -- I think we should leave this
alone rather than talk about implementing a Class class. Along these
lines, there is also the "category of objects", so someone pedantic
might be tempted to argue that "ZZ.category()" could return "Category
of objects of category Rings". This would be silly, but consistent
with the others -- so I think they're silly too. (Although perhaps
necessary.)

> Then I see three options:
>
> 1) (Current behaviour) f.category() returns
> "f.category_for().hom_category()" - but we certainly don't want that
> "ZZ.hom([1]) in Rings().hom_category()".

There are, I think, two reasonable expectations for what
"hom_category" could mean: The first, and current, is the category
whose objects are homsets and whose morphisms (presumably) are
identities. The second is the category whose objects are morphisms of
C, and whose morphisms are identities. This could be returned by
something like "Rings().morphisms()". Those not in favor of option
(3) might like f.category() to return f.category_for().morphisms().
In this scenario, it might help clarify the distinction to
change .hom_category() to .homset_category(), if possible.

>
> 2) ``Map`` inherits the method category() from ``Element`` - then,
> f.category() is the "category of elements of f.parent()". We could
> probably live with f being contained in that category, but the
> question is whether we like to think of that as a category.

If I understand correctly, this option make "ZZ.hom([1]).category()"
return "Category of elements of Set of Homomorphisms from Integer Ring
to Integer Ring". That's my least favorite.

>
> 3) f.category() returns f.category_for(). But you don't like
> "ZZ.hom([1]) in Rings()".

+1
This is consistent with one of the definitions of category (it may be
the prominent one), and with popular usage.

>
> If there is no fourth option, it seems to me we should better
> remove .category() from all classes deriving from Element.

I believe elements should not have a category. But I thought it was
against the spirit of object-oriented programming to remove methods
from a derived class. I tried once very hard to figure out how to do
it, and couldn't find a good way -- do you have one? (And do you not
feel guilty when doing it? :)

best,
Niles

Simon King

unread,
Jan 1, 2011, 3:29:36 AM1/1/11
to sage-algebra
Hi Niles,

On 31 Dez. 2010, 23:53, Niles <nil...@gmail.com> wrote:
> ... oh dear, I think this may be turning into a rant -- I didn't want
> that :)  In any case, I guess you can see why I'm in favor of
> "ZZ.hom([1]) in Rings()" being True, while *not* in favor of "ZZ(1) in
> Rings()" being True.

I think it really is a delicate question, and it seems to change daily
whether I am "weakly +1" or "weakly -1" to "ZZ.hom([1]) in Rings()".
Today, I tend to -1, since I thought: "For what purpose would one
write 'bla in C' in a program?"

One would certainly use it in the form "if bla in C: do something with
bla". But if "bla in C" gives the same answer for things that are so
different as morphisms and objects, I'd say it is practically useless.

> > *If* we agree that the "category of elements of a parent P" is not
> > nonsense then the "category of morphisms of a category C" is not
> > nonsense either.
>
> I agree -- the morphisms in both of these categories should all be
> identity maps.  With this understanding, "category of elements" and
> "category of morphisms" are good stand-in's for "class of elements"
> and "class of morphisms" (in the set-theoretic sense of the word
> class, not the object-oriented sense) -- I think we should leave this
> alone rather than talk about implementing a Class class.

Isn't "class" a reserved word in Python?

But, should we have "category of elements of one parent" (that's the
current meaning), or should one have "class of elements of all parents
in a give category"?

> > 1) (Current behaviour) f.category() returns
> > "f.category_for().hom_category()" - but we certainly don't want that
> > "ZZ.hom([1]) in Rings().hom_category()".
>
> There are, I think, two reasonable expectations for what
> "hom_category" could mean: The first, and current, is the category
> whose objects are homsets and whose morphisms (presumably) are
> identities.
>  The second is the category whose objects are morphisms of
> C, and whose morphisms are identities.  This could be returned by
> something like "Rings().morphisms()".

By the way, in the German Wikipedia, I found the approach of having
"Hom(X,Y) for any pair of objects". And I found that the class of
morphisms is sometimes denoted "Fl(C)" (French flèche = arrow).

> > 2) ``Map`` inherits the method category() from ``Element`` - then,
> > f.category() is the "category of elements of f.parent()". We could
> > probably live with f being contained in that category, but the
> > question is whether we like to think of that as a category.
>
> If I understand correctly, this option make "ZZ.hom([1]).category()"
> return "Category of elements of Set of Homomorphisms from Integer Ring
> to Integer Ring".  That's my least favorite.

Yes, unless we change Element.category() into something that returns
the "class of elements of objects of category C".

> > If there is no fourth option, it seems to me we should better
> > remove .category() from all classes deriving from Element.
>
> I believe elements should not have a category.

I believe that, too.

>  But I thought it was
> against the spirit of object-oriented programming to remove methods
> from a derived class.

Well, Element derives from Element as well, so, my suggestion was to
remove it there :)

Cheers,
Simon

Niles

unread,
Jan 1, 2011, 8:24:47 AM1/1/11
to sage-algebra
Hi Simon :)

On Jan 1, 3:29 am, Simon King <simon.k...@uni-jena.de> wrote:


> Well, Element derives from Element as well, so, my suggestion was to
> remove it there :)
>

This won't remove the .category() method though -- it will be
inherited from SageObject, and then ZZ(1).category() will return
"Category of objects". To me, this is just as good/bad as "Category
of elements of bla". I guess the advantage is that we don't have to
deal with the question of what "bla" should be. "The category of
elements of ZZ" and "the category of all elements of all commutative
rings" are, I think, both reasonable answers.


> But, should we have "category of elements of one parent" (that's the
> current meaning), or should one have "class of elements of all parents
> in a give category"?
>

Whatever happens, I hope .category() will return a category of some
kind, rather than "class of ..."


> On 31 Dez. 2010, 23:53, Niles <nil...@gmail.com> wrote:
>
> > ... oh dear, I think this may be turning into a rant -- I didn't want
> > that :) In any case, I guess you can see why I'm in favor of
> > "ZZ.hom([1]) in Rings()" being True, while *not* in favor of "ZZ(1) in
> > Rings()" being True.
>
> I think it really is a delicate question, and it seems to change daily
> whether I am "weakly +1" or "weakly -1" to "ZZ.hom([1]) in Rings()".
> Today, I tend to -1, since I thought: "For what purpose would one
> write 'bla in C' in a program?"

I agree that it's delicate. And I think the source of the delicacy is
that there are two commonly accepted and useful definitions of
category -- do you think there's more to it than that?

Do you think sage can reasonably expect to be consistent with both
definitions? I have some hope that it can, but also some concern.

>
> One would certainly use it in the form "if bla in C: do something with
> bla". But if "bla in C" gives the same answer for things that are so
> different as morphisms and objects, I'd say it is practically useless.

Well, it would be useful in cases where you have a morphism of one
category, and want to test whether it's in a particular subcategory or
not, or whether it coerces to some other category. Note that "bla in
ZZ[x]" gives the same answer for lots of different things (prime
numbers, composite numbers, monomials, ...). Since a category holds
much more data than a ring, I'm happy to consider more things to be
"in" a category.

> By the way, in the German Wikipedia, I found the approach of having
> "Hom(X,Y) for any pair of objects". And I found that the class of
> morphisms is sometimes denoted "Fl(C)" (French flèche = arrow).
>

thanks -- I should have checked these too :)

I've also seen Ob(C) and Mor(C) for the objects and morphisms of C.
Perhaps another way of approaching this issue is to say that
containment is well-defined for sets, and even for classes, but a
category -- using either definition -- is neither of these. Thus "x
in C" must be shorthand for "x in X", where X is some class. And
there are a number of natural choices for what X should be. Using one
definition, X should be the class of objects and (possibly) homsets of
C. Using another definition, X should be the class of objects and
morphisms of C.

We could opt to be as permissive as possible, and take X to be the
objects, homsets, *and* morphisms of C; what do you think of that?


At the risk of pushing this too far, I also want to mention the
"single-sorted" definition of category:

http://ncatlab.org/nlab/show/single-sorted+definition+of+a+category

This is the morphism-centric counterpart to the "objects and homsets"
definition of category, defining it as a collection of morphisms with
some additional data (the essential idea is that an object can be
identified by its identity morphism). I don't see it being terribly
useful right now to implement all possible definitions of category,
but I do think we should try to avoid excluding them, if we can. For
example, I can think of a some applications in algebraic geometry or
algebraic topology where it would be useful to be able to implement
internal categories or enriched categories -- these arise as
generalizations of different definitions of category. If someone does
eventually want to work on one of these, it would be nice (although
not necessary) if the category framework is flexible enough to be
compatible with them.

best,
Niles

Simon King

unread,
Jan 1, 2011, 10:05:42 AM1/1/11
to sage-algebra
Hi Niles,

On 1 Jan., 14:24, Niles <nil...@gmail.com> wrote:
> > Well, Element derives from Element as well, so, my suggestion was to
> > remove it there :)
>
> This won't remove the .category() method though -- it will be
> inherited from SageObject, and then ZZ(1).category() will return
> "Category of objects".

Ouch, that's right.

But why is it that SageObject has a method category? After all, there
is a distinct class sage.structure.category_object.CategoryObject - it
directly derives from SageObject, but overrides the category() method.

I see that Parent (which clearly should have a category() method)
inherits from CategoryObject.

So, why don't we remove the category() method from SageObject? If a
class ought to have a category() method, then it should be derived of
CategoryObject, and if it doesn't (like Element) then it should just
be derived from SageObject.

> > I think it really is a delicate question, and it seems to change daily
> > whether I am "weakly +1" or "weakly -1" to "ZZ.hom([1]) in Rings()".
> > Today, I tend to -1, since I thought: "For what purpose would one
> > write 'bla in C' in a program?"
>
> I agree that it's delicate.  And I think the source of the delicacy is
> that there are two commonly accepted and useful definitions of
> category -- do you think there's more to it than that?

In this case, I just tried to take the point of view of a programmer,
not a mathematician, and tried to think of the use of "bla in C" in a
program.

> Do you think sage can reasonably expect to be consistent with both
> definitions?  I have some hope that it can, but also some concern.

No, that's hardly possible. We should stick with one definition.

> > One would certainly use it in the form "if bla in C: do something with
> > bla". But if "bla in C" gives the same answer for things that are so
> > different as morphisms and objects, I'd say it is practically useless.
>
> Well, it would be useful in cases where you have a morphism of one
> category, and want to test whether it's in a particular subcategory or
> not, or whether it coerces to some other category.

That's the purpose of C.has_morphism(f), proposed in the posts above.

> Note that "bla in
> ZZ[x]" gives the same answer for lots of different things (prime
> numbers, composite numbers, monomials, ...).
> Since a category holds
> much more data than a ring, I'm happy to consider more things to be
> "in" a category.

I see a clear difference between "bla in ZZ['x'] for bla=1,x,primes,
non-primes,..." and "bla in Rings() for objects, homsets, morphisms
and perhaps elements":

If you have "bla in ZZ['x']" then it is guaranteed that bla is an
element, and (after coercion) one can do arithmetic with all other
elements of ZZ['x'].

I would expect to have a similar guarantee for "bla in Rings()".
Hence, I would expect that I can define Hom(bla,X) for any other thing
that satisfies "X in Rings()". From that point of view, "bla in
Rings()" should return "False", if bla is a morphism. Therefore ...

> We could opt to be as permissive as possible, and take X to be the
> objects, homsets, *and* morphisms of C; what do you think of that?

... I am a bit sceptic.

> At the risk of pushing this too far, I also want to mention the
> "single-sorted" definition of category:
>
> http://ncatlab.org/nlab/show/single-sorted+definition+of+a+category

Nice definition, but probably it would not be a good idea to rely on a
non-mainstream definition in a CAS without a compelling reason.

Best regards,
Simon

Niles

unread,
Jan 1, 2011, 12:56:49 PM1/1/11
to sage-algebra


On Jan 1, 10:05 am, Simon King <simon.k...@uni-jena.de> wrote:

> But why is it that SageObject has a method category? After all, there
> is a distinct class sage.structure.category_object.CategoryObject - it
> directly derives from SageObject, but overrides the category() method.
>
> I see that Parent (which clearly should have a category() method)
> inherits from CategoryObject.
>
> So, why don't we remove the category() method from SageObject? If a
> class ought to have a category() method, then it should be derived of
> CategoryObject, and if it doesn't (like Element) then it should just
> be derived from SageObject.

This sounds great to me. Just to see what would be involved, I
removed SageObject.category(), Element.category() and ran some
doctests. There were a number of failures in sage/structure and sage/
categories, but they seemed mostly to come from ._test_category();
maybe they wouldn't be too hard to fix up. I also tested sage/
algebras and sage/schemes; there were just two failures in algebras,
and none in schemes. This makes me hopeful that the rest of sage
doesn't depend too much on SageObject.category().

To carry this out, would we first have to implement a deprecation
warning, and then wait for a couple of releases before removing the
functionality?

>
> > Well, it would be useful in cases where you have a morphism of one
> > category, and want to test whether it's in a particular subcategory or
> > not, or whether it coerces to some other category.
>
> That's the purpose of C.has_morphism(f), proposed in the posts above.
>

I'm willing to leave it at that for now. Is it accurate to say that
removing .category() from SageObject and Element and implementing
Category.has_morphism() will resolve most of our issues? Then "x in
C" will be shorthand for "x is an object of C", and testing for
individual morphisms and for homsets will be handled by other methods,
which seems reasonable.

Do you know who put the category methods in SageObject and Element in
the first place, and whether there are other issues we should consider
before removing them?

best (and happy new year! :)
Niles

Simon King

unread,
Jan 1, 2011, 1:22:22 PM1/1/11
to sage-algebra
Hi Niles!

On 1 Jan., 18:56, Niles <nil...@gmail.com> wrote:
> ...
> This sounds great to me.  Just to see what would be involved, I
> removed SageObject.category(), Element.category() and ran some
> doctests.

Good, thank you!

> There were a number of failures in sage/structure and sage/
> categories, but they seemed mostly to come from ._test_category();
> maybe they wouldn't be too hard to fix up.

If SageObject has no category() then probably _test_category() should
be moved to CategoryObject (currently CategoryObject simply inherits
_test_category() from SageObject) .

>  I also tested sage/
> algebras and sage/schemes; there were just two failures in algebras,
> and none in schemes.  This makes me hopeful that the rest of sage
> doesn't depend too much on SageObject.category().

Good news.

> To carry this out, would we first have to implement a deprecation
> warning, and then wait for a couple of releases before removing the
> functionality?

I guess that is a question for sage-devel?

> I'm willing to leave it at that for now.  Is it accurate to say that
> removing .category() from SageObject and Element and implementing
> Category.has_morphism() will resolve most of our issues?  Then "x in
> C" will be shorthand for "x is an object of C", and testing for
> individual morphisms and for homsets will be handled by other methods,
> which seems reasonable.

I think that's the plan.

> Do you know who put the category methods in SageObject and Element in
> the first place, and whether there are other issues we should consider
> before removing them?

Sorry, I don't know (but I am sure that s/he is reading sage-
algebra...)

Best regards,
Simon

Nicolas M. Thiery

unread,
Jan 5, 2011, 8:56:21 AM1/5/11
to sage-a...@googlegroups.com
Hi Niles, Simon,

On Sat, Jan 01, 2011 at 07:05:42AM -0800, Simon King wrote:
> > > I think it really is a delicate question, and it seems to change daily
> > > whether I am "weakly +1" or "weakly -1" to "ZZ.hom([1]) in Rings()".
> > > Today, I tend to -1, since I thought: "For what purpose would one
> > > write 'bla in C' in a program?"
> >
> > I agree that it's delicate. �And I think the source of the delicacy is
> > that there are two commonly accepted and useful definitions of
> > category -- do you think there's more to it than that?

Yes, a category C is made of two things: Obj(C) and Mor(C). Very much
like a graph G = (V,E) is defined in term of two sets: its nodes and
its vertices. So in principle, we should only allow for

P in Obj(C)
f in Mor(C)

Now, especially given our categories are named, I find it convenient,
to allow for:

P in VectorSpaces()

whose meaning is immediately clear to any reader. On the other hand:

f in VectorSpaces()

to test if f is a morphism in VectorSpaces() is not intuitive. So I am
also +1 on having ``x in C'' mean exactly ``x is an object of C'', as
is the case currently.


> > What about the following variant:
> >
> > � � � � sage: f.is_morphism(category = C)
> >
> > I guess it depends whether the actual code doing the test depends more
> > on the kind of morphism or on the category.

> If one writes code then, I guess, one typically knows that something
> (C) is a category and wants to know whether some argument (f) is a
> morphism in C. Hence, typically it is guaranteed that C is a
> category, but there is nothing known about f. Hence, one always
> needed to code "if hasattr(f,'is_morphism') and f.is_morphism(C):"
> -- which is awkward.
> Therefore, I prefer C.has_morphism(f) over f.is_morphism(C).

That's a good point.

So, right now, my preferred idiom is ``f in C.morphisms()'', because
it's the straightforward translation of ``f in Mor(C)''. But I guess
``C.has_morphism(f)'' will do until we have another concrete use for
``C.morphisms()''.

Keep up the good refactoring work!

John Cremona

unread,
Jan 5, 2011, 10:13:41 AM1/5/11
to sage-a...@googlegroups.com
For what it's worth, I agree with everything Nicolas just said. (I
have been following this discussion silently during the holiday
period.)

John

> Nicolas M. Thiéry "Isil" <nth...@users.sf.net>
> http://Nicolas.Thiery.name/
>

Simon King

unread,
Jan 5, 2011, 12:05:38 PM1/5/11
to sage-algebra
Hi Nicolas!

On 5 Jan., 14:56, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> whose meaning is immediately clear to any reader. On the other hand:
>
>         f in VectorSpaces()
>
> to test if f is a morphism in VectorSpaces() is not intuitive.

I somehow agree.

> So, right now, my preferred idiom is ``f in C.morphisms()'', because
> it's the straightforward translation of ``f in Mor(C)''. But I guess
> ``C.has_morphism(f)'' will do until we have another concrete use for
> ``C.morphisms()''.

Why wait? I guess it would be straight forward to introduce a very
basic structure whose purpose is: Knowing that it is a class of
morphisms; knowing the category to which these morphisms belong;
testing containment.

Projected usage:

sage: M = Rings().morphisms()
sage: M
Class of morphisms in category of rings

M should belong to a category, but of course this is not Rings() in
this example. In general, M will not be a set but a class.

Is there a category of classes? Do all classes form a class?

And if one can define it: Should the "Category of classes" exist in
Sage, likely being very similar to `Sets`?

I guess I am not sure about the exact meaning of Objects(): Is
Objects() a sub- or a super-category of the category of classes? Or is
it actually the same?
Note that if we have Classes() in addition to Sets() then it should be
easy to implement the notion of "small" categories.

So, we should have either
sage: M in Objects()
True
or (with more work)
sage: M in Classes()
True

Concerning the category for which M was defined: What about the method
name "defining_category"? Better suggestions? Then:

sage: M.defining_category() is Rings()
True
sage: ZZ.hom([1]) in M
True
sage: ZZ in M
False

Code draft:

{{{
class MorphismSet(Parent):
def __init__(self, category, is_small=False):
Parent.__init__(self, category=Sets() if is_small else
Objects()) # or better "Sets() if is_small else Classes()"??
self.__cat = category
def _repr_(self):
return "Class of morphisms in category of
%s"%self.__cat._repr_object_names()
def __contains__(self, f):
return self.__cat.has_morphism(f)
def an_element(self):
E = self.__cat.example()
if E is NotImplemented:
raise NotImplementedError, "Please implement the method
'example()' for category of %s, or implement the method 'an_element()'
for %s"%(self.__cat._repr_object_names(), repr(self))
return E.Hom(E).an_element()
}}}

Of course, for now, the only purpose of that class is to provide an
intuitive syntax for testing morphism containment. But I wouldn't
object if eventually someone would add useful stuff to it. And for
symmetry, there could also be an ObjectSet class.

Cheers,
Simon

Simon King

unread,
Jan 5, 2011, 12:43:39 PM1/5/11
to sage-algebra
On 5 Jan., 18:05, Simon King <simon.k...@uni-jena.de> wrote:
> class MorphismSet(Parent):
>     def __init__(self, category, is_small=False):
>         ...

... or rather
class MorphismSet(Parent,UniqueRepresentation):
@staticmethod
def __classcall__(cls, category, is_small=False):
return super(MorphismSet, cls).__classcall__(cls, category,
is_small)
def __init__(self, category, is_small)
Parent.__init__(self, category= Sets() if is_small else
Objects())
self.__cat = category
self.__small = is_small
def is_small (self):
return self.__small
def _repr_(self):
return "%s of morphisms in category of %s"%("Set" if
self.__small else "Class",self.__cat._repr_object_names())
...

Nicolas M. Thiery

unread,
Jan 5, 2011, 3:31:28 PM1/5/11
to sage-a...@googlegroups.com
On Wed, Jan 05, 2011 at 09:05:38AM -0800, Simon King wrote:
> > So, right now, my preferred idiom is ``f in C.morphisms()'', because
> > it's the straightforward translation of ``f in Mor(C)''. But I guess
> > ``C.has_morphism(f)'' will do until we have another concrete use for
> > ``C.morphisms()''.
>
> Why wait? I guess it would be straight forward to introduce a very
> basic structure whose purpose is: Knowing that it is a class of
> morphisms; knowing the category to which these morphisms belong;
> testing containment.

I don't see any reason if you don't anymore :-) Please go ahead!

> Projected usage:
>
> sage: M = Rings().morphisms()
> sage: M
> Class of morphisms in category of rings

+1

> M should belong to a category

Do we really need this at this point? Here again, I would just leave
what you wrote somewhere (e.g. a track ticket) and skip that step
until we have more use cases for "Classes" and other business with
non-small categories.

> Concerning the category for which M was defined: What about the method
> name "defining_category"? Better suggestions?

Sounds good. An alternative would be to use base_category() since
that's already in use for other objects parametrized by a category
(although those objects are probably all categories)

> sage: M.defining_category() is Rings()
> True
> sage: ZZ.hom([1]) in M
> True
> sage: ZZ in M
> False

+1

>
> Code draft:
>
> {{{
> class MorphismSet(Parent):
> def __init__(self, category, is_small=False):
> Parent.__init__(self, category=Sets() if is_small else
> Objects()) # or better "Sets() if is_small else Classes()"??
> self.__cat = category
> def _repr_(self):
> return "Class of morphisms in category of
> %s"%self.__cat._repr_object_names()
> def __contains__(self, f):
> return self.__cat.has_morphism(f)
> def an_element(self):
> E = self.__cat.example()
> if E is NotImplemented:
> raise NotImplementedError, "Please implement the method
> 'example()' for category of %s, or implement the method 'an_element()'
> for %s"%(self.__cat._repr_object_names(), repr(self))
> return E.Hom(E).an_element()
> }}}

+1

Maybe MorphismsOfCategory for the name of the class?

> Of course, for now, the only purpose of that class is to provide an
> intuitive syntax for testing morphism containment. But I wouldn't
> object if eventually someone would add useful stuff to it.

+1.

> And for symmetry, there could also be an ObjectSet class.

+1, at least as soon as we will have an actual need for it.

Nicolas M. Thiery

unread,
Jan 5, 2011, 3:37:16 PM1/5/11
to sage-a...@googlegroups.com

I'd say not to worry too much about this small business. I actually
would be fine with an output like ``hom(Category of Vector Spaces)''
even though it's not perfectly consistent with other representations
for parents.

Simon King

unread,
Jan 6, 2011, 5:55:59 AM1/6/11
to sage-algebra
Hi Nicolas, John and Niles,

On 5 Jan., 21:37, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> I'd say not to worry too much about this small business. I actually
> would be fine with an output like ``hom(Category of Vector Spaces)''
> even though it's not perfectly consistent with other representations
> for parents.

Well, any set is a class. Hence, for now, I return *small* morphism/
object classes only for sub-categories of FiniteEnumeratedSets() (I
hope it is correct that these are all small categories?).

Also, for now I simply assume that Objects() is a good replacement for
a (pseudo)category of all classes. After all, Objects() is thought of
as the biggest category in Sage. So, if the category C is small then
C.morphisms() is in Sets(), and otherwise C.morphisms() is in
Objects(). I suppose that's a sufficient approximation to the
mathematical reality.

One question about the layout/name:

How should I call the module in which ObjectsOfCategory and
MorphismOfCategory are implemented? I thought about
sage.categories.class_of, because
sage.categories.class_of.MorphismsOfCategory sounds quite natural to
me (from a grammatical point of view).

Cheers,
Simon

Simon King

unread,
Jan 6, 2011, 6:16:51 AM1/6/11
to sage-algebra
Yet another question:

Up to now, I derived ObjectsOfCategory from Parent and
UniqueRepresentation. Since it derives from Parent, I tried to
implment coercion in the usual way (C1.objects() coerces into
C2.objects() if C1 is a sub-category of C2, and C2 coerces into
C2.objects() as well).

However, I can not use the default call method of parents (providing
_element_constructor_), since it relies on a cdef'd method _call_
whose return value is of class `Element`. But the objects of a
category are, in general, no instances of Element.

Am I allowed to make an exception and provide a custom call method?

Cheers,
Simon

Nicolas M. Thiery

unread,
Jan 6, 2011, 7:07:12 AM1/6/11
to sage-a...@googlegroups.com
On Thu, Jan 06, 2011 at 02:55:59AM -0800, Simon King wrote:
> Well, any set is a class. Hence, for now, I return *small* morphism/
> object classes only for sub-categories of FiniteEnumeratedSets() (I
> hope it is correct that these are all small categories?).

Sounds right (btw: I can't wait until we have FiniteSets). I would
guess that EnumeratedSets (whose objets are countable) probably would
work as well. Any category expert around?

> Also, for now I simply assume that Objects() is a good replacement for
> a (pseudo)category of all classes. After all, Objects() is thought of
> as the biggest category in Sage. So, if the category C is small then
> C.morphisms() is in Sets(), and otherwise C.morphisms() is in
> Objects(). I suppose that's a sufficient approximation to the
> mathematical reality.

+1

> One question about the layout/name:
>
> How should I call the module in which ObjectsOfCategory and
> MorphismOfCategory are implemented? I thought about
> sage.categories.class_of, because
> sage.categories.class_of.MorphismsOfCategory sounds quite natural to
> me (from a grammatical point of view).

What about putting them either directly in category.py (if we see
those as just small helper class for Category), or alternatively in
two separate files: morphisms_of_category.py / objects_of_category.py
(in case we seem them as subject to future expansion).

Nicolas M. Thiery

unread,
Jan 6, 2011, 7:11:53 AM1/6/11
to sage-a...@googlegroups.com

The specifications currently say that something that inherits from
Parent should be in the Sets() category and reciprocally. It's
probably assumed as well in many places that the elements of a Parent
are ... Elements.

What about just inheriting from UniqueRepresentation and SageObject,
and not worry about fancy coercions, to just Keep It Simple?

Nicolas M. Thiery

unread,
Jan 6, 2011, 7:14:39 AM1/6/11
to sage-a...@googlegroups.com
On Thu, Jan 06, 2011 at 01:07:12PM +0100, Nicolas M. Thiery wrote:
> What about putting them either directly in category.py (if we see
> those as just small helper class for Category), or alternatively in
> two separate files: morphisms_of_category.py / objects_of_category.py
> (in case we seem them as subject to future expansion).

By the way: those classes are good candidates to by cythoned, to
reduce the overhead in ``f in C.morphisms()''. That's a good reason to
put them in separate file(s).

Another point: instead of using UniqueRepresentation, an alternative
would be to have C.morphisms() be a cached method (which eventually
should be super fast).

Simon King

unread,
Jan 6, 2011, 12:18:00 PM1/6/11
to sage-algebra
Hi Nicolas,

On 6 Jan., 13:11, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> The specifications currently say that something that inherits from
> Parent should be in the Sets() category and reciprocally.

Really? So, a class is no parent?

But the problems only arose when I tried to use the coeercion
framework: The coercion map inherits from Map, which has a method
_call_ of signature
cpdef Element _call_(self, x)
Since the coerce map is supposed to return something that is an object
in a sppecific category, it will, in general, not inherit from
Element.

> What about just inheriting from UniqueRepresentation and SageObject,
> and not worry about fancy coercions, to just Keep It Simple?

Since I also propose to slim SageObject down (namely: It should have
*no* category), I would rather inherit from UniqueRepresentation and
CategoryObject.

But I thought: *If* we want fancy coercion, then it would be better to
think of them right away. It is certainly difficult to implement them
later.

Cheers,
Simon

Simon King

unread,
Jan 6, 2011, 12:25:31 PM1/6/11
to sage-algebra
On 6 Jan., 13:14, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> By the way: those classes are good candidates to by cythoned, to
> reduce the overhead in ``f in C.morphisms()''. That's a good reason to
> put them in separate file(s).

Originally, I had MorphismsOf Category in sage.categories.morphism,
which is Cythoned. But then, there was a problem with the
__classcall__ method involved in UniqueRepresentation - the same code
worked when I had put it into a python file.

I wonder: Would Cython really be faster? After all, __contains__ still
is a Python method, and there are no tight loops which would benefit
from cythonisation.

> Another point: instead of using UniqueRepresentation, an alternative
> would be to have C.morphisms() be a cached method (which eventually
> should be super fast).

Probably a cached method would be better anyway. My current concept
involves testing whether the category in question is a sub-category of
FiniteEnumeratedSets() - this is not exactly super-fast, I guess, and
it should thus be done only once.

Cheers,
Simon

Nicolas M. Thiery

unread,
Jan 6, 2011, 4:54:56 PM1/6/11
to sage-a...@googlegroups.com
On Thu, Jan 06, 2011 at 09:18:00AM -0800, Simon King wrote:
> Really? So, a class is no parent?

Not with the current specs. I am not saying that we could not make the
specs evolve, but care should be taken.

> Since I also propose to slim SageObject down (namely: It should have
> *no* category), I would rather inherit from UniqueRepresentation and
> CategoryObject.

Or just to have no category method at this point for C.objects() and
C.morphisms(). Whatever feels good to you.

> But I thought: *If* we want fancy coercion, then it would be better to
> think of them right away. It is certainly difficult to implement them
> later.

Or the way around: if we think that fancy coercion could eventually be
interesting, then we should leave that area as blank as possible to
leave our hands free when times come.

Well, maybe it just that I have been a bit boiled in the past;
e.g. categories would have taken somewhat less time to implement if
their had been no category support in Sage at all beforehand.

Nicolas M. Thiery

unread,
Jan 6, 2011, 5:04:08 PM1/6/11
to sage-a...@googlegroups.com
On Thu, Jan 06, 2011 at 09:25:31AM -0800, Simon King wrote:
> I wonder: Would Cython really be faster? After all, __contains__ still
> is a Python method, and there are no tight loops which would benefit
> from cythonisation.

To be well accepted, high level idioms (like ``f in C.morphism()``)
need to be (eventually) implementable without overhead.

Otherwise developers start to avoid using it (and we want then to use
a lot of ``assert f in VectorSpaces().morphisms()`` in their code), or
complain about the accumulation of cruft that slows down Sage.

> Probably a cached method would be better anyway. My current concept
> involves testing whether the category in question is a sub-category of
> FiniteEnumeratedSets() - this is not exactly super-fast, I guess, and
> it should thus be done only once.

+1

Simon King

unread,
Jan 7, 2011, 2:58:14 AM1/7/11
to sage-algebra
Hi Nicolas,

On 6 Jan., 23:04, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> On Thu, Jan 06, 2011 at 09:25:31AM -0800, Simon King wrote:
> > I wonder: Would Cython really be faster? After all, __contains__ still
> > is a Python method, and there are no tight loops which would benefit
> > from cythonisation.
>
> To be well accepted, high level idioms (like ``f in C.morphism()``)
> need to be (eventually) implementable without overhead.

I think that's impossible.

If you have ``f in C.morphisms()`` then (1) you ask for C.morphism(),
and (2) you test whether f is a morphism of C. If you care for speed,
then you would directly do (2) - the price to pay is using a less
convenient notation. Similarly, you can not expect that ``P in
C.objects()`` will be as fast as ``P in C``.

Currently, I have
sage: C = Rings()
sage: P.<x,y> = QQ[]
sage: f = P.hom([x^2,y^2])
sage: f in C.morphisms()
True
sage: timeit('f in C.morphisms()')
625 loops, best of 3: 36.5 µs per loop
sage: timeit('C.has_morphism(f)')
625 loops, best of 3: 22 µs per loop
sage: P in C.objects()
True
sage: timeit('P in C.objects()')
625 loops, best of 3: 52.2 µs per loop
sage: timeit('P in C')
625 loops, best of 3: 36.9 µs per loop

Cheers,
Simon

Nicolas M. Thiery

unread,
Jan 7, 2011, 3:55:35 AM1/7/11
to sage-a...@googlegroups.com
Good morning,

On Thu, Jan 06, 2011 at 11:58:14PM -0800, Simon King wrote:
> On 6 Jan., 23:04, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
> wrote:
> > To be well accepted, high level idioms (like ``f in C.morphism()``)
> > need to be (eventually) implementable without overhead.
>
> I think that's impossible.
>
> If you have ``f in C.morphisms()`` then (1) you ask for C.morphism(),
> and (2) you test whether f is a morphism of C. If you care for speed,
> then you would directly do (2) - the price to pay is using a less
> convenient notation. Similarly, you can not expect that ``P in
> C.objects()`` will be as fast as ``P in C``.

Ok. I meant with *negligible* overhead. And I think that this is
possible (though me probably want to optimize that later; one
important first step being the optimization of cached_method). And
it's important (eventually), for otherwise developers will run away
from good idioms.

Simon King

unread,
Jan 7, 2011, 5:46:54 AM1/7/11
to sage-algebra
Hi Nicolas,

On 7 Jan., 09:55, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:
> Ok. I meant with *negligible* overhead. And I think that this is
> possible (though me probably want to optimize that later; one
> important first step being the optimization of cached_method). And
> it's important (eventually), for otherwise developers will run away
> from good idioms.

I do think that cached_method would be worth to optimize.

For now, I worked around the missing optimization. Namely: The
containment test in a category relies on all_super_categories(), which
is a cached method. Now, in the containment test of ObjectsOfCategory,
I simply avoid to call all_super_categories(), but directly access the
cache (which is _cache__all_super_categories[(False,),()]).

Of course, when calling objects(), one still has an overhead due to
the missing cached_method optimization. But with my little trick, my
implementation of containment in object classes can already compete
with the current non-optimized implementation of containment in
categories:

sage: G = SymmetricGroup(5)
sage: P.<x,y> = QQ[]
sage: C = Rings()
sage: C2 = G.category()
# test that X in C.objects() is the same as X in C:
sage: P in C
True
sage: P in C.objects()
True
sage: G in C
False
sage: G in C.objects()
False
sage: P in C2
False
sage: P in C2.objects()
False
sage: G in C2
True
sage: G in C2.objects()
True

Now, for the timings:
sage: timeit('P in C')
625 loops, best of 3: 36.8 µs per loop
sage: timeit('P in C.objects()')
625 loops, best of 3: 34.4 µs per loop
sage: timeit('G in C')
625 loops, best of 3: 26.2 µs per loop
sage: timeit('G in C.objects()')
625 loops, best of 3: 24 µs per loop
sage: timeit('P in C2')
625 loops, best of 3: 47.4 µs per loop
sage: timeit('P in C2.objects()')
625 loops, best of 3: 40.1 µs per loop
sage: timeit('G in C2')
625 loops, best of 3: 41.7 µs per loop
sage: timeit('G in C2.objects()')
625 loops, best of 3: 32.2 µs per loop

Of course, the current containment test in categories could be
improved by the same trick. But at least, the timings show that a
"good" syntax *can* be competitive.

My plan is:
* Optimize "X in C", since I believe this notation is preferred over
"X in C.objects()".
* Optimize "C.has_morphism(X)" and "X in C.morphisms()", so that it
can compete with the current "X in C.hom_category()" (which is an
awful and flawed notation - it's the current test that X is a
*morphism*!!).

Cheers,
Simon

Simon King

unread,
Jan 7, 2011, 6:17:56 AM1/7/11
to sage-algebra
On 7 Jan., 11:46, Simon King <simon.k...@uni-jena.de> wrote:
> My plan is:
> * Optimize "X in C", since I believe this notation is preferred over
> "X in C.objects()".

Done.

> * Optimize "C.has_morphism(X)" and "X in C.morphisms()", so that it
> can compete with the current "X in C.hom_category()" (which is an
> awful and flawed notation - it's the current test that X is a
> *morphism*!!).

Done as well. Here is an example.

Setting:
sage: P.<x,y> = QQ[]
sage: f = P.hom([x^2,y^2])
sage: S = Set([1,2,3])
sage: g = S.Hom(S).identity()
sage: C = Rings()
sage: C2 = Sets()
sage: f in C.hom_category()

Non-optimized implementation, flawed notation:
sage: f in C.hom_category()
True
sage: f in C2.hom_category()
True
sage: g in C.hom_category()
False
sage: g in C2.hom_category()
True
sage: timeit('f in C.hom_category()')
625 loops, best of 3: 39.9 µs per loop
sage: timeit('f in C2.hom_category()')
625 loops, best of 3: 40.7 µs per loop
sage: timeit('g in C.hom_category()')
625 loops, best of 3: 42.5 µs per loop
sage: timeit('g in C2.hom_category()')
625 loops, best of 3: 39.8 µs per loop

Better implementation, better notation:
sage: f in C.morphisms()
True
sage: f in C2.morphisms()
True
sage: g in C.morphisms()
False
sage: g in C2.morphisms()
True
sage: timeit('f in C.morphisms()')
625 loops, best of 3: 30.9 µs per loop
sage: timeit('f in C2.morphisms()')
625 loops, best of 3: 25.2 µs per loop
sage: timeit('g in C.morphisms()')
625 loops, best of 3: 21.4 µs per loop
sage: timeit('g in C2.morphisms()')
625 loops, best of 3: 19.8 µs per loop

So, I guess I'll now have to write doc tests and finally open a
ticket.

Cheers,
Simon

Nicolas M. Thiery

unread,
Jan 7, 2011, 6:32:54 AM1/7/11
to sage-a...@googlegroups.com
On Fri, Jan 07, 2011 at 03:17:56AM -0800, Simon King wrote:
> > * Optimize "X in C", since I believe this notation is preferred over
> > "X in C.objects()".
>
> Done.
>
> > * Optimize "C.has_morphism(X)" and "X in C.morphisms()", so that it
> > can compete with the current "X in C.hom_category()" (which is an
> > awful and flawed notation - it's the current test that X is a
> > *morphism*!!).

> Done as well.

Excellent!

Please leave a note in the code stating that the ugly encapsulation
breaking direct access to _cache__all_super_categories should be
reverted at once when cached_method will finally be optimized (#8611).

Actually, you might want to leave a reminder about that on the 8611
ticket.

Simon King

unread,
Jan 10, 2011, 2:01:04 PM1/10/11
to sage-algebra
Hi Niles,

On 31 Dez. 2010, 23:53, Niles <nil...@gmail.com> wrote:
> ...
> > If there is no fourth option, it seems to me we should better
> > remove .category() from all classes deriving from Element.
>
> I believe elements should not have a category.  But I thought it was
> against the spirit of object-oriented programming to remove methods
> from a derived class.  I tried once very hard to figure out how to do
> it, and couldn't find a good way -- do you have one?  (And do you not
> feel guilty when doing it? :)

I found a way a few minute ago, and, no, I do not feel guilty!

The idea is to introduce a class "Eraser" that has a __get__ method
that raises an attribute error:

sage: class Eraser(object):
....: def __get__(self,inst,cls=None):
....: raise AttributeError
....:

Now, we create a class that inherits from CategoryObject and thus
ought to have a category() method. But we override this class method
by an instance of Eraser:

sage: class Foo(sage.structure.category_object.CategoryObject):
....: category = Eraser()
....:

Then, Foo instances raise an Attribute Error when the attribute
"category" is requested. Hence, by definition, they do not have a
"category" attribute, although it is still available by dir() and TAB
completion:

sage: x = Foo(Rings())
sage: x
<class '__main__.Foo'>
sage: 'category' in dir(x)
True
sage: x.cat<TAB>
x.categories x.category
sage: x.category
Traceback (most recent call last):
...
AttributeError:

If you want to access the category() method that Foo inherits from
CategoryObject, you can use "super":

sage: super(Foo,x).category()
Category of rings

Best regards,
Simon

Niles

unread,
Jan 11, 2011, 12:21:37 PM1/11/11
to sage-algebra
Hi Simon,

I have thought about something like this too, although defining an
Eraser class is a particularly elegant idea which hadn't occurred to
me (in the multivariate power series code, I wrote
NotImplementedError's for each and every method which either didn't
make sense, or hadn't yet been rewritten for multivariate series).
Here are some other thoughts, in no particular order:

* I think you're right that AttributeError is better for this purpose
than NotImplementedError

* I can't quite tell from the way you wrote above -- do you plan to
include a note about 'super' in the AttributeError message? Some hint
for the casual user would, I think, be nice. It might also be good to
allow Eraser() to take an optional string argument and pass it
(possibly with additional information) to the AttributeError.

* This still feels a little contrary to the principle of object
oriented design, in that a class which inherits from another should be
promising to do _everything_ that the superclass does, plus more.
However, this may represent only my narrow view on the topic, a
possibility that seems especially likely since I haven't found any
reasonable references which substantiate this view.

* Moreover, there are a lot of places in sage where this kind of
"partial inheritance" should happen -- the multivariate power series
code I wrote being one of the first that leaps to mind!

* There might be an argument to be made that the existence of an
Eraser class would allow people to be a little more lazy and/or sloppy
when they consider designing code and write code: "Don't know what to
do when half of your inherited methods don't make sense? Just Erase()
them!". On the other hand, the added freedom might get more code
written and added to sage when people don't get "stuck" on a design
issue related to tricky inheritance.

* It's a little disappointing that these erased methods still show up
in tab completion, but that seems like an acceptable concession,
especially given that the inherited methods will show up whether they
are "erased" or not!

* Naming: some other options which come to mind are Uninherit(),
Undefine(), UndefineAttribute(), UndefineMethod(), UndefinedMethod(),
ErasedAttribute(), etc.


So, in the end, I think this is a great idea even though I have
conflicting reactions :) Maybe it would be a good idea to ask sage-
devel about the idea too. If nothing else, it would raise awareness
for something that has potential to be useful throughout sage.

best,
Niles

Simon King

unread,
Jan 11, 2011, 2:26:37 PM1/11/11
to sage-algebra
Hi Niles,

On 11 Jan., 18:21, Niles <nil...@gmail.com> wrote:
>  * I think you're right that AttributeError is better for this purpose
> than NotImplementedError

Yes, it is about the *existence* of the attribute.

>  * I can't quite tell from the way you wrote above -- do you plan to
> include a note about 'super' in the AttributeError message?

Perhaps there is a misunderstanding: I do *not* plan to use the eraser
in the ticket that I am currently preparing. I think it is a hack that
may be quite confusing to people.

What I plan with regards to Element/Morphism.category() is as follows:

* Currently, the top-most class with a category() is SageObject. I
believe this is wrong: There should be objects that rightfully belong
to Sage, but should not have a category. So, I suggest to remove
category() (and thus also _test_category()) from SageObject.

* Directly inheriting from SageObject is the class CategoryObject.
Currently, it has its own category() method, overriding what it
inherited from SageObject. I suggest to keep CategoryOject.category()
and move SageObject._test_category() to CategoryObject.

* Element and Morphism currently inherit from SageObject, whereas
Parent inherits from CategoryObject. I would not touch these
inheritances. I would remove Element.category() and
Morphism.category(). Since I removed SageObject.category(), Element
and Morphism would now be without category().

So, no need for an Eraser.

The only purpose of the Eraser was to answer your question, but it had
nothing to do with my current plans.

However, if you think it would be useful, I could provide a tool
called "erased" so that one can, e.g. write
sage: class Foo(Parent):
....: base = erased
....:
sage: F = Foo(ZZ)
sage: 'base' in dir(F)
True
sage: hasattr(F,'base')
False
sage: F.base
AttributeError: 'Foo' object has no attribute 'base'
sage: F.base_ring()
Integer Ring

But that would be on a different ticket.

>  Some hint
> for the casual user would, I think, be nice.  It might also be good to
> allow Eraser() to take an optional string argument and pass it
> (possibly with additional information) to the AttributeError.

Right.

>  * This still feels a little contrary to the principle of object
> oriented design, in that a class which inherits from another should be
> promising to do _everything_ that the superclass does, plus more.

Sure. This is why I do *not* suggested to use that trick, and will not
use it.

>  * It's a little disappointing that these erased methods still show up
> in tab completion, but that seems like an acceptable concession,
> especially given that the inherited methods will show up whether they
> are "erased" or not!

I think this is not possible, because __dict__ will certainly contain
the erased name as a key (the associated value is the Eraser
instance!). But, as much as I know, the keys of the __dict__ will
always show up in dir().

> So, in the end, I think this is a great idea even though I have
> conflicting reactions :)  Maybe it would be a good idea to ask sage-
> devel about the idea too.  If nothing else, it would raise awareness
> for something that has potential to be useful throughout sage.

Yeah, might be good to ask on Sage-devel. I think the eraser is a
silly idea, but perhaps people disagree and like to use it...

Cheers,
Simon

Niles

unread,
Jan 11, 2011, 7:09:47 PM1/11/11
to sage-algebra
Hi!

On Jan 11, 2:26 pm, Simon King <simon.k...@uni-jena.de> wrote:
> Hi Niles,
>
> On 11 Jan., 18:21, Niles <nil...@gmail.com> wrote:
>
> Perhaps there is a misunderstanding: I do *not* plan to use the eraser
> in the ticket that I am currently preparing. I think it is a hack that
> may be quite confusing to people.
>
yes, I was a little confused -- sorry


> What I plan with regards to Element/Morphism.category() is as follows:
> ...
this is what I had thought before -- what you actually plan sounds
very good and reasonable :)

>
> Yeah, might be good to ask on Sage-devel. I think the eraser is a
> silly idea, but perhaps people disagree and like to use it...

Well, "silly" was one of the first thoughts I had too, but upon
further reflection I do think there's a reasonable use-case for it.
Couldn't hurt to see what people think anyway . . .

best,
Niles

Nicolas M. Thiery

unread,
Jan 12, 2011, 4:32:38 AM1/12/11
to sage-a...@googlegroups.com
On Tue, Jan 11, 2011 at 04:09:47PM -0800, Niles wrote:
> > Yeah, might be good to ask on Sage-devel. I think the eraser is a
> > silly idea, but perhaps people disagree and like to use it...
>
> Well, "silly" was one of the first thoughts I had too,

+1 :-)

> but upon further reflection I do think there's a reasonable use-case
> for it.

As a temporary workaround for code being refactored in several steps,
that can be ok. But in general this is definitely a CodeSmell.

> Couldn't hurt to see what people think anyway . . .

Yup.

Niles

unread,
Jan 12, 2011, 10:38:10 AM1/12/11
to sage-algebra
Hi Nicholas,

On Jan 12, 4:32 am, "Nicolas M. Thiery" <Nicolas.Thi...@u-psud.fr>
wrote:

>
> As a temporary workaround for code being refactored in several steps,
> that can be ok. But in general this is definitely a CodeSmell.
>

I've never heard of CodeSmell before, so I looked it up ([1]:
Wikipedia). One of the "common code smells" is precisely relevant:

* Refused request: a class that overrides a method of a base class in
such a way that the contract of the base class is not honored by the
derived class. See Liskov substitution principle.

Thanks for the pointer!

-Niles

[1]: http://en.wikipedia.org/wiki/Code_smell

Nicolas M. Thiery

unread,
Jan 12, 2011, 12:40:04 PM1/12/11
to sage-a...@googlegroups.com
On Wed, Jan 12, 2011 at 07:38:10AM -0800, Niles wrote:
> I've never heard of CodeSmell before, so I looked it up

I am glad that my little hook caught your curiosity :-)

Reply all
Reply to author
Forward
0 new messages