#20745 closed enhancement (fixed)
Implement simplicial sets
Reported by:  jhpalmieri  Owned by:  

Priority:  major  Milestone:  sage7.5 
Component:  algebraic topology  Keywords:  days74 
Cc:  tscrim, jeremy.l.martin, cnassau  Merged in:  
Authors:  John Palmieri  Reviewers:  Christian Nassau 
Report Upstream:  N/A  Work issues:  
Branch:  35790d2 (Commits)  Commit:  
Dependencies:  Stopgaps: 
Description (last modified by )
As the summary says...
For possible followup tickets:
 simplicial abelian groups and EilenbergMac Lane spaces.
 effective homology and related computations that Kenzo can do but we can't yet.
 mapping spaces.
 Discrete Morse theory.
 universal covering spaces for classifying spaces, support for maps between infinite simplicial sets.
Attachments (2)
Change History (113)
comment:1 Changed 3 years ago by
 Branch set to u/jhpalmieri/simplicial_sets
 Commit set to de4e351cf4f30b5a197fd302cd2c9e5a347f3f4d
comment:2 Changed 3 years ago by
 Commit changed from de4e351cf4f30b5a197fd302cd2c9e5a347f3f4d to 7e8f261d5f7d4700020668836d1dacba2660b801
Branch pushed to git repo; I updated commit sha1. New commits:
7e8f261  Simplicial sets: delete code for pushouts: it is incomplete and broken.

comment:3 Changed 3 years ago by
 Commit changed from 7e8f261d5f7d4700020668836d1dacba2660b801 to 05bf22845f13d6c6ea293aa3d067d383345bac39
comment:4 Changed 3 years ago by
Pushouts are now fixed.
comment:5 Changed 3 years ago by
 Cc tscrim jeremy.l.martin added
comment:6 Changed 3 years ago by
 Commit changed from 05bf22845f13d6c6ea293aa3d067d383345bac39 to b3eb08e9548fa604e5a2f9f43edec20ade5e4b8f
Branch pushed to git repo; I updated commit sha1. New commits:
b3eb08e  Simplicial sets: fullfledged pushouts and pullbacks

comment:7 Changed 3 years ago by
 Commit changed from b3eb08e9548fa604e5a2f9f43edec20ade5e4b8f to c5f3a75208f05542bd668ac4ba063de61949df4e
comment:8 Changed 3 years ago by
 Commit changed from c5f3a75208f05542bd668ac4ba063de61949df4e to 37f505e97e341c4fb89d56b8f717e85e28c79e7b
comment:9 Changed 3 years ago by
 Commit changed from 37f505e97e341c4fb89d56b8f717e85e28c79e7b to 7d4e7e56a4a83351469a9c516f5506218197d39f
Branch pushed to git repo; I updated commit sha1. This was a forced push. New commits:
7d4e7e5  Simplicial sets: simplicial model for the Hopf fibration.

comment:10 Changed 3 years ago by
 Commit changed from 7d4e7e56a4a83351469a9c516f5506218197d39f to b9d40ee2ceeceaf8465269131625cd6ccb6d17ad
comment:11 Changed 3 years ago by
 Status changed from new to needs_review
I'm marking this as "needs review" now. One possible future development: mapping spaces for simplicial sets: given simplicial sets K and L, you can view Hom(K,L) as a simplicial set. This would be another example of an infinite simplicial set, so any computations would be done using an appropriate nskeleton.
comment:12 Changed 3 years ago by
comment:13 Changed 3 years ago by
 Commit changed from b9d40ee2ceeceaf8465269131625cd6ccb6d17ad to 899e5698dc17532ebb158f4497de9aa2b20afeaa
Branch pushed to git repo; I updated commit sha1. New commits:
899e569  Fix some Sphinx references.

comment:14 Changed 3 years ago by
 Commit changed from 899e5698dc17532ebb158f4497de9aa2b20afeaa to 1230ce16d2cd5c7cda4b26dcda0f7774ca46e437
Branch pushed to git repo; I updated commit sha1. New commits:
1230ce1  Simplicial sets: products and coproducts of maps.

comment:15 Changed 3 years ago by
 Status changed from needs_review to needs_work
comment:16 Changed 3 years ago by
 Commit changed from 1230ce16d2cd5c7cda4b26dcda0f7774ca46e437 to 9fbfee64243c72d653f7cada254eca756fa4622f
Branch pushed to git repo; I updated commit sha1. New commits:
a518237  Add is_connected method to generic cell complexes.

8b615cd  simplicial sets: start to improve implementation for infinite simplicial sets

d0b13d9  merging with 7.3.beta9

c1ddea1  Simplicial sets: improve implementation for infinite simplicial sets.

56620b7  simplicial sets: make smash product class inherit from Factors.

d41a256  simplicial set: change a little terminology.

456eb80  fix some whitespace

ac7f6ea  simplicial sets: minor editing of docstrings, some reorganization

e628254  simplicial sets: documentation in categories/simplicial_sets.py

9fbfee6  Simplicial sets: make Pushout, Pullback, etc., inherit from UniqueRepresentation:

comment:17 Changed 3 years ago by
 Status changed from needs_work to needs_review
comment:18 Changed 3 years ago by
 Description modified (diff)
I'm marking this as "needs_review" now.
comment:19 Changed 3 years ago by
 Commit changed from 9fbfee64243c72d653f7cada254eca756fa4622f to bcb0332845a09120cb294b77a70148c4d465b5d9
Branch pushed to git repo; I updated commit sha1. New commits:
bcb0332  disambiguate two references

comment:20 Changed 3 years ago by
 Commit changed from bcb0332845a09120cb294b77a70148c4d465b5d9 to 94c21df3bb5018899dd23ff05e0881cb905f43e5
comment:21 followup: ↓ 23 Changed 3 years ago by
This looks like a lot of very nice and very useful code! I have just started to play around with it and have two quick comments:
SimplicialSets
is not in the global namespace, whereasSimplicialComplex
is globally available per default.
 I tried
TestSuite(RP7).run()
and got lots of errors, presumably becausean_element
is not implemented.
I have no fixed opinion whether these observations count as faults or facts, though.
comment:22 Changed 3 years ago by
 Cc cnassau added
comment:23 in reply to: ↑ 21 Changed 3 years ago by
Hi Christian, thanks for taking a look!
Replying to cnassau:
This looks like a lot of very nice and very useful code! I have just started to play around with it and have two quick comments:
SimplicialSets
is not in the global namespace, whereasSimplicialComplex
is globally available per default.
It is more fiddly to construct simplicial sets than simplicial complexes, to the point that I don't know if ordinary users will want to use SimplicialSet
much. (With hindsight, I would also not have included DeltaComplex
in the global namespace either.) My hope is that users will use the predefined simplicial sets (especially simplices and spheres) and build other simplicial sets from those using quotients, products, mapping cones, etc.
 I tried
