# publicsympy/sympy

### Subversion checkout URL

You can clone with HTTPS or Subversion.

# Implement CT Base Classes#1338

Merged
merged 57 commits into from almost 2 years ago
 +1,337 1

### 4 participants

In this pull request I create the module categories, the base classes of this module, tests for them, documentation, and pretty printing.

commented on the diff June 10, 2012
 doc/src/modules/categories.txt 
 ... ... @@ -0,0 +1,27 @@ 1 +Category Theory Module
 2 Collaborator Krastanov added a note June 10, 2012 There is an effort to minimize the number of warnings generated by Sphinx. Could you check that the number of warnings with and without your new documentation stays the same (do not forget to run make clean between tests). Be aware that the warnings can come from docstrings and not from the txt files in the doc folder. scolobb added a note June 10, 2012 I get 16 warnings in master and 16 in my branch. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Collaborator

SymPy Bot Summary: There were test failures.

@scolobb: Please fix the test failures.

Interpreter: /opt/pym32/bin/python2.5 (2.5.6-final-0)
Architecture: Linux (32-bit)
Cache: yes
Test command: setup.py test
master hash: 8086d7f
branch hash: 26ea1dc

Automatic review by SymPy Bot.

Oh, sorry, I was lazy to run the tests on my computer :-(

Fixing the failures right now :-(

 doc/src/modules/categories.txt 
 ... ... @@ -0,0 +1,27 @@ 1 +Category Theory Module 2 +====================== 3 + 4 +.. module:: sympy.categories 5 + 6 +Introduction 7 +------------ 8 + 9 +The category theory module for SymPy will allow manipulating diagrams 10 +within a single category, including drawing them in TikZ and deciding 11 +whether they are commutative or not. 12 + 13 +The module is yet in its pre-embryonic stage.
 sympy/categories/baseclasses.py 
 ... ... @@ -0,0 +1,654 @@ 1 +from sympy.core import Set, Basic, FiniteSet, EmptySet, Dict 2 + 3 +class Class(Set): 4 + """ 5 + The base class for any kind of class in set-theoretic sense.
 sympy/categories/baseclasses.py 
 ... ... @@ -0,0 +1,654 @@ 1 +from sympy.core import Set, Basic, FiniteSet, EmptySet, Dict 2 + 3 +class Class(Set): 4 + """ 5 + The base class for any kind of class in set-theoretic sense. 6 + 7 + In axiomatic set theories, everything is a class. A class which 8 + can is a member of another class is a set. A class which is not a
 sympy/categories/baseclasses.py 
 ... ... @@ -0,0 +1,654 @@ 1 +from sympy.core import Set, Basic, FiniteSet, EmptySet, Dict 2 + 3 +class Class(Set): 4 + """ 5 + The base class for any kind of class in set-theoretic sense. 6 + 7 + In axiomatic set theories, everything is a class. A class which 8 + can is a member of another class is a set. A class which is not a 9 + member of another class is a proper class. The class {1, 2} is a
 2 Collaborator ness01 added a note June 10, 2012 latex the {1, 2} scolobb added a note June 11, 2012 Done. There's a (minor?) problem, however. I cannot get .. autoclass:: Class to work in categories.txt: Traceback (most recent call last):categories File "/usr/lib64/python2.7/site-packages/sphinx/ext/autodoc.py", line 326, in import_object obj = self.get_attr(obj, part) File "/usr/lib64/python2.7/site-packages/sphinx/ext/autodoc.py", line 232, in get_attr return safe_getattr(obj, name, *defargs) File "/usr/lib64/python2.7/site-packages/sphinx/util/inspect.py", line 67, in safe_getattr raise AttributeError(name) AttributeError: Class  I understand that Class isn't the best name for a Python class, and Sphinx is the first to bail out at me because of that. Initially I hoped that hiding Class under sympy.categories would address such problems. Do you think I should rename Class? Note that Class is absolutely rudimentary at the moment and I expect to actually see it implemented in the set theory module, when the time comes. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((30 lines not shown)) 30 + 31 + >>> from sympy.categories import Object 32 + >>> Object("A") == Object("A") 33 + True 34 + 35 + """ 36 + def __new__(cls, name): 37 + if not name: 38 + raise ValueError("Anonymous Objects are not allowed.") 39 + 40 + return Basic.__new__(cls, name) 41 + 42 + @property 43 + def name(self): 44 + """ 45 + Returns the name of this object.
 2 Collaborator ness01 added a note June 10, 2012 This needs a doctest. [I feel pedantic in pointing it out, and it annoys me to no end in writing my own code, but all new user-facin methods should come with a doctest. scolobb added a note June 11, 2012 I have added doctest for all user-facing methods. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((34 lines not shown)) 34 + 35 + """ 36 + def __new__(cls, name): 37 + if not name: 38 + raise ValueError("Anonymous Objects are not allowed.") 39 + 40 + return Basic.__new__(cls, name) 41 + 42 + @property 43 + def name(self): 44 + """ 45 + Returns the name of this object. 46 + """ 47 + return self._args[0] 48 + 49 + def __eq__(self, obj):
 4 Collaborator ness01 added a note June 10, 2012 apparently the second argument of such methods should be "other" Collaborator ness01 added a note June 10, 2012 In fact, why do you need this? Shouldn't basic provide these methods? scolobb added a note June 11, 2012 Now, when I store the names of objects in Dummy symbols, Basic.__eq__ will never return True for objects with the same name, because all Dummy's are different. Changed all (un)equality operators to use "other" to refer to their argument. scolobb added a note June 12, 2012 I'm now using Symbols when the supplied name is not empty, which allows me to fully rely on Basic's comparison and hash functions. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((47 lines not shown)) 47 + return self._args[0] 48 + 49 + def __eq__(self, obj): 50 + if not isinstance(obj, Object): 51 + return False 52 + 53 + return self.name == obj.name 54 + 55 + def __ne__(self, obj): 56 + if not isinstance(obj, Object): 57 + return True 58 + 59 + return self.name != obj.name 60 + 61 + def __hash__(self): 62 + return hash(self.name)
 4 Collaborator ness01 added a note June 10, 2012 You should hash in the type as well. But actually, I think this should just re-use the hash method of basic. But actually, if you don't implement __eq__ and __ne__ (see above), then you don't need __hash__ either. scolobb added a note June 11, 2012 Unfortunately, the consequence of using dummies: In [7]: hash(Dummy("A")) == hash(Dummy("A")) Out[7]: False  I'm still reluctant to pull in Symbol for just storing a name, though. Collaborator ness01 added a note June 12, 2012 I think you should use symbol. What's the point in not using a class that is available? My suggestion was: if the user provides a name, store that as a symbol otherwise, store a dummy scolobb added a note June 12, 2012 Well, Symbol is a rather large class, I was reluctant to use it just to store a string. I've changed the code to use Symbol and Dummy now, according to your recommendation. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Collaborator

SymPy Bot Summary: There were test failures.

@scolobb: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYlfIbDA

Interpreter: /usr/bin/python2.7 (2.7.3-candidate-2)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 8086d7f
branch hash: 26ea1dc

Automatic review by SymPy Bot.

 sympy/categories/baseclasses.py 
 ((60 lines not shown)) 60 + 61 + def __hash__(self): 62 + return hash(self.name) 63 + 64 +class Morphism(Basic): 65 + """ 66 + The base class for any kind of morphism in an abstract category. 67 + 68 + In abstract categories, a morphism is an arrow between two 69 + category objects. The object where the arrow starts is called the 70 + domain, while the object where the arrow ends is called the 71 + codomain. 72 + 73 + Two simple (not composed) morphisms with the same name, domain, 74 + and codomain are the same morphisms. A simple unnamed morphism is 75 + not equal to any other morphism.
 2 Collaborator ness01 added a note June 10, 2012 why are there anonymous morphisms but no anonymous objects? scolobb added a note June 11, 2012 That's because an object is essentially a name, so an object without a name is essentially nothing. On the other hand, a morphism, is an arrow between two objects, which may also have a name. Anonymous morphisms are useful when talking about identities (doesn't matter which name you chose, of course). Also, Diagram makes some use of such Morphisms: when constructing all possible compositions, the new composites are anonymous and are unique. However, I fixed this: In [5]: a = Morphism(A, A) In [6]: a == a Out[6]: True  which used to be False. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((91 lines not shown)) 91 + >>> A = Object("A") 92 + >>> B = Object("B") 93 + >>> C = Object("C") 94 + >>> f = Morphism(A, B, "f") 95 + >>> g = Morphism(B, C, "g") 96 + >>> f == Morphism(A, B, "f") 97 + True 98 + >>> f == Morphism(A, B, "") 99 + False 100 + >>> g * f 101 + Morphism(Object("B"), Object("C"), "g") * 102 + Morphism(Object("A"), Object("B"), "f") 103 + >>> id_A = Morphism(A, A, identity=True) 104 + >>> id_A == Morphism(A, A, identity=True) 105 + True 106 + >>> f * id_A == f
 3 Collaborator ness01 added a note June 10, 2012 I think you should explain what the __eq__ operator does. [I.e. to what extend does it flatten composed morphisms, use commutative diagrams, whatever.] scolobb added a note June 11, 2012 It's the job of __mul__ (compose, in fact) to discard identity morphisms, in this case. As for __eq__, I believe I did some explanation in the docstring of Morphism (paragraphs 3 and 4). Do you think I should add something more? Collaborator ness01 added a note June 12, 2012 Hm I think this is ok ... I just wasn't sure (as a user, without looking at the code), how the identity morphisms are taken care of. Since you have a doctest demonstrating this, I think you can leave it as is. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((102 lines not shown)) 102 + Morphism(Object("A"), Object("B"), "f") 103 + >>> id_A = Morphism(A, A, identity=True) 104 + >>> id_A == Morphism(A, A, identity=True) 105 + True 106 + >>> f * id_A == f 107 + True 108 + 109 + """ 110 + def __new__(cls, domain, codomain, name="", identity=False): 111 + if identity and (domain != codomain): 112 + raise ValueError( 113 + "identity morphisms must have the same domain and codomain") 114 + 115 + # The last component of self.args represents the components of 116 + # this morphism. 117 + return Basic.__new__(cls, domain, codomain, name, identity, [])
 2 Collaborator ness01 added a note June 10, 2012 The .args in a basic should be Basics as well (I'm not entirely sure, but I think strings are allowd ... @rlamy ?). My point is: the last argument should not be a list, but rather a (sympy) Tuple. scolobb added a note June 11, 2012 For the record: according to the recent discussion on the list, strings are not allowed. I've changed to list to Tuple. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((126 lines not shown)) 126 + @property 127 + def codomain(self): 128 + """ 129 + Returns the codomain of this morphism. 130 + """ 131 + return self.args[1] 132 + 133 + @property 134 + def name(self): 135 + """ 136 + Returns the name of this morphism. 137 + """ 138 + return self.args[2] 139 + 140 + @property 141 + def identity(self):
 sympy/categories/baseclasses.py 
 ((147 lines not shown)) 147 + 148 + @property 149 + def components(self): 150 + """ 151 + Returns the components of this morphisms. 152 + 153 + Examples 154 + ======== 155 + 156 + >>> from sympy.categories import Object, Morphism 157 + >>> A = Object("A") 158 + >>> B = Object("B") 159 + >>> C = Object("C") 160 + >>> f = Morphism(A, B, "f") 161 + >>> g = Morphism(B, C, "g") 162 + >>> (g * f).components == [f, g]
 4 Collaborator ness01 added a note June 10, 2012 Why not just do (g * f).components and doctest the resulting printout? scolobb added a note June 12, 2012 Because In [13]: str((g * f).components) Out[13]: (Morphism(Object("A"), Object("B"), "f"), Morphism(Object("B"), Object("C"), "g"))  I think directly comparing to [f, g] (or actually Tuple(f, g) as it is now) is easier to grasp. Do you think I should still show (g * f).components? Collaborator ness01 added a note June 12, 2012 On 12.06.2012 13:28, Sergiu Ivanov wrote: > + > + @property > + def components(self): > + """ > + Returns the components of this morphisms. > + > + Examples > + ======== > + > +>>> from sympy.categories import Object, Morphism > +>>> A = Object("A") > +>>> B = Object("B") > +>>> C = Object("C") > +>>> f = Morphism(A, B, "f") > +>>> g = Morphism(B, C, "g") > +>>> (g * f).components == [f, g] Because  In [13]: str((g * f).components) Out[13]: (Morphism(Object("A"), Object("B"), "f"), Morphism(Object("B"), Object("C"), "g"))  I think directly comparing to [f, g] (or actually Tuple(f, g) as it is now) is easier to grasp. Do you think I should still show (g * f).components? I see. I think showing .components is the best solution, then. scolobb added a note June 12, 2012 Showing (g * f).components now. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((187 lines not shown)) 187 + 188 + >>> from sympy.categories import Object, Morphism 189 + >>> A = Object("A") 190 + >>> B = Object("B") 191 + >>> C = Object("C") 192 + >>> f = Morphism(A, B, "f") 193 + >>> g = Morphism(B, C, "g") 194 + >>> f.compose(g) is None 195 + True 196 + >>> g.compose(f) 197 + Morphism(Object("B"), Object("C"), "g") * 198 + Morphism(Object("A"), Object("B"), "f") 199 + >>> (g.compose(f, "h")).name 200 + 'h' 201 + 202 + """
 2 Collaborator ness01 added a note June 10, 2012 You should check for the type of g and raise an exception if it is wrong. scolobb added a note June 12, 2012 Done. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((215 lines not shown)) 215 + new_name, False, g.components + 216 + self.components) 217 + 218 + def __mul__(self, g): 219 + """ 220 + Returns the result of the composition of self with the 221 + argument, if this composition is defined. 222 + 223 + The semantics of multiplication is as follows: f * g = 224 + f.compose(g). 225 + 226 + See Also 227 + ======= 228 + compose 229 + """ 230 + return self.compose(g)
 4 Collaborator ness01 added a note June 10, 2012 Catch the type exception here and return NotImplemented. scolobb added a note June 12, 2012 I read this as "... and raise NotImplemented". Feel free to correct. Collaborator ness01 added a note June 12, 2012 No, return NotImplemented. That signals to python that your code cannot deal with the other object. Then the interpreter may call other.__rmul__(self), or something along these lines. scolobb added a note June 12, 2012 Oh, I didn't know about that constant and this functionality. Thank you :-) Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((275 lines not shown)) 275 + return (self.name == g.name) and \ 276 + (self.domain == g.domain) and \ 277 + (self.codomain == g.codomain) 278 + else: 279 + # One of the morphisms is composed. Compare the 280 + # components. 281 + for (self_component, g_component) in zip(self.components, g.components): 282 + if self_component != g_component: 283 + return False 284 + return True 285 + 286 + def __ne__(self, g): 287 + return not (self == g) 288 + 289 + def __hash__(self): 290 + return hash((self.name, self.domain, self.codomain))
 6 Collaborator ness01 added a note June 10, 2012 again, hash in the type. Probably safest to just reuse basic.hash. scolobb added a note June 12, 2012 Basic.hash doesn't work nicely with anonymous morphisms (note the composite morphism g * f): In [20]: f = Morphism(A, B, "f"); g = Morphism(B, C, "g") In [21]: g * f == g * f Out[21]: True In [22]: hash(g * f) == hash(g * f) Out[22]: False  That's because anonymous morphisms store instances of Dummy, just as it (now) happens in Object. I can only use Symbol's in Morphism at all times; in this case an explicit __hash__ will not be necessary. Do you think I should do that? Collaborator ness01 added a note June 12, 2012 On 12.06.2012 14:11, Sergiu Ivanov wrote: > + return (self.name == g.name) and \ > + (self.domain == g.domain) and \ > + (self.codomain == g.codomain) > + else: > + # One of the morphisms is composed. Compare the > + # components. > + for (self_component, g_component) in zip(self.components, g.components): > + if self_component != g_component: > + return False > + return True > + > + def __ne__(self, g): > + return not (self == g) > + > + def __hash__(self): > + return hash((self.name, self.domain, self.codomain)) Basic.hash doesn't work nicely with anonymous morphisms (note the composite morphism g * f):  In [20]: f = Morphism(A, B, "f"); g = Morphism(B, C, "g") In [21]: g * f == g * f Out[21]: True In [22]: hash(g * f) == hash(g * f) Out[22]: False  That's because anonymous morphisms store instances of Dummy, just as it (now) happens in Object. I can only use Symbol's in Morphism at all times; in this case an explicit __hash__ will not be necessary. Do you think I should do that? I suppose that's fine. Are you making it clear somewhere that the name of a morphisms has no relevance (other than for printing) for composite morphisms? Collaborator ness01 added a note June 12, 2012 On 12.06.2012 14:11, Sergiu Ivanov wrote: > + return (self.name == g.name) and \ > + (self.domain == g.domain) and \ > + (self.codomain == g.codomain) > + else: > + # One of the morphisms is composed. Compare the > + # components. > + for (self_component, g_component) in zip(self.components, g.components): > + if self_component != g_component: > + return False > + return True > + > + def __ne__(self, g): > + return not (self == g) > + > + def __hash__(self): > + return hash((self.name, self.domain, self.codomain)) Basic.hash doesn't work nicely with anonymous morphisms (note the composite morphism g * f):  In [20]: f = Morphism(A, B, "f"); g = Morphism(B, C, "g") In [21]: g * f == g * f Out[21]: True In [22]: hash(g * f) == hash(g * f) Out[22]: False  That's because anonymous morphisms store instances of Dummy, just as it (now) happens in Object. I can only use Symbol's in Morphism at all times; in this case an explicit __hash__ will not be necessary. Do you think I should do that? Actually (sorry for not thinking this through earlier): why do composed morphisms have names at all? I see why it can be useful to name morphisms in a diagram (also composed ones, presumably), but that's not really the point. The kinds of names we attach to both morphisms and objects are "internal" - they are used *purely* to distinguish them, and are not necessarily related to user-visible names. I would think that composed morphisms are identified by their components, and as such don't need names. In fact, I think composed and named morphisms should live in separate classes! What do you think? scolobb added a note June 12, 2012 Are you making it clear somewhere that the name of a morphisms has no relevance (other than for printing) for composite morphisms? The docstring in Morphism says: Two composed morphisms are equal if they have the same components, in the same order (which guarantees the equality of domains and codomains). The names of such composed morphisms are not taken in consideration at comparison. scolobb added a note June 12, 2012 Actually (sorry for not thinking this through earlier): why do composed morphisms have names at all? I see why it can be useful to name morphisms in a diagram (also composed ones, presumably), but that's not really the point. The kinds of names we attach to both morphisms and objects are "internal" - they are used purely to distinguish them, and are not necessarily related to user-visible names. Yes, agreed. I would think that composed morphisms are identified by their components, and as such don't need names. In fact, I think composed and named morphisms should live in separate classes! That's an unexpected point of view, for me :-) And you do raise important points. I'll try to muse a bit here, and I'll then sleep it over. Initially, I thought that what you suggested sounded like removing the name from Morphism and deriving a class NamedMorphism from Morphism. However, in this case NamedMorphism inherits the components from Morphism. If we do if the other way round (derive Morphism from NamedMorphism), then Morphism will have a name, which defeats the whole point. This means that there should be a third class, MorphismBase, which, uhm, has only a domain and a codomain. CompositeMorphism derives from MorphismBase and can have components, while NamedMorphism derives from MorphismBase and adds a name. Then, hm, we derive IdentityMorphism from MorphismBase(?), because names don't really make much sense for identities, and identities cannot be composite. Does this go along the lines you were thinking of? If yes, I can't say I am really enthusiastic, because, to factor out the name of a morphism, we add two classes. Also, there is essentially no way of treating a collection of morphisms uniformly, since some morphisms don't have names, others don't have components; one will always have to check the type. Not that I say it's critical or even necessary; just throwing about ideas. Generally, I like it when the class model follows the real-world model with great granularity; but in this case I'm afraid whether this isn't going to be an overkill. Perhaps we could move this discussion to the mailing list so that more people can pronounce their opinion. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((277 lines not shown)) 277 + (self.codomain == g.codomain) 278 + else: 279 + # One of the morphisms is composed. Compare the 280 + # components. 281 + for (self_component, g_component) in zip(self.components, g.components): 282 + if self_component != g_component: 283 + return False 284 + return True 285 + 286 + def __ne__(self, g): 287 + return not (self == g) 288 + 289 + def __hash__(self): 290 + return hash((self.name, self.domain, self.codomain)) 291 + 292 +class Category(Basic):
Collaborator

SymPy Bot Summary: There were test failures.

@scolobb: Please fix the test failures.

Interpreter: /usr/bin/python3 (3.2.3-candidate-2)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 8086d7f
branch hash: 26ea1dc

Automatic review by SymPy Bot.

commented on the diff June 11, 2012
 sympy/categories/baseclasses.py 
 ((288 lines not shown)) 288 + 289 + def __hash__(self): 290 + return hash((self.name, self.domain, self.codomain)) 291 + 292 +class Category(Basic): 293 + r""" 294 + An (abstract) category. 295 + 296 + A category [JoyOfCats] is a quadruple \mbox{K} = (O, \hom, id, 297 + \circ) consisting of 298 + 299 + * a (set-theoretical) class O, whose members are called 300 + K-objects, 301 + 302 + * for each pair (A, B) of K-objects, a set \hom(A, B) whose 303 + members are called K-morphisms from A to B,
 4 Collaborator ness01 added a note June 11, 2012 so your categories are locally small? scolobb added a note June 12, 2012 Nope, that's the canonical definition of a category. Collaborator ness01 added a note June 12, 2012 On 12.06.2012 14:23, Sergiu Ivanov wrote: > + > + def __hash__(self): > + return hash((self.name, self.domain, self.codomain)) > + > +class Category(Basic): > + r""" > + An (abstract) category. > + > + A category [JoyOfCats] is a quadruple \mbox{K} = (O, \hom, id, > + \circ) consisting of > + > + * a (set-theoretical) class O, whose members are called > + K-objects, > + > + * for each pair (A, B) of K-objects, a set \hom(A, B) whose > + members are called K-morphisms from A to B, Nope, that's the canonical definition of a category. Wikipedia defines hom(A, B) to be a class, calls a category small if both K and the hom-classes are all sets, and locally small if the hom-classes are sets. That's the terminology I'm used to. But I don't think it matters much, so stick to whatever JoyOfCats uses as definition. scolobb added a note June 12, 2012 Hm, that's right. However, in the majority of textbooks I've seen (including an edition of McLane's "Categories for the Working Mathematician"), hom-classes are defined to be sets. I will indeed stick with what I have now, but I'll keep the term "locally-small" in mind. Thanks for pointing out! Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((301 lines not shown)) 301 + 302 + * for each pair (A, B) of K-objects, a set \hom(A, B) whose 303 + members are called K-morphisms from A to B, 304 + 305 + * for a each K-object A, a morphism id:A\rightarrow A, 306 + called the K-identity of A, 307 + 308 + * a composition law \circ associating with every K-morphisms 309 + f:A\rightarrow B and g:B\rightarrow C a K-morphism g\circ 310 + f:A\rightarrow C, called the composite of f and g. 311 + 312 + Composition is associative, K-identities are identities with 313 + respect to composition, and the sets \hom(A, B) are pairwise 314 + disjoint. 315 + 316 + This class nothing about its objects and morphisms. Concrete
 sympy/categories/baseclasses.py 
 ((321 lines not shown)) 321 + commutative in a :class:Category by supplying the argument 322 + commutative in the constructor. 323 + 324 + Examples 325 + ======== 326 + 327 + >>> from sympy.categories import Object, Morphism, Diagram, Category 328 + >>> from sympy import FiniteSet 329 + >>> A = Object("A") 330 + >>> B = Object("B") 331 + >>> C = Object("C") 332 + >>> f = Morphism(A, B, "f") 333 + >>> g = Morphism(B, C, "g") 334 + >>> d = Diagram([f, g]) 335 + >>> K = Category("K", commutative=[d]) 336 + >>> K.commutative == FiniteSet(d)
 5 Collaborator ness01 added a note June 11, 2012 Again, just do '>>> K.commutative' and test the output. scolobb added a note June 12, 2012 K.commutative gives  {Diagram({IdentityMorphism(Object("A"), ""): EmptySet(), IdentityMorphism(Object("B"), ""): EmptySet(), IdentityMorphism(Object("C"), ""): EmptySet(), Morphism(Object("A"), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g") * Morphism(Object("A"), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g"): EmptySet()}, {}, {Object("B"), Object("C"), Object("A")})}  Should I still go for it? Collaborator ness01 added a note June 12, 2012 On 12.06.2012 19:04, Sergiu Ivanov wrote: > + commutative in a :class:Category by supplying the argument > + commutative in the constructor. > + > + Examples > + ======== > + > +>>> from sympy.categories import Object, Morphism, Diagram, Category > +>>> from sympy import FiniteSet > +>>> A = Object("A") > +>>> B = Object("B") > +>>> C = Object("C") > +>>> f = Morphism(A, B, "f") > +>>> g = Morphism(B, C, "g") > +>>> d = Diagram([f, g]) > +>>> K = Category("K", commutative=[d]) > +>>> K.commutative == FiniteSet(d) K.commutative gives  {Diagram({IdentityMorphism(Object("A"), ""): EmptySet(), IdentityMorphism(Object("B"), ""): EmptySet(), IdentityMorphism(Object("C"), ""): EmptySet(), Morphism(Object("A"), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g") * Morphism(Object("A"), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g"): EmptySet()}, {}, {Object("B"), Object("C"), Object("A")})}  Should I still go for it? Hm. Well obviously you should not go for this. Might this be an indication of poor printing? Does it come out better if you pretty print it? I mean surely "checking which commutative diagrams are set in my category" is a reasonable thing to do, and the answer should look reasonable as well, shouldn't it? scolobb added a note June 12, 2012 Well, in isympy: In [6]: A = Object("A"); B = Object("B"); C = Object("C") In [7]: f = Morphism(A, B, "f"); g = Morphism(B, C, "g") In [8]: d = Diagram([f, g]) In [9]: d.premises Out[9]: {A→A: ∅, B→B: ∅, C→C: ∅, f:A→B: ∅, g∘f:A→C: ∅, g:B→C: ∅}  which is fairly not bad IMHO. Do you think I should be using the pretty in the doctests? (use_unicode=False, maybe) I felt reluctant to do that at the time of writing :-) Collaborator ness01 added a note June 12, 2012 On 12.06.2012 19:11, Sergiu Ivanov wrote: > + commutative in a :class:Category by supplying the argument > + commutative in the constructor. > + > + Examples > + ======== > + > +>>> from sympy.categories import Object, Morphism, Diagram, Category > +>>> from sympy import FiniteSet > +>>> A = Object("A") > +>>> B = Object("B") > +>>> C = Object("C") > +>>> f = Morphism(A, B, "f") > +>>> g = Morphism(B, C, "g") > +>>> d = Diagram([f, g]) > +>>> K = Category("K", commutative=[d]) > +>>> K.commutative == FiniteSet(d) Well, in isympy:  In [6]: A = Object("A"); B = Object("B"); C = Object("C") In [7]: f = Morphism(A, B, "f"); g = Morphism(B, C, "g") In [8]: d = Diagram([f, g]) In [9]: d.premises Out[9]: {A→A: ∅, B→B: ∅, C→C: ∅, f:A→B: ∅, g∘f:A→C: ∅, g:B→C: ∅}  which is fairly not bad IMHO. Do you think I should be using the pretty in the doctests? (use_unicode=False, maybe) I felt reluctant to do that at the time of writing :-) Yes, I think that is a good idea. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((329 lines not shown)) 329 + >>> A = Object("A") 330 + >>> B = Object("B") 331 + >>> C = Object("C") 332 + >>> f = Morphism(A, B, "f") 333 + >>> g = Morphism(B, C, "g") 334 + >>> d = Diagram([f, g]) 335 + >>> K = Category("K", commutative=[d]) 336 + >>> K.commutative == FiniteSet(d) 337 + True 338 + 339 + See Also 340 + ======== 341 + Diagram 342 + """ 343 + def __new__(cls, name, objects=EmptySet(), commutative=EmptySet()): 344 + new_category = Basic.__new__(cls, name, objects, FiniteSet(commutative))
 2 Collaborator ness01 added a note June 11, 2012 Why are objects and commutative treated differently. (My guess would be that FiniteSet(commutative) raises an exception if it is not a finite set. If so, please add a short comment. Also I hope this is tested ;).) scolobb added a note June 12, 2012 Hm, that's a good point. I thought of the argument objects as of a (set theoretic) class, in which case it seemed that I didn't have to explicitly convert objects to Class. I now do Class(objects). And yeah, I've added the tests :-) There were no tests before because I don't really do anything to objects in Category and neither do I plan to. Nevertheless, I've now added the tests :-) Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((347 lines not shown)) 347 + @property 348 + def name(self): 349 + """ 350 + Returns the name of this category. 351 + """ 352 + return self.args[0] 353 + 354 + @property 355 + def objects(self): 356 + """ 357 + Returns the class of objects of this category. 358 + """ 359 + return self.args[1] 360 + 361 + @property 362 + def commutative(self):
 4 Collaborator ness01 added a note June 11, 2012 I think this should have a better name (like commutative_diagrams, but feel free to pick) scolobb added a note June 12, 2012 I totally agree that commutative_diagrams is better, but isn't it too long? Collaborator ness01 added a note June 12, 2012 On 12.06.2012 14:50, Sergiu Ivanov wrote: > + @property > + def name(self): > + """ > + Returns the name of this category. > + """ > + return self.args[0] > + > + @property > + def objects(self): > + """ > + Returns the class of objects of this category. > + """ > + return self.args[1] > + > + @property > + def commutative(self): I totally agree that commutative_diagrams is better, but isn't it too long? I wouldn't think so. There is nothing wrong with a long but prescriptive name, if it isn't used very much. scolobb added a note June 12, 2012 Changed to commutative_diagrams, which is definitely better than just commutative. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((401 lines not shown)) 401 + 402 + No checks are carried out of whether the supplied object and 403 + morphisms do belong to one and the same category. 404 + 405 + Examples 406 + ======== 407 + 408 + >>> from sympy.categories import Object, Morphism, Diagram 409 + >>> from sympy import FiniteSet 410 + >>> A = Object("A") 411 + >>> B = Object("B") 412 + >>> C = Object("C") 413 + >>> f = Morphism(A, B, "f") 414 + >>> g = Morphism(B, C, "g") 415 + >>> d = Diagram([f, g]) 416 + >>> Morphism(A, A, identity=True) in d.premises.keys()
 4 Collaborator ness01 added a note June 11, 2012 If it's not too long, why not just show d.premises? scolobb added a note June 12, 2012 d.premises gives  {IdentityMorphism(Object("A"), ""): EmptySet(), IdentityMorphism(Object("B"), ""): EmptySet(), IdentityMorphism(Object("C"), ""): EmptySet(), Morphism(Object("A"), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g") * Morphism(Object("A"), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g"): EmptySet()}  d.premises.keys: [Morphism(Object("B"), Object("C"), "g"), IdentityMorphism(Object("C"), ""), Morphism(Object("A"), Object("B"), "f"), IdentityMorphism(Object("A"), ""), IdentityMorphism(Object("B"), ""), Morphism(Object("B"), Object("C"), "g") * Morphism(Object("A"), Object("B"), "f")]  In my book that qualifies as too long :-) However, I can try to break it down into lines so that it gets more readable. Collaborator ness01 added a note June 12, 2012 On 12.06.2012 19:08, Sergiu Ivanov wrote: > + > + No checks are carried out of whether the supplied object and > + morphisms do belong to one and the same category. > + > + Examples > + ======== > + > +>>> from sympy.categories import Object, Morphism, Diagram > +>>> from sympy import FiniteSet > +>>> A = Object("A") > +>>> B = Object("B") > +>>> C = Object("C") > +>>> f = Morphism(A, B, "f") > +>>> g = Morphism(B, C, "g") > +>>> d = Diagram([f, g]) > +>>> Morphism(A, A, identity=True) in d.premises.keys() d.premises gives  {IdentityMorphism(Object("A"), ""): EmptySet(), IdentityMorphism(Object("B"), ""): EmptySet(), IdentityMorphism(Object("C"), ""): EmptySet(), Morphism(Object("A"), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g") * Morphism(Object("A"), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g"): EmptySet()}  d.premises.keys:  [Morphism(Object("B"), Object("C"), "g"), IdentityMorphism(Object("C"), ""), Morphism(Object("A"), Object("B"), "f"), IdentityMorphism(Object("A"), ""), IdentityMorphism(Object("B"), ""), Morphism(Object("B"), Object("C"), "g") * Morphism(Object("A"), Object("B"), "f")]  In my book that qualifies as too long :-) However, I can try to break it down into lines so that it gets more readable. you are right again scolobb added a note June 12, 2012 I use pretty now, so it looks like >>> print pretty(d.premises, use_unicode=False) {A->A: EmptySet(), B->B: EmptySet(), C->C: EmptySet(), f:A->B: EmptySet(), g*f :A->C: EmptySet(), g:B->C: EmptySet()}  and {g*f:A->C: {unique}}  Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((444 lines not shown)) 444 + if key in dictionary: 445 + dictionary[key] = dictionary[key] | value 446 + return True 447 + else: 448 + dictionary[key] = value 449 + return False 450 + 451 + @staticmethod 452 + def _add_morphism(morphisms, morphism, props, add_identities=True): 453 + """ 454 + Adds a morphism and its attributes to the supplied dictionary 455 + morphisms. If add_identities is True, also adds the 456 + identity morphisms for the domain and the codomain of 457 + morphism. 458 + """ 459 + if Diagram._set_dict_union(morphisms, morphism, props) == False:
 2 Collaborator ness01 added a note June 11, 2012 never compare to True or False if you know the result already is True or False. I.e. make this if not ...: scolobb added a note June 12, 2012 Done. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
commented on the diff June 11, 2012
 sympy/categories/baseclasses.py 
 ((467 lines not shown)) 467 + 468 + id_dom = Morphism(morphism.domain, morphism.domain, identity=True) 469 + id_cod = Morphism(morphism.codomain, morphism.codomain, identity=True) 470 + Diagram._set_dict_union(morphisms, id_dom, empty) 471 + Diagram._set_dict_union(morphisms, id_cod, empty) 472 + 473 + for existing_morphism, existing_props in morphisms.items(): 474 + new_props = existing_props & props 475 + if morphism.domain == existing_morphism.codomain: 476 + left = morphism * existing_morphism 477 + Diagram._set_dict_union(morphisms, left, new_props) 478 + if morphism.codomain == existing_morphism.domain: 479 + right = existing_morphism * morphism 480 + Diagram._set_dict_union(morphisms, right, new_props) 481 + 482 + def __new__(cls, *args):
 2 Collaborator ness01 added a note June 11, 2012 Add a docstring here explaining args. scolobb added a note June 12, 2012 Oh yes, sure. Added. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((538 lines not shown)) 538 + @property 539 + def premises(self): 540 + """ 541 + Returns the premises of this diagram. 542 + 543 + Examples 544 + ======== 545 + >>> from sympy.categories import Object, Morphism, Diagram 546 + >>> from sympy import EmptySet, Dict 547 + >>> A = Object("A") 548 + >>> B = Object("B") 549 + >>> f = Morphism(A, B, "f") 550 + >>> id_A = Morphism(A, A, identity=True) 551 + >>> id_B = Morphism(B, B, identity=True) 552 + >>> d = Diagram([f]) 553 + >>> d.premises == Dict({f:EmptySet(), id_A:EmptySet(), id_B:EmptySet()})
 sympy/categories/baseclasses.py 
 ((636 lines not shown)) 636 + 637 + for morphism in self.premises.keys(): 638 + if (morphism.domain == A) and (morphism.codomain == B): 639 + premises |= FiniteSet(morphism) 640 + for morphism in self.conclusions.keys(): 641 + if (morphism.domain == A) and (morphism.codomain == B): 642 + conclusions |= FiniteSet(morphism) 643 + 644 + return (premises, conclusions) 645 + 646 + def __eq__(self, other): 647 + if not isinstance(other, Diagram): 648 + return False 649 + 650 + return (self.premises == other.premises) and \ 651 + (self.conclusions == other.conclusions)
 6 Collaborator ness01 added a note June 11, 2012 Why don't you also compare objects? [I guess this is why you don't want to use Basic.eq?] Also, you need to provide hash. scolobb added a note June 12, 2012 objects fully depends on premises and I only store it with the goal of not rebuilding the list every time it is needed. Actually, I'm not that sure whether it's a good idea to keep objects in .args. Since I am using it for caching purposes, perhaps I should employ just a private attribute? If we decide that objects stays in .args, I'll write the __hash__ method. Slipped by me back then :-( Collaborator ness01 added a note June 13, 2012 But even if the objects are essentially just cached, they are determined deterministically from premises. So it wouldn't change the behaviour to compare them as well? [Don't bother about the performance difference.] scolobb added a note June 13, 2012 Nope, when I remove the comparison operators, the tests still pass. Do you think I should just remove custom comparison? Collaborator ness01 added a note June 13, 2012 I think that is best. scolobb added a note June 13, 2012 Done. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/tests/test_baseclasses.py 
 ((65 lines not shown)) 65 + 66 + u1 = u.flatten() 67 + 68 + assert u1.domain == A 69 + assert u1.codomain == D 70 + assert u1.name == "" 71 + assert u1.components == [u1] 72 + 73 + u1 = u.flatten("u") 74 + 75 + assert u1.domain == A 76 + assert u1.codomain == D 77 + assert u1.name == "u" 78 + assert u1.components == [u1] 79 + 80 + assert f == Morphism(A, B, "f")
 sympy/categories/tests/test_baseclasses.py 
 ((70 lines not shown)) 70 + assert u1.name == "" 71 + assert u1.components == [u1] 72 + 73 + u1 = u.flatten("u") 74 + 75 + assert u1.domain == A 76 + assert u1.codomain == D 77 + assert u1.name == "u" 78 + assert u1.components == [u1] 79 + 80 + assert f == Morphism(A, B, "f") 81 + assert f != g 82 + assert f != Morphism(A, B, "") 83 + assert Morphism(A, B, "") != Morphism(A, B, "") 84 + 85 + assert hash(f) == hash(Morphism(A, B, "f"))
 2 Collaborator ness01 added a note June 11, 2012 This as well. scolobb added a note June 12, 2012 I cannot see where I test it once more :-) I have however done some reshuffling to test_morphism and dropped a few more repetitions. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/tests/test_baseclasses.py 
 ((77 lines not shown)) 77 + assert u1.name == "u" 78 + assert u1.components == [u1] 79 + 80 + assert f == Morphism(A, B, "f") 81 + assert f != g 82 + assert f != Morphism(A, B, "") 83 + assert Morphism(A, B, "") != Morphism(A, B, "") 84 + 85 + assert hash(f) == hash(Morphism(A, B, "f")) 86 + 87 + id_A = Morphism(A, A, identity=True) 88 + id_B = Morphism(B, B, identity=True) 89 + 90 + assert id_A.identity == True 91 + assert id_A == Morphism(A, A, name="f", identity=True) 92 + assert id_A != Morphism(A, A, name="f")
 2 Collaborator ness01 added a note June 11, 2012 Maybe also test id_A != id_B scolobb added a note June 12, 2012 Added. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/tests/test_baseclasses.py 
 ((95 lines not shown)) 95 + assert f * id_A == f 96 + assert id_B * f == f 97 + 98 + raises(ValueError, lambda: Morphism(A, B, identity=True)) 99 + 100 +def test_diagram(): 101 + A = Object("A") 102 + B = Object("B") 103 + C = Object("C") 104 + 105 + f = Morphism(A, B, "f") 106 + g = Morphism(B, C, "g") 107 + id_A = Morphism(A, A, "1_A") 108 + id_B = Morphism(B, B, "1_B") 109 + 110 + empty = EmptySet()
 2 Collaborator ness01 added a note June 11, 2012 Note that, here and elsewhere, you can also access the empty set as S.EmptySet (but admittedly that's a little long, so what you do is fine.) scolobb added a note June 12, 2012 I don't really know why I didn't use S.EmptySet, since it seems that I knew about it at the time of writing :-) Thanks for pointing out :-) (Since you say it's fine as it is now, I'm not going to change it, though.) Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/tests/test_baseclasses.py 
 ((107 lines not shown)) 107 + id_A = Morphism(A, A, "1_A") 108 + id_B = Morphism(B, B, "1_B") 109 + 110 + empty = EmptySet() 111 + 112 + # Test the addition of identities. 113 + d1 = Diagram([f]) 114 + 115 + assert d1.objects == FiniteSet(A, B) 116 + assert d1.hom(A, B) == (FiniteSet(f), empty) 117 + assert d1.hom(A, A) == (FiniteSet(Morphism(A, A, identity=True)), empty) 118 + assert d1.hom(B, B) == (FiniteSet(Morphism(B, B, identity=True)), empty) 119 + 120 + # Test the addition of composites. 121 + d2 = Diagram([f, g]) 122 + homAC = d2.hom(A, C)[0]
commented on the diff June 11, 2012
 sympy/categories/baseclasses.py 
 ((385 lines not shown)) 385 + the diagram. For a more formal approach to this notion see 386 + [Pare1970]. 387 + 388 + A commutative diagram is often accompanied by a statement of the 389 + following kind: "if such morphisms with such properties exist, 390 + then such morphisms which such properties exist and the diagram is 391 + commutative". To represent this, an instance of :class:Diagram 392 + includes a collection of morphisms which are the premises and 393 + another collection of conclusions. premises and 394 + conclusions associate morphisms belonging to the corresponding 395 + categories with the :class:FiniteSet's of their properties. 396 + 397 + The set of properties of a composite morphism is the intersection 398 + of the sets of properties of its components. The domain and 399 + codomain of a conclusion morphism should be among the domains and 400 + codomains of the morphisms listed as the premises of a diagram.
 sympy/printing/pretty/pretty.py 
 ((22 lines not shown)) 1467 + for component in reversed(morphism.components): 1468 + if not component.name: 1469 + # Composition with an anonymous morphism is an 1470 + # anonymous morphism. 1471 + return prettyForm(tail) 1472 + 1473 + pretty_name += pretty_symbol(component.name) + circle 1474 + 1475 + pretty_name = pretty_name[:-1] 1476 + 1477 + pretty_name_form = self._print(pretty_name) 1478 + return prettyForm(pretty_name_form.right(":", tail)[0]) 1479 + 1480 + def _print_Category(self, category): 1481 + return self._print(pretty_symbol(category.name)) 1482 +
 5 Collaborator ness01 added a note June 11, 2012 I suppose _print_Diagram will come only later? scolobb added a note June 12, 2012 Well, I believe that the default pretty printing an example of which I showed in this comment is quite OK. As for LaTeX pretty printing, I'm not sure to what extent IPython notebooks support LaTeX and its packages. It would be great if the full package set is supported, but if it isn't, I don't think implementing some custom drawing is worth it. scolobb added a note June 12, 2012 Oh, OK, I missed the fact that the default printing of Diagram uses string representations of Morphism and Object, not pretty representations :-( I think I should write a simple _print_Diagram which would show the diagram as two dictionaries, formatted similarly to what is show in this comment (which I have already invoked). Do you have adverse arguments? Collaborator ness01 added a note June 13, 2012 Yes this sounds like a good idea. Note that ipython notebooks are not the only reason to implement latex printing. But I agree that there seems no reasonable way (at the moment) to print diagrams in latex, so don't bother. When the real diagram printing code is there we can see if it is practical to invoke during latex printing. scolobb added a note June 13, 2012 For the sake of completeness, I've nevertheless added LaTeX pretty printing which exactly mimics Unicode printing. It's not going to be hard to remove should need happen :-) Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/printing/pretty/tests/test_pretty.py 
 ((5 lines not shown)) 3695 +def test_categories(): 3696 + from sympy.categories import Object, Morphism, Category 3697 + A1 = Object("A1") 3698 + A2 = Object("A2") 3699 + A3 = Object("A3") 3700 + 3701 + f1 = Morphism(A1, A2, "f1") 3702 + f2 = Morphism(A2, A3, "f2") 3703 + anonymous = Morphism(A1, A3, "") 3704 + 3705 + K1 = Category("K1") 3706 + 3707 + assert pretty(A1) == "A1" 3708 + assert upretty(A1) == u"A₁" 3709 + 3710 + assert pretty(f1) == "f1:A1->A2"
 4 Collaborator ness01 added a note June 11, 2012 I personally think this looks a bit crowded. How about a space after the colon? scolobb added a note June 12, 2012 Let me try it right here: f: A->B. I've also had this idea: f:A-->B. I think I like the longer arrow slightly better. Do you think not having a space after the colon is a bad thing if we make the arrow longer? scolobb added a note June 12, 2012 There's a slight advantage to a longer arrow in the fact that it also works for anonymous morphisms: A-->B :-) Here comes a real-life example, formatted according to both suggestions: {A->A: EmptySet(), B->B: EmptySet(), C->C: EmptySet(), f: A->B: EmptySet(), g*f: A->C: EmptySet(), g: B->C: EmptySet()}  {A-->A: EmptySet(), B-->B: EmptySet(), C-->C: EmptySet(), f:A-->B: EmptySet(), g*f:A-->C: EmptySet(), g:B-->C: EmptySet()}  Collaborator ness01 added a note June 13, 2012 What I don't like about the --> is that it looks like a rational map to me. But you can use unicode in pretty printing to get a non-broken long arrow, and I think this looks good. Here is how I print homomorphisms in agca: In [2]: RepeatedIsomorphismComplex(QQ[x].free_module(1).identity_hom()) Out[2]: 1 1 0 <── ℚ[x] <── ℚ[x] <── ... In [4]: QQ[x].free_module(2).identity_hom() Out[4]: ⎡1 0⎤ 2 2 ⎢ ⎥ : ℚ[x] ──> ℚ[x] ⎣0 1⎦  You see that I generally use quite a lot of spaces. But this is in real ipython pretty-printing. For dictionaries like you give example above, I think no space between "f:" and the arrow is best. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/printing/str.py 
 ... ... @@ -526,6 +526,18 @@ def _print_DMP(self, p): 526 526  def _print_DMF(self, expr): 527 527  return self._print_DMP(expr) 528 528   529 + def _print_Object(self, object): 530 + return 'Object("%s")' % object.name 531 + 532 + def _print_Morphism(self, morphism): 533 + result = "" 534 + for component in reversed(morphism.components): 535 + result += 'Morphism(%s, %s, "%s") * ' % \ 536 + (component.domain, component.codomain, component.name) 537 + return result[:-3] 538 + 539 + def _print_Category(self, category): 540 + return 'Category("%s")' % category.name
 2 Collaborator ness01 added a note June 11, 2012 _print_diagram? scolobb added a note June 12, 2012 Here I believe the default string representation will do: Diagram({IdentityMorphism(Object("A"), ""): EmptySet(), IdentityMorphism(Object("B"), ""): EmptySet(), IdentityMorphism(Object("C"), ""): EmptySet(), Morphism(Object("A "), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g") * Morphism(Object("A"), Object("B"), "f"): EmptySet(), Morphism(Object("B"), Object("C"), "g" ): EmptySet()}, {}, {Object("B"), Object("C"), Object("A")})  It is longish, that's right, but a lot of string representations of stuff are longish in SymPy ;-) Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Collaborator
commented June 11, 2012

I'm finished with the initial review pass. Let me know when you are done addressing the comments.

The two new commits add IdentityMorphism and do other, more cosmetic modifications to the existing classes to them comply with the requirement of having only instances of Basic in .args. I expect the tests to pass now (2.7 and 2.5 pass on my box).

Collaborator

SymPy Bot Summary: There were test failures.

@scolobb: Please fix the test failures.

Interpreter: /opt/pym32/bin/python2.5 (2.5.6-final-0)
Architecture: Linux (32-bit)
Cache: yes
Test command: setup.py test
master hash: 2d16ab9
branch hash: 32996a7

Automatic review by SymPy Bot.

Collaborator

SymPy Bot Summary: There were test failures.

@scolobb: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sY6owbDA

Interpreter: /usr/bin/python2.7 (2.7.3-candidate-2)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 2d16ab9
branch hash: 32996a7

Automatic review by SymPy Bot.

Collaborator

SymPy Bot Summary: There were test failures.

@scolobb: Please fix the test failures.

Interpreter: /usr/bin/python3 (3.2.3-candidate-2)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 2d16ab9
branch hash: 32996a7

Automatic review by SymPy Bot.

Judging by the character of failures, they are conditioned by the fact that my branch is slightly behind master. I'll finish complying with the comments and will rebase then.

 sympy/categories/baseclasses.py 
 ((26 lines not shown)) 26 + 27 + Two objects with the same name are the same object. 28 + 29 + Examples 30 + ======== 31 + 32 + >>> from sympy.categories import Object 33 + >>> Object("A") == Object("A") 34 + True 35 + 36 + """ 37 + def __new__(cls, name): 38 + if not name: 39 + raise ValueError("Anonymous Objects are not allowed.") 40 + 41 + return Basic.__new__(cls, Dummy(name))
 5 Collaborator rlamy added a note June 13, 2012 You shouldn't abuse Dummy like that, it will cause you trouble. Collaborator Krastanov added a note June 13, 2012 You shouldn't abuse Dummy like that, it **will** cause you trouble. Could you be more precise? Is it performance concerns? Or just that this should be done like in Symbol? Why? Collaborator rlamy added a note June 13, 2012 A Dummy() is a dummy variable with a complex value, which has basically nothing to do with the name of an abstract object. The semantic mismatch will cause problems and require hacks. It has actually already done so, see below. Collaborator ness01 added a note June 13, 2012 Note that Sergiu is about to update this part: the name will be stored as Symbol, unless the name is empty. The latter case is supposed to indicate an "anonymous" object, no two of which are supposed to be the same. I suggested Dummy for this case - it seems to match the behaviour of Dummy quite precisely. Is it still a bad idea? scolobb added a note June 13, 2012 I have already updated this, I just haven't pushed the updated branch because I have played with git rebase a little bit so I didn't want to mess up the comments. Also note that if we do transition to a more complex class structure as discussed in this thread, Dummy will not be used at all, since anonymous Object's are not supported any more and the same fate is going to happen to anonymous Morphism's. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((6 lines not shown)) 6 + The base class for any kind of class in set-theoretic sense. 7 + 8 + In axiomatic set theories, everything is a class. A class which 9 + can is a member of another class is a set. A class which is not a 10 + member of another class is a proper class. The class {1, 2} is a 11 + set; the class of all sets is a proper class. 12 + 13 + This class is essentially a synonym for :class:sympy.core.Set. 14 + The goal of this class is to assure easier migration to the 15 + eventual proper implementation of set theory. 16 + """ 17 + is_proper = False 18 + 19 +class Object(Basic): 20 + """ 21 + The base class for any kind of object in an abstract category.
 2 Collaborator rlamy added a note June 13, 2012 If a category can contain any kind of object, then any instance of Basic should work. So do you actually need the Object class at all? scolobb added a note June 13, 2012 If a category can contain any kind of object, then any instance of Basic should work. Yes, that was my plan. So do you actually need the Object class at all? Yes, because in the most abstract setting I need a class which will only hold a name. When thinking about this a week ago, I considered using Symbol instead, but then I figured that it would be better to use a narrower solution. I do think now that it may be possible to derive Object from Symbol directly, instead of Basic. I am still strongly inclined to think that I need a special Object class. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Collaborator
commented June 14, 2012

@scolobb please let me know when you think you are done here.

This is finally the new version of the code. @ness01, I think I'm done addressing the first wave of comments :-)

Tests with Python 2.7 pass. I'll now check 2.5 and 3.2 as well.

commented on the diff June 15, 2012
 sympy/categories/baseclasses.py 
 ((24 lines not shown)) 24 + class is the recommended way to create abstract objects in 25 + abstract categories. 26 + """ 27 + 28 +class Morphism(Basic): 29 + """ 30 + The base class for any morphism in an abstract category. 31 + 32 + In abstract categories, a morphism is an arrow between two 33 + category objects. The object where the arrow starts is called the 34 + domain, while the object where the arrow ends is called the 35 + codomain. 36 + 37 + Two morphisms between the same pair of objects are considered to 38 + be the same morphisms. To distinguish between morphisms between 39 + the same objects use :class:NamedMorphism.
 3 Collaborator ness01 added a note June 15, 2012 Is it a good idea to allow instantiation of this class? Why not just make __new__ raise some sort of exception, making it clear that one of the derived classes has to be used? Or in fact make __new__ have the interface you wanted first (with name and identity options), but return a derived class. scolobb added a note June 15, 2012 Well, I had the impression that anonymous morphisms, which are instances of Morphism, could be useful. I'm not that sure now that you've asked. I use anonymous morphisms rather extensively in tests and examples, but I can't say whether it's such a good idea. Moreover, I start to think that anonymous moprhisms are bad for the same reason as anonymous objects are bad. All right, I'll redo it right away, thank you for changing my mind :-) scolobb added a note June 15, 2012 Yeah, I've just went through the code once again; it looks like I only use instances of Morphism when I'm lazy to use NamedMorphism's. And it turns out that whenever I need something beyond creating a morphism, anonymous morphisms do not suffice, which confirms your point. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/categories/baseclasses.py 
 ((173 lines not shown)) 173 + Examples 174 + ======== 175 + 176 + >>> from sympy.categories import Object, Morphism, NamedMorphism 177 + >>> A = Object("A") 178 + >>> B = Object("B") 179 + >>> f = NamedMorphism(A, B, "f") 180 + >>> f 181 + NamedMorphism(Object("A"), Object("B"), "f") 182 + >>> f.name 183 + 'f' 184 + 185 + See Also 186 + ======== 187 + 188 + Moprhism
 sympy/categories/baseclasses.py 
 ((335 lines not shown)) 335 + >>> B = Object("B") 336 + >>> C = Object("C") 337 + >>> f = Morphism(A, B) 338 + >>> g = Morphism(B, C) 339 + >>> (g * f).domain 340 + Object("A") 341 + 342 + """ 343 + return self.components[0].domain 344 + 345 + @property 346 + def codomain(self): 347 + """ 348 + Returns the codomain of this composite morphism. 349 + 350 + The domain of the composite morphism is the codomain of its
 doc/src/modules/categories.txt 
 ... ... @@ -0,0 +1,36 @@ 1 +Category Theory Module 2 +====================== 3 + 4 +.. module:: sympy.categories 5 + 6 +Introduction 7 +------------ 8 + 9 +The category theory module for SymPy will allow manipulating diagrams 10 +within a single category, including drawing them in TikZ and deciding 11 +whether they are commutative or not. 12 + 13 +The module is still in its pre-embryonic stage.
 sympy/categories/baseclasses.py 
 ((545 lines not shown)) 545 + 546 + No checks are carried out of whether the supplied object and 547 + morphisms do belong to one and the same category. 548 + 549 + Examples 550 + ======== 551 + 552 + >>> from sympy.categories import Object, NamedMorphism, Diagram 553 + >>> from sympy import FiniteSet, pretty 554 + >>> A = Object("A") 555 + >>> B = Object("B") 556 + >>> C = Object("C") 557 + >>> f = NamedMorphism(A, B, "f") 558 + >>> g = NamedMorphism(B, C, "g") 559 + >>> d = Diagram([f, g]) 560 + >>> print pretty(d.premises.keys(), use_unicode=False)
 1 Collaborator ness01 added a note June 15, 2012 just use pprint [here and elsewhere] Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 sympy/printing/pretty/pretty.py 
 ... ... @@ -1446,6 +1446,71 @@ def _print_DMP(self, p): 1446 1446  def _print_DMF(self, p): 1447 1447  return self._print_DMP(p) 1448 1448   1449 + def _print_Object(self, object): 1450 + return self._print(pretty_symbol(object.name)) 1451 + 1452 + def _print_Morphism(self, morphism): 1453 + arrow = "-->" 1454 + if self._use_unicode: 1455 + arrow = u"\u27f6 " 1456 + circle = u"\u2218"
 3 Collaborator ness01 added a note June 15, 2012 circle seems not to be used here? scolobb added a note June 15, 2012 Nope, it's not :-) By the way, it is not possible to create a Morphism anymore, yet this function is used by _print_NamedMorphism and _print_CompositeMorphism, therefore I have left it here for now. If having such a function is not OK (after all, Morphism can never be instatiated), I'm not sure what would be the way out. I obviously feel against duplicating the functionality in the two functions. Collaborator ness01 added a note June 16, 2012 No that's completely fine. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Collaborator
commented June 15, 2012

I think this is almost good to go. The tests and doctests pass on my system. coverage_doctest reports 69% which is pretty good (in my experience this tool is extremely pedantic...); Note that running with --verbose shows you what is missing. Also the coverage (run bin/coverage_report sympy/categories) is pretty good and at 94%. I think it is probably a good idea to boost this to 99% or so (should take no longer than 5 minutes, I think).

added some commits May 28, 2012
 scolobb Add Class, Object, and Morphism. These classes represent the very bowels of the module. They are yet in their incipient form, which is sufficient, however, to start writing the tests. fe78af1 scolobb Write the tests for categories.Object and categories.Morphism. The tests do not pass at the moment. The goal is to define the desired behaviour of the methods in Morphism by means of tests. 133bb56 scolobb Implement composing and flattening morphisms. Composing two morphisms results in a morphism which knows which morphisms it was composed from. Flattening such a morphism results in forgetting the information about components. f888e7e scolobb Extend the string printer to print Object and Morphism. Also add the corresponding tests. 631cb3d scolobb When composing morphisms, intersperse morphism names with '*'. The initial approach to use spaces to separate morphisms names proved inconsistent with the later idea to use '*' when printing morphism composition. 1ee252d scolobb Amend the string representation of composition of morphisms. The initial version did not comply with the requirement that the represented object be able to be reconstructed from the string representation. bab67ea scolobb Don't automatically construct the name of the composed morphism. This also concerns flattened morphisms. While initially it seemed to be a good idea to automatically construct new morphism names, it proved later that trying to be clever when it's not necessary is never good. ff04329 scolobb Add pretty printing for Object and Morphism. Pretty printing supports anonymous objects and morphisms. Also add the corresponding tests. 2e81231 scolobb Add printing to LaTeX. Also add the tests for printing to LaTeX. Anonymous objects and morphisms are supported. 9334d84 scolobb Add (un)equality operators to Object and Morphism. Also extend the docstrings and the tests to cover the added semantics. In morphism equality, composed morphisms are only compared by their components. Thus, the names of composed morphisms are insignificant at comparison. 9cf15ea scolobb Add some nice formatting to the docstrings. Follow the formatting style found in, for example, sympy.core.symbol. 044cb74 scolobb Add examples to Object and Morphism. 65da9ae scolobb Update the comment in __init__.py to include the principal reference. This book should be referenced by the notation [JoyOfCats]. 7866fc8 scolobb Add Category. This is the base class for all categories. It will contain very little functionality related to objects and morphisms. d539ba1 scolobb Extend string, LaTeX, and pretty printers to support Category. Only the name of the Category is currently printed. It is improbable that much additional information will ever be included in the pretty printed representation of a Category, just because it is so in the books on the topic. 629f648 scolobb Add the skeleton of Diagram. This commit defines the class and its methods without functionality. The functionality itself will be implemented after the tests have been added. fd0a694 scolobb Add some tests for Diagram. The tests also attempt to cover some subtle aspects of how the diagram treats its objects and morphism. 462a5e1 scolobb In Morphism, define __new__ to allow keyword arguments. Since Morphism derives from Basic, and Basic.__new__ does not accept keyword arguments, Morphism could not be constructed with keyword arguments. While this is all right for simple constructors like those of Object and Category, the constructor of Morphism will quickly become awkward to use if the user will be required to supply all arguments in order. c9b027b scolobb Implement identity morphisms. A Morphism can now be defined to be an identity. Identity morphisms are defined not to influence composition. All identity morphisms of one and the same object are equal. fa74c09 scolobb Make Morphism and Object check for the type of the other object in co… …mparisons. Objects of these classes would previously throw exceptions when compared to something improper (like None). 207ee8a scolobb Fix the tests for Diagram to properly use identity morphism. Identity morphisms were implemented after the tests for Diagram, so there was a need to slightly correct them to properly use identities. 7fcee70 scolobb Implement Diagram. Also add some examples to the docstrings. Diagram currently relies on the Basic to do the hashing, because Basic seems to get things right in this regard. 0a33621 scolobb Have Category store a set of commutative diagrams. Not much more beyond storing a set of diagrams is done at the moment. 26debd8 scolobb Add a documentation chapter about the category theory module. The page currently doesn't currently do much beyond pulling in the corresponding docstrings and, in fact, that's the purpose of adding the chapter at this moment. 2723826 scolobb Redo the docstrings to make them look better. This includes slight changes to examples, as well as (ab)using LaTeX wherever necessary. bdef398 scolobb Remove support for anonymous objects. Anonymous objects were initially implemented as a way of failing gracefully in the case when no explicit name was supplied. However, an anonymous object does not make much sense. c60d285 scolobb Explicitly test the equality of Morphism's. Previously the equality of morphisms was implicitly tested in other contexts. 96f2c3f scolobb Rewrite the existing classes to be fully immutable. The previous version of the classes was "partially" mutable: one had to use functions such as add_premises, assert_commutative, etc. Now, however, all setting up is done in constructors, the objects being immutable. This has made the code of the module simpler, easier to use, better integrated into the Basic inheritance tree, and much more consistent. 39ee393 scolobb Raise ValueError when composing uncomposable morphisms. 629c916 scolobb Fix a long string in test_str.py. A long string in tests was split on two lines in an ugly way. 0ce0464 scolobb Fix some bogus errors in examples in the docstrings. During a major rewrite a couple commits before, I botched up a couple docstrings. This commit fixes that problem. baf2d46 scolobb Add IdentityMorphism. Having a separate class instead of a flag probably fits better into SymPy philosophy. This allows to have Morphism cope without a flag specifying whether it is an identity or not. This commit also adds a different string representation for IdentityMorphism. d02283a scolobb Make test_args.py pass. This implies making everything stored in Basic.args to be an instance of Basic and also adding the corresponding tests to test_args.py. Instead of storing string names, instances of Symbol or Dummy are now stored in Basic.args. cd926f2 scolobb Fix some formulations and formatting in docs. Cosmetic fixes mostly. bb7e1a7 scolobb Add doctests for all user-facing methods. I suppose that some of the docstrings have now become a bit redundant; I don't think that's a problem at all with docstrings, however. d1f1440 scolobb Use "other" as argument name in equality relations. Previously, in __eq__/__ne__ I would use some arbitrary name to refer to the value compared to self. The convention is however to use "other". fca51f2 scolobb Make an anonymous Morphism equal to itself. Previously, for a = Morphism(A, B), with A and B being Object's, a == a was False, which would have been very inconvenient. Now a == a is correctly True. 3b85a22 scolobb Rename Morphism.identity to is_identity. This name expresses the idea better. 8dedae3 scolobb Raise TypeError when composing a Morphism with a non-Morphism. Invoking Morphism.compose on something that is not a Morphism or a derived class now raises TypeError. __mul__ will in this case raise a NotImplementedError instead. 9fffc9b scolobb Don't allow unnamed categories. Should the user supply an empty name to a constructor of Category, raise ValueError. 20c545d scolobb In Category, explicitly construct a Class of objects. Also test the way Category stores objects and fix some related doctests. 313246f scolobb Directly show the output of certain statements in doctests. This commit fixes a tendency to compare the results of certain statements with their expected results instead of directly showing the results in doctests. d9ac130 scolobb Don't compare to True or False. Despite my hopes that such comparison might be better in some cases, I am hereby removing it. 46aef91 scolobb Add a docstring to Category.__new__. This constructor required further documentation since it attempts to cleverly interpret its arguments. 926781c scolobb Fix redundancies in test_morphism. I have been adding tests incrementally, which resulted in some mess and redundancy in test_morphism. 488f051 scolobb Fix a typo in test_diagram. test_diagram missed an assertion in testing whether composite morphisms are properly added. 813eeb7 scolobb In Category, rename commutative to commutative_diagrams. The second name is definitely more descriptive, and now that it's not only me who thinks this it's OK to have long names, this commit changes everything to use the new name. 47403e4 scolobb Add pretty printing to Diagram. This pretty printing largely relies on the pretty printing routines for Dict, Morphism, and Object. 0a2e132 scolobb Remove explicit comparison operators from Diagram. My initial intention was to attempt to speed-up the comparison by not actually comparing the objects of two diagrams, because objects fully depend on the premises. f8f429a scolobb Add the new Morphism class hierarchy. This includes adding the base class Morphism, and the classes IdentityMorphism, NamedMorphism, and CompositeMorphism. This also includes doing necessary adjustments to the other classes in the module and to the tests. 6de72a1 scolobb Update the printing system to fit the new Morphism class hierarchy. Printing has become easier with the new class hierarchy. This commit also updates the relevant tests. 8eecc16 scolobb Add examples for the new Morphism class hierarchy. Also fix the older examples to properly utilise the new classes. 47d9af7 scolobb Make Object a direct descendent of Symbol. This is because Object has essentially become a Symbol. However, an Object class is still needed in order to avoid the abuse of the name "symbol". Note that the printing routines have remained the same for Object, for the sake of convenience. d03d55c scolobb Use a longer arrows in pretty representation of morphisms. Shorter arrows look somewhat too crowded. 25f746a scolobb Forbid anonymous morphisms. This makes the code even more streamlined and removes some extra checks. 5e28059 scolobb Fix minor typos and use pprint in the docstrings. This commit fixes some typos in the docstrings and removes one gratuitous line in pretty.py. It also fixes the docstrings to use pprint instead of doing print pretty(something). 7aa0545 scolobb Assure 100% test coverage. This commit increases the test coverage from ~94% to full coverage. 9e0f2a1

I think this is almost good to go. The tests and doctests pass on my system. coverage_doctest reports 69% which is pretty good (in my experience this tool is extremely pedantic...); Note that running with --verbose shows you what is missing.

Wow, that's a very cool tool! How lovely of the developers to have it bundled with SymPy :-)

I have removed the doctests from the docstring of Morphism, because Morphism cannot be instatiated any more. This, I suppose, has lowered the score to 61%. Do you think I should try to do something to please the tool?

Also the coverage (run bin/coverage_report sympy/categories) is pretty good and at 94%. I think it is probably a good idea to boost this to 99% or so (should take no longer than 5 minutes, I think).

And this one is another very cool tool! I guess I've never read the documentation on how to set up the development workflow sufficiently attentively :-/

Working on bumping up the test coverage proved a very nice exercise; I was carried away by enthusiasm and brought the test coverage to 100%. I'll push the latest version after I have run the full test suite (that's about 10 mins from now).

