Quick Start

You can construct a SimplexTree by supplying simplices. Any collection of integer-valued Iterable’s will do, e.g. a list of lists:

from simplextree import SimplexTree
st = SimplexTree([[0,1,2], [0,1], [4,5]]) 
print(st) 
Simplex Tree with (5, 4, 1) (0, 1, 2)-simplices

Batch insertion, removal, and membership queries are supported

st.insert([[1,4], [1,5], [6]])
print(st)
Simplex Tree with (6, 6, 1) (0, 1, 2)-simplices
st.remove([[6]])
print(st)
Simplex Tree with (5, 6, 1) (0, 1, 2)-simplices
print(st.find([[6], [0,1]]))
[False  True]

Collections of simplices are returned as simple lists-of-tuples:

print(st.simplices())
[(0,), (1,), (2,), (4,), (5,), (0, 1), (0, 2), (1, 2), (1, 4), (1, 5), (4, 5), (0, 1, 2)]

You can restrict to specific dimensions by supplying the argument p:

print(st.simplices(p=1)) 
[(0, 1), (0, 2), (1, 2), (1, 4), (1, 5), (4, 5)]

Familiar Pythonic Collection semantics are supported, including __contains__, __iter__ support, and __len__:

print([0,1,2] in st)
print([len(simplex) for simplex in st])
print(len(st))
True
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3]
12

The cofaces of any simplex can be listed with cofaces:

print("Cofaces([1]): " + str(st.cofaces([1])))
Cofaces([1]): [(1,), (1, 2), (1, 4), (1, 5), (0, 1), (0, 1, 2)]

The maximal simplices can be listed with maximal:

print("Maximal: " + str(st.maximal()))
Maximal: [(0, 1, 2), (1, 4), (1, 5), (4, 5)]

Basic properties are also available as attributes

st.n_simplices, st.dimension, st.vertices, st.connected_components
(array([5, 6, 1], dtype=uint64), 2, [0, 1, 2, 4, 5], [1, 1, 1, 1, 1])

Interoperability with numpy is provided whenever possible

import numpy as np 
all(np.all(st.triangles == np.array(st.simplices(p=2)), axis=0))
True

Other complex-wide operations are supported, like k-expansions

st.insert([[1,4]]) 
st.expand(2)       
print(st)
Simplex Tree with (5, 6, 2) (0, 1, 2)-simplices

The trie-structure can also be inspected on the python side with print_tree:

st.print_tree()
0 (h = 2): .( 1 2 )..( 2 )
1 (h = 2): .( 2 4 5 )..( 5 )
2 (h = 0): 
4 (h = 1): .( 5 )
5 (h = 0): 

Another way to inspect the trie structure is to use a traversal, e.g. the depth-first manner (prefix-order):

st.traverse("dfs", f=print)
(0,)
(0, 1)
(0, 1, 2)
(0, 2)
(1,)
(1, 2)
(1, 4)
(1, 4, 5)
(1, 5)
(2,)
(4,)
(4, 5)
(5,)

Several traversal orderings are provided, e.g. breadth-first (or level-order) could be used as well:

st.traverse("bfs", f=print)
(0,)
(1,)
(2,)
(4,)
(5,)
(0, 1)
(0, 2)
(1, 2)
(1, 4)
(1, 5)
(4, 5)
(0, 1, 2)
(1, 4, 5)

In fact, most operations on the Simplex tree are actually implemented using traversals. For example, you can traverse only the maximal faces like so:

st.traverse("maximal", f=print)
(0, 1, 2)
(1, 4, 5)

You can supply any Callable to f (the default is to print). For example, to extract the dimensions of the maximal simplices:

maximal_dims = []
st.traverse("maximal", f=lambda s: maximal_dims.append(len(s)-1))
print(maximal_dims)
[2, 2]

The simplex type can be configured via the s_type attribute.

st.s_type = list
print(st.maximal())
[[0, 1, 2], [1, 4, 5]]

The default (and recommended) type is tuple. If the type is Hashable, simplex properties can be tracked easily external dictionary (a technique inspired by Boosts property maps):

st.s_type = tuple
colors = ["red", "orange", "yellow", "purple"]
simplex_colors = { s : colors[len(s) - 1] for s in st }
print(simplex_colors)
{(0,): 'red', (1,): 'red', (2,): 'red', (4,): 'red', (5,): 'red', (0, 1): 'orange', (0, 2): 'orange', (1, 2): 'orange', (1, 4): 'orange', (1, 5): 'orange', (4, 5): 'orange', (0, 1, 2): 'yellow', (1, 4, 5): 'yellow'}

On the other hand, if you work a lot with numpy, you may want the default simplex type to be an array:

st.s_type = np.array
edges = np.array(st.simplices(p=1))
print(edges)
[[0 1]
 [0 2]
 [1 2]
 [1 4]
 [1 5]
 [4 5]]