TestSuite(RP7).run()
and got lots of errors, presumably becausean_element
is not implemented.
I didn't try that. It might be worth looking into.
comment:24 Changed 3 years ago by
I just tried (and eventually succeeded) to construct the quotient RP7/RP4
and to compute its cohomology. My first approach failed, since I found no way to define the inclusion map:
sage: RP4 = sage.homology.simplicial_set_catalog.RealProjectiveSpace(4) sage: RP7 = sage.homology.simplicial_set_catalog.RealProjectiveSpace(7) sage: X=Hom(RP4,RP7) sage: X({i:i for i in RP4.nondegenerate_simplices()})  ValueError Traceback (most recent call last) ... ValueError: this simplex is not in this simplicial set
Can this be done somehow?
A possibly related problem is that the ambient spaces are different; maybe they (and the projective spaces) should have unique representation?
sage: RP4.ambient_space() is RP7.ambient_space() False
(Apologies if this is explained in the documentation somewhere  I usually only read documentation as a last resort.)
comment:25 followup: ↓ 26 Changed 3 years ago by
I thought about unique representation; my conclusion was that it was the wrong thing to do. If you construct two 4spheres, you want them to be different so you can take their disjoint union, etc. A related implementation detail is that when you construct an abstract nsimplex, it is always distinct from previously created ones.
The easiest way to do what you're talking about is
sage: RP7 = simplicial_sets.RealProjectiveSpace(7) sage: RP4 = RP7.n_skeleton(4) sage: RP4.inclusion_map() ... sage: RP7.quotient(RP4) ...
Or I think you also do
sage: C2 = groups.misc.MultiplicativeAbelian([2]) sage: BC2 = simplicial_sets.ClassifyingSpace(C2) sage: RP7 = BC2.n_skeleton(7) sage: RP4 = BC2.n_skeleton(4)
to get a common ambient space.
comment:26 in reply to: ↑ 25 Changed 3 years ago by
Replying to jhpalmieri:
I thought about unique representation; my conclusion was that it was the wrong thing to do. If you construct two 4spheres, you want them to be different so you can take their disjoint union, etc. A related implementation detail is that when you construct an abstract nsimplex, it is always distinct from previously created ones.
I get your point and I now see that this is also very well explained in the documentation.
I don't understand the origin of the term f_vector
: I can see that this is also used in the other cell complex related parts, but what might the "f" stand for? I'm just curious here...
comment:27 Changed 3 years ago by
fvector is standard in combinatorics, but I don't know what the "f" means. "face", I'm guessing?
comment:28 Changed 3 years ago by
I have just tried to implement the "Catalan simplicial set", modeled on your code for the nerve of a monoid. It seems to work fine, but I get this error from all_n_simplices
:
sage: Cat.all_n_simplices(3)  NotImplementedError Traceback (most recent call last) ... NotImplementedError: this simplicial set may be infinite, so specify max_dim
I think this could be fixed by changing one line in the definition of all_n_simplices
, i.e. change
non_degen = [_ for _ in self.nondegenerate_simplices() if _.dimension() <= n]
to
non_degen = [_ for _ in self.nondegenerate_simplices(max_dim=n) if _.dimension() <= n]
I have noted that the classifying spaces currently do not have an "all_n_simplices" method.
sage: X Classifying space of Finite Field of size 3 sage: X.all_n_simplices(5) ... AttributeError: 'Nerve_with_category' object has no attribute 'all_n_simplices'
Is this deliberate?
comment:29 Changed 3 years ago by
This isn't deliberate. I think it's a good idea to make the change you suggest and to implement all_n_simplices
for classifying spaces. I'm testing this right now; I'll push the change if it works.
comment:30 Changed 3 years ago by
 Commit changed from 94c21df3bb5018899dd23ff05e0881cb905f43e5 to 8dac89119b0fbcdad9505f32513d251da4aaaa33
comment:31 followup: ↓ 32 Changed 3 years ago by
It seems there are some other calls to nondegenerate_simplices(...)
that should have an approriate max_dim
value: if I try to compute the homology of this Catalan simplicial set I otherwise get an error:
sage: Cat.chain_complex(dimensions=range(5)) ... NotImplementedError: this simplicial set may be infinite, so specify max_dim
I'm attaching my code so that you can see what I'm doing.
One strange thing is that my Cat
has a couple of useful methods (e.g. homology
) that are missing from the classifying spaces. The only relevant difference that I'm aware of is their categories, though that should add *more* methods, not hide them:
sage: Cat.categories() [Category of simplicial sets, Category of sets, Category of sets with partial maps, Category of objects] sage: simplicial_sets.ClassifyingSpace(GF(3)).categories() [Category of pointed simplicial sets, Category of simplicial sets, Category of sets, Category of sets with partial maps, Category of objects]
I think something like
simplicial_sets.ClassifyingSpace(GF(3)).betti(3)
should probably work out of the box (i.e. without explicitly taking a skeleton).
comment:32 in reply to: ↑ 31 Changed 3 years ago by
Replying to cnassau:
It seems there are some other calls to
nondegenerate_simplices(...)
that should have an approriatemax_dim
value: if I try to compute the homology of this Catalan simplicial set I otherwise get an error:
The issue here is that your Cat
inherits from SimplicialSet
:
sage: sage.homology.simplicial_set.SimplicialSet? Init signature: sage.homology.simplicial_set.SimplicialSet(self, data, base_point=None, name=None, check=True, category=None) Docstring: A finite simplicial set. ...
So it is assumed to be finite, which means it has more methods defined for it, like chain_complex
, which are not defined for (for example) classifying spaces. Classifying spaces instead inherit from SimplicialSet_arbitrary
. Note that SimplicialSet
is just an alias for SimplicialSet_finite
, and we could get rid of this alias and delete SimplicialSet
if it would be clearer that way.
I think something like
simplicial_sets.ClassifyingSpace(GF(3)).betti(3)should probably work out of the box (i.e. without explicitly taking a skeleton).
Methods like betti
come from the class GenericCellComplex
, and those are assumed to be finite. So we would need to implement betti
for SimplicialSet_arbitrary
, which we could do. Maybe also chain_complex
and n_chains
.
comment:33 Changed 3 years ago by
 Description modified (diff)
At some point, we should lift up some of the things I did for Hochschild (co)homology to the chain complex and homology method to handle infinitedimensional complexes (with each dimension a finite complex). Although, offhand I can't think of a good way to handle the cases when there are an infinite number of cells in a given dimension.
comment:34 Changed 3 years ago by
I'm puzzled by the following output: the zeroeth cohomology only comes out right once:
sage: X=simplicial_sets.ClassifyingSpace(GF(3)) ; X Classifying space of Finite Field of size 3 sage: X.cohomology(0) Z sage: X.cohomology((0,)) {0: 0} sage: X.cohomology(range(4)) {0: 0, 1: 0, 2: 0, 3: 0}
I could imagine that this is a pointed vs. nonpointed issue, but I see the same behaviour for my Catalan set, which has no basepoint.
PS: I now see that the documentation specifies reduced homology, but the answer seems inconsistent nonetheless...
comment:35 Changed 3 years ago by
That's a bug in the simplicial set chain complex code. Here is a patched version, which also implements chain_complex
and n_chains
for arbitrary simplicial sets, not just finite ones.
comment:36 Changed 3 years ago by
 Commit changed from 8dac89119b0fbcdad9505f32513d251da4aaaa33 to 1443151ed3285ccfa132d9edf784c8eb9960ea90
Branch pushed to git repo; I updated commit sha1. New commits:
1443151  simplicial sets: implement betti, chain_complex, and n_chains for

comment:37 Changed 3 years ago by
 Commit changed from 1443151ed3285ccfa132d9edf784c8eb9960ea90 to cecb9bc13cc3167d0b6a6c73c7b71000db7b19ef
Branch pushed to git repo; I updated commit sha1. New commits:
cecb9bc  fix typo in documentation

comment:38 followup: ↓ 41 Changed 3 years ago by
Here's a minor thing I just noted: latex() of cells in a classifying space doesn't print correctly:
sage: Q=simplicial_sets.ClassifyingSpace(groups.misc.WeylGroup('B2')) sage: Q.n_cells(1) [[ 0 1] [1 0], [ 0 1] [ 1 0], [ 0 1] [1 0], [ 1 0] [ 0 1], [1 0] [ 0 1], [1 0] [ 0 1], [0 1] [1 0]] sage: latex(Q.n_cells(1)) \left[\Delta^{1}, \Delta^{1}, \Delta^{1}, \Delta^{1}, \Delta^{1}, \Delta^{1}, \Delta^{1}\right]
On a related note, I don't quite like the very verbose printing of degeneracies (but that might be a matter of taste). I would probably prefer something like "s_0(f * f)
" instead of "Simplex obtained by applying degeneracy s_0 to f * f
".
comment:39 followup: ↓ 42 Changed 3 years ago by
I think it would be valuable if one could construct simplices in a classifying space from the corresponding sequence of monoid elements. Here's how I currently construct a map from RP2 to BD8:
sage: D8=DihedralGroup(4) sage: X=simplicial_sets.ClassifyingSpace(D8) sage: g=D8((1,3)) sage: RP2=simplicial_sets.RealProjectiveSpace(2) sage: e0,e1,e2 = RP2.nondegenerate_simplices() sage: H=Hom(RP2,X) sage: X.n_skeleton(2) ;# hack to create X._simplex_data Simplicial set with 57 nondegenerate simplices sage: i=H({e0:X.base_point(),e1:dict(X._simplex_data)[(g,)],e2:dict(X._simplex_data)[(g,g)]}) ; i Simplicial set morphism: From: RP^2 To: Classifying space of Dihedral group of order 8 as a permutation group Defn: [1, f, f * f] > [(), (1,3), (1,3) * (1,3)]
It would be nice to have a method on X that wraps the dict(X._simplex_data)[(g1,g2,...)]
(and is able to recognize degenerate simplices and implicitly constructs the required nskeleton).
I would also like to use the universal covering space for BD8 here, but since this is infinite there would currently be no way to construct the ED8 > BD8 as a simplical map. I'll add this to the description of this ticket as a possible followup topic.
comment:40 Changed 3 years ago by
 Description modified (diff)
