@@ -778,6 +778,10 @@ def is_abelian(self):
778
778
"""
779
779
raise NotImplementedError ("is_abelian" )
780
780
781
+ ##########################################################################
782
+ # Methods related to the category hierarchy
783
+ ##########################################################################
784
+
781
785
def category_graph (self ):
782
786
r"""
783
787
Returns the graph of all super categories of this category
@@ -1013,6 +1017,228 @@ def _super_categories_for_classes(self):
1013
1017
self ._all_super_categories
1014
1018
return self ._super_categories_for_classes
1015
1019
1020
+ ##########################################################################
1021
+ # Methods handling of full subcategories
1022
+ ##########################################################################
1023
+
1024
+ def is_structure_category (self ):
1025
+ """
1026
+ Return whether ``self`` is a structure category.
1027
+
1028
+ In Sage, a *structure* category `C` is a category that defines
1029
+ new structure or operations. Equivalently, `C` is *not* a full
1030
+ subcategory of the join of its super categories: the morphisms
1031
+ need to preserve more structure, and thus the homsets are
1032
+ smaller.
1033
+
1034
+ By default, a category is a structure category, while
1035
+ :ref:`category with axiom <category-primer-axioms>` or a
1036
+ :ref:`functorial construction category
1037
+ <category-primer-functorial-constructions>` is not.
1038
+
1039
+ EXAMPLES:
1040
+
1041
+ Here are some typical structure categories, with the
1042
+ additional structure they define::
1043
+
1044
+ sage: Sets().is_structure_category()
1045
+ True
1046
+ sage: Magmas().is_structure_category() # `*`
1047
+ True
1048
+ sage: AdditiveMagmas().is_structure_category() # `+`
1049
+ True
1050
+ sage: LeftModules(ZZ).is_structure_category() # left multiplication by scalar
1051
+ True
1052
+ sage: Coalgebras(QQ).is_structure_category() # coproduct
1053
+ True
1054
+ sage: CoxeterGroups().is_structure_category() # distinguished generators
1055
+ True
1056
+ sage: Crystals().is_structure_category() # crystal operators
1057
+ True
1058
+
1059
+ On the other hand, the category of semigroups is not a
1060
+ structure category, since its operation `+` is already defined
1061
+ by the category of magmas::
1062
+
1063
+ sage: Semigroups().is_structure_category()
1064
+ False
1065
+
1066
+ Most :ref:`categories with axiom <category-primer-axioms>` and
1067
+ most :ref:`functorial construction categories
1068
+ <category-primer-functorial-constructions>` don't define new
1069
+ structure::
1070
+
1071
+ sage: Sets().Finite().is_structure_category()
1072
+ False
1073
+ sage: Rings().Commutative().is_structure_category()
1074
+ False
1075
+ sage: Modules(QQ).FiniteDimensional().is_structure_category()
1076
+ False
1077
+ sage: Sets().CartesianProducts().is_structure_category()
1078
+ False
1079
+ sage: Sets().Quotients().is_structure_category()
1080
+ False
1081
+ sage: Modules(QQ).TensorProducts().is_structure_category()
1082
+ False
1083
+ sage: Algebras(QQ).Graded().is_structure_category()
1084
+ False
1085
+
1086
+ Exceptions include the category of unital magmas or the
1087
+ category of additive magmas which define a unit which is
1088
+ preserved by morphisms::
1089
+
1090
+ sage: Magmas().Unital().is_structure_category()
1091
+ True
1092
+ sage: AdditiveMagmas().AdditiveUnital().is_structure_category()
1093
+ True
1094
+
1095
+ or the category of graded modules which defines a grading
1096
+ which is preserved by morphisms::
1097
+
1098
+ sage: Modules(ZZ).Graded().is_structure_category()
1099
+ True
1100
+
1101
+ .. NOTE::
1102
+
1103
+ There are a couple categories that add some structure,
1104
+ where the structure can be useful to manipulate morphisms
1105
+ but where, in most use cases, we don't want the morphisms
1106
+ to necessarily preserve it. For example, in the context
1107
+ of finite dimensional vector spaces, having a
1108
+ distinguished basis allows for representing morphisms by
1109
+ matrices; yet considering only morphisms that preserve
1110
+ that distinguished basis would be boring.
1111
+
1112
+ In such cases, we might want to eventually have two
1113
+ categories, one where the additional structure is
1114
+ preseved, and one where it's not necessarily preserved
1115
+ (we would need to find an idiom for this).
1116
+
1117
+ At this point, a choice is to be made each time, according
1118
+ to the main use cases. Some of those choices are yet to be
1119
+ settled. For example, should by default:
1120
+
1121
+ - an euclidean domain morphism preserve euclidean
1122
+ division?::
1123
+
1124
+ sage: EuclideanDomains().is_structure_category()
1125
+ True
1126
+
1127
+ - an enumerated set morphism preserve the distinguished
1128
+ enumeration?::
1129
+
1130
+ sage: EnumeratedSets().is_structure_category()
1131
+ False
1132
+
1133
+ - a module with basis morphism preserve the distinguished
1134
+ basis?::
1135
+
1136
+ sage: Modules(QQ).WithBasis().is_structure_category()
1137
+ False
1138
+
1139
+ .. SEEALSO::
1140
+
1141
+ This method together with the methods overloading it
1142
+ provide the basic data to determine, for a given category,
1143
+ the super categories that define some structure (see
1144
+ :meth:`super_structure_categories`), and to test whether a
1145
+ category is a full subcategory of some other category (see
1146
+ :meth:`is_full_subcategory`).
1147
+
1148
+ The support for modeling full subcategories has been
1149
+ introduced in :trac:`16340`.
1150
+ """
1151
+ return True
1152
+
1153
+ @cached_method
1154
+ def super_structure_categories (self ):
1155
+ r"""
1156
+ Return the super structure categories of ``self``.
1157
+
1158
+ OUTPUT: a frozen set
1159
+
1160
+ This method is used in :meth:`is_full_subcategory` for
1161
+ deciding whether a category is a full subcategory of some
1162
+ other category, and for documentation purposes. It is computed
1163
+ recursively from the result of :meth:`is_structure_category`
1164
+ on the super categories of ``self``.
1165
+
1166
+ EXAMPLES::
1167
+
1168
+ sage: Objects().super_structure_categories()
1169
+ frozenset([])
1170
+
1171
+ sage: def structure_categories(C):
1172
+ ....: return Category._sort(C.super_structure_categories())
1173
+
1174
+ sage: structure_categories(Sets())
1175
+ (Category of sets, Category of sets with partial maps)
1176
+ sage: structure_categories(Magmas())
1177
+ (Category of magmas, Category of sets, Category of sets with partial maps)
1178
+
1179
+ In the following example, we only list the smallest structure
1180
+ categories to get a more readable output::
1181
+
1182
+ sage: def structure_categories(C):
1183
+ ....: return Category._sort_uniq(C.super_structure_categories())
1184
+
1185
+ sage: structure_categories(Magmas())
1186
+ (Category of magmas,)
1187
+ sage: structure_categories(Rings())
1188
+ (Category of unital magmas, Category of additive unital additive magmas)
1189
+ sage: structure_categories(Fields())
1190
+ (Category of euclidean domains,)
1191
+ sage: structure_categories(Algebras(QQ))
1192
+ (Category of unital magmas,
1193
+ Category of right modules over Rational Field,
1194
+ Category of left modules over Rational Field)
1195
+ sage: structure_categories(HopfAlgebras(QQ).Graded().WithBasis().Connected())
1196
+ (Category of hopf algebras over Rational Field,
1197
+ Category of graded modules over Rational Field)
1198
+ """
1199
+ result = { D for C in self .super_categories () for D in C .super_structure_categories () }
1200
+ if self .is_structure_category ():
1201
+ result .add (self )
1202
+ return frozenset (result )
1203
+
1204
+ def is_full_subcategory (self , other ):
1205
+ """
1206
+ Return whether ``self`` is a full subcategory of ``other``.
1207
+
1208
+ This is computed by testing if ``self`` is a subcategory of
1209
+ ``other``, and checking whether they have the same structure,
1210
+ as determined by :meth:`super_structure_categories` from the
1211
+ result of :meth:`is_structure_category` on the super
1212
+ categories.
1213
+
1214
+ EXAMPLES::
1215
+
1216
+ sage: Magmas().Associative().is_full_subcategory(Magmas())
1217
+ True
1218
+ sage: Magmas().Unital().is_full_subcategory(Magmas())
1219
+ False
1220
+ sage: Rings().is_full_subcategory(Magmas().Unital() & AdditiveMagmas().AdditiveUnital())
1221
+ True
1222
+
1223
+ .. TODO::
1224
+
1225
+ Those are consequences of :class:`EuclideanDomains`
1226
+ currently being a structure category. Is this what we
1227
+ want?::
1228
+
1229
+ sage: EuclideanDomains().is_full_subcategory(Rings())
1230
+ False
1231
+ sage: Fields().is_full_subcategory(Rings())
1232
+ False
1233
+ """
1234
+ return self .is_subcategory (other ) and \
1235
+ len (self .super_structure_categories ()) == \
1236
+ len (other .super_structure_categories ())
1237
+
1238
+ ##########################################################################
1239
+ # Test methods
1240
+ ##########################################################################
1241
+
1016
1242
def _test_category_graph (self , ** options ):
1017
1243
"""
1018
1244
Check that the category graph matches with Python's method resolution order
@@ -1094,6 +1320,11 @@ def _test_category(self, **options):
1094
1320
1095
1321
_cmp_key = _cmp_key
1096
1322
1323
+
1324
+ ##########################################################################
1325
+ # Construction of the associated abstract classes for parents, elements, ...
1326
+ ##########################################################################
1327
+
1097
1328
def _make_named_class (self , name , method_provider , cache = False , picklable = True ):
1098
1329
"""
1099
1330
Construction of the parent/element/... class of ``self``.
@@ -2750,6 +2981,21 @@ def super_categories(self):
2750
2981
"""
2751
2982
return self .__super_categories
2752
2983
2984
+ def is_structure_category (self ):
2985
+ r"""
2986
+ Return whether ``self`` is a structure category.
2987
+
2988
+ .. SEEALSO:: :meth:`Category.is_structure_category`
2989
+
2990
+ A join category defines no new structure.
2991
+
2992
+ EXAMPLES::
2993
+
2994
+ sage: Modules(ZZ).is_structure_category()
2995
+ False
2996
+ """
2997
+ return False
2998
+
2753
2999
def _subcategory_hook_ (self , category ):
2754
3000
"""
2755
3001
Returns whether ``category`` is a subcategory of this join category
0 commit comments