sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.32k stars 451 forks source link

Meta-ticket: ForgetfulFunctor not working properly #31247

Open mjungmath opened 3 years ago

mjungmath commented 3 years ago

We have currently the following bug:

sage: from sage.categories.functor import ForgetfulFunctor
sage: F = ForgetfulFunctor(Rings(), Sets())
sage: F(ZZ)
Integer Ring

The result should rather be:

sage: Set(ZZ)
Set of elements of Integer Ring

so that:

Tickets:

CC: @tscrim @mkoeppe @tobiasdiez @nthiery @simon-king-jena @hivert

Component: categories

Issue created by migration from https://trac.sagemath.org/ticket/31247

mjungmath commented 3 years ago
comment:1

Digging into the source code, the forgetful functor F calls the following code from its class parent Functor:

    def _apply_functor(self, x):
        """
        Apply the functor to an object of ``self``'s domain.

        NOTE:

        Each subclass of :class:`Functor` should overload this method. By default,
        this method coerces into the codomain, without checking whether the
        argument belongs to the domain.

        TESTS::

            sage: from sage.categories.functor import Functor
            sage: F = Functor(FiniteFields(),Fields())
            sage: F._apply_functor(ZZ)
            Rational Field

        """
        return self.__codomain(x)

Here, __codomain should be the destination category. However, the call function of a category is treated as some kind of coercion/conversion:

    def __call__(self, x, *args, **opts):
        """
        Construct an object in this category from the data in ``x``,
        or throw ``TypeError`` or ``NotImplementedError``.

        If ``x`` is readily in ``self`` it is returned unchanged.
        Categories wishing to extend this minimal behavior should
        implement :meth:`._call_`.

        EXAMPLES::

            sage: Rings()(ZZ)
            Integer Ring
        """
        if x in self:
            return x
        return self._call_(x, *args, **opts)

as the following note in sage/categories/sets_cat.py indicates, too:

    def _call_(self, X, enumerated_set=False):
        r"""
        Construct an object in this category from the data ``X``.

        INPUT:

        - ``X`` -- an object to be converted into a set

        - ``enumerated_set`` -- if set to ``True`` and the input is either a
          Python tuple or a Python list then the output will be a finite
          enumerated set.

        EXAMPLES::

            sage: Sets()(ZZ)
            Integer Ring
            sage: Sets()([1, 2, 3])
            {1, 2, 3}

            sage: S = Sets()([1, 2, 3]); S.category()
            Category of finite sets
            sage: S = Sets()([1, 2, 3], enumerated_set=True); S.category()
            Category of facade finite enumerated sets

        .. NOTE::

           Using ``Sets()(A)`` used to implement some sort of forgetful functor
           into the ``Sets()`` category. This feature has been removed, because
           it was not consistent with the semantic of :meth:`Category.__call__`.
           Proper forgetful functors will eventually be implemented, with
           another syntax.
        """
        if enumerated_set and type(X) in (tuple, list, range):
            from sage.categories.enumerated_sets import EnumeratedSets
            return EnumeratedSets()(X)
        from sage.sets.set import Set
        return Set(X)

Proper forgetful functors will eventually be implemented, with another syntax. It seems, this hasn't been done so far.

mjungmath commented 3 years ago
comment:2

One could try the following: overload _apply_functor for forgetful functors and call _call_ directly. That would be a minimal solution, but I am not sure whether it is the most elegant.

tscrim commented 3 years ago
comment:4

I don't think this is a bug. The code is performing as it should because ZZ is already in Sets(). It doesn't need to create a new object, much less an instance of a different class. I think if you want to change your object, you should call the explicit constructor rather than relying on a functor. Moreover, if it is not going to be the current behavior, then I think you're asking for a solution to an impossible problem.

mjungmath commented 3 years ago
comment:5

Replying to @tscrim:

I don't think this is a bug. The code is performing as it should because ZZ is already in Sets(). It doesn't need to create a new object, much less an instance of a different class.

Then, the same would hold for manifolds in #31241. Differentiable manifolds are also in the category of topological manifolds (or at least should be). But we already agreed that it's a bug there. What is the difference then?

I think if you want to change your object, you should call the explicit constructor rather than relying on a functor. Moreover, if it is not going to be the current behavior, then I think you're asking for a solution to an impossible problem.

Well, the forgetful functor has the purpose to forget imposed structures, and see the object as an object in the super category only. This condition is currently not met:

