Skip to content

simplicialcomplex

SimplicialComplex

Bases: AbstractSimplicialComplex

Embodies the concept of simplicial complex.

Source code in src/dxtr/complexes/simplicialcomplex.py
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
class SimplicialComplex(AbstractSimplicialComplex):
    """Embodies the concept of simplicial complex."""

    def __init__(self, indices: list[list[int]],
                 vertices: Optional[Iterable[Iterable[float]]] = None, 
                 name: Optional[str] = None) -> None:
        """Initializes a `SimplicialComplex` object.

        Parameters
        ----------
        indices : list of list of int
            The list of vertex indices forming the highest degree simplices.
        vertices : iterable of iterable of float, optional
            The coordinates of the vertices. Default is None.
        name : str, optional
            A name for the complex. Default is None.
        """

        super().__init__(indices, name)

        if _isproperly_embedded(self, vertices):
            self.build_geometry(vertices)


    def __str__(self) -> str:
        """Returns a string representation of the complex.

        Returns
        -------
        str
            A string representation of the complex.
        """
        if self._name is None:
            description = f'{self.dim}D'
            description += ' Abstract ' if self.isabstract else ' '
            description += f'Simplicial Complex of shape {self.shape}, '
            if not self.isabstract:
                description += f'embedded in R^{self.emb_dim}.'
            return description
        else:
            return self._name

    @classmethod
    def from_file(cls, path:str, 
                  name: Optional[str]=None) -> SimplicialComplex:
        """Instantiates a `SimplicialComplex` from a `.ply` file.

        Parameters
        ----------
        path : str
            The path to the `.ply` file.
        name : str, optional
            A name for the complex. Default is None.

        Returns
        -------
        SimplicialComplex
            The instantiated simplicial complex.
        """

        from dxtr.io import read_ply

        indices, vertices = read_ply(path)

        return cls(indices, vertices, name)

    @valid_input
    def to_file(self, file_name:str, format:Optional[str]='.ply', 
                folder:Optional[str|Path]=None) -> None:
        """Saves the `SimplicialComplex` instance to a file.

        Parameters
        ----------
        file_name : str
            The name of the file to write on disk.
        format : str, optional
            The type of file to write. Default is `.ply`.
        folder : str or Path, optional
            The location where to write the file. Default is the current working directory.

        Notes
        -----
        * `SimplicialComplex` instances can be saved as `.ply` or `.vtk`.
        * By default, the chosen format is `.ply`.
        """

        from dxtr.io.write import write_ply, format_path_properly
        from dxtr.utils.wrappers import UGrid

        path = format_path_properly(folder, file_name, format)

        if path.suffix=='.ply':
            write_ply(self[-1].vertex_indices, self[0].vertices, path=path)

        elif path.suffix=='.vtk':
            ugrid = UGrid.generate_from(self)
            ugrid.save(path)

    @property
    def name(self) -> str:
        """Gets the name of the complex.

        Returns
        -------
        str
            The name of the complex.
        """
        if self._name is None:
            return self.__str__()[:21]
        else:
            return self._name

    @name.setter
    def name(self, new_name:str) -> None:
        """Sets a custom name for the complex.

        Parameters
        ----------
        new_name : str
            The new name for the complex.
        """
        self._name = new_name

    @property
    def isabstract(self) -> bool:
        """Checks if the complex is embedded within a geometrical space.

        Returns
        -------
        bool
            True if the complex is abstract, False otherwise.
        """
        return self._isabstract

    @property
    def emb_dim(self) -> int:
        """Gets the dimension of the embedding Euclidean space.

        Returns
        -------
        int
            The dimension of the embedding Euclidean space.
        """
        return self._embedded_dim

    @property
    def vertices(self) -> np.ndarray[float]:
        """Gets the vertices corresponding to the 0-simplices.

        Returns
        -------
        np.ndarray of float
            The vertices corresponding to the 0-simplices.
        """
        return self._vertices

    def build_geometry(self, vertices:Iterable[Iterable[float]]) -> None:
        """Formats the vertices and computes the k-volumes of all simplices.

        Parameters
        ----------
        vertices : iterable of iterable of float
            The coordinates of the vertices.
        """
        _set_vertices(self, vertices)
        _compute_volumes(self)

    def update_geometry(self, displacement:dict[int,np.ndarray[float]]) -> None:
        """Updates some vertices and the corresponding geometrical properties.

        Parameters
        ----------
        displacement : dict of int to np.ndarray of float
            The displacement to add to the selected vertices.
            - keys: The indices of the vertices to move.
            - values: The displacement to add to the selected vertices.

        Notes
        -----
        * When applied to a simplicial complex, the only recomputed geometrical property is the simplex volume.
        """

        moved_vids = list(displacement.keys())
        dplcmt = list(displacement.values())

        self._vertices[moved_vids] += dplcmt 
        _compute_volumes(self, surrounding=moved_vids)

