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/
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!
+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!
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.
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
> Nicolas M. Thiéry "Isil" <nth...@users.sf.net>
> http://Nicolas.Thiery.name/
>
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.
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.
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).
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?
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).
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.
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
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.
> 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.
+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.
I am glad that my little hook caught your curiosity :-)