sage: from sage.categories.functor import ForgetfulFunctor 
....: F = ForgetfulFunctor(Rings(), Sets()) 
....: F(ZZ).category()                                                    
Join of Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces
sage: F(ZZ)(1) + F(ZZ)(2)                                                       
3

Usual set operations are not even compatible:

sage: Set([1,2,3]).union(F(ZZ))                                                    
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
...
TypeError: X (=Integer Ring) must be a Set

Of course, different structures need different implementations.

For manifolds, the current stage of behavior is rather bad. Differentiable manifolds can be seen as topological manifolds, however each differentiable manifold comes with a differentiable structure which Sage keeps implicitly track of. This must be dropped after the forgetful functor had been applied. But with the above behavior, this is not possible because the original instance would be returned.

mkoeppe commented 3 years ago
comment:6

Sage development has entered the release candidate phase for 9.3. Setting a new milestone for this ticket based on a cursory review of ticket status, priority, and last modification date.

mkoeppe commented 3 years ago
comment:7

Another example:

sage: R.<x> = QQ[]
sage: phi = R.hom([x^2]); phi
Ring endomorphism of Univariate Polynomial Ring in x over Rational Field
  Defn: x |--> x^2
sage: phi.category_for()
Join of Category of euclidean domains and Category of commutative algebras over (number fields and quotient fields and metric spaces) and Category of infinite sets
sage: F = ForgetfulFunctor(Rings(), Sets())
sage: F(phi)
Ring endomorphism of Univariate Polynomial Ring in x over Rational Field
  Defn: x |--> x^2

I hoped to compute the correct category of an image in #32121 (Replace MapCombinatorialClass, add methods Map.image, Map.pushforward)...

tscrim commented 3 years ago
comment:8

This in and of itself is not a bug IMO as per comment:4. However, what is a bug IMO is that

sage: F(phi).parent()
Set of Homomorphisms from Univariate Polynomial Ring in x over Rational Field
 to Univariate Polynomial Ring in x over Rational Field

which leads to this

sage: F(phi).category_for()
Join of Category of euclidean domains
 and Category of commutative algebras over (number fields and quotient fields and metric spaces)
 and Category of infinite sets

For morphisms, where the category is part of the homset constructor, we can create the new homset parent and then convert the morphism into that parent. Mostly this just consists of rebuilding the same object but with a different parent. This is meaningful when we want to compose maps (as opposed to manipulating sets).

mkoeppe commented 2 years ago

Description changed:

--- 
+++ 
@@ -13,4 +13,10 @@
 sage: Set(ZZ)
 Set of elements of Integer Ring

+so that: +- F(ZZ).category() gives InfiniteEnumeratedSets() +- ... and thus Hom(F(ZZ), ...) will construct morphisms in the correct category +- it uses equality as sets, not as rings

+ +

mkoeppe commented 2 years ago

Description changed:

--- 
+++ 
@@ -19,4 +19,6 @@
 - it uses equality as sets, not as rings

+Tickets: 
+- #34384 Proper `ForgetfulFunctor` with `codomain=Sets()` or its full subcategories
mkoeppe commented 2 years ago
comment:14

Replying to @tscrim:

I don't think this is a bug. The code is performing as it should because ZZ is already in Sets(). It doesn't need to create a new object, much less an instance of a different class.

Mathematically one would, of course, say that the forgetful functor should act like the identity on a parent.

But our parents are equipped with a distinguished category.

So the forgetful functor needs to construct a new parent in which the distinguished category is replaced by the codomain.

mkoeppe commented 2 years ago

Description changed:

--- 
+++ 
@@ -21,4 +21,4 @@

 Tickets: 
 - #34384 Proper `ForgetfulFunctor` with `codomain=Sets()` or its full subcategories
-
+- #31241 Forgetful Functors for Manifold Objects
tscrim commented 2 years ago
comment:16

Replying to @mkoeppe:

Replying to @tscrim:

I don't think this is a bug. The code is performing as it should because ZZ is already in Sets(). It doesn't need to create a new object, much less an instance of a different class.

Mathematically one would, of course, say that the forgetful functor should act like the identity on a parent.

That strongly depends on how mathematically precise you want to connect a parent to its mathematical model. Strictly speaking, ZZ is just a set, but the ring of integers is really (ZZ, +, *) with distinguished binary operations +: ZZ x ZZ -> ZZ and *: ZZ x ZZ -> ZZ. However, the "ring" ZZ is just modeling the set and it is the implementation of the elements that carry the information of + and *. However, if you never add or multiply the elements together, then 1 is just an element of the set ZZ.