emb_dim property

Gets the dimension of the embedding Euclidean space.

Returns:

Type Description
int

The dimension of the embedding Euclidean space.

isabstract property

Checks if the complex is embedded within a geometrical space.

Returns:

Type Description
bool

True if the complex is abstract, False otherwise.

name property writable

Gets the name of the complex.

Returns:

Type Description
str

The name of the complex.

vertices property

Gets the vertices corresponding to the 0-simplices.

Returns:

Type Description
np.ndarray of float

The vertices corresponding to the 0-simplices.

__init__(indices, vertices=None, name=None)

Initializes a SimplicialComplex object.

Parameters:

Name Type Description Default
indices list of list of int

The list of vertex indices forming the highest degree simplices.

required
vertices iterable of iterable of float

The coordinates of the vertices. Default is None.

None
name str

A name for the complex. Default is None.

None
Source code in src/dxtr/complexes/simplicialcomplex.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def __init__(self, indices: list[list[int]],
             vertices: Optional[Iterable[Iterable[float]]] = None, 
             name: Optional[str] = None) -> None:
    """Initializes a `SimplicialComplex` object.

    Parameters
    ----------
    indices : list of list of int
        The list of vertex indices forming the highest degree simplices.
    vertices : iterable of iterable of float, optional
        The coordinates of the vertices. Default is None.
    name : str, optional
        A name for the complex. Default is None.
    """

    super().__init__(indices, name)

    if _isproperly_embedded(self, vertices):
        self.build_geometry(vertices)

__str__()

Returns a string representation of the complex.

Returns:

Type Description
str

A string representation of the complex.

Source code in src/dxtr/complexes/simplicialcomplex.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def __str__(self) -> str:
    """Returns a string representation of the complex.

    Returns
    -------
    str
        A string representation of the complex.
    """
    if self._name is None:
        description = f'{self.dim}D'
        description += ' Abstract ' if self.isabstract else ' '
        description += f'Simplicial Complex of shape {self.shape}, '
        if not self.isabstract:
            description += f'embedded in R^{self.emb_dim}.'
        return description
    else:
        return self._name

build_geometry(vertices)

Formats the vertices and computes the k-volumes of all simplices.

Parameters:

Name Type Description Default
vertices iterable of iterable of float

The coordinates of the vertices.

required
Source code in src/dxtr/complexes/simplicialcomplex.py
193
194
195
196
197
198
199
200
201
202
def build_geometry(self, vertices:Iterable[Iterable[float]]) -> None:
    """Formats the vertices and computes the k-volumes of all simplices.

    Parameters
    ----------
    vertices : iterable of iterable of float
        The coordinates of the vertices.
    """
    _set_vertices(self, vertices)
    _compute_volumes(self)

from_file(path, name=None) classmethod

Instantiates a SimplicialComplex from a .ply file.

Parameters:

Name Type Description Default
path str

The path to the .ply file.

required
name str

A name for the complex. Default is None.

None

Returns:

Type Description
SimplicialComplex

The instantiated simplicial complex.

Source code in src/dxtr/complexes/simplicialcomplex.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@classmethod
def from_file(cls, path:str, 
              name: Optional[str]=None) -> SimplicialComplex:
    """Instantiates a `SimplicialComplex` from a `.ply` file.

    Parameters
    ----------
    path : str
        The path to the `.ply` file.
    name : str, optional
        A name for the complex. Default is None.

    Returns
    -------
    SimplicialComplex
        The instantiated simplicial complex.
    """

    from dxtr.io import read_ply

    indices, vertices = read_ply(path)

    return cls(indices, vertices, name)