comment:41 in reply to: ↑ 38 Changed 3 years ago by
Replying to cnassau:
Here's a minor thing I just noted: latex() of cells in a classifying space doesn't print correctly:
Is there a standard way of customizing the LaTeX names of SageObjects
? We could fix this anyway, but as you say, it's pretty minor.
On a related note, I don't quite like the very verbose printing of degeneracies (but that might be a matter of taste). I would probably prefer something like "
s_0(f * f)
" instead of "Simplex obtained by applying degeneracy s_0 to f * f
".
The standard way of printing a nondegenerate simplex is "Nondegenerate simplex of dimension d". I don't think that "s_0 (Nondegenerate simplex ...)" looks very good. I suppose we could check if the nondegenerate simplex has a custom name and use "s_0 (name)" if so, the verbose version if not.
If you feel like making these changes, go ahead. They're not at the top of my list, though.
comment:42 in reply to: ↑ 39 Changed 3 years ago by
Replying to cnassau:
I think it would be valuable if one could construct simplices in a classifying space from the corresponding sequence of monoid elements. Here's how I currently construct a map from RP2 to BD8:
sage: D8=DihedralGroup(4) sage: X=simplicial_sets.ClassifyingSpace(D8) sage: g=D8((1,3)) sage: RP2=simplicial_sets.RealProjectiveSpace(2) sage: e0,e1,e2 = RP2.nondegenerate_simplices() sage: H=Hom(RP2,X) sage: X.n_skeleton(2) ;# hack to create X._simplex_data Simplicial set with 57 nondegenerate simplices sage: i=H({e0:X.base_point(),e1:dict(X._simplex_data)[(g,)],e2:dict(X._simplex_data)[(g,g)]}) ; i Simplicial set morphism: From: RP^2 To: Classifying space of Dihedral group of order 8 as a permutation group Defn: [1, f, f * f] > [(), (1,3), (1,3) * (1,3)]It would be nice to have a method on X that wraps the
dict(X._simplex_data)[(g1,g2,...)]
(and is able to recognize degenerate simplices and implicitly constructs the required nskeleton).
That sounds like a good idea.
comment:43 Changed 3 years ago by
One thing that strikes me as problematic is that you're not using the Parent/Element? framework for the simplicial sets: currently, the sets themselves are declared to be Parent objects, but their simplices are implemented by a separate AbstractSimplex? class. Shouldn't it be "better" (and more in line with the general Sage "philosophy") to realize the simplices as Element instances? or is there maybe a good reason why you did not take that course?
I actually feel a bit bad about bringing this up, because rewiring the code to use the Element framework would not just be a lot of work, but also potentially open a can of worms with somewhat unclear benefits.
But anyway, regarding the latex representation of cells in a BG, for example, it would be nice to have a dedicated element class for simplices in a nerve; these elements would remember the chain of monoid elements used to construct them and forward calls to _repr_ and _latex_ to those monoid elements. Among other things, this avoids setting a custom_name for all (nondegenerate) simplices, which might be regarded a little wasteful. [Of course, it does set a custom "_chain" attribute then, which takes up the same space  but this is arguably more "valuable"]
Actually, I just tried this approach out (i.e. the Nerve with a custom Element subclass). It seems to work fine  except that for the life of me I cannot get the sorting of these elements in line with the existing code from AbstractSimplex?... so plenty of doctests fail. This is probably just a small indication of the nasty suprises that might be lurking here.
Anyway, I'm just curious to hear what your view on this is!
comment:44 Changed 3 years ago by
One benefit to using the Parent/Element framework is coercion; in particular, for input of morphisms. Another is you can take advantage of coercion for rich comparisons (side note, IIRC, you do not need __ne__
with @total_ordering
). In principle, it should be as easy as changing the inheritance from SageObject
to Element
and setting Element = AbstractSimplex
in the parent class. To get coercions, a few _coerce_map_from_
might need to be implemented.
From now (finally) having a moment to take a skimthrough, I would separate the examples into a separate file for easier readability and separationofconcerns.
comment:45 followups: ↓ 47 ↓ 49 Changed 3 years ago by
I'm finally getting back to this.
 I think you do need to define
__ne__
with@total_ordering
: I think@total_ordering
only deals with<
,>
, etc., not!=
. Looking at the@total_ordering
source code seems to confirm this; furthermore, if I delete that method, I get all sorts of breakages.
 Now I remember why I'm not using the
Parent/Element
framework: when you define anElement
, you are supposed to assign a parent to it, but when defining simplices for a simplicial set, you often want to define the simplices first, without assigning a parent to them. In particular, you should be able to define an abstract simplex without having a parent attached to it, and this disagrees with the documentation for theElement
class in Sage:Subtypes must either call ``__init__()`` to set ``_parent``, or may set ``_parent`` themselves if that would be more efficient.
You also might want one simplex to be an element of several different simplicial sets (not two copies of that simplex, but that exact simplex, for example when constructing subsimplices). Suggestions for how to get around this?
comment:46 Changed 3 years ago by
 Commit changed from cecb9bc13cc3167d0b6a6c73c7b71000db7b19ef to 64c8d0a232bd0969978066607a6cd3ca42096d88
Branch pushed to git repo; I updated commit sha1. New commits:
64c8d0a  Simplicial sets: move examples to a separate file.

comment:47 in reply to: ↑ 45 Changed 3 years ago by
Replying to jhpalmieri:
I'm finally getting back to this.
 I think you do need to define
__ne__
with@total_ordering
: I think@total_ordering
only deals with<
,>
, etc., not!=
. Looking at the@total_ordering
source code seems to confirm this; furthermore, if I delete that method, I get all sorts of breakages.
Okay, good to know. Thanks. I really wish we had a default __ne__
for SageObject
that just called return not self == other
...
 Now I remember why I'm not using the
Parent/Element
framework: when you define anElement
, you are supposed to assign a parent to it, but when defining simplices for a simplicial set, you often want to define the simplices first, without assigning a parent to them. In particular, you should be able to define an abstract simplex without having a parent attached to it, and this disagrees with the documentation for theElement
class in Sage:Subtypes must either call ``__init__()`` to set ``_parent``, or may set ``_parent`` themselves if that would be more efficient.You also might want one simplex to be an element of several different simplicial sets (not two copies of that simplex, but that exact simplex, for example when constructing subsimplices). Suggestions for how to get around this?
You can point to the underlying data between simplicies, which would minimize the overhead. The other option would be to have an Element
version of an abstract simplex, where you copy all of the data. However, both of these have some drawbacks, and it might depend more on how this will be used what the best course of action is. The good news is that these are easy enough to refactor, so future changes should be straightforward if we decide to not change anything now.
comment:48 Changed 3 years ago by
I'm inclined not to modify the Element
structure right now.
I looked into the TestSuite
.
 it's easy enough to implement