Collaborator
commented June 16, 2012
Collaborator
commented June 16, 2012

As far as I can tell this is now good to go.

@asmeurer any objections to pushing sergiu's branch? This is mostly new code, so we shouldn't destroy anything existing.

@Krastanov could you possibly run the tests on this branch? I know the bot no longer works automatically, but I also know you have many python versions set up.

Python 2.5 and 2.7 tests all pass on my box. I'll run Python 3.2 later in the day.

Yet, it would of course be absolutely great if someone could test the thing on their computer as well.

Python 3.2 tests fail with the errors I mentioned yesterday on the list. (These failures are not related to my changes.)

Collaborator
commented June 17, 2012

In my opinion this can go in.

@scolobb If nobody complains till then, I'll push this on monday evening.

Great news, thank you.

merged commit d1ff275 into from June 18, 2012
closed this June 18, 2012
Collaborator
commented June 18, 2012

This is in. Good work!

Oh yeah, thank you! :-) I appreciate the appreciation :-)

Collaborator
commented June 26, 2012

Please investigate if we should have added something to "modules" and/or "tests" lists in setup.py.

Showing 57 unique commits by 1 author.

Jun 16, 2012
Add Class, Object, and Morphism.
These classes represent the very bowels of the module.  They are
yet in their incipient form, which is sufficient, however, to start
writing the tests.
fe78af1
Write the tests for categories.Object and categories.Morphism.
The tests do not pass at the moment.  The goal is to define the desired
behaviour of the methods in Morphism by means of tests.
133bb56
Implement composing and flattening morphisms.
Composing two morphisms results in a morphism which knows which
morphisms it was composed from.  Flattening such a morphism results in
forgetting the information about components.
f888e7e
Extend the string printer to print Object and Morphism.
Also add the corresponding tests.
631cb3d
When composing morphisms, intersperse morphism names with '*'.
The initial approach to use spaces to separate morphisms names proved
inconsistent with the later idea to use '*' when printing morphism
composition.
1ee252d
Amend the string representation of composition of morphisms.
The initial version did not comply with the requirement that the
represented object be able to be reconstructed from the string
representation.
bab67ea
Don't automatically construct the name of the composed morphism.
This also concerns flattened morphisms.  While initially it seemed to be
a good idea to automatically construct new morphism names, it proved
later that trying to be clever when it's not necessary is never good.
ff04329
Add pretty printing for Object and Morphism.
Pretty printing supports anonymous objects and morphisms.  Also add the
corresponding tests.
2e81231
Add printing to LaTeX.
Also add the tests for printing to LaTeX.  Anonymous objects and
morphisms are supported.
9334d84
Add (un)equality operators to Object and Morphism.
Also extend the docstrings and the tests to cover the added semantics.
In morphism equality, composed morphisms are only compared by their
components.  Thus, the names of composed morphisms are insignificant at
comparison.
9cf15ea
Add some nice formatting to the docstrings.
Follow the formatting style found in, for example,
sympy.core.symbol.
044cb74
Add examples to Object and Morphism. 65da9ae
Update the comment in __init__.py to include the principal reference.
This book should be referenced by the notation [JoyOfCats].
7866fc8
Add Category.
This is the base class for all categories.  It will contain very little
functionality related to objects and morphisms.
d539ba1
Extend string, LaTeX, and pretty printers to support Category.
Only the name of the Category is currently printed.  It is improbable
that much additional information will ever be included in the pretty
printed representation of a Category, just because it is so in the books
on the topic.
629f648
Add the skeleton of Diagram.
This commit defines the class and its methods without functionality.
The functionality itself will be implemented after the tests have been
added.
fd0a694
Add some tests for Diagram.
The tests also attempt to cover some subtle aspects of how the diagram
treats its objects and morphism.
462a5e1
In Morphism, define __new__ to allow keyword arguments.
Since Morphism derives from Basic, and Basic.__new__ does not accept
keyword arguments, Morphism could not be constructed with keyword
arguments.  While this is all right for simple constructors like those
of Object and Category, the constructor of Morphism will quickly become
awkward to use if the user will be required to supply all arguments in
order.
c9b027b
Implement identity morphisms.
A Morphism can now be defined to be an identity.  Identity morphisms are
defined not to influence composition.  All identity morphisms of one and
the same object are equal.
fa74c09
Make Morphism and Object check for the type of the other object in co…
…mparisons.

