Skip to content

Persistent homology

Tools for computing persistence diagrams of local atomic environments, used to identify medium-range order and topological features.

!!! warning "Partially quarantined" LocalPD and the standalone get_local_persistence function are currently broken and raise NotImplementedError on use. get_persistence_diagram works standalone. See Known Issues for details.

Requires the optional dionysus/diode dependencies — see Installation.

NOTE: LocalPD and the standalone get_local_persistence below are currently broken and quarantined (raise NotImplementedError on use). Both were written against neighborhood.get_persistence_diagram(...) as a bound method on GlassAtoms/ASE Atoms, but that method was moved out into the standalone get_persistence_diagram(atoms, ...) function in this module and the call sites were never updated, so they fail with AttributeError. See docs/vitrum/known_issues.md. get_persistence_diagram itself is unaffected and works standalone.

get_local_persistence(atoms, center_id, cutoff)

Calculate the persistence diagram of the local environment of an atom.

Parameters:

Name Type Description Default
center_id int or str

The atomic number or symbol of the central atom.

required
cutoff float

The cutoff distance for the local environment.

required

Returns:

Name Type Description
list

A list of pandas.DataFrame containing the persistence diagram of the local environment.

Source code in src/vitrum/persistent_homology.py
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
def get_local_persistence(atoms, center_id, cutoff):
    """
    Calculate the persistence diagram of the local environment of an atom.

    Parameters:
        center_id (int or str): The atomic number or symbol of the central atom.
        cutoff (float): The cutoff distance for the local environment.

    Returns:
        list: A list of pandas.DataFrame containing the persistence diagram of the local environment.
    """
    raise NotImplementedError(
        "get_local_persistence is currently broken: it calls neighborhood.center() "
        "(which does not do minimum-image centering on an ASE Atoms object) and "
        "neighborhood.get_persistence_diagram() (which does not exist as a bound method; "
        "use the standalone get_persistence_diagram(atoms, ...) function instead). "
        "See docs/vitrum/known_issues.md."
    )
    persistence_diagrams = []
    if isinstance(center_id, str):
        types = atoms.get_chemical_symbols()
    if isinstance(center_id, int):
        types = atoms.get_atomic_numbers()
    centers = np.where(types == center_id)[0]
    for i in centers:
        neighbors = np.where(atoms.get_dist()[i, :] < cutoff)[0]
        neighborhood = atoms[neighbors]
        neighborhood.center()
        persistence_diagrams.append(neighborhood.get_persistence_diagram())
    return persistence_diagrams

get_persistence_diagram(atoms, dimension=1, weights=None)

Calculate the persistence diagram of the given data points.

Parameters:

Name Type Description Default
dimension int

The dimension of the persistence diagram to calculate. Defaults to 1.

1
weights dict or list

The weights to assign to each data point. Can be a dictionary mapping chemical symbols to weights or a list of weights. Defaults to None.

None

Returns:

Type Description

pandas.DataFrame: The persistence diagram as a DataFrame with columns "Birth" and "Death".

Source code in src/vitrum/persistent_homology.py
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
def get_persistence_diagram(atoms, dimension=1, weights=None):
    """
    Calculate the persistence diagram of the given data points.

    Parameters:
        dimension (int, optional): The dimension of the persistence diagram to calculate. Defaults to 1.
        weights (dict or list, optional): The weights to assign to each data point. Can be a dictionary mapping chemical symbols to weights or a list of weights. Defaults to None.

    Returns:
        pandas.DataFrame: The persistence diagram as a DataFrame with columns "Birth" and "Death".
    """
    coord = atoms.get_positions()
    data = np.column_stack([atoms.get_chemical_symbols(), coord])
    dfpoints = pd.DataFrame(data, columns=["Atom", "x", "y", "z"])
    chem_species = np.unique(atoms.get_chemical_symbols())

    if weights is None:
        radii = [0 for i in chem_species]
    elif isinstance(weights, dict):
        radii = [weights[i] for i in chem_species]
    elif isinstance(weights, list):
        radii = weights

    conditions = [(dfpoints["Atom"] == i) for i in chem_species]
    choice_weight = [i**2 for i in radii]

    dfpoints["w"] = np.select(conditions, choice_weight)
    dfpoints["x"] = pd.to_numeric(dfpoints["x"])
    dfpoints["y"] = pd.to_numeric(dfpoints["y"])
    dfpoints["z"] = pd.to_numeric(dfpoints["z"])

    points = dfpoints[["x", "y", "z", "w"]].to_numpy()
    simplices = diode.fill_weighted_alpha_shapes(points)
    f = dionysus.Filtration(simplices)
    m = dionysus.homology_persistence(f)
    dgms = dionysus.init_diagrams(m, f)

    # Gather the PD of loop in a dataframe
    dfPD = pd.DataFrame(
        data={
            "Birth": [p.birth for p in dgms[dimension]],
            "Death": [p.death for p in dgms[dimension]],
        }
    )
    return dfPD