But our parents are equipped with a distinguished category.

I think it is better to treat it like "this is the smallest category where this object (currently) makes sense to belong to."

So the forgetful functor needs to construct a new parent in which the distinguished category is replaced by the codomain.

I still contest that the set of objects is still the set of objects. Equality is not always the mathematical equality you are thinking of (which we actually often think of as being "isomorphic to").

Even allowing that we wanted a new parent, this would be an impossible problem to solve as we would either have to have a class for the forgetful functor for each category (a huge number) or we would need to know which methods to remove (any parent can implement their own custom version or ones that only make sense in category C' not its super category C).

If the user wants to do equality as some other type of object, then we should tell the user to build other type. For example, Set(ZZ) to do equality as a Set object.

Morphisms already have a hook for specifying the category. This might not be fully implemented, but if we wanted to consider the isomorphism x |-> 2x of Q as a Q-vector space, but it doesn't make sense as a ring morphism. The forgetful functor should change the category of the morphism.

mkoeppe commented 2 years ago
comment:17

Replying to @tscrim:

Even allowing that we wanted a new parent, this would be an impossible problem to solve as we would either have to have a class for the forgetful functor for each category (a huge number) or we would need to know which methods to remove (any parent can implement their own custom version or ones that only make sense in category C' not its super category C).

I agree that it is impossible to solve in general.

That should not stop us from implementing it for useful cases. In #34384, I implement it for codomain=Sets().

There's warnings for the rest. We could also raise a NotImplementedError.

mkoeppe commented 2 years ago
comment:18

Replying to @tscrim:

Replying to @mkoeppe:

our parents are equipped with a distinguished category.

... namely, what the method category() returns.

I think it is better to treat it like "this is the smallest category where this object (currently) makes sense to belong to."

... this is ill-defined or tautological

mkoeppe commented 2 years ago
comment:19

Replying to @tscrim:

the "ring" ZZ is just modeling the set and it is the implementation of the elements that carry the information of + and *.

This distinction is irrelevant because the parent ZZ is also equipped with a distinguished element class.

tscrim commented 2 years ago
comment:20

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

our parents are equipped with a distinguished category.

... namely, what the method category() returns.

That in no way means that any parent is specifically meant to be associated with that category and no other. If that was true, there would be absolutely no reason to have a category option to Hom.

I think it is better to treat it like "this is the smallest category where this object (currently) makes sense to belong to."

... this is ill-defined or tautological

No and no. It is well-defined based upon what is implemented. It is not tautological because, e.g., ZZ is also in Sets(). Furthermore, saying it is tautological makes no sense because then any definition involving "the smallest" would be tautological.

tscrim commented 2 years ago
comment:21

Replying to @mkoeppe:

Replying to @tscrim:

the "ring" ZZ is just modeling the set and it is the implementation of the elements that carry the information of + and *.

This distinction is irrelevant because the parent ZZ is also equipped with a distinguished element class.

In this case, but broadly within Sage, that is completely false. We have many things that have multiple types of element classes. Most (all?) of them are homsets. We don't (yet) have a good system to handle these parents.

mkoeppe commented 2 years ago
comment:22

Replying to @tscrim:

We have many things that have multiple types of element classes. Most (all?) of them are homsets.

The discussion of parents with multiple elements classes is obviously irrelevant for this discussion.

tscrim commented 2 years ago
comment:23

Replying to @mkoeppe:

Replying to @tscrim:

We have many things that have multiple types of element classes. Most (all?) of them are homsets.

The discussion of parents with multiple elements classes is obviously irrelevant for this discussion.

You just tried to make a point about it. In fact, having a framework for multiple types of elements has been a wishlist item for a while.

mkoeppe commented 2 years ago
comment:24

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

our parents are equipped with a distinguished category.

... namely, what the method category() returns.

That in no way means that any parent is specifically meant to be associated with that category and no other.

That's not what "distinguished" means.

tscrim commented 2 years ago
comment:25

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

our parents are equipped with a distinguished category.

... namely, what the method category() returns.

That in no way means that any parent is specifically meant to be associated with that category and no other.

That's not what "distinguished" means.

If you do not mean it in that way, which is what typically means by "distinguished", then the result of category() is irrelevant.

mkoeppe commented 2 years ago
comment:26

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

We have many things that have multiple types of element classes. Most (all?) of them are homsets.

The discussion of parents with multiple elements classes is obviously irrelevant for this discussion.

You just tried to make a point about it. In fact, having a framework for multiple types of elements has been a wishlist item for a while.

Whether you have just one element class or multiple element classes -- the parent is still equipped with the list of the element classes that can be used with it.

And therefore, once more, the distinction of whether the operations are carried in the parent or in the elements is irrelevant.

mkoeppe commented 2 years ago
comment:27

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

I think it is better to treat it like "this is the smallest category where this object (currently) makes sense to belong to."

By smallest, do you mean largest?

mkoeppe commented 2 years ago
comment:28

What's ill-defined is the expression "makes sense to belong to".

tscrim commented 2 years ago
comment:29

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

We have many things that have multiple types of element classes. Most (all?) of them are homsets.

The discussion of parents with multiple elements classes is obviously irrelevant for this discussion.

You just tried to make a point about it. In fact, having a framework for multiple types of elements has been a wishlist item for a while.

Whether you have just one element class or multiple element classes -- the parent is still equipped with the list of the element classes that can be used with it.

And therefore, once more, the distinction of whether the operations are carried in the parent or in the elements is irrelevant.

It is quite important because you care about the extra structure placed on top of ZZ the set. The parent never models the ring operations because they are invoked with, e.g., __add__ and __mul__. It might carry the implementation, but you need to construct elements to do the multiplication of elements. If you do not do any + or * operations on the elements, you have a set.

Now you are probably going to try and counter with something along the lines of "but equality as rings is different". However, Python/programming equality == does not have to be the same as mathematical equality. For example, does 2 == 4 / 2? I could say no because the QQ representation is different (provided I don't normalize). So you could have ZZ with a different ring structure, which does not compare as == to ZZ. This is fine because its representation within Sage is different. If we want to compare them as sets (for argument's sake here, consider a finite subset), we have Set for this.

mkoeppe commented 2 years ago
comment:30

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

our parents are equipped with a distinguished category.

... namely, what the method category() returns.

That in no way means that any parent is specifically meant to be associated with that category and no other.

That's not what "distinguished" means.

If you do not mean it in that way, which is what typically means by "distinguished", then the result of category() is irrelevant.

Well, the category returned by category() is certainly not arbitrary. It determines which methods have been mixed into the parent class (and which methods will be mixed into element classes).

tscrim commented 2 years ago
comment:31

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

I think it is better to treat it like "this is the smallest category where this object (currently) makes sense to belong to."

By smallest, do you mean largest?

I mean in smallest the sense of super/sub categories as we have defined the terminology in Sage, which is backwards of how we like to think of with forgetful functors.

tscrim commented 2 years ago
comment:32

Replying to @mkoeppe:

What's ill-defined is the expression "makes sense to belong to".

The implementation with the methods required by the category.

mkoeppe commented 2 years ago
comment:33

This is exactly what makes it tautological. The category returned category() defines exactly those methods that have been mixed into the parent.

tscrim commented 2 years ago
comment:34

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

our parents are equipped with a distinguished category.

... namely, what the method category() returns.

That in no way means that any parent is specifically meant to be associated with that category and no other.

That's not what "distinguished" means.

If you do not mean it in that way, which is what typically means by "distinguished", then the result of category() is irrelevant.

Well, the category returned by category() is certainly not arbitrary. It determines which methods have been mixed into the parent class (and which methods will be mixed into element classes).

However those are just default implementations to reduce code duplication. There are some methods that are ambiguous, the biggest one I can think of is gens(), which is why we are now advocating for *_generators().

tscrim commented 2 years ago
comment:35

Replying to @mkoeppe:

This is exactly what makes it tautological. The category returned category() defines exactly those methods that have been mixed into the parent.

If you don't want to consider your object in that category, then you don't call any of those methods. Neither do the methods say what category it belongs to. I could implement ZZ as a set with an operation coproduct that could have nothing to do with bialgebras. That doesn't mean it should be treated as a bialgebra.

mkoeppe commented 2 years ago
comment:36

Replying to @tscrim:

something along the lines of "but equality as rings is different".

Yes, because equality as XYZ is a statement about maps in the category XYZ.

Which is why the forgetful functor when applied to a parent with a distinguished category needs to return a parent with the changed category.

Completely unrelated to the "equality of representations" issues that you talked about.

mkoeppe commented 2 years ago
comment:37

Replying to @tscrim:

So you could have ZZ with a different ring structure, which does not compare as == to ZZ. This is fine because its representation within Sage is different. If we want to compare them as sets (for argument's sake here, consider a finite subset), we have Set for this.

Yes, Set(...) is the forgetful functor, sending the parent with a distinguished category C to a parent whose distinguished category is a full subcategory of Sets().

34384 makes this functor available systematically as the ForgetfulFunctor(..., Sets()). That's all

mkoeppe commented 2 years ago
comment:38

Replying to @tscrim:

If you don't want to consider your object in that category, then you don't call any of those methods. Neither do the methods say what category it belongs to.

Exactly, we declare a distinguished category when we call Parent(..., category=...) -- because it would be ill-defined to guess it from the defined methods.

mkoeppe commented 2 years ago
comment:39

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

If you do not mean it in that way, which is what typically means by "distinguished", then the result of category() is irrelevant.

Well, the category returned by category() is certainly not arbitrary. It determines which methods have been mixed into the parent class (and which methods will be mixed into element classes).

However those are just default implementations to reduce code duplication.

Hardly. It defines and specifies the required methods and even provides tests for these specifications.

tscrim commented 2 years ago
comment:40

Replying to @mkoeppe:

Replying to @tscrim:

something along the lines of "but equality as rings is different".

Yes, because equality as XYZ is a statement about maps in the category XYZ.

Isomorphism is not equality. It is very important not to confuse the two. As soon as you start talking about maps, you are talking isomorphism. In fact, I don't think many category theorists even talk about equality (everything is up to isomorphism).

In fact, that is exactly my point, the forgetful functor is about maps, not the objects. The category for the morphisms should change, but the objects do not need to change. You just no longer want to perform certain operations.

Which is why the forgetful functor when applied to a parent with a distinguished category needs to return a parent with the changed category.

Changing the implementation is not a mathematical operation. It is a programming one. This further reinforces that we should be talking about Python ==, not mathematical equality. The distinguished category has nothing to do with its mathematical equality statement.

Completely unrelated to the "equality of representations" issues that you talked about.

So == should always be mathematical equality, in some definition that you have not made precise? What about the cases when we want this isomorphic to (this occurs in permutation groups) or when no canonical representative exists (e.g., elements in the symbolic ring or for manifolds). We could go for this, but then should we remove code that doesn't comply or things that do not have canonical representatives? If we allow == for non-canoical representatives, then what makes those cases special compared to these parents?

tscrim commented 2 years ago
comment:41

Replying to @mkoeppe:

Replying to @tscrim:

So you could have ZZ with a different ring structure, which does not compare as == to ZZ. This is fine because its representation within Sage is different. If we want to compare them as sets (for argument's sake here, consider a finite subset), we have Set for this.

Yes, Set(...) is the forgetful functor, sending the parent with a distinguished category C to a parent whose distinguished category is a full subcategory of Sets().

I can agree with this statement.

34384 makes this functor available systematically as the ForgetfulFunctor(..., Sets()). That's all

We can do this, but that is just some semmantic sugar with making Set() a distinguished object in the category Sets() for the ForgetfulFunctor(). I have no objections. What I do have a strong objection to is saying if it did not do that, then it is a bug.

tscrim commented 2 years ago
comment:42

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

If you do not mean it in that way, which is what typically means by "distinguished", then the result of category() is irrelevant.

Well, the category returned by category() is certainly not arbitrary. It determines which methods have been mixed into the parent class (and which methods will be mixed into element classes).

However those are just default implementations to reduce code duplication.

Hardly. It defines and specifies the required methods and even provides tests for these specifications.

Yes, you're right here. They do provide specifications for those methods. However, they do also (generally) need to be consistent with things in the super categories because those ParentMethods will become superclasses. Good programming means we are generally consistent here, but there can be good reason to break that.

mkoeppe commented 2 years ago
comment:43

Replying to @tscrim:

the forgetful functor is about maps, not the objects. The category for the morphisms should change, but the objects do not need to change. You just no longer want to perform certain operations.

But the objects have a distinguished homset (which is determined by the distinguished category).

mkoeppe commented 2 years ago
comment:44

Replying to @tscrim:

Replying to @mkoeppe:

Replying to @tscrim:

something along the lines of "but equality as rings is different".

Yes, because equality as XYZ is a statement about maps in the category XYZ.

... namely the identity map.

mkoeppe commented 2 years ago
comment:45

Replying to @tscrim:

Completely unrelated to the "equality of representations" issues that you talked about.

So == should always be mathematical equality, in some definition that you have not made precise?

The coercion system is in charge of this, it defines (up to implementation restrictions and bugs) our model of "equality".

No changes to it are necessary, nor do I propose changes to it.

tscrim commented 2 years ago
comment:46

Replying to @mkoeppe:

Replying to @tscrim:

the forgetful functor is about maps, not the objects. The category for the morphisms should change, but the objects do not need to change. You just no longer want to perform certain operations.

But the objects have a distinguished homset (which is determined by the distinguished category).

No, they do not. Hom takes a category argument to specify the category the morphism belongs to:

sage: Hom(QQ, ZZ)
Set of Homomorphisms from Rational Field to Integer Ring
sage: Hom(QQ, ZZ, category=Sets())
Set of Morphisms from Rational Field to Integer Ring in Category of sets
mkoeppe commented 2 years ago
comment:47

Replying to @tscrim:

34384 makes this functor available systematically as the ForgetfulFunctor(..., Sets()). That's all

We can do this, but that is just some semantic sugar

... that's a new one, yes, functors do provide semantic sugar which enables composability.

mkoeppe commented 2 years ago
comment:48

Replying to @tscrim:

Replying to @mkoeppe:

But the objects have a distinguished homset (which is determined by the distinguished category).

No, they do not. Hom takes a category argument to specify the category the morphism belongs to:

sage: Hom(QQ, ZZ)
Set of Homomorphisms from Rational Field to Integer Ring

Here the distinguished category determined the default.

tscrim commented 2 years ago
comment:49

Replying to @mkoeppe:

Replying to @tscrim:

Completely unrelated to the "equality of representations" issues that you talked about.

So == should always be mathematical equality, in some definition that you have not made precise?

The coercion system is in charge of this, it defines (up to implementation restrictions and bugs) our model of "equality".

Not for elements of the same parent or between different parents.

No changes to it are necessary, nor do I propose changes to it.

You are implicitly changing the requirements (and hence, the definition) by calling something a bug that IMSO should not be (nor has it previously been considered to be AFAIK).

If I have a new ring structure on Z, lets call it TT, then ZZ == TT would be False. Now if we apply the forgetful functor to them, they would still be false. I am saying this is fine and proper because they are different representations of the same set, but you are saying it is a bug.

mkoeppe commented 2 years ago
comment:50

Replying to @tscrim:

You are implicitly changing the requirements (and hence, the definition) by calling something a bug that IMSO should not be (nor has it previously been considered to be AFAIK).

Read https://github.com/sagemath/sage-prod/blob/develop/src/sage/categories/sets_cat.py#L254

tscrim commented 2 years ago
comment:51

Replying to @mkoeppe:

Replying to @tscrim:

Replying to @mkoeppe:

But the objects have a distinguished homset (which is determined by the distinguished category).

No, they do not. Hom takes a category argument to specify the category the morphism belongs to:

sage: Hom(QQ, ZZ)
Set of Homomorphisms from Rational Field to Integer Ring

Here the distinguished category determined the default.

Mathematically, any homset should also take the category as input. We just usually suppress it from notion as context determines, just like what is done here. Now I will concede that applying the forgetful functor should make it clear what category we want to do it in, but that is just breaking the syntactic (semantic?) sugar. You would rather try to solve an impossible problem than simply telling the user to type fewer characters with a more intuitive semantic?

mkoeppe commented 2 years ago
comment:52

Replying to @tscrim:

If I have a new ring structure on Z, lets call it TT, then ZZ == TT would be False. Now if we apply the forgetful functor to them, they would still be false. I am saying this is fine and proper because they are different representations of the same set

I wouldn't say that it's fine and proper; I'd say there is an implementation restriction that stops us from detecting that they are equal. Sage is of course full of such implementation restrictions.

A proper implementation of the forgetful functor will improve this situation: It removes the implementation restriction.

mkoeppe commented 2 years ago
comment:53

Replying to @tscrim:

Now I will concede that applying the forgetful functor should make it clear what category we want to do it in

Yes, in particular because after applying it, the distinguished category has changed. We can just call the method .category() if we want to know it.

tscrim commented 2 years ago
comment:54

Replying to @mkoeppe:

Replying to @tscrim:

You are implicitly changing the requirements (and hence, the definition) by calling something a bug that IMSO should not be (nor has it previously been considered to be AFAIK).

Read https://github.com/sagemath/sage-prod/blob/develop/src/sage/categories/sets_cat.py#L254

This is not evidence of anything about a general bug for ForgetfulFunctor. That is about this category's implementation of __call__, which is what ForgetfulFunctor does. If F: C -> D, then F(X) is performs D(x). It is up to each category to impose how it wants to perform its __call__, which is an implementation detail.