from __future__ import annotations
from collections.abc import Iterable
import numpy as np
from matminer.featurizers.site import CrystalNNFingerprint
from matminer.featurizers.structure import SiteStatsFingerprint
from pymatgen.core.structure import IStructure
[docs]def get_site_fingerprints(
structure: IStructure,
as_dict: bool = True,
preset: str = "CrystalNNFingerprint_ops",
) -> list[dict[str, int]] | np.ndarray:
"""Gets the fingerprint for all sites in a structure.
Args:
structure: A structure.
as_dict: Whether to return the fingerprints as a dictionary of
``{'op': val}``. Defaults to ``True``.
preset: The preset to use when calculating the fingerprint. See
:class:`matminer.featurizers.structure.SiteStatsFingerprint``
for more details.
Returns:
The fingerprint for all sites in the structure. If ``as_dict == True``,
the data will be returned as a :obj:`list` of :obj:`dict` containing the
order parameters as::
[{'op': val}]
for each site. If ``as_dict == False``, the data will be returned as a
:class:`numoy.ndarray` containing the fingerprint for each site as::
[site_index][op_index]
"""
ssf = SiteStatsFingerprint.from_preset(preset, stats=None)
# transpose fingerprint from [op_type][site] to [site][op_type]
site_fingerprints = np.array(ssf.featurize(structure)).T
if as_dict:
labels = ssf.feature_labels()
site_fingerprints = [dict(zip(labels, x)) for x in site_fingerprints]
return site_fingerprints
[docs]def get_structure_fingerprint(
structure: IStructure,
preset: str = "CrystalNNFingerprint_ops",
stats: tuple[str] | None = ("mean", "std_dev"),
prototype_match: bool = False,
) -> np.ndarray:
"""Gets the fingerprint for a structure.
Args:
structure: A structure.
preset: The preset to use when calculating the fingerprint. See
:class:`matminer.featurizers.structure.SiteStatsFingerprint``
for more details.
stats: The stats to include in fingerprint. See
:class:`matminer.featurizers.structure.SiteStatsFingerprint``
for more details.
prototype_match: Whether to use distance cutoffs and electron negativity
differences when calculating the structure fingerprint.
Returns:
The structure fingerprint as a :class:`numpy.ndarray`.
"""
# TODO: Add distance_cutoff option to matminer so we can user preset arg
# currently don't use SiteStatsFingerprint.from_preset as we need to pass in
# distance_cutoffs param
if prototype_match:
ssf = SiteStatsFingerprint(
CrystalNNFingerprint.from_preset(
"ops", cation_anion=False, distance_cutoffs=None, x_diff_weight=None
),
stats=stats,
)
else:
ssf = SiteStatsFingerprint(
CrystalNNFingerprint.from_preset("ops", cation_anion=False), stats=stats
)
return np.array(ssf.featurize(structure))
[docs]def get_fingerprint_distance(
structure_a: IStructure | Iterable, structure_b: IStructure | Iterable
) -> float:
"""Gets the euclidean distance between the fingerprints of two structures.
Args:
structure_a: The first structure or fingerprint. Can be provided as a
structure or a fingerprint. If provided as a structure, the
fingerprint will be calculated first, so generally it is quicker
to pre-calculate the fingerprint if comparing against multiple
structures in turn.
structure_b: The second structure or fingerprint. Can be provided as a
structure or a fingerprint. If provided as a structure, the
fingerprint will be calculated first, so generally it is quicker
to pre-calculate the fingerprint if comparing against multiple
structures in turn.
Returns:
The euclidean distance between fingerprints as a :class:`numpy.ndarray`.
"""
if issubclass(type(structure_a), IStructure):
fingerprint_a = get_structure_fingerprint(structure_a)
else:
fingerprint_a = np.array(structure_a)
if issubclass(type(structure_b), IStructure):
fingerprint_b = get_structure_fingerprint(structure_b)
else:
fingerprint_b = np.array(structure_b)
return np.linalg.norm(fingerprint_a - fingerprint_b)