_an_element_
: just return a vertex (as long as it has one).  I also get a failure because
SimplicialSet_finite
inherits fromGenericCellComplex
, which has an@abstract_method
which is not defined for simplicial sets:join
. I will define ajoin
method for simplicial sets which just raises aNotImplementedError
, rather than just skip this test. (Or implement joins, but I don't think I want to do that right now.)  I also get a pickling error, and I don't know how to debug it:
File "sage/structure/sage_object.pyx", line 436, in sage.structure.sage_object.SageObject.dumps (build/cythonized/sage/structure/sage_object.c:3727) s = cPickle.dumps(self, protocol=2) PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
Any suggestions?
comment:49 in reply to: ↑ 45 ; followups: ↓ 51 ↓ 55 Changed 3 years ago by
Replying to jhpalmieri:
 Now I remember why I'm not using the
Parent/Element
framework: when you define anElement
, you are supposed to assign a parent to it, but when defining simplices for a simplicial set, you often want to define the simplices first, without assigning a parent to them. In particular, you should be able to define an abstract simplex without having a parent attached to it, and this disagrees with the documentation for theElement
class in Sage:Subtypes must either call ``__init__()`` to set ``_parent``, or may set ``_parent`` themselves if that would be more efficient.You also might want one simplex to be an element of several different simplicial sets (not two copies of that simplex, but that exact simplex, for example when constructing subsimplices). Suggestions for how to get around this?
I have to think about these questions a bit longer. You are probably referring to constructions like these from the doctests:
sage: v = AbstractSimplex(0, name='v') sage: a = AbstractSimplex(0, name='a') sage: b = AbstractSimplex(0, name='b') sage: c = AbstractSimplex(0, name='c') sage: e0 = AbstractSimplex(1, name='e_0') sage: e1 = AbstractSimplex(1, name='e_1') sage: e2 = AbstractSimplex(1, name='e_2') sage: X = SimplicialSet({e2: (b, a)}) sage: Y0 = SimplicialSet({e2: (b,a), e0: (c,b), e1: (c,a)})
My feeling is that AbstractSimplex
could maybe become a SimplicialSet
itself, namely an alias for a generic nsimplex
. Whether this works out seems hard to say without actually trying to implement it. I think there are potential benefits of sticking closer to the Sage parent/element framework, but whether this is worthwhile depends on the pain implied by the implementation.
comment:50 followup: ↓ 52 Changed 3 years ago by
It looks like you're trying to pickle a lambda function, which you cannot do. IIRC, the usual workaround is to have an explicitly named function/method somewhere.
comment:51 in reply to: ↑ 49 Changed 3 years ago by
Replying to cnassau:
I have to think about these questions a bit longer. You are probably referring to constructions like these from the doctests:
sage: v = AbstractSimplex(0, name='v') sage: a = AbstractSimplex(0, name='a') sage: b = AbstractSimplex(0, name='b') sage: c = AbstractSimplex(0, name='c') sage: e0 = AbstractSimplex(1, name='e_0') sage: e1 = AbstractSimplex(1, name='e_1') sage: e2 = AbstractSimplex(1, name='e_2') sage: X = SimplicialSet({e2: (b, a)}) sage: Y0 = SimplicialSet({e2: (b,a), e0: (c,b), e1: (c,a)})My feeling is that
AbstractSimplex
could maybe become aSimplicialSet
itself, namely an alias for a genericnsimplex
. Whether this works out seems hard to say without actually trying to implement it. I think there are potential benefits of sticking closer to the Sage parent/element framework, but whether this is worthwhile depends on the pain implied by the implementation.
It's not just doctests. Any time you want to define a simplicial set in Sage, you build it out of instances of AbstractSimplex
. To construct a sphere, you define a 0simplex and an nsimplex, and then tell Sage what each face of the nsimplex is (= the unique degenerate (n1)simplex coming from the vertex).
So I guess we could have a hierarchy of classes: a basic simplicial set to model simplices, then a fullfledged simplicial set built out of those basic ones? Would the fullfledged simplicial sets have the basic ones as elements? I'm not sure how it would work. If you have ideas for an implementation, let me know.
comment:52 in reply to: ↑ 50 Changed 3 years ago by
Replying to tscrim:
It looks like you're trying to pickle a lambda function, which you cannot do. IIRC, the usual workaround is to have an explicitly named function/method somewhere.
Oh, I see. For some of the examples of simplicial sets, like the n
sphere, I have lines like
S._latex_ = lambda: 'S^{{{}}}'.format(n) return S
So maybe the class SimplicialSet_finite
(or SimplicialSet_arbitrary
) should have a custom latex_name
argument which gets used in its _latex_
method, to avoid this sort of thing.
comment:53 Changed 3 years ago by
 Commit changed from 64c8d0a232bd0969978066607a6cd3ca42096d88 to 18710835161be845f2ea06e0ecfa7fdff1f995fd
Branch pushed to git repo; I updated commit sha1. New commits:
1871083  Simplicial sets: move Nerve to simplicial_set_examples, add

comment:54 Changed 3 years ago by
Here is partial progress: latex names for AbstractSimplex
. I'll work on latex names for simplicial sets and on the pickling issue later.
comment:55 in reply to: ↑ 49 ; followup: ↓ 56 Changed 3 years ago by
Replying to cnassau:
My feeling is that
AbstractSimplex
could maybe become aSimplicialSet
itself, namely an alias for a genericnsimplex
. Whether this works out seems hard to say without actually trying to implement it. I think there are potential benefits of sticking closer to the Sage parent/element framework, but whether this is worthwhile depends on the pain implied by the implementation.
This idea was somehwat off, but the following seems to work:
1) create a new parent singleton
SetOfAbstractSimplices
as a parent for allAbstractSimplex
objects
2) make
SimplicialSets
a subcategory ofFacadeSets
, so they can all share their elements with theSetOfAbstractSimplices
I have implemented this (very roughly) and think this might be a feasible approach. My changes are shown in this commit:
https://git.sagemath.org/sage.git/commit/?id=bcc53e3b9100163d7c1c2d0a42aceb9458f83c8a
One of the benefits is that simplicial sets can then have custom element classes: this is illustrated in the Nerve, where simplices only remember the chain of group elements, not their "custom name" or "custom latex name". And their names are only computed when somebody actually asks for them.
(caveat: "latex(degeneration(nervesimplex))" is still wrong with this code: there should probably be a _latex_nondegenerate_ method that the Nerve.Element class should override, but these are details left for later).
Pickling remains a problem because of the lack of unique representation; I suggest to disable the _test_pickling for the simplicial stuff (have done that for elements in that commit).
In case you could warm up to these suggestions there might be a few further rearrangements, that might prove useful; much of the AbstractSimplex? code should probably moved into the ElementMethods? of the SimplicialSets? category, for example.
comment:56 in reply to: ↑ 55 Changed 3 years ago by
Replying to cnassau:
2) make
SimplicialSets
a subcategory ofFacadeSets
, so they can all share their elements with theSetOfAbstractSimplices
Please no, I've seen very subtle behavior problems with facade sets/parents. Facades don't "share" elements, they just allow you to do P(foo)
, but you run into trouble when you really do want elements of P
. A much better approach is to pass underlying data and have the elements behave like adapter classes (it just can require more lines of documentation). I'm +1 on having actual element classes, but it doesn't need to be here for this to get a positive review.
Pickling remains a problem because of the lack of unique representation; I suggest to disable the _test_pickling for the simplicial stuff (have done that for elements in that commit).
Unique representation is just one way to deal with pickling, but it is not the only way. comment:52 is the likely culprit to me, and I'm +1 on having a (customizable) _latex_name
attribute/input. We do this, e.g., in CombinatorialFreeModule
and in the manifolds.
comment:57 Changed 3 years ago by
 Commit changed from 18710835161be845f2ea06e0ecfa7fdff1f995fd to cd9cfffdf37d0c9e24ac5ecc0c255130f5945e23
Branch pushed to git repo; I updated commit sha1. New commits:
cd9cfff  simplicial sets: add latex_name for SimplicialSet_finite.

comment:58 followup: ↓ 59 Changed 3 years ago by
I removed the lambda functions, but pickling still doesn't work, with a different error this time:
File "/Users/palmieri/Desktop/Sage_stuff/git/sage/local/lib/python2.7/sitepackages/sage/homology/simplicial_set.py", line 3227, in __hash__ return hash(self._data) File "sage/structure/parent.pyx", line 855, in sage.structure.parent.Parent.__getattr__ (build/cythonized/sage/structure/parent.c:8320) attr = getattr_from_other_class(self, self._category.parent_class, name) File "sage/structure/misc.pyx", line 253, in sage.structure.misc.getattr_from_other_class (build/cythonized/sage/structure/misc.c:1755) raise dummy_attribute_error AttributeError: 'SimplicialSet_finite_with_category' object has no attribute '_data'
I don't understand this because when I define a simplicial set, first of all, self._data
is defined in the __init__
method, and second of all, I can access X._data
from the command line with no problem. I must be missing something obvious.
comment:59 in reply to: ↑ 58 ; followup: ↓ 60 Changed 3 years ago by
Replying to jhpalmieri:
I removed the lambda functions, but pickling still doesn't work, with a different error this time:
I don't think there's a chance to make pickling work to the satisfaction of the TestSuite
: you can never have "loads(dumps(simplex)) == simplex
" since both sides are different python objects and equality of AbstractSimplex
objects is tied to their id
. And since SimplicialSet
objects are uniquely determined by their simplices this means that they too can't be recovered from their pickle.
Of course, the "loads(dumps(...))" call itself should eventually work. It seems pickle
takes the hash before the object is fully constructed:
sage: from sage.structure.sage_object import StringIO sage: pickle.load(StringIO(dumps(CP2,compress=false)))  AttributeError Traceback (most recent call last) <ipythoninput7695809fd4beb> in <module>() ... /waste/cn/sagegit/local/lib/python/pickle.pyc in load_setitem(self) > 1202 dict[key] = value 1203 dispatch[SETITEM] = load_setitem
I have no idea why this might be the case, though... a hidden recursive reference, maybe...? FWIW, pickling succeeds if the __hash__
routine returns 1 in case of a missing _data
member.
comment:60 in reply to: ↑ 59 Changed 3 years ago by
Replying to cnassau:
Replying to jhpalmieri:
I removed the lambda functions, but pickling still doesn't work, with a different error this time:
I don't think there's a chance to make pickling work to the satisfaction of the
TestSuite
: you can never have "loads(dumps(simplex)) == simplex
" since both sides are different python objects and equality ofAbstractSimplex
objects is tied to theirid
. And sinceSimplicialSet
objects are uniquely determined by their simplices this means that they too can't be recovered from their pickle.
I would say that is a bad equality check (that is only for nondegenerate simplices). It should (along with the hash) compare the underlying simplices. I think the first check, which was suppose to be an optimization, should just be removed (likewise for __hash__
).
comment:61 followups: ↓ 62 ↓ 68 Changed 3 years ago by
I can certainly make the change