to_file(file_name, format='.ply', folder=None)

Saves the SimplicialComplex instance to a file.

Parameters:

Name Type Description Default
file_name str

The name of the file to write on disk.

required
format str

The type of file to write. Default is .ply.

'.ply'
folder str or Path

The location where to write the file. Default is the current working directory.

None
Notes
  • SimplicialComplex instances can be saved as .ply or .vtk.
  • By default, the chosen format is .ply.
Source code in src/dxtr/complexes/simplicialcomplex.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@valid_input
def to_file(self, file_name:str, format:Optional[str]='.ply', 
            folder:Optional[str|Path]=None) -> None:
    """Saves the `SimplicialComplex` instance to a file.

    Parameters
    ----------
    file_name : str
        The name of the file to write on disk.
    format : str, optional
        The type of file to write. Default is `.ply`.
    folder : str or Path, optional
        The location where to write the file. Default is the current working directory.

    Notes
    -----
    * `SimplicialComplex` instances can be saved as `.ply` or `.vtk`.
    * By default, the chosen format is `.ply`.
    """

    from dxtr.io.write import write_ply, format_path_properly
    from dxtr.utils.wrappers import UGrid

    path = format_path_properly(folder, file_name, format)

    if path.suffix=='.ply':
        write_ply(self[-1].vertex_indices, self[0].vertices, path=path)

    elif path.suffix=='.vtk':
        ugrid = UGrid.generate_from(self)
        ugrid.save(path)

update_geometry(displacement)

Updates some vertices and the corresponding geometrical properties.

Parameters:

Name Type Description Default
displacement dict of int to np.ndarray of float

The displacement to add to the selected vertices. - keys: The indices of the vertices to move. - values: The displacement to add to the selected vertices.

required
Notes
  • When applied to a simplicial complex, the only recomputed geometrical property is the simplex volume.
Source code in src/dxtr/complexes/simplicialcomplex.py
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
def update_geometry(self, displacement:dict[int,np.ndarray[float]]) -> None:
    """Updates some vertices and the corresponding geometrical properties.

    Parameters
    ----------
    displacement : dict of int to np.ndarray of float
        The displacement to add to the selected vertices.
        - keys: The indices of the vertices to move.
        - values: The displacement to add to the selected vertices.

    Notes
    -----
    * When applied to a simplicial complex, the only recomputed geometrical property is the simplex volume.
    """

    moved_vids = list(displacement.keys())
    dplcmt = list(displacement.values())

    self._vertices[moved_vids] += dplcmt 
    _compute_volumes(self, surrounding=moved_vids)

primal_edge_vectors(of, normalized=False)

Computes the primal and/or dual edge vectors of a SimplicialComplex.

Parameters:

Name Type Description Default
of SimplicialComplex

The simplicial complex of interest.

required
normalized bool

If true the returned vectors have length one. Default is False.

False

Returns:

Type Description
np.ndarray of float

An array of shape (N1, D), N1 = number of 1-simplices, D = embedding dimension.

Notes
  • This algorithm does not take into consideration the orientation of 1-simplices. This might be a problem for some applications in the future. This limitation must be remembered.
Source code in src/dxtr/complexes/simplicialcomplex.py
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
@typecheck(SimplicialComplex)
def primal_edge_vectors(of:SimplicialComplex, normalized:bool=False
                 ) -> np.ndarray[float]:
    """Computes the primal and/or dual edge vectors of a `SimplicialComplex`.

    Parameters
    ----------
    of : SimplicialComplex
        The simplicial complex of interest.
    normalized : bool, optional
        If true the returned vectors have length one. Default is False.

    Returns
    -------
    np.ndarray of float
        An array of shape (N1, D), N1 = number of 1-simplices, 
        D = embedding dimension.

    Notes
    -----
    * This algorithm does not take into consideration the orientation of 1-simplices. This might be a problem for some applications in the future. This limitation must be remembered.
    """

    mfld = of

    edges = mfld[0].coboundary @ mfld[0].vertices

    if normalized:
        edges /= lng.norm(edges, axis=-1).reshape(*edges.shape[:-1], 1)

        # to deal with the edges of size 0 on the borders of open complexes.
        if np.isnan(edges).any(): edges[np.where(np.isnan(edges))] = 0

    return edges