Objects of these classes would previously throw exceptions when compared
to something improper (like None).
207ee8a
Fix the tests for Diagram to properly use identity morphism.
Identity morphisms were implemented after the tests for Diagram, so
there was a need to slightly correct them to properly use identities.
7fcee70
Implement Diagram.
Also add some examples to the docstrings.  Diagram currently relies on
the Basic to do the hashing, because Basic seems to get things right in
this regard.
0a33621
Have Category store a set of commutative diagrams.
Not much more beyond storing a set of diagrams is done at the moment.
26debd8
Add a documentation chapter about the category theory module.
The page currently doesn't currently do much beyond pulling in the
corresponding docstrings and, in fact, that's the purpose of adding the
chapter at this moment.
2723826
Redo the docstrings to make them look better.
This includes slight changes to examples, as well as (ab)using LaTeX
wherever necessary.
bdef398
Remove support for anonymous objects.
Anonymous objects were initially implemented as a way of failing
gracefully in the case when no explicit name was supplied.  However, an
anonymous object does not make much sense.
c60d285
Explicitly test the equality of Morphism's.
Previously the equality of morphisms was implicitly tested in other
contexts.
96f2c3f
Rewrite the existing classes to be fully immutable.
The previous version of the classes was "partially" mutable: one had to
use functions such as add_premises, assert_commutative, etc.  Now,
however, all setting up is done in constructors, the objects being
immutable.  This has made the code of the module simpler, easier to
use, better integrated into the Basic inheritance tree, and much more
consistent.
39ee393
Raise ValueError when composing uncomposable morphisms. 629c916
Fix a long string in test_str.py.
A long string in tests was split on two lines in an ugly way.
0ce0464
Fix some bogus errors in examples in the docstrings.
During a major rewrite a couple commits before, I botched up a couple
docstrings.  This commit fixes that problem.
baf2d46
Add IdentityMorphism.
Having a separate class instead of a flag probably fits better into
SymPy philosophy.  This allows to have Morphism cope without a flag
specifying whether it is an identity or not.  This commit also adds a
different string representation for IdentityMorphism.
d02283a
Make test_args.py pass.
This implies making everything stored in Basic.args to be an instance of
of storing string names, instances of Symbol or Dummy are now stored in
Basic.args.
cd926f2
Fix some formulations and formatting in docs.
Cosmetic fixes mostly.
bb7e1a7
Add doctests for all user-facing methods.
I suppose that some of the docstrings have now become a bit redundant; I
don't think that's a problem at all with docstrings, however.
d1f1440
Use "other" as argument name in equality relations.
Previously, in __eq__/__ne__ I would use some arbitrary name to refer to
the value compared to self.  The convention is however to use "other".
fca51f2
Make an anonymous Morphism equal to itself.
Previously, for a = Morphism(A, B), with A and B being Object's, a == a
was False, which would have been very inconvenient.  Now a == a is
correctly True.
3b85a22
Rename Morphism.identity to is_identity.
This name expresses the idea better.
8dedae3
Raise TypeError when composing a Morphism with a non-Morphism.
Invoking Morphism.compose on something that is not a Morphism or a
derived class now raises TypeError.  __mul__ will in this case raise a
NotImplementedError instead.
9fffc9b
Don't allow unnamed categories.
Should the user supply an empty name to a constructor of Category, raise
ValueError.
20c545d
In Category, explicitly construct a Class of objects.
Also test the way Category stores objects and fix some related doctests.
313246f
Directly show the output of certain statements in doctests.
This commit fixes a tendency to compare the results of certain
statements with their expected results instead of directly showing the
results in doctests.
d9ac130
Don't compare to True or False.
Despite my hopes that such comparison might be better in some cases, I
am hereby removing it.
46aef91
Add a docstring to Category.__new__.
This constructor required further documentation since it attempts to
cleverly interpret its arguments.
926781c
Fix redundancies in test_morphism.
I have been adding tests incrementally, which resulted in some mess and
redundancy in test_morphism.
488f051
Fix a typo in test_diagram.
test_diagram missed an assertion in testing whether composite morphisms
are properly added.
813eeb7
In Category, rename commutative to commutative_diagrams.
The second name is definitely more descriptive, and now that it's not
only me who thinks this it's OK to have long names, this commit changes
everything to use the new name.
47403e4
Add pretty printing to Diagram.
This pretty printing largely relies on the pretty printing routines for
Dict, Morphism, and Object.
0a2e132
Remove explicit comparison operators from Diagram.
My initial intention was to attempt to speed-up the comparison by not
actually comparing the objects of two diagrams, because objects fully
depend on the premises.
f8f429a
Add the new Morphism class hierarchy.
This includes adding the base class Morphism, and the classes
IdentityMorphism, NamedMorphism, and CompositeMorphism.  This also
includes doing necessary adjustments to the other classes in the module
and to the tests.
6de72a1
Update the printing system to fit the new Morphism class hierarchy.
Printing has become easier with the new class hierarchy.  This commit
also updates the relevant tests.
8eecc16
Add examples for the new Morphism class hierarchy.
Also fix the older examples to properly utilise the new classes.
47d9af7
Make Object a direct descendent of Symbol.
This is because Object has essentially become a Symbol.  However, an
Object class is still needed in order to avoid the abuse of the name
"symbol".  Note that the printing routines have remained the same for
Object, for the sake of convenience.
d03d55c
Use a longer arrows in pretty representation of morphisms.
Shorter arrows look somewhat too crowded.
25f746a
Forbid anonymous morphisms.
This makes the code even more streamlined and removes some extra checks.
5e28059
Fix minor typos and use pprint in the docstrings.
This commit fixes some typos in the docstrings and removes one
gratuitous line in pretty.py.  It also fixes the docstrings to use
pprint instead of doing print pretty(something).
7aa0545
Assure 100% test coverage.
This commit increases the test coverage from ~94% to full coverage.
9e0f2a1
Something went wrong with that request. Please try again.