Skip to content

simplicialmanifold

SimplicialManifold

Bases: SimplicialComplex

Embodies the concept of simplicial manifold.

Source code in src/dxtr/complexes/simplicialmanifold.py
 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
class SimplicialManifold(SimplicialComplex):
    """Embodies the concept of simplicial manifold."""

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

        Parameters
        ----------
        simplices : 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 manifold. Default is None.
        """
        super().__init__(simplices, vertices, name)

        if _isvalid_for_dualization(self):
            self.build_dual_geometry()

    def __str__(self):
        """Returns a string representation of the manifold.

        Returns
        -------
        str
            A string representation of the manifold.
        """
        description = f'{self.dim}D Simplicial Manifold of '
        description += f'shape {self.shape}, '
        description += f'embedded in R^{self.emb_dim}.'
        return description

    @property
    def name(self) -> str:
        """Name of the manifold."""
        if self._name is None:
            return self.__str__()[:22]
        else:
            return self._name

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

    @property
    def deficit_angles(self) -> Optional[np.ndarray[float]]:
        """Returns the deficit angles computed on all (n-2)-simplices.

        Notes
        -----
        The deficit angle is a discrete version of the gaussian curvature.
        """
        return self[self.dim-2]._deficit_angles

    @property
    def dihedral_angles(self) -> Optional[np.ndarray[float]]:
        """Returns the dihedral angles computed on all (n-1)-simplices.
        """
        return self[self.dim-1]._dihedral_angles

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

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

        Returns
        -------
        SimplicialManifold
            The instantiated simplicial manifold.
        """

        from dxtr.io import read_ply

        indices, vertices = read_ply(path)

        return cls(indices, vertices, name)

    def build_dual_geometry(self) -> None:
        """Computes 0-cells positions, covolumes, deficit & dihedral angles.

        Notes
        -----
        Position vectors of 0-cells/n-simplices are taken as
        the circumcenter of the position vectors of the surrounding
        n-cells/0-simplices.
        Sign of mtrx_inv is set to satisfy: mtrx_inv * mtrx = -1 ^(k*(n-k))
        """
        _compute_circumcenters(self)
        _compute_covolumes(self)
        _compute_deficit_angles(self)
        _compute_dihedral_angles(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
        -----
        The `SimplicialManifold` version of this method calls the one from
        the mother class `SimplicialComplex` to update the positions of 
        the vertices and the volumes of the simplices.
        In this specific version, circumcenters and covolumes are also
        updated.
        Deficit angles are updated through a specific method:
        `update_deficit_angle()`.
        """

        super().update_geometry(displacement)

        moved_vids = list(displacement.keys())

        _compute_circumcenters(self, surrounding=moved_vids)
        _compute_covolumes(self, surrounding=moved_vids)
        _compute_deficit_angles(self, surrounding=moved_vids)
        _compute_dihedral_angles(self, surrounding=moved_vids)

deficit_angles property

Returns the deficit angles computed on all (n-2)-simplices.

Notes

The deficit angle is a discrete version of the gaussian curvature.

dihedral_angles property

Returns the dihedral angles computed on all (n-1)-simplices.

name property writable

Name of the manifold.

__init__(simplices, vertices, name=None)

Initializes a SimplicialManifold object.

Parameters:

Name Type Description Default
simplices 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.

required
name str

A name for the manifold. Default is None.

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

    Parameters
    ----------
    simplices : 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 manifold. Default is None.
    """
    super().__init__(simplices, vertices, name)

    if _isvalid_for_dualization(self):
        self.build_dual_geometry()

__str__()

Returns a string representation of the manifold.

Returns:

Type Description
str

A string representation of the manifold.

Source code in src/dxtr/complexes/simplicialmanifold.py
62
63
64
65
66
67
68
69
70
71
72
73
def __str__(self):
    """Returns a string representation of the manifold.

    Returns
    -------
    str
        A string representation of the manifold.
    """
    description = f'{self.dim}D Simplicial Manifold of '
    description += f'shape {self.shape}, '
    description += f'embedded in R^{self.emb_dim}.'
    return description

build_dual_geometry()

Computes 0-cells positions, covolumes, deficit & dihedral angles.

Notes

Position vectors of 0-cells/n-simplices are taken as the circumcenter of the position vectors of the surrounding n-cells/0-simplices. Sign of mtrx_inv is set to satisfy: mtrx_inv * mtrx = -1 ^(k*(n-k))

Source code in src/dxtr/complexes/simplicialmanifold.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def build_dual_geometry(self) -> None:
    """Computes 0-cells positions, covolumes, deficit & dihedral angles.

    Notes
    -----
    Position vectors of 0-cells/n-simplices are taken as
    the circumcenter of the position vectors of the surrounding
    n-cells/0-simplices.
    Sign of mtrx_inv is set to satisfy: mtrx_inv * mtrx = -1 ^(k*(n-k))
    """
    _compute_circumcenters(self)
    _compute_covolumes(self)
    _compute_deficit_angles(self)
    _compute_dihedral_angles(self)

from_file(path, name=None) classmethod

Instantiates a SimplicialManifold from a .ply file.

Parameters:

Name Type Description Default
path str

The path to the .ply file.

required
name str

A name for the manifold. Default is None.

None

Returns:

Type Description
SimplicialManifold

The instantiated simplicial manifold.

Source code in src/dxtr/complexes/simplicialmanifold.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
@classmethod
def from_file(cls, path: str, 
              name: Optional[str]=None) -> SimplicialManifold:
    """Instantiates a `SimplicialManifold` from a `.ply` file.

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

    Returns
    -------
    SimplicialManifold
        The instantiated simplicial manifold.
    """

    from dxtr.io import read_ply

    indices, vertices = read_ply(path)

    return cls(indices, vertices, name)

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

The SimplicialManifold version of this method calls the one from the mother class SimplicialComplex to update the positions of the vertices and the volumes of the simplices. In this specific version, circumcenters and covolumes are also updated. Deficit angles are updated through a specific method: update_deficit_angle().

Source code in src/dxtr/complexes/simplicialmanifold.py
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
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
    -----
    The `SimplicialManifold` version of this method calls the one from
    the mother class `SimplicialComplex` to update the positions of 
    the vertices and the volumes of the simplices.
    In this specific version, circumcenters and covolumes are also
    updated.
    Deficit angles are updated through a specific method:
    `update_deficit_angle()`.
    """

    super().update_geometry(displacement)

    moved_vids = list(displacement.keys())

    _compute_circumcenters(self, surrounding=moved_vids)
    _compute_covolumes(self, surrounding=moved_vids)
    _compute_deficit_angles(self, surrounding=moved_vids)
    _compute_dihedral_angles(self, surrounding=moved_vids)

dual_edge_vectors(of, normalized=False)

Computes vectors along the dual 1-cells of a SimplicialManifold.

Parameters:

Name Type Description Default
of SimplicialManifold

The SimplicialManifold to work on.

required
normalized bool

If True, returned unit vectors. Default is False.

False

Returns:

Type Description
np.ndarray of float

A (N,D) array with N = number of (n-1)-simplices, D = the dimension of the embedding space.

Notes

In the case of a non-closed manifold, the outer dual edges are oriented outward from the circumcenters of the top-simplices toward the circumcenters of the (n-1)-simplices on the border.

Source code in src/dxtr/complexes/simplicialmanifold.py
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
@typecheck(SimplicialManifold)
def dual_edge_vectors(of:SimplicialManifold, 
                      normalized:bool=False) -> np.ndarray[float]:
    """Computes vectors along the dual 1-cells of a `SimplicialManifold`.

    Parameters
    ----------
    of : SimplicialManifold
        The `SimplicialManifold` to work on.
    normalized : bool, optional
        If True, returned unit vectors. Default is False.

    Returns
    -------
    np.ndarray of float
        A (N,D) array with N = number of (n-1)-simplices, 
        D = the dimension of the embedding space.

    Notes
    -----
    In the case of a non-closed manifold, the outer dual edges are 
    oriented outward from the circumcenters of the top-simplices toward
    the circumcenters of the (n-1)-simplices on the border.
    """

    mfld = of

    vertices = mfld[-1].circumcenters
    edges = mfld[-1].boundary @ vertices

    if not mfld.isclosed:
        in_eids = mfld.interior()[1]
        inner_edges = dict(zip(in_eids, edges[in_eids]))

        brd_eids = mfld.border()[1]
        brd_vertices = mfld[-2].circumcenters[brd_eids]

        brd_tids = [mfld.cofaces(mfld.dim-1)[eid][0] for eid in brd_eids]

        outer_edges = brd_vertices - vertices[brd_tids]
        outer_edges = dict(zip(brd_eids, outer_edges))

        all_edges = inner_edges | outer_edges
        edges = np.asarray([edge for _, edge in sorted(all_edges.items())])

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

        # to deal with the edges of length 0.
        if np.isnan(edges).any(): 
            nul_edids = np.where(np.isnan(edges))[0]
            edges[nul_edids] = 0
            logger.warning(f'There are {len(nul_edids)} vanishing dual edges.')

    return edges

edge_vectors(manifold, dual=False, normalized=False)

Computes edge vectors on SimplicialComplex or SimplicialManifold.

Parameters:

Name Type Description Default
manifold SimplicialManifold or SimplicialComplex

The SimplicialManifold to work on.

required
dual bool

If True, the computation is performed on the dual complex. Else, it is performed on the primal one. Default is False.

False
normalized bool

If True, the computed vectors are of unit norm. Default is False.

False

Returns:

Type Description
np.ndarray of float

A (N,D) array with N = number of (n-1)-simplices if on==True else number of 0-simplices, D = the dimension of the embedding space.

Notes

Can only compute dual edges on SimplicialManifold not on SimplicialComplex.

Source code in src/dxtr/complexes/simplicialmanifold.py
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
@typecheck(SimplicialComplex)
def edge_vectors(manifold:SimplicialManifold|SimplicialComplex, 
                 dual:bool=False, normalized:bool=False) -> np.ndarray[float]:
    """Computes edge vectors on `SimplicialComplex` or `SimplicialManifold`.

    Parameters
    ----------
    manifold : SimplicialManifold or SimplicialComplex
        The `SimplicialManifold` to work on.
    dual : bool, optional
        If True, the computation is performed on the dual complex. 
        Else, it is performed on the primal one. Default is False.
    normalized : bool, optional
        If True, the computed vectors are of unit norm. Default is False.

    Returns
    -------
    np.ndarray of float
        A (N,D) array with N = number of (n-1)-simplices if on==True else
        number of 0-simplices, D = the dimension of the embedding space.

    Notes
    -----
    Can only compute dual edges on `SimplicialManifold` not on
    `SimplicialComplex`.
    """
    if dual:
        if not isinstance(manifold, SimplicialManifold):
            logger.warning(
                'Cannot compute dual edge vectors on `SimplicialComplex`.')
            return None
        else:
            return dual_edge_vectors(manifold, normalized=normalized)
    else:
        return primal_edge_vectors(manifold, normalized=normalized)