src/sage/homology/simplicial_set.py
diff git a/src/sage/homology/simplicial_set.py b/src/sage/homology/simplicial_set.py index 2f9987a..3ef1c2f 100644
a b class AbstractSimplex(SageObject): 484 484 """ 485 485 if self.is_nondegenerate(): 486 486 return id(self) 487 return hash(self.nondegenerate()) ^ hash(self._degens)487 return id(self.nondegenerate()) ^ hash(self._degens) 488 488 489 489 def __eq__(self, other): 490 490 """ … … class AbstractSimplex(SageObject): 512 512 """ 513 513 if not isinstance(other, AbstractSimplex): 514 514 return False 515 if self.is_nondegenerate() and other.is_nondegenerate():516 return self is other517 515 return (self._degens == other._degens 518 516 and self.nondegenerate() is other.nondegenerate()) 519 517
loads(dumps(x)) == x
will still fail, which is fine with me.
This is for AbstractSimplex
, though. My other problem was for pickling with SimplicialSet_finite
, which I still don't understand. (I agree with Christian that loads(dumps(X)) == X
will fail, but I would like loads(dumps(X))
to not raise an error.)
comment:62 in reply to: ↑ 61 Changed 3 years ago by
Replying to jhpalmieri:
My other problem was for pickling with
SimplicialSet_finite
, which I still don't understand. (I agree with Christian thatloads(dumps(X)) == X
will fail, but I would likeloads(dumps(X))
to not raise an error.)
The problem seems to be that every AbstractSimplex
in a SimplicialSet
keeps a list of its parents in the _faces
attribute. I just deactivated these lines and "loads(dumps(...))" works again:
# Finally, store the faces of each nondegenerate simplex as an # attribute of the simplex itself. for x in simplices: x._faces[self] = data[x]
I think during unpickling python tries to repopulate the x._faces
for each x in its _data
, and here it needs the hash of self
even though self
is not fully reconstructed, yet.
FWIW, I think this _faces
attribute in the AbstractSimplex
should probably be removed. The category should specify a "faces(element)
" parent method, while the actual parent implementations are free to implement this in whatever way they like.
comment:63 followup: ↓ 67 Changed 3 years ago by
The current data structure is flawed, I think. I think it's a good idea that two simplicial sets can contain the same AbstractSimplex
, but then to make geometric sense, that AbstractSimplex
should have the same faces in each of those simplicial sets (possibly up to some identifications, if a quotient operation is involved). In other words, I agree that the ._faces
attribute, keyed by simplicial sets, should be removed.
So I've been trying to think of other options. Here's one idea:
PreSimplex
 like the currentAbstractSimplex
, it would have a dimension and optionally, degeneracies, an underlying nondegeneratePreSimplex
, a name, etc. It's not a fullfledged simplex because it doesn't have faces.
SimplicialSet_finite
. Define by specifying a dictionary, as now, where each key is aPreSimplex
and the corresponding value is its list of faces. At that point, eachPreSimplex
would define anElement
of the simplicial set and would remember its faces. Could you also specifyElements
of other simplicial sets as keys? If you specify the samePreSimplex
as belonging to two different simplicial sets, what should happen? Raise an error unless the faces match up?
comment:64 Changed 3 years ago by
 Commit changed from cd9cfffdf37d0c9e24ac5ecc0c255130f5945e23 to 2b2a2c9df6428459b2a0875f4e483842e2635335
Branch pushed to git repo; I updated commit sha1. New commits:
2b2a2c9  simplicial sets: remove ._faces attribute from AbstractSimplex. Minor cleanup.

comment:65 Changed 3 years ago by
Here are minor changes: remove the ._faces
attribute and other minor cleanup. It may still be a good idea to change the data structure more, though.
comment:66 Changed 3 years ago by
I guess my proposal is not that different from the current branch, except that it also would involve a new Element
class for any PreSimplex
/AbstractSimplex
contained in a simplicial set. This would have the advantage of using the parent/element framework. I still had a few questions in comment:63.
I'm open to other ideas, too.
comment:67 in reply to: ↑ 63 ; followup: ↓ 71 Changed 3 years ago by
Replying to jhpalmieri:
I think it's a good idea that two simplicial sets can contain the same
AbstractSimplex
, but then to make geometric sense, thatAbstractSimplex
should have the same faces in each of those simplicial sets (possibly up to some identifications, if a quotient operation is involved).
I like your basic approach to construct a simplicial set from a collection of simplices + additional face information. However, instead of creating a PreSimplex
class without faces we could also just *add* faces to the AbstractSimplex
, which turns them into regular $n$simplices (this should also be easier to explain). Consider the code
sage: a,b = [AbstractSimplex(0,name=x) for x in "ab"] sage: e,f = [AbstractSimplex(1,name=x) for x in "ef"] sage: X = SimplicialSet_finite({e:(a,b),f:(a,b)}) sage: Y = SimplicialSet_finite({e:(e.face(0),e.face(1)),f:(e.face(0),e.face(1))}) sage: Z = SimplicialSet_finite({e:(f.face(0),f.face(0)),f:(f.face(0),f.face(1))})
As far as the Parent/Element
framework is concerned, none of $a,b,e,f$ would be actual elements of $X$, $Y$, or $Z$; rather they would be Elements of the SetOfAbstractSimplices
singleton (which models the infinite coproduct of all \Delta_n^{(x)}
where $x$ can be, say, any python object).
To create elements you would have to cast them to the parent (which might sometimes happen implicitly):
sage: X.n_cells(0) == [a,b] False sage: X.n_cells(0) == [X(a), X(b)] True sage: Y.n_cells(0) [ Face d0 of e, Face d1 of e] sage: Z.n_cells(0) [ Face d0 of f, Face d1 of f ] sage: Z(e.face(1)) == Z(e).face(1) True sage: Z(e).faces() ( Face d0 of f, Face d0 of f )
Here my assumption is that we're *not* following my earlier suggestion to use FacadeSets
; rather every element/simplex belongs to a unique parent, following the advice of Travis in comment 56.
comment:68 in reply to: ↑ 61 Changed 3 years ago by
Replying to jhpalmieri:
I can certainly make the change
Not quite, the change I was suggesting was

src/sage/homology/simplicial_set.py
diff git a/src/sage/homology/simplicial_set.py b/src/sage/homology/simplicial_set.py index 2f9987a..3ef1c2f 100644
a b class AbstractSimplex(SageObject): (this hunk was shorter than expected) 484 484 """ 485 if self.is_nondegenerate():486 return id(self)487 485 return hash(self.nondegenerate()) ^ hash(self._degens) 488 486 489 487 def __eq__(self, other): 490 488 """ … … class AbstractSimplex(SageObject): 512 512 """ 513 513 if not isinstance(other, AbstractSimplex): 514 514 return False 515 if self.is_nondegenerate() and other.is_nondegenerate():516 return self is other517 515 return (self._degens == other._degens 518 516 and self.nondegenerate() is other.nondegenerate()) 519 517
comment:69 Changed 3 years ago by
Travis, with this change to __hash__
, hash(self.nondegenerate())
would not be defined. If you make your change plus change that to id(self.nondegenerate()) ^ hash(self._degens)
, then it somehow messes up the ordering on simplices: the ordering becomes random, somehow depending on id
more than with the current code. I don't know the details, but I also don't know why you object to keeping the original if
.
comment:70 Changed 3 years ago by
Ah, I see. I was thinking the nondegenerate simplex was an instance of Simplex
. So this problem with picking is basically what we had to contend with for SageManifolds. What we did was use UniqueRepresentation
with add a hidden key value that was the time the object was created. You could use the same trick here for nondegenerate simplicies. You could then also use UniqueRepresentation
for the degenerate simplicies as well and would have a much faster equality and hash operations.
comment:71 in reply to: ↑ 67 ; followup: ↓ 75 Changed 3 years ago by
Replying to cnassau:
Replying to jhpalmieri:
I think it's a good idea that two simplicial sets can contain the same
AbstractSimplex
, but then to make geometric sense, thatAbstractSimplex
should have the same faces in each of those simplicial sets (possibly up to some identifications, if a quotient operation is involved).I like your basic approach to construct a simplicial set from a collection of simplices + additional face information. However, instead of creating a
PreSimplex
class without faces we could also just *add* faces to theAbstractSimplex
, which turns them into regular $n$simplices (this should also be easier to explain). Consider the codesage: a,b = [AbstractSimplex(0,name=x) for x in "ab"] sage: e,f = [AbstractSimplex(1,name=x) for x in "ef"] sage: X = SimplicialSet_finite({e:(a,b),f:(a,b)}) sage: Y = SimplicialSet_finite({e:(e.face(0),e.face(1)),f:(e.face(0),e.face(1))}) sage: Z = SimplicialSet_finite({e:(f.face(0),f.face(0)),f:(f.face(0),f.face(1))})
So an AbstractSimplex
may or may not have faces, right? In your example, e
and f
do not have faces at the start, but after defining X
, they do. So I guess at the start, e.face(0)
would raise an error.
This gets back to my questions: what should happen if you do
sage: a,b,c = [AbstractSimplex(0,name=x) for x in "abc"] sage: e,f = [AbstractSimplex(1,name=x) for x in "ef"] sage: K = SimplicialSet_finite({e:(a,b)}) sage: L = SimplicialSet_finite({e:(b,c)})
Raise an error because of the inconsistent face data for e
?
Should you also be able to do
sage: M = SimplicialSet_finite({f:(a,b), e:True})
or some other noniterable value for e
, which would just mean: e
has already been defined, so use its existing faces? Or pass a tuple instead of a dictionary as defining data, where each entry is either a fullfledged simplex or a pair (instance of AbstractSimplex, list of its faces)
? Maybe implement the latter as follows: if sigma
is an instance of AbstractSimplex
, then sigma[0]
would return sigma
while sigma[1]
would return its tuple of faces. Then you could easily deal with a tuple which has entries either of the form (f: (a,b))
or e
.
Is the point of the SetOfAbstractSimplices
construction is just to have a parent around, so that AbstractSimplex
can inherit from Element
?
comment:72 Changed 3 years ago by
 Commit changed from 2b2a2c9df6428459b2a0875f4e483842e2635335 to 7ac9b30d34cf8ef9c30d0617f002421a78f8c763
