SimplicialComplex
: An abstract topological space¶
- class simplicial.SimplicialComplex(rep=None)¶
The base class for a finite abstract simplicial complex.
A simplicial complex is a generalisation of a network in which vertices (0-simplices) and edges (1-simplices) can be composed into triangles (2-simplices), tetrahedra (3-simplices) and so forth. This class actually implements closed simplicial complexes that contain every simplex, every face of that simplex, every face of those simplices, and so forth. Operations to add and remove simplices cascade to keep the complex closed: if a simplex is an element of a complex, then all its faces are also elements, and so on recursively.
This class includes the entire interface for simplicial complexes, but defers the implementation to a representation class. By default this is
ReferenceRepresentation
, but this can be changed to use alternate representations if required.- Parameters
rep (
Optional
[Representation
]) – (optional) the representation to use for this complex
Adding simplices¶
A simplex is added to a complex by providing some or all of the following information: a name for the simplex, a list of its faces, and a dict of attributes.
- SimplicialComplex.addSimplex(fs=[], id=None, attr=None)¶
Add a simplex to the complex whose faces are the elements of fs. The faces must all be distinct. If no faces are given then the simplex is a 0-simplex (point). If no id is provided one is created. If present, attr should be a dict of attributes for the simplex.
To add a simplex from its basis (rather than its faces) use
addSimplexByBasis()
.- Parameters
fs (
List
[Any
]) – (optional) a list of faces of the simplexid (
Optional
[Any
]) – (optional) name for the simplexattr (
Optional
[Dict
[str
,Any
]]) – (optional) dict of attributes
- Return type
Any
- Returns
the name of the new simplex
Since a simplex is uniquely defined by its basis, we can simply provide the basis and let simplicial work out all the other simplices that need to be added. This can be a major simplification for higher-order simplices.
- SimplicialComplex.ensureBasis(bs, attr=None)¶
For a given set of simplices, ensure they constitute a basis. This means that any simplices already present must be 0-simplices, and any missing ones will be created.
- Parameters
bs (
List
[Any
]) – the simplicesattr (
Optional
[Dict
[str
,Any
]]) – (optional) attributes for any simplices added
- SimplicialComplex.addSimplexWithBasis(bs, id=None, attr=None)¶
Add a simplex by providing its basis, which uniquely defines it. This method adds all the simplices necessary to define the new simplex, using
simplexByBasis()
to find and re-use any that are already in the complex. All simplices created (including the top-most one) are given the attributes provided.To add a simplex defined by its faces, use
addSimplex()
.Defining a k-simplex requires a basis of (k + 1) 0-simplices.
- Parameters
bs (
List
[Any
]) – the basisid (
Optional
[Any
]) – (optional) the name of the new simplex (synthesised if omitted)attr (
Optional
[Dict
[str
,Any
]]) – (optional) dict of attributes (defaults to none)
- Return type
Any
- Returns
the name of the new simplex (which will be id if provided)
There are some operations that create new simplices from old.
- SimplicialComplex.barycentricSubdivide(simplex)¶
Performs Barycentric subdivision on a simplex. This deletes the \(k\)-simplex, introduces a new 0-simplex, and creates \((k + 1)\) new \(k\)-simplices from the original basis and the new base point.
- Parameters
simplex (
Any
) – the simplex to subdivide- Return type
Any
- Returns
the new basis point
Finally, it is also possible to copy simplices from one complex to another. This will cause problems if the two complexes share simplices with common names (since names must be unique), in which case a renaming mapping can be applied automatically during the copy process.
- SimplicialComplex.addSimplicesFrom(c, rename=None)¶
Add simplices from the given complex. The rename parameter is an optional mapping of the names in c that can be provided as a dict of old names to new names or a function from names to names.
If the relabeling is a dict it may be incomplete, in which case simplices retain their names. (If the relabeling is a function, it has to handle all names.)
This operation is equivalent to copying the other complex, re-labeling it using
relabel()
and then copying it into this complex directly. The caveats on attributes containing simplex names mentioned in respect torelabel()
apply toaddSimplicesFrom()
too.- Parameters
c (
SimplicialComplex
) – the other complexrename (
Union
[Dict
[Any
,Any
],Callable
[[Any
],Any
],None
]) – (optional) renaming dict or function
- Return type
List
[Any
]- Returns
a list of simplex names
Simplex attributes¶
Each simplex can have a dict of attributes associated with it.
- SimplicialComplex.getAttributes(s)¶
Return the attributes associated with the given simplex.
- Parameters
s (
Any
) – the simplex- Return type
Dict
[str
,Any
]- Returns
a dict of attributes
- SimplicialComplex.setAttributes(s, attr)¶
Set the attributes associated with a simplex.
- Parameters
s (
Any
) – the simplexattr (
Dict
[str
,Any
]) – a dict of attributes
- SimplicialComplex.__getitem__(s)¶
Return the attributes associated with the given simplex. Equivalent to
getAttributes()
.- Parameters
s (
Any
) – the simplex- Return type
Dict
[str
,Any
]- Returns
a dict of attributes
- SimplicialComplex.__setitem__(s, attr)¶
Set the attributes associated with a simplex. Equivalent to
setAttributes()
.- Parameters
s (
Any
) – the simplexattr (
Dict
[str
,Any
]) – a dict of attributes
Deleting simplices¶
Since SimplicialComplex
represents closed simplicial
complexes – in which all the faces of all the simplices in a complex
are also in the complex – deletion requires that we cascade deletions
to delete all the simplices of which the to-be-deleted simplex is a part
(a face, or the face of a face, and so forth).
- SimplicialComplex.deleteSimplex(s)¶
Delete a simplex and all simplices of which it is a part.
- Parameters
s (
Any
) – the simplex
- SimplicialComplex.deleteSimplices(ss)¶
Delete all simplices in the given list.
- Parameters
ss (
List
[Any
]) – the simplices
- SimplicialComplex.deleteSimplexWithBasis(bs)¶
Delete the simplex with the given basis.
- Parameters
bs (
List
[Any
]) – the basis
Relabelling simplices¶
Simplex labels are often meaningful – but not always. They can be relabelled using either functions or dicts, or simply to make them all unique relative to another complex. This is handy when composing complexes.
- SimplicialComplex.relabelSimplex(s, q)¶
Relabel a simplex. This is a simple form of
relabel()
working on individual simplices. The simplex q mustn’t exist in the complex.- Parameters
s (
Any
) – the simplex to renameq (
Any
) – the new name
- SimplicialComplex.relabel(rename)¶
Re-label simplices using the given relabeling, which may be a dict from old names to new names or a function taking a name and returning a new name.
If the relabeling is a dict it may be incomplete, in which case unmentioned simplices retain their names. (If the relabeling is a function, it has to handle all names.)
The relabelling function is guaranteed to only ever be called once for every simplex, so it’s completely acceptable to pass a function that just returns a sequence of unique identifiers.
In both cases,
relabel()
will complain if the relabeling generates as a “new” name a name already in the complex. (This detection isn’t completely foolproof: just don’t do it.)(Be careful with attributes: if a simplex has an attribute the value of which is the name of another simplex, then renaming will destroy the connection and lead to problems.)
The dict returned maps old simplex names to new ones for only those simp[lices whose names were actually changed. All other simplices will have their names left alone.
- Parameters
rename (
Union
[Dict
[Any
,Any
],Callable
[[Any
],Any
]]) – the relabeling, a dict or function- Return type
Dict
[Any
,Any
]- Returns
a mapping from old to new simplex names, for those that changed
- SimplicialComplex.relabelDisjointFrom(c)¶
Relabel this complex to have no labels in common with the complex given. This ensures that, if the two complexes are composed using
compose()
, they will not have any simplices merged.The labels really will be meaningless, but the dict returned is a mapping from original to new names for those simplices whose names were actually changed: others are left as they were. The change set should be minimal, changing only those names that need to be changed to avoid collisions.
- Parameters
c (
SimplicialComplex
) – a complex- Return type
Dict
[Any
,Any
]- Returns
a mapping of old to new simplex names, for those that actually changed
Copying and equality¶
Complexes can be freely copied, creating independent versions of the same complex.
- SimplicialComplex.copy(c=None)¶
Return a copy of this complex. The two complexes have the same simplices, attributes, and topology, and can be modified independently.
If the copy is put into an existing complex, there must not be any simplex names in common. To deal with the case of two complexes with simplices that should be fused on the basis of their labels, use
compose()
.If no target complex is provided, a new complex is created using the same representation as ourselves.
- Parameters
c (
Optional
[SimplicialComplex
]) – (optional) the complex to copy to (defaults to a new complex)- Return type
- Returns
a copy of this complex
They can also be tested for equality.
- SimplicialComplex.__eq__(c)¶
True if the two complexes have the same simplices with the same relationships. Note that this does not compare simplex attributes: it’s purely topological equality.
- Parameters
c (
SimplicialComplex
) – the other complex- Return type
bool
- Returns
True if the two complexes are equal
- SimplicialComplex.__ne__(c)¶
True if the two complexes differ topologically. The dual of
__eq__()
. Note that this does not compare simplex attributes.- Parameters
c (
SimplicialComplex
) – the other complex- Return type
bool
- Returns
True if the two complexes are unequal
(See later for other comparison operators relating to sub-complexes.)
Querying the structure of the complex¶
- SimplicialComplex.orderOf(s)¶
Return the order of a simplex.
- Parameters
s (
Any
) – the simplex- Return type
int
- Returns
the order of the simplex
- SimplicialComplex.indexOf(s)¶
Return the unique-within-its-order index of the given simplex. Indices form a dense sequence. The index isn’t robust to changes in the complex, so the index returned for a simplex may change if simplices are added or removed.
Note that indices are unique only within the same order, not across all simplices; simplex names are however globally unique.
- Parameters
s (
Any
) – the simplex- Return type
int
- Returns
an index
- SimplicialComplex.maxOrder()¶
Return the largest order of simplices in the complex, that is to say, the largest order for which a call to
simplicesOfOrder()
will return a non-empty list.- Return type
int
- Returns
the largest order that contains at least one simplex, or -1
- SimplicialComplex.numberOfSimplices()¶
Return the number of simplices in the complex.
- Return type
int
- Returns
the number of simplices
- SimplicialComplex.__len__()¶
Return the size of the complex, a synonym for
numberOfSimplices()
.- Return type
int
- Returns
the size of the complex
- SimplicialComplex.numberOfSimplicesOfOrder()¶
Return a list of the number of simplices of each order in the complex. Use
simplicesOfOrder()
to retrieve the actual simplices.- Return type
List
[int
]- Returns
a list of number of simplices at each order
- SimplicialComplex.isBasis(bs, fatal=False)¶
Return True if the given set of simplices is a basis, that is, a set of 0-simplices. The simplices must already exist in the complex. The operation returns a boolean unless fatal is True, in which case a missing or non-basis element will raise an exception.
- Parameters
bs (
List
[Any
]) – the simplicesfatal (
bool
) – (optional) make missing or higher-order simplices fatal (defaults to False)
- Returns
True if the given simplices form a basis
Retrieving simplices¶
Simplices can be retrieved in a large number of ways. When we return a collection of simplices that are always of the same order (such as faces or basis) we return a Python set; if we potentially return simplices of different orders we return a list, sorted in terms of the simplex order (low-order simplices first by default).
- SimplicialComplex.simplices(reverse=False)¶
Return all the simplices in the complex. The simplices are returned in order of their orders, 0-simplices first unless the reverse parameter is True, in which case 0-simplices will be last.
- Parameters
reverse (
bool
) – (optional) reverse the sort order if True- Return type
List
[Any
]- Returns
a list of simplices
- SimplicialComplex.simplicesOfOrder(k)¶
Return all the simplices of the given order. This will be empty for any order greater than that returned by
maxOrder()
.The simplices are returned in “canonical” order, meaning the order they appear in the boiundary operator matrices.
- Parameters
k (
int
) – the desired order- Return type
List
[Any
]- Returns
a set of simplices, which may be empty
- SimplicialComplex.allSimplices(p, reverse=False)¶
Return all the simplices that match the given predicate, which should be a function from a complex and a simplex to a boolean. The simplices are sorted according to their orders, but with no guarantees of ordering between simplices of the same order.
- Parameters
p (
Callable
[[SimplicialComplex
,Any
],bool
]) – a predicatereverse (
bool
) – (optional) reverse the order
- Return type
List
[Any
]- Returns
the set of simplices satisfying the predicate
- SimplicialComplex.anySimplex(p)¶
Return some simplex for which the predicate is true. The predicate should be a function from a complex and a simplex to a boolean. There are no guarantees as to what simplex, of what order, will be simplex chosen, just that it matches the predicate.
- Parameters
p (
Callable
[[SimplicialComplex
,Any
],bool
]) – a predicate- Return type
Optional
[Any
]- Returns
a simplex matching the predicate, or None
- SimplicialComplex.simplexWithBasis(bs, fatal=False)¶
Return the simplex with the given basis, if it exists in the complex. If no such simplex exists, or if the given set is not a basis, then None is returned; if fatal is True, then an exception is raised instead.
- Parameters
bs (
List
[Any
]) – the basisfatal (
bool
) – (optional) make failure raise an exception (defaults to False)
- Return type
Any
- Returns
the simplex or None
- SimplicialComplex.containsSimplex(s)¶
Test whether the complex contains the given simplex.
- Parameters
s (
Any
) – the simplex- Return type
bool
- Returns
True if the simplex is in the complex
- SimplicialComplex.containsSimplexWithBasis(bs)¶
Test whether the complex contains a simplex with the given basis.
- Params bs
the basis
- Return type
bool
- Returns
True is the complex contains a simplex with this basis
- SimplicialComplex.faces(s)¶
Return the faces of a simplex.
- Parameters
s (
Any
) – the simplex- Return type
Set
[Any
]- Returns
a set of faces
- SimplicialComplex.cofaces(s)¶
Return the simplices the given simnplex is a face of. This is not transitive: all the simplices returned will be of an order one greater than the given simplex.
This is a synonym for
faceOf()
. The transitive closure ofcofaces()
ispartOf()
.- Parameters
s (
Any
) – the simplex- Return type
Set
[Any
]- Returns
a list of simplices
- SimplicialComplex.faceOf(s)¶
Return the simplices that the given simplex is a face of. A synonym of
cofaces()
.- Parameters
s (
Any
) – the simplex- Return type
Set
[Any
]- Returns
a list of simplices
- SimplicialComplex.partOf(s, reverse=False, exclude_self=False)¶
Return the transitive closure of all simplices of which the simplex is part: a face of, or a face of a face of, and so forth. This is the dual of
closureOf()
. If exclude_self is False (the default), the set include the simplex itself. The simplices are returned in increasing order unless reverse is True, in which case they are returned largest order first.In some of the topology literature this operation is called the star.
This method is essentially the dual of
closureOf()
, looking up the simplex orders rather than down.- Parameters
s (
Any
) – the simplexreverse (
bool
) – (optional) reverse the sort order (defaults to False)exclude_self (
bool
) – (optional) exclude the simplex itself (default to False)
- Return type
Set
[Any
]- Returns
the list of simplices the simplex is part of
- SimplicialComplex.basisOf(s)¶
Return the basis of a simplex, the set of 0-simplices that define it. Is s is an 0-simplex then it is its own basis
- Parameters
s (
Any
) – the simplex- Return type
Set
[Any
]- Returns
the set of 0-simplices that form the basis of s
- SimplicialComplex.closureOf(s, reverse=False, exclude_self=False)¶
Return the closure of a simplex. The closure is defined as the simplex plus all its faces, transitively down to its basis. If exclude_self is True, the closure excludes the simplex itself.
This method is essentially the dual of
partOf()
, looking down the simplex orders rather than up.By default the simplices are returned in increasing order, basis first: setting reverse to True will generate the simplices in decreasing order, highest order first.
- Parameters
s (
Any
) – the simplexreverse (
bool
) – (optional) return simplex in decreasing order (defaults to False)exclude_self (
bool
) – (optional) exclude the simplex itself (defaults to False)
- Return type
Set
[Any
]- Returns
the closure of the simplex
Sub-complexes¶
A sub-complex is created by removing some (or all) of the simplices
from an existing complex. The most common approach is to restrict the
basis of the complex to some sub-set of 0-simplices; another common
approach is to select this basis using some predicate (for example
using allSimplices()
) and then restricting the complex to all
simplices that can be built from this basis.
- SimplicialComplex.restrictBasisTo(bs)¶
Restrict the complex to include only those simplices whose bases are wholly contained in the given set of 0-simplices. All other simplices, including other 0-simplices, are deleted.
- Parameters
bs (
List
[Any
]) – the basis- Return type
- Returns
the complex
We define several relationships related to sub-complexes, all based around the idea of one complex being “less than” another when the latter contains all for former’s simplices in the same topological rrelationships.
- SimplicialComplex.isSubComplexOf(c)¶
True if this complex is a (possibly equal) sub-complex of c. This is defined as all the simplices in this complex appear in c, with the same topological relationships.
- Parameters
c (
SimplicialComplex
) – the other complex- Return type
bool
- Returns
True if this is a sub-complex of c
- SimplicialComplex.__lt__(c)¶
True if this complex is a strictly smaller sub-complex of c.
- Parameters
c (
SimplicialComplex
) – the other complex- Return type
bool
- Returns
True if this is a sub-complex of c and with fewer simplices
- SimplicialComplex.__le__(c)¶
True if this complex is a (possibly equal) sub-complex of c.
- Parameters
c (
SimplicialComplex
) – the other complex- Return type
bool
- Returns
True if this is a sub-complex of c
- SimplicialComplex.__gt__(c)¶
True if c is a structly smaller sub-complex of this one. The dual of
__lt__()
.- Parameters
c (
SimplicialComplex
) – the other complex- Return type
bool
- Returns
True if c is a sub-complex of this and has fewer simplices
- SimplicialComplex.__ge__(c)¶
True if c is a sub-complex of this one. The dual of
__le__()
.- Parameters
c (
SimplicialComplex
) – the other complex- Return type
bool
- Returns
True if c is a sub-complex of this
Global properties¶
Various global properties of complexes can be defined that provide an overview of its topological structure.
- SimplicialComplex.eulerCharacteristic()¶
Return the Euler characteristic of this complex.
- Return type
int
- Returns
the Euler characteristic
Homology¶
One can regard homology simply as a more sophisticated version of the Euler characteristic: a way of counting the holes in a structure at various dimensions. Unlike the Euler characteristic, homology is able to differentiate between a triangulated portion of the plane and two triangulated “islands” one of which contains a hole. The Euler characteristic of both these structures is 1, but they have different homology groups and therefore different Betti numbers.
In simplicial
we implement the simplest possible version of
simplicial homology in which simplices are treated as un-oriented, which
leads to chain coefficients over a field \([0, 1]\). This massively
simplifies both the calculations and the explanations.
- SimplicialComplex.isChain(ss, p=None, fatal=False)¶
Test whether the given set of simplices is a p-chain for some order p. A p-chain is a collection of simplices of order p. If fatal is True then any missing or wrong-order simplices raise an exception.
A basis is an 0-chain (see
isBasis()
).- Parameters
ss (
Set
[Any
]) – the simplicesp (
Optional
[int
]) – (optional) the order (defaults to the order of the first simplex in the chain)fatal (
bool
) – (optional) raise an exception for invalid chains (defaults to False)
- Return type
bool
- Returns
True if the simplices form a p-chain
- SimplicialComplex.boundary(ss)¶
Return the boundary of the given p-chain. This will be a (p - 1)-chain of simplices from the complex.
- Parameters
ss (
Set
[Any
]) – a chain (list) of simplices- Return type
Set
[Any
]- Returns
the boundary of the chain
- SimplicialComplex.boundaryOperator(k)¶
Return the boundary operator of the k-simplices in the complex (usually denoted \(\partial_k\)) as a numpy matrix. The columns correspond to simplices of order k while rows correspond to simplices of order (k - 1). The matrix has a 1 when a (k - 1) simplex is a face of the corresponding k-simplex, and 0 otherwise.
The boundary of the 0-simplices is a matrix with one row, all zeros. The boundary of an order greater than the maximum order of the complex is a 0x0 matrix.
- Parameters
k (
int
) – the order of simplices- Return type
ndarray
- Returns
the boundary matrix
- SimplicialComplex.disjoint(ss)¶
Test whether the elements of a set of simplices are disjoint, defined as if they share no common simplices in their closures. (This doesn’t mean that they aren’t part of a common super-simplex, however.) The simplices need not be of the same order, i.e., need not form a p-chain.
- Parameters
ss (
Set
[Any
]) – the simplices- Return type
bool
- Returns
True if the simplices are disjoint
- SimplicialComplex.smithNormalForm(k)¶
Reduce a boundary operator matrix to Smith Normal Form, which has a leading diagonal of ones for some prefix of its leading diagonal and is zero everywhere else.
- Parameters
k (
int
) – the order of the boundary operator- Return type
ndarray
- Returns
the Smith Normal Form of the boundary operator matrix
- SimplicialComplex.bettiNumbers(ks=None)¶
Return a dict of Betti numbers for the different dimensions of the complex.
- Parameters
ks (
Optional
[List
[int
]]) – (optional) dimensions to compute (defaults to all)- Return type
List
[int
]- Returns
a dict of Betti numbers
- SimplicialComplex.Z(ks=None)¶
Return a list of the basis of the group of non-boundary k-chains for the requested orders, usually denoted \(Z_k\).
- Parameters
ks (
Optional
[List
[int
]]) – (optional) dimensions of holes (defaults to all)- Return type
Dict
[int
,Set
[Any
]]- Returns
a dict of lists of p-chains
Deriving new complexes¶
New complexes can be derived from old ones by various processes. These
operations are also used to create abstract complexes from “concrete”
complexes that have an associated geometric embedding via the
Embedding
class.
The flag complex is a complex that includes all higher simplices whose faces are present. Essentially it “fills in the holes”: three sides of a triangle will result in the triangle 2-simplex being created, and so on. It is possible to create the flag complex from scratch, or incrementally by filling-in any holes left after creating new simplices.
- SimplicialComplex.flagComplex()¶
Generate the flag complex of this complex. The flag complex is formed by creating all the “implied” simplices for which all faces are present. For example, three 1-simplices forming an (empty) triangle will be “closed” by creating a 2-simplex with them as its faces. This may in turn allow a further 3-simplex to be formed if the new 2-simplex closes a tetrahedron, and so forth.
- Return type
- Returns
the flag complex
- SimplicialComplex.growFlagComplex(newSimplices)¶
Grow the flag complex incrementally by filling-in any higher-order simplices formed by the addition of a set of new simplices to the complex.
The set of new simplices can contain simplices of any order, although 0-simplices will have no effect. For a lot of applications (for example for building a Vietoris-Rips complex) the new simplices will all be 1-simplices (edges).
- Parameters
newSimplices (
Set
[Any
]) – a collection of new simplices
Composing complexes¶
Complexes can be combined to form larger complexes. The basic operation works by fusing two complexes on the basis of simplex labels: use relabelling to make disjoint complexes, and then set the corresponding simplices.
- SimplicialComplex.compose(c, d=None)¶
Compose the complex c with us, generating a new complex. The complex labels need not be disjoint: if two k-simplices have the same label and basis, they will be unified. If the simplex names are all disjoint, then this is equivalent to
copy()
.If the target complex d is provided, the current complex is first copied into it using
copy()
.If two simplices are merged their attributes are also merged, with those of the simplex in c overwriting those in us.
- Parameters
c (
SimplicialComplex
) – the other complexd (
Optional
[SimplicialComplex
]) – (optional) tyhe compolex to compose into
- Return type
- Returns
a new complex
Representation¶
The representation of the complex is occasionally needed when sub-classing, but in general should be transparent once set.
- SimplicialComplex.representation()¶
Returns the representation used by this complex,
- Return type
- Returns
the representation
Use of exceptions¶
SimplicialComplex
does not define any new exceptions. It
raises KeyError
if a simplex if referenced that is not present, or
if a basis is used to retrieve a simplex that isn’t defined for it. It
raises ValueError
for problems with the orders of simplices and
other structure errors.