Branch pushed to git repo; I updated commit sha1. New commits:
7ac9b30  Simplicial sets: fix pickling by making NonDegenerateSimplex inherit

comment:73 Changed 3 years ago by
 Commit changed from 7ac9b30d34cf8ef9c30d0617f002421a78f8c763 to afbad4505350d79488d56067151c6f109782693a
Branch pushed to git repo; I updated commit sha1. New commits:
afbad45  Simplicial sets: AbstractSimplex now inherits from UniqueRepresentation

comment:74 Changed 3 years ago by
Progress toward Travis's remarks: this make NonDegenerateSimplex
a new class which inherits from UniqueRepresentation
. It fixes pickling, and now simplicial sets (at least the first few examples I tried) pass their test suite. AbstractSimplex
now also inherits from UniqueRepresentation
. I had to add new inequality methods, since as far as I can tell, UniqueRepresentation
doesn't work with @total_ordering
.
comment:75 in reply to: ↑ 71 Changed 3 years ago by
Replying to jhpalmieri:
So an
AbstractSimplex
may or may not have faces, right? In your example,e
andf
do not have faces at the start, but after definingX
, they do. So I guess at the start,e.face(0)
would raise an error.
No, sorry, what I meant is that AbstractSimplex
should become a fully funcional simplex in its own right. So e.face(0)
would print as Face d0 of e
(and maybe still be an instance of AbstractSimplex
, but I'm not sure on this).
This gets back to my questions: what should happen if you do
sage: a,b,c = [AbstractSimplex(0,name=x) for x in "abc"] sage: e,f = [AbstractSimplex(1,name=x) for x in "ef"] sage: K = SimplicialSet_finite({e:(a,b)}) sage: L = SimplicialSet_finite({e:(b,c)})Raise an error because of the inconsistent face data for
e
?
No, no error: the point is that e
is neither a simplex of K
nor of L
, it can just be coerced into those sets. Then K(e) != L(e)
since these simplices belong to different parents. And there's no problem with K(e.face(0)) = K(e).face(0) != L(e).face(0)
.
Should you also be able to do
sage: M = SimplicialSet_finite({f:(a,b), e:True})or some other noniterable value for
e
, which would just mean:e
has already been defined, so use its existing faces?
Yes, I think that sounds like a good idea. This should be equivalent to {f:(a,b), e:(e.face(0),e.face(1))}
, so e
defines a disjoint 1simplex (assuming that a,b are not faces of e).
Or pass a tuple instead of a dictionary as defining data, where each entry is either a fullfledged simplex or a pair
(instance of AbstractSimplex, list of its faces)
? Maybe implement the latter as follows: ifsigma
is an instance ofAbstractSimplex
, thensigma[0]
would returnsigma
whilesigma[1]
would return its tuple of faces. Then you could easily deal with a tuple which has entries either of the form(f: (a,b))
ore
.
This sounds like a nice way to make the defining data a bit simpler to specify (although it's probably just syntactic sugar).
Is the point of the
SetOfAbstractSimplices
construction is just to have a parent around, so thatAbstractSimplex
can inherit fromElement
?
Essentially yes. And it might make the AbstractSimplex? business easier to grasp from a mathematical point of view: the face data defining a SimplicialSet_finite
$X$ amount to the specification of an assembly map
\coprod_{i\in I} \Delta_{n_i} \rightarrow X
An AbstractSimplex
should be the tautological simplex $\iota_n\in\Delta_n$ for one of these summands.
comment:76 Changed 3 years ago by
Okay, I think I understand. I guess each nondegenerate AbstractSimplex
corresponds to one of the \Delta_n
's, and then the degeneracies are clear mathematically, and I think we handle them as in the current code. So then I have the same question you do, which is what structure should the face of a nondegenerate AbstractSimplex
have?
 We could just create a class
FaceOfAbstractSimplex
for any (iterated) face of one of the\Delta_n
's. It wouldn't have to do much, since most manipulations will occur within a simplicial set other thanSetOfAbstractSimplices
.
 Or we could think that when we define the
n
dimensionalAbstractSimplex
indexed byx
, we in fact define a family ofi
simplices,i <= n
, namely the actual simplex\Delta_{n,x}
and all of its faces, all indexed byx
. Thuse.face(0)
would be anAbstractSimplex
with the same indexing element ase
, but of one dimensional lower.
comment:77 followup: ↓ 79 Changed 3 years ago by
Unless you pass a unique tag up in the __classcall__
, it won't get put into the key for UniqueRepresentation
. Actually, probably what you had before was good. (Sorry, I haven't had time to full digest the code, clearly stuff off my plate slowly but surely,) You probably could just put UniqueRepresentation
first so its __hash__
and __eq__
get placed first by the MRO.
You might also consider having a separate class for degenerate simplicies and then have some constructor (e.g., a ClasscallMetaclass
with __classcall_private__
in AbstractSimplex
or have a global function entry point of Simplex
, which probably should become a subclass of AbstractSimplex
and/or NonDegenerateSimplex
).
I am also against this idea of having a singleton parent for the set of all simplicies. It feels like overkill and I fear we could quickly find ourselves in a form of pointer hell (parent hell?) trying to keep everything organized.
comment:78 Changed 3 years ago by
In the most recent version, I got rid of the line __hash__ = UniqueRepresentation.__hash__
or whatever it was. (In the earlier version, if I put UniqueRepresentation
first, then it seemed that I had to override the four methods __lt__
, __gt__
, etc., instead of the two methods __eq__
and __hash__
. This is moot now.) The most recent version also has a global function entry point (now called AbstractSimplex
) for both AbstractSimplex_class
and NonDegenerateSimplex
, although in practice, I think I really just need NonDegenerateSimplex
. I have to examine that.
Also, I only want the unique tag for NonDegenerateSimplex
, not for AbstractSimplex_class
: NonDegenerateSimplex
inherits from AbstractSimplex_class
, and in particular, if I start with the same nondegenerate simplex and apply the same degeneracies two different times, I should get the degenerate simplex. So for AbstractSimplex_class
, the determining data should be the underlying nondegenerate simplex plus the degeneracies, but not the time stamp. That is,
sage: sigma.apply_degeneracies(1, 0) == sigma.apply_degeneracies(1, 0)
should return True
(as opposed to NonDegenerateSimplex(3) == NonDegenerateSimplex(3)
, which should return False
).
With what I had a while ago, there were some pickling issues. I don't know at which stage in the revisions they went away, though.
I am not sure about the singleton parent. I can see advantages and drawbacks. It might be good to have AbstractSimplex_class
inherit from Element
. An alternative is to have a separate Element
class for SimplicialSet_finite
(or for SimplicialSet_arbitrary
) which inherits from AbstractSimplex_class
.
comment:79 in reply to: ↑ 77 Changed 3 years ago by
Replying to tscrim:
I am also against this idea of having a singleton parent for the set of all simplicies. It feels like overkill and I fear we could quickly find ourselves in a form of pointer hell (parent hell?) trying to keep everything organized.
The only question is whether AbstractSimplex
should become an Element
or not: if they are Element
s, they need a Parent
and a singleton is the best choice (the alternative is one parent per AbstractSimplex
which is the pointer/parent hell that you mention).
comment:80 Changed 3 years ago by
 Commit changed from afbad4505350d79488d56067151c6f109782693a to 6e2128d11f18858110557fc2d0506799dd1f177c
Branch pushed to git repo; I updated commit sha1. New commits:
6e2128d  simplicial sets: add some documentation about uniqueness of simplices.

comment:81 Changed 3 years ago by
 Commit changed from 6e2128d11f18858110557fc2d0506799dd1f177c to 410d09f17b5a99ab40a70b29e25935785c59be52
Branch pushed to git repo; I updated commit sha1. New commits:
410d09f  simplicial sets: remove unique_tag from __classcall_private__ for

comment:82 Changed 3 years ago by
 Commit changed from 410d09f17b5a99ab40a70b29e25935785c59be52 to 4f2e6fd77b81e80a2f18267c294b0e15d5e5b427
Branch pushed to git repo; I updated commit sha1. New commits:
4f2e6fd  simplicial sets: improve the 'image' method for morphisms.

comment:83 Changed 3 years ago by
Before the last push I was seeing sporadic failures when doctesting simplicial_set_morphism.py
. After that change, I ran tests 20 times and had no failures, so I think it is fixed.
comment:84 Changed 3 years ago by
 Commit changed from 4f2e6fd77b81e80a2f18267c294b0e15d5e5b427 to 341ed16ec82b6bd074f6b55721315a8edfde334a
Branch pushed to git repo; I updated commit sha1. New commits:
341ed16  simplicial sets: less verbose printing of simplices

comment:85 Changed 3 years ago by
There are some issues with having a class inherit from both Element
and UniqueRepresentation
. If I get rid of UniqueRepresentation
, I think I only lose pickling, and I'm not very concerned by that. So I'm thinking of replacing it with WithEqualityById
for nondegenerate simplices.
comment:86 followup: ↓ 87 Changed 3 years ago by
I also just did a little testing, timing creation of some simplicial set. Using WithEqualityById
is always at least as fast as using UniqueRepresentation
, in some cases about 25% faster. To clarify, I am suggesting making the changes

src/sage/homology/simplicial_set.py
diff git a/src/sage/homology/simplicial_set.py b/src/sage/homology/simplicial_set.py index bc2c114..721ae42 100644
a b lazy_import('sage.categories.simplicial_sets', 'SimplicialSets') 285 286 ######################################################################## 286 287 # The classes for simplices. 287 288 288 class AbstractSimplex_class( UniqueRepresentation,SageObject):289 class AbstractSimplex_class(SageObject): 289 290 """ 290 291 A simplex of dimension ``dim``. 291 292 … … class AbstractSimplex_class(UniqueRepresentation, SageObject): 892 942 return simplex 893 943 894 944 895 class NonDegenerateSimplex(AbstractSimplex_class): 896 def __init__(self, dim, name=None, latex_name=None, 897 unique_tag=None): 945 class NonDegenerateSimplex(WithEqualityById, AbstractSimplex_class): 946 def __init__(self, dim, name=None, latex_name=None): 898 947 """ 899 948 A nondegenerate simplex. 900 949
and the necessary followups.
comment:87 in reply to: ↑ 86 ; followup: ↓ 88 Changed 3 years ago by
Replying to jhpalmieri:
I also just did a little testing, timing creation of some simplicial set. Using
WithEqualityById
is always at least as fast as usingUniqueRepresentation
, in some cases about 25% faster.
I think UniqueRepresentation
should be reserved for Parent
s, so this change seems to go in the right direction.
comment:88 in reply to: ↑ 87 Changed 3 years ago by
Replying to cnassau:
Replying to jhpalmieri:
I also just did a little testing, timing creation of some simplicial set. Using
WithEqualityById
is always at least as fast as usingUniqueRepresentation
, in some cases about 25% faster.I think
UniqueRepresentation
should be reserved forParent
s, so this change seems to go in the right direction.
That is a fallacy. Just because it is used frequently there, does not mean it must only be used there. It is designed to work with objects that are uniquely defined by their inputs.
Now it is a hack, to perhaps a slight abuse, to use UniqueRepresentation
for NonDegenerateSimplex
pickling. However, good supporting support means it is much easier to pass between threads (maybe only processes?), where IIRC the pickling is used for that communication. Without good equality, then we cannot use UniqueRepresentation
for SimplicialSet
(I know we currently don't, but I'm just trying to say it imposes this limitation).
I think the root of the problem is the design of the parent and elements. I'm thinking we might want to lean towards doing something closer to what we have for SimplicialComplex
. We define NonDegenerateSimplex
(uniquely) by its set of vertices (or just by its dimension). The names of the (abstract) simplices could become an attribute of SimplicialSet
(given at initialization of SimplicialSet
). Perhaps in some ways I'm also looking to simplify the constructor, which the fact you must import things is a code smell. It just feels like things are a bit overly complex.
comment:89 Changed 3 years ago by
Simplicial sets are intrinsically more complicated to define than simplicial complexes. I also don't understand what you're suggesting. Can you give a sample of how you would like to be able to define a simplicial set? Here is one model for a circle, with two vertices and two edges:
sage: from sage.homology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: e = AbstractSimplex(1) sage: f = AbstractSimplex(1) sage: SimplicialSet({e: (v,w), f: (v,w)})
How else might this look?
comment:90 Changed 3 years ago by
 Milestone changed from sage7.3 to sage7.5
Something like
SimplicialSet({0: (0, 1), 1: (0, 1)})
where the keys are the names of the topdimensional simplices and the tuples are the vertices. So an nsphere with a latex name to the (top) simplex would look like:
SimplicialSet({0: (0,)*n}, latex_names={0: "\\mathbb{S}^{%s}"%n)
Internally, we still have classes for the abstract simplicies, but the equality/construction is checking the set of vertices, degenerices, and (latex) name.
Perhaps I'm oversimplifying things because I didn't see a complicated example.
There's also the question of do we want to allow mutable simplicial sets.
comment:91 Changed 3 years ago by
In a simplicial set, simplices are not determined by their vertices, and not everything is determined by the topdimensional faces. You can have an edge starting and ending at the same vertex, and then you can have a 2simplex which has that edge for one of its faces. You can also have degenerate simplices, for example a vertex determines a degenerate 1simplex. And then you can have a 2simplex which has the same degenerate 1simplex for all of its faces: this gives a 2sphere, where you are collapsing the boundary of the 2simplex to a single vertex.
Note also that you have to distinguish between a 2simplex one of whose faces is a degenerate 1simplex (imagine a triangle where you collapse one edge to a point) and a 2simplex one of whose faces is a nondegenerate edge starting and ending at the same vertex (a triangle where you have glued two corners together).
For every nondegenerate simplex in a simplicial set, you have to specify each of its faces, and each of those faces might be nondegenerate or might be obtained by applying a sequence of degeneracies to a lowerdimensional nondegenerate simplex. So I think you need a good way to define simplices of various dimensions so that you can have a specification like
{sigma: (face_0, face_1, ..., face_n)}
as part of the dictionary defining a simplicial set.
comment:92 Changed 3 years ago by
Also, I should point out that an n
simplex determines n+1
degenerate simplices one dimension higher, so if you have a 3simplex sigma
whose faces are obtained by applying degeneracies to 1simplices, you can't just say sigma: (e,f,g)
 you have to say sigma: (s_0 e, s_0 f, s_1 g)
or in Python syntax:
sigma: (e.apply_degeneracies(0), ...)
(or something along those lines).
Edit: and if it's a 3simplex, it should have 4 faces, not 3.
comment:93 Changed 3 years ago by
 Commit changed from 341ed16ec82b6bd074f6b55721315a8edfde334a to eabd3f84717b4c9b59268528321b604eb01e7947
Branch pushed to git repo; I updated commit sha1. New commits:
eabd3f8  Simplicial sets: define _latex_ method more widely and use it in Nerve

comment:94 Changed 3 years ago by
 Commit changed from eabd3f84717b4c9b59268528321b604eb01e7947 to 603a3cf2b119ba8842be0bdc52b45b06bebbd9b0
Branch pushed to git repo; I updated commit sha1. New commits:
603a3cf  Simplicial sets: use WithEqualityById instead of UniqueRepresentation

comment:95 Changed 3 years ago by
I've checked again, and simplicial set construction is faster this way, so I've pushed the change.
I also tried to implement a Parent/Element? structure, but I couldn't get it to work. It was easy enough to create a single parent for all instances of AbstractSimplex_class
, but then assigning a parent to each simplex when creating a simplicial set was causing difficulties. I tried creating a new class, inheriting from AbstractSimplex_class
, for a simplex as an element of a specific simplicial set. The main point was to assign a parent to each instance, and otherwise use the methods from AbstractSimplex_class
, but something was going wrong with equality (I think), so (for example) morphisms weren't working: the simplices that were supposed to be in the codomain were claimed to not be in the codomain. I spent quite a while with it, and now I'm giving up because I don't see enough benefit to making the change.
comment:96 Changed 3 years ago by
 Commit changed from 603a3cf2b119ba8842be0bdc52b45b06bebbd9b0 to e1ae3f24b2de53d401872c6b81c388280ca410c3
Branch pushed to git repo; I updated commit sha1. New commits:
e1ae3f2  Merge branch 'develop' into t/20745/simplicial_sets

comment:97 Changed 3 years ago by
Merged with 7.5.beta0, so I moved the references to the master bibliography file.
comment:98 Changed 3 years ago by
 Commit changed from e1ae3f24b2de53d401872c6b81c388280ca410c3 to 600157d4d7b2e5cfa736a4eb3e2c4fc235cee074
Branch pushed to git repo; I updated commit sha1. New commits:
600157d  Simplicial sets: add _latex_ methods to the constructions.

comment:99 Changed 3 years ago by
It seems that morphisms and homsets in Sage don't have default _latex_
methods. Odd.
comment:100 followup: ↓ 101 Changed 3 years ago by
Ping.
Changed 3 years ago by
comment:101 in reply to: ↑ 100 Changed 3 years ago by
Replying to jhpalmieri:
Ping.
Pong (and sorry for the prolonged delay)!
Part of me still thinks that it would be nice to use the Parent/Element? framework, but since I don't see any nonintrusive way to incorporate this myself I think it is the wise choice to just go ahead with the code as it is. This is a significant amount of valuable code, after all, and it would be a pity if its inclusion into Sage would be delayed much further.
I went over the code once more and noted a couple of very minor possible improvements; these are outlined in the file "suggestions.diff" that I have just attached. Please let me know what you think about those and let's hope to then close this ticket before christmas.
Cheers, Christian
comment:102 Changed 3 years ago by
I like your suggested changes, so I've implemented them (here and there with minor rewordings). I also like using "base" much better than "X" for the simplicial set from which you build a cone or suspension, so I changed not only the public methods but also the code and the attribute self._X
.
comment:103 Changed 3 years ago by
 Commit changed from 600157d4d7b2e5cfa736a4eb3e2c4fc235cee074 to 6f01bda365933115981e6008b8c0a61915719fac
comment:104 followup: ↓ 106 Changed 3 years ago by
I think I stumbled over a little bug looking at products of the Point with itself. Using other simplicial sets seems to work:
sage: P=simplicial_sets.Point() sage: P.product(P) ... ValueError: the base point is not a simplex in this simplicial set sage: Q = sage.homology.simplicial_set_constructions.ProductOfSimplicialSets((P,P,P)) ; Q Point x Point x Point sage: Q.all_n_simplices(2) ... AttributeError: 'ProductOfSimplicialSets_with_category' object has no attribute '_simplices' sage: Q.n_skeleton(3) ... TypeError: unbound method n_skeleton() must be called with SimplicialSet_finite instance as first argument (got ProductOfSimplicialSets_with_category instance instead)
comment:105 Changed 3 years ago by
 Commit changed from 6f01bda365933115981e6008b8c0a61915719fac to 35790d29bdbe4a9b61b80076e4acc64eb650ea03
Branch pushed to git repo; I updated commit sha1. New commits:
35790d2  trac 20745: fix bug with product of a point with itself

comment:106 in reply to: ↑ 104 Changed 3 years ago by
Replying to cnassau:
I think I stumbled over a little bug looking at products of the Point with itself. Using other simplicial sets seems to work:
sage: P=simplicial_sets.Point() sage: P.product(P) ... ValueError: the base point is not a simplex in this simplicial set
I can fix this one: see the latest commit.
sage: Q = sage.homology.simplicial_set_constructions.ProductOfSimplicialSets((P,P,P)) ; Q Point x Point x Point sage: Q.all_n_simplices(2) ... AttributeError: 'ProductOfSimplicialSets_with_category' object has no attribute '_simplices' sage: Q.n_skeleton(3) ... TypeError: unbound method n_skeleton() must be called with SimplicialSet_finite instance as first argument (got ProductOfSimplicialSets_with_category instance instead)
I don't know if this is a bug. The intended way to construct products is with the product
method for simplicial sets. If you want to use a class constructor, you should use ProductOfSimplicialSets_finite
if all of the arguments are finite. When I do that, I don't see any problems.
comment:107 Changed 3 years ago by
 Reviewers set to Christian Nassau
 Status changed from needs_review to positive_review
I doubt it makes sense to wait for patchbot results; the failures reported for the last two months ("Unable to read current working directory: No such file or directory") are clearly bogus (not enough space under /tmp maybe). I manually tested src/categories and src/homology and all looks fine. The documentation can also be built and looks nice. I'd say this ticket is ready to go...
comment:108 Changed 3 years ago by
That's great! Thanks very much.
comment:109 Changed 2 years ago by
 Branch changed from u/jhpalmieri/simplicial_sets to 35790d29bdbe4a9b61b80076e4acc64eb650ea03
 Resolution set to fixed
 Status changed from positive_review to closed
comment:110 Changed 2 years ago by
 Commit 35790d29bdbe4a9b61b80076e4acc64eb650ea03 deleted
Another ticket which is using SAGE_SRC
at runtime instead of something appropriate. You data is properly installed in SAGE_EXTCODE
, use it. Follow up at #22220.
comment:111 Changed 2 years ago by
It would have been nice to lazilyimport all of this. Because of this ticket, Sage now requires pyparsing
at startup (see src/sage/homology/simplicial_set_examples.py
).
Here is an initial implementation. It is not ready for review.
New commits:
simplicial sets, initial implementation
simplicial sets: construct CP^n using Kenzo output.