clease
Advanced tools
| Metadata-Version: 2.1 | ||
| Name: clease | ||
| Version: 0.11.6 | ||
| Version: 1.0.0 | ||
| Summary: CLuster Expansion in Atomistic Simulation Environment | ||
@@ -12,3 +12,3 @@ Home-page: https://gitlab.com/computationalmaterials/clease/ | ||
| Keywords: Cluster Expansion,Monte Carlo,Computational materials,Materials research | ||
| Classifier: Development Status :: 4 - Beta | ||
| Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) | ||
@@ -15,0 +15,0 @@ Classifier: Programming Language :: Python :: 3.7 |
@@ -16,21 +16,21 @@ ase<3.23,>=3.20 | ||
| [all] | ||
| clease-gui | ||
| clang-format>=14.0.3 | ||
| ipython | ||
| mock | ||
| pytest | ||
| pyclean>=2.0.0 | ||
| pytest-mock | ||
| pylint | ||
| black>=22.1.0 | ||
| sphinx | ||
| pre-commit | ||
| pytest-cov | ||
| cython | ||
| clease-gui | ||
| clang-format>=14.0.3 | ||
| tox>=3.24.0 | ||
| pip | ||
| black>=22.1.0 | ||
| tox>=3.24.0 | ||
| pytest-benchmark[histogram]>=3.4.1 | ||
| pylint | ||
| twine | ||
| mock | ||
| sphinx_rtd_theme | ||
| build | ||
| twine | ||
| pytest | ||
| sphinx | ||
| pytest-mock | ||
| pyclean>=2.0.0 | ||
| pytest-benchmark[histogram]>=3.4.1 | ||
| pytest-cov | ||
@@ -37,0 +37,0 @@ [dev] |
@@ -11,3 +11,2 @@ LICENSE.md | ||
| clease/cluster_coverage.py | ||
| clease/concentration.py | ||
| clease/convexhull.py | ||
@@ -26,7 +25,4 @@ clease/corr_func.py | ||
| clease/mp_logger.py | ||
| clease/new_struct.py | ||
| clease/plot_post_process.py | ||
| clease/regression_old.py | ||
| clease/sparsifier.py | ||
| clease/structure_generator.py | ||
| clease/structure_mapper.py | ||
@@ -33,0 +29,0 @@ clease/svd.py |
| # pylint: disable=undefined-variable | ||
| import logging | ||
| from deprecated import deprecated | ||
| from .version import * | ||
@@ -5,0 +4,0 @@ |
@@ -1,1 +0,1 @@ | ||
| 0.11.6 | ||
| 1.0.0 |
@@ -314,3 +314,3 @@ from typing import Sequence, Set, Dict, List, Iterator, Tuple | ||
| ) | ||
| logger.info("Trivial supercell with repetition: (%d, %d, %d)", nx, ny, nz) | ||
| logger.debug("Trivial supercell with repetition: (%d, %d, %d)", nx, ny, nz) | ||
| else: | ||
@@ -323,3 +323,3 @@ # Choose the generalized pathway. | ||
| ) | ||
| logger.info("Non-trivial supercell, will wrap using cartesian coordinates") | ||
| logger.debug("Non-trivial supercell, will wrap using cartesian coordinates") | ||
@@ -326,0 +326,0 @@ lut = self.create_four_vector_lut(template) |
+85
-117
@@ -8,4 +8,5 @@ """Module that fits ECIs to energy data.""" | ||
| import multiprocessing as mp | ||
| from typing import Optional, Dict, List | ||
| from typing import Dict, List | ||
| from deprecated import deprecated | ||
| import numpy as np | ||
@@ -23,3 +24,3 @@ from ase.db import connect | ||
| __all__ = ("Evaluate",) | ||
| __all__ = ("Evaluate", "supports_alpha_cv") | ||
@@ -116,3 +117,2 @@ # Initialize a module-wide logger | ||
| self.scheme = None | ||
| self.scheme_string = None | ||
| self.nsplits = nsplits | ||
@@ -159,3 +159,2 @@ self.num_repetitions = num_repetitions | ||
| self.eci = None | ||
| self._alpha = None | ||
| self.e_pred_loo = None | ||
@@ -173,2 +172,17 @@ self.parallel = parallel | ||
| @property | ||
| def scoring_scheme(self) -> str: | ||
| return self._scoring_scheme | ||
| @scoring_scheme.setter | ||
| def scoring_scheme(self, value: str) -> None: | ||
| if not isinstance(value, str): | ||
| raise TypeError(f"Scoring scheme should be string, got {value!r}") | ||
| value = value.lower() | ||
| allowed_schemas = {"loocv", "loocv_fast", "k-fold"} | ||
| if value not in allowed_schemas: | ||
| schemas_s = ", ".join(sorted(allowed_schemas)) | ||
| raise ValueError(f"Unknown scoring scheme: {value}. Allowed schemas: {schemas_s}") | ||
| self._scoring_scheme = value | ||
| @property | ||
| def concentrations(self): | ||
@@ -188,3 +202,3 @@ """ | ||
| allowed_fitting_schemes = ["ridge", "tikhonov", "lasso", "l1", "l2", "ols"] | ||
| allowed_fitting_schemes = ["ridge", "tikhonov", "lasso", "l1", "l2", "ols", "linear"] | ||
| if isinstance(fitting_scheme, LinearRegression): | ||
@@ -194,3 +208,2 @@ self.scheme = fitting_scheme | ||
| fitting_scheme = fitting_scheme.lower() | ||
| self.scheme_string = fitting_scheme | ||
| if fitting_scheme not in allowed_fitting_schemes: | ||
@@ -206,5 +219,7 @@ raise ValueError(f"Fitting scheme has to be one of {allowed_fitting_schemes}") | ||
| self.scheme = Lasso(alpha=alpha) | ||
| else: | ||
| elif fitting_scheme in ["ols", "linear"]: | ||
| # Perform ordinary least squares | ||
| self.scheme = LinearRegression() | ||
| else: | ||
| raise ValueError(f"Unknown fitting scheme: {fitting_scheme}") | ||
| else: | ||
@@ -217,9 +232,4 @@ raise ValueError( | ||
| # If the fitting scheme is changed, the ECIs computed are no | ||
| # longer consistent with the scheme | ||
| # By setting it to None, a new calculation is performed | ||
| # when the ECIs are requested | ||
| # Also reset the "last alpha" | ||
| # Unset the ECI, so a new fit is required. | ||
| self.eci = None | ||
| self._alpha = None | ||
@@ -281,60 +291,47 @@ N = len(self.e_dft) | ||
| """Check whether we need to calculate the ECI values.""" | ||
| return self.eci is None or self._alpha != self.scheme.alpha | ||
| return self.eci is None | ||
| def fit(self) -> None: | ||
| """Determine the ECI for a given alpha. | ||
| """Determine the ECI with the given regressor. | ||
| This method also saves the last value of alpha used and | ||
| the corresponding ECIs (self.eci) such that ECIs are not calculated | ||
| repeated if alpha value is unchanged. | ||
| This will always calculate a new fit. | ||
| """ | ||
| if self.fit_required(): | ||
| self._do_fit() | ||
| self.eci = self.scheme.fit(self.cf_matrix, self.e_dft) | ||
| assert self.eci is not None | ||
| def _do_fit(self) -> None: | ||
| """Fit using the current scheme, and remember the alpha""" | ||
| self.eci = self.scheme.fit(self.cf_matrix, self.e_dft) | ||
| # Remember the latest alpha | ||
| self._alpha = self.scheme.alpha | ||
| def get_eci(self, allow_fit: bool = True) -> np.ndarray: | ||
| def get_eci(self) -> np.ndarray: | ||
| """Determine and return ECIs for a given alpha. | ||
| Raises a ValueError if no fit has been performed yet. | ||
| Args: | ||
| allow_fit (bool, optional): Is a new fit allowed to be run before returning the ECI's? | ||
| If no ECI's are present yet, and no re-fitting was allowed, then a ValueError | ||
| is raised. | ||
| Defaults to True. | ||
| Returns: | ||
| np.ndarray: A 1D array of floats with all ECI values. | ||
| """ | ||
| if allow_fit: | ||
| self.fit() | ||
| if self.eci is None: | ||
| # getting ECI's was not allowed to fit, and we havn't run a fit yet. | ||
| raise ValueError("ECI's has not been fit yet.") | ||
| raise ValueError("ECI's has not been fit yet. Call the Evaluate.fit method first.") | ||
| return self.eci | ||
| def get_eci_dict(self, allow_fit: bool = True): | ||
| def get_eci_dict(self, cutoff_tol: float = 1e-14) -> Dict[str, float]: | ||
| """Determine cluster names and their corresponding ECI value and return | ||
| them in a dictionary format. | ||
| Args: | ||
| cutoff_tol (float, optional): Cutoff value below which the absolute ECI | ||
| value is considered to be 0. Defaults to 1e-14. | ||
| Returns: | ||
| Dict[str, float]: Dictionary with the CF names and the corresponding | ||
| ECI value. | ||
| """ | ||
| Determine cluster names and their corresponding ECI value and return | ||
| them in a dictionary format.""" | ||
| self.get_eci(allow_fit=allow_fit) | ||
| eci = self.get_eci() | ||
| # sanity check | ||
| if len(self.cf_names) != len(self.eci): | ||
| if len(self.cf_names) != len(eci): | ||
| raise ValueError("lengths of cf_names and ECIs are not same") | ||
| i_nonzeros = np.nonzero(self.eci)[0] | ||
| pairs = [] | ||
| for i, cname in enumerate(self.cf_names): | ||
| if i not in i_nonzeros: | ||
| continue | ||
| pairs.append((cname, self.eci[i])) | ||
| all_nonzero = np.abs(eci) > cutoff_tol | ||
| # Only keep the all non-zero values. | ||
| return {cf: val for cf, val, nonzero in zip(self.cf_names, eci, all_nonzero) if nonzero} | ||
| return dict(pairs) | ||
| def load_eci_dict(self, eci_dict: Dict[str, float]) -> None: | ||
@@ -365,3 +362,3 @@ """Load the ECI's from a dictionary. Any ECI's which are missing | ||
| def save_eci(self, fname="eci.json"): | ||
| def save_eci(self, fname="eci.json", **kwargs): | ||
| """ | ||
@@ -375,7 +372,10 @@ Save a dictionary of cluster names and their corresponding ECI value | ||
| json filename. If no extension if given, .json is added | ||
| kwargs: | ||
| Extra keywords are passed on to the :meth:`~get_eci_dict` method. | ||
| """ | ||
| full_fname = add_file_extension(fname, ".json") | ||
| with open(full_fname, "w") as outfile: | ||
| json.dump(self.get_eci_dict(), outfile, indent=2, separators=(",", ": ")) | ||
| json.dump(self.get_eci_dict(**kwargs), outfile, indent=2, separators=(",", ": ")) | ||
| @deprecated(reason="Use the clease.plot_post_process module instead.", version="0.11.7") | ||
| def plot_fit(self, interactive=False, savefig=False, fname=None, show_hull=True): | ||
@@ -419,9 +419,6 @@ """Plot calculated (DFT) and predicted energies for a given alpha. | ||
| cv_name = "LOOCV" | ||
| if self.scoring_scheme == "loocv": | ||
| cv = self.loocv() * 1000.0 | ||
| elif self.scoring_scheme == "loocv_fast": | ||
| cv = self.loocv_fast() * 1000.0 | ||
| elif self.scoring_scheme == "k-fold": | ||
| cv = self.k_fold_cv() * 1000.0 | ||
| cv = self.get_cv_score() * 1000.0 | ||
| if self.scoring_scheme == "k-fold": | ||
| cv_name = f"{self.nsplits}-fold" | ||
| t = np.arange(rmin - 10, rmax + 10, 1) | ||
@@ -629,18 +626,9 @@ fig = plt.figure() | ||
| if fitting_schemes is None: | ||
| if self.scheme_string is None: | ||
| raise ValueError("No fitting scheme supplied!") | ||
| if self.scheme_string in ["lasso", "l1"]: | ||
| from clease.regression import Lasso | ||
| if not supports_alpha_cv(self.scheme): | ||
| raise ValueError(f"Scheme must support scalar alpha, got {self.scheme!r}") | ||
| fitting_schemes = Lasso.get_instance_array( | ||
| alpha_min, alpha_max, num_alpha=num_alpha, scale=scale | ||
| ) | ||
| elif self.scheme_string in ["ridge", "l2", "tikhonov"]: | ||
| from clease.regression import Tikhonov | ||
| fitting_schemes = self.scheme.get_instance_array( | ||
| alpha_min, alpha_max, num_alpha=num_alpha, scale=scale | ||
| ) | ||
| fitting_schemes = Tikhonov.get_instance_array( | ||
| alpha_min, alpha_max, num_alpha=num_alpha, scale=scale | ||
| ) | ||
| for scheme in fitting_schemes: | ||
@@ -691,9 +679,5 @@ if not isinstance(scheme, LinearRegression): | ||
| self.set_fitting_scheme(fitting_scheme=scheme) | ||
| if self.scoring_scheme == "loocv": | ||
| cv[i] = self.loocv() | ||
| elif self.scoring_scheme == "loocv_fast": | ||
| cv[i] = self.loocv_fast() | ||
| elif self.scoring_scheme == "k-fold": | ||
| cv[i] = self.k_fold_cv() | ||
| num_eci = len(np.nonzero(self.get_eci(allow_fit=True))[0]) | ||
| cv[i] = self.get_cv_score() | ||
| self.fit() | ||
| num_eci = len(np.nonzero(self.get_eci())[0]) | ||
| alpha = scheme.get_scalar_parameter() | ||
@@ -722,9 +706,5 @@ alphas.append(alpha) | ||
| self.scheme.alpha = alpha | ||
| if self.scoring_scheme == "loocv": | ||
| cv = self.loocv() | ||
| elif self.scoring_scheme == "loocv_fast": | ||
| cv = self.loocv_fast() | ||
| elif self.scoring_scheme == "k-fold": | ||
| cv = self.k_fold_cv() | ||
| num_eci = len(np.nonzero(self.get_eci(allow_fit=True))[0]) | ||
| cv = self.get_cv_score() | ||
| self.fit() | ||
| num_eci = len(np.nonzero(self.get_eci())[0]) | ||
| self._cv_scores.append({"alpha": alpha, "cv": cv}) | ||
@@ -740,2 +720,3 @@ logger.info(f"{alpha:.10f}\t {num_eci}\t {cv:.10f}") | ||
| @deprecated(reason="Use the alpha_CV method instead.", version="0.11.7") | ||
| def plot_CV( | ||
@@ -840,2 +821,3 @@ self, | ||
| @deprecated(reason="Logfile is being removed.", version="0.11.7") | ||
| def _get_alphas_cv_from_file(self, logfile): | ||
@@ -893,2 +875,3 @@ alphas = [] | ||
| @deprecated(reason="Use the clease.plot_post_process module instead.", version="0.11.7") | ||
| def plot_ECI(self, ignore_sizes=(0,), interactive=True): | ||
@@ -1133,3 +1116,3 @@ """Plot the all the ECI. | ||
| pred = self.eci.dot(cf) | ||
| pred = self.get_eci().dot(cf) | ||
@@ -1172,7 +1155,9 @@ if row.get("final_struct_id", -1) != -1: | ||
| if self.scoring_scheme == "loocv": | ||
| cv = self.loocv() * 1000.0 | ||
| cv = self.loocv() | ||
| elif self.scoring_scheme == "loocv_fast": | ||
| cv = self.loocv_fast() * 1000.0 | ||
| cv = self.loocv_fast() | ||
| elif self.scoring_scheme == "k-fold": | ||
| cv = self.k_fold_cv() * 1000.0 | ||
| cv = self.k_fold_cv() | ||
| else: | ||
| raise ValueError(f"Unknown scoring scheme: {self.schoring_scheme}") | ||
| return cv | ||
@@ -1186,30 +1171,5 @@ | ||
| """ | ||
| if self.eci is None: | ||
| raise ValueError("ECIs have not been fitted yet.") | ||
| return self.cf_matrix.dot(self.eci) | ||
| eci = self.get_eci() | ||
| return self.cf_matrix.dot(eci) | ||
| def subtract_predict_dft(self) -> np.ndarray: | ||
| """ | ||
| Subtract CE predicted energy from DFT energy | ||
| :return: Energy difference between DFT and CE model | ||
| (i.e., E_DFT - E_CE) in the original unit (typically eV) | ||
| """ | ||
| e_pred = self.get_energy_predict() | ||
| delta_e = self.e_dft - e_pred | ||
| return delta_e | ||
| def subtract_predict_dft_loo(self) -> Optional[np.ndarray]: | ||
| """ | ||
| Subtract CE predicted energy from DFT energy in the leave-one-out | ||
| scheme. | ||
| :return: Energy difference between DFT and CE model | ||
| (i.e., E_DFT - E_CE_LOO) in the original unit (typically eV) | ||
| """ | ||
| if self.e_pred_loo is None: | ||
| return None | ||
| delta_e_loo = self.e_dft - self.e_pred_loo | ||
| return delta_e_loo | ||
| def get_eci_by_size(self) -> Dict[str, Dict[str, list]]: | ||
@@ -1275,4 +1235,12 @@ """ | ||
| cv = evaluator.k_fold_cv() | ||
| num_eci = len(np.nonzero(evaluator.get_eci(allow_fit=True))[0]) | ||
| evaluator.fit() | ||
| num_eci = len(np.nonzero(evaluator.get_eci())[0]) | ||
| logger.info("%.10f\t %d\t %.10f", alpha, num_eci, cv) | ||
| return cv | ||
| def supports_alpha_cv(scheme: LinearRegression) -> bool: | ||
| """Determine whether a regression scheme supports alpha CV""" | ||
| if scheme.is_scalar() and hasattr(scheme, "alpha"): | ||
| return True | ||
| return False |
@@ -79,2 +79,3 @@ """Monte Carlo method for ase.""" | ||
| self.reset_averagers() | ||
| self._reset_internal_counters() | ||
@@ -183,3 +184,2 @@ def update_current_energy(self) -> None: | ||
| ms_per_step = 1000.0 * self.status_every_sec / (self.current_step - prev) | ||
| accept_rate = self.num_accepted / self.current_step | ||
| logger.info( | ||
@@ -190,3 +190,3 @@ "%d of %d steps. %.2f ms per step. Acceptance rate: %.2f", | ||
| ms_per_step, | ||
| accept_rate, | ||
| self.current_accept_rate, | ||
| ) | ||
@@ -261,2 +261,10 @@ prev = self.current_step | ||
| @property | ||
| def current_accept_rate(self) -> float: | ||
| """Return the current accept rate as a value between 0 and 1.""" | ||
| if self.current_step == 0: | ||
| # No steps have been taken yet. | ||
| return 0.0 | ||
| return self.num_accepted / self.current_step | ||
| def get_thermodynamic_quantities(self): | ||
@@ -271,2 +279,3 @@ """Compute thermodynamic quantities.""" | ||
| quantities["temperature"] = self.temperature | ||
| quantities["accept_rate"] = self.current_accept_rate | ||
| at_count = self.count_atoms() | ||
@@ -273,0 +282,0 @@ for key, value in at_count.items(): |
@@ -6,2 +6,3 @@ from typing import List, Tuple | ||
| from clease import Evaluate, ConvexHull | ||
| from clease.evaluate import supports_alpha_cv | ||
@@ -28,6 +29,6 @@ | ||
| plot_args = {} | ||
| X = evaluate.get_energy_predict() | ||
| Y = evaluate.e_dft | ||
| xlabel = plot_args.get("xlabel", "E_CE (eV/atom)") | ||
| ylabel = plot_args.get("ylabel", "E_DFT (eV/atom)") | ||
| X = evaluate.e_dft | ||
| Y = evaluate.get_energy_predict() | ||
| xlabel = plot_args.get("xlabel", "E_DFT (eV/atom)") | ||
| ylabel = plot_args.get("ylabel", "E_CE (eV/atom)") | ||
| title = plot_args.get("title", f"Fit using {len(evaluate.e_dft)} data points.") | ||
@@ -51,3 +52,3 @@ | ||
| cv = evaluate.get_cv_score() | ||
| cv = evaluate.get_cv_score() * 1000 | ||
| rmse = evaluate.rmse() * 1000 | ||
@@ -92,3 +93,5 @@ | ||
| def plot_fit_residual(evaluate: Evaluate, plot_args: dict = None) -> Figure: | ||
| def plot_fit_residual( | ||
| evaluate: Evaluate, plot_args: dict = None, interactive: bool = False | ||
| ) -> Figure: | ||
| """ | ||
@@ -112,3 +115,4 @@ Figure object subtracted (DFT) and predicted energies. | ||
| X = evaluate.e_dft | ||
| Y = evaluate.subtract_predict_dft() | ||
| Y = evaluate.get_energy_predict() - X # eV/atom | ||
| Y *= 1000 # meV/atom | ||
| xlabel = plot_args.get("xlabel", "#OCC") | ||
@@ -120,6 +124,7 @@ ylabel = plot_args.get("ylabel", r"$E_{DFT} - E_{pred}$ (meV/atom)") | ||
| fig, ax = plt.subplots(ncols=2, sharey=True, gridspec_kw=gridspec_kw) | ||
| ax[0].axhline(0, ls="--") | ||
| plot_line = ax[0].plot(X, Y, "v", mfc="none")[0] | ||
| ax[0].set_xlabel(r"$E_{DFT}$ (eV/atom)") | ||
| ax[0].set_ylabel(ylabel) | ||
| ax[0].set_title(title) | ||
| ax[0].set_ylabel(ylabel) | ||
| ax[0].axhline(0, ls="--") | ||
| ax[0].plot(X, Y, "v", mfc="none") | ||
@@ -133,2 +138,18 @@ hist, bin_edges = np.histogram(Y, bins=30) | ||
| if interactive: | ||
| # pylint: disable=import-outside-toplevel | ||
| from clease.interactive_plot import ShowStructureOnClick, AnnotatedAx | ||
| annotations = _make_annotations_plot_fit(evaluate) | ||
| # Construct the annotated axis objects. | ||
| annotated_ax = AnnotatedAx( | ||
| ax[0], | ||
| [plot_line], | ||
| annotations, | ||
| structure_names=[evaluate.names], | ||
| ) | ||
| # Attach interactivity to the fig object. | ||
| ShowStructureOnClick(fig, annotated_ax, evaluate.settings.db_name) | ||
| return fig | ||
@@ -202,2 +223,4 @@ | ||
| """ | ||
| if not supports_alpha_cv(evaluate.scheme): | ||
| raise ValueError(f"Scheme {evaluate.scheme!r} doesn't support alpha CV.") | ||
| if plot_args is None: | ||
@@ -204,0 +227,0 @@ plot_args = {} |
@@ -58,2 +58,6 @@ from typing import Tuple | ||
| """ | ||
| if len(self.alpha) != X.shape[1]: | ||
| msg = "Alpha has wrong number of dimensions. It should be equal to the number " | ||
| msg += f"of features {X.shape[1]}, but got {len(self.alpha)}." | ||
| raise ValueError(msg) | ||
| if self._constraint is None: | ||
@@ -60,0 +64,0 @@ return X.T.dot(X) + self.alpha, X.T.dot(y) |
@@ -10,3 +10,3 @@ from typing import List, Sequence, Dict, Union, Callable, Tuple | ||
| from clease.data_normalizer import DataNormalizer | ||
| from clease.tools import split_dataset | ||
| from clease.tools import split_dataset, get_size_from_cf_name | ||
| from .regression import LinearRegression | ||
@@ -69,2 +69,5 @@ from .constrained_ridge import ConstrainedRidge | ||
| is recommended to put normalize=False for such cases. | ||
| :param cf_names: List of strings, used to initialize the size and diameters | ||
| which will be used. | ||
| """ | ||
@@ -79,2 +82,3 @@ | ||
| normalize: bool = True, | ||
| cf_names: List[str] = None, | ||
| ) -> None: | ||
@@ -91,2 +95,5 @@ super().__init__() | ||
| self._constraint = None | ||
| if cf_names is not None: | ||
| self.diameters_from_names(cf_names) | ||
| self.sizes_from_names(cf_names) | ||
@@ -144,3 +151,3 @@ def add_constraint(self, A: np.ndarray, c: np.ndarray) -> None: | ||
| """ | ||
| self.sizes = [int(n[1]) for n in names] | ||
| self.sizes = [get_size_from_cf_name(n) for n in names] | ||
@@ -158,3 +165,4 @@ def diameters_from_names(self, names: List[str]) -> None: | ||
| for n in names: | ||
| if n[1] == "0" or n[1] == "1": | ||
| size = get_size_from_cf_name(n) | ||
| if size in {0, 1}: | ||
| diameters.append(0.0) | ||
@@ -161,0 +169,0 @@ else: |
@@ -11,3 +11,2 @@ """Definition of ClusterExpansionSettings Class. | ||
| from deprecated import deprecated | ||
| import numpy as np | ||
@@ -62,6 +61,2 @@ from ase import Atoms | ||
| max_cluster_size (int | None, optional): Deprecated. Specifies the maximum cluster | ||
| body size. Defaults to None. A DeprecationWarning will be raised, | ||
| if this value is not None. | ||
| max_cluster_dia (Sequence[float], optional): A list of int or float containing the | ||
@@ -107,5 +102,2 @@ maximum diameter of clusters (in Å). Defaults to ``(5., 5., 5.)``, i.e. | ||
| db_name: str = "clease.db", | ||
| # max_cluster_size is only here for deprecation purposes | ||
| # if it is not None, the user has manually specified a value | ||
| max_cluster_size=None, | ||
| max_cluster_dia: Sequence[float] = (5.0, 5.0, 5.0), | ||
@@ -150,5 +142,3 @@ include_background_atoms: bool = False, | ||
| self.max_cluster_dia = _format_max_cluster_dia( | ||
| max_cluster_dia, max_cluster_size=max_cluster_size | ||
| ) | ||
| self.max_cluster_dia = np.array(max_cluster_dia) | ||
@@ -691,3 +681,4 @@ self.basis_func_type = basis_func_type | ||
| # For compatibility, since it's no longer in KWARG_KEYS | ||
| kwargs["max_cluster_size"] = dct.pop("max_cluster_size") | ||
| # Avoid crashing. | ||
| dct.pop("max_cluster_size") | ||
@@ -753,50 +744,2 @@ settings = cls(*args, **kwargs) | ||
| def _format_max_cluster_dia(max_cluster_dia, max_cluster_size=None): | ||
| """Formatter of max_cluster_dia.""" | ||
| if max_cluster_size is None and not isinstance(max_cluster_dia, (int, float)): | ||
| # Assume max_cluster_dia is sequence[float], and user didn't specify any | ||
| # (now deprecated) max_cluster sizes. | ||
| return np.array(max_cluster_dia) | ||
| return _old_format_max_cluster_dia(max_cluster_dia, max_cluster_size) | ||
| def _old_format_max_cluster_dia(max_cluster_dia, max_cluster_size): | ||
| # User specified an old version of MCS and MCD | ||
| dep_msg = f""" | ||
| max_cluser_size should no longer be specfied explicitly, | ||
| and max_cluster_dia should no longer be an int or float. | ||
| Specify cluster sizes with max_cluster_dia as an array-like instead. | ||
| Got max_cluster_size '{max_cluster_size}' and max_cluster_dia '{max_cluster_dia}'. | ||
| Try instead to use max_cluster_dia as an array, e.g. max_cluster_dia=[5., 5., 5.] | ||
| for 2-, 3- and 4-body clusters of cutoff 5 Å. | ||
| """ | ||
| @deprecated(version="0.10.6", reason=dep_msg) | ||
| def _formatter(): | ||
| # max_cluster_dia is list or array | ||
| if isinstance(max_cluster_dia, (list, np.ndarray, tuple)): | ||
| # Length should be either max_cluster_size+1 or max_cluster_size-1 | ||
| mcd = np.array(max_cluster_dia, dtype=float) | ||
| if len(max_cluster_dia) == max_cluster_size + 1: | ||
| # Remove the first two entries, assume they are 0- and 1-body diameters | ||
| mcd = mcd[2:] | ||
| elif len(max_cluster_dia) == max_cluster_size - 1: | ||
| # Assume max_cluster_dia contains 2+ body clusters | ||
| pass | ||
| else: | ||
| raise ValueError("Invalid length for max_cluster_dia.") | ||
| elif isinstance(max_cluster_dia, (int, float)): | ||
| if max_cluster_size is None: | ||
| raise ValueError("Received no max_cluster_size, but a float for max_cluster_dia") | ||
| mcd = np.ones(max_cluster_size - 1, dtype=float) * max_cluster_dia | ||
| # Case for *None* or something else | ||
| else: | ||
| raise TypeError(f"max_cluster_dia is of wrong type, got: {type(max_cluster_dia)}") | ||
| return mcd.round(decimals=3) | ||
| return _formatter() | ||
| def _get_prim_cell_id_from_connection(prim_cell: Atoms, connection: Database) -> int: | ||
@@ -803,0 +746,0 @@ """Retrieve the primitive cell ID from a database connection""" |
@@ -54,2 +54,4 @@ .. testsetup:: | ||
| >>> from clease import Evaluate | ||
| >>> import clease.plot_post_process as pp | ||
| >>> import matplotlib.pyplot as plt | ||
| >>> | ||
@@ -64,7 +66,10 @@ >>> eva = Evaluate(settings=settings, scoring_scheme='k-fold', nsplits=10) | ||
| >>> eva.set_fitting_scheme(fitting_scheme='l1', alpha=alpha) | ||
| >>> eva.plot_fit(interactive=False) | ||
| >>> eva.fit() # Run the fit with these settings. | ||
| >>> | ||
| >>> fig = pp.plot_fit(eva) | ||
| >>> plt.show() | ||
| >>> | ||
| >>> # plot ECI values | ||
| >>> eva.plot_ECI() | ||
| >>> | ||
| >>> fig = pp.plot_eci(eva) | ||
| >>> plt.show() | ||
| >>> # save a dictionary containing cluster names and their ECIs | ||
@@ -71,0 +76,0 @@ >>> eva.save_eci(fname='eci_l1') |
@@ -7,2 +7,24 @@ .. _releasenotes: | ||
| 1.0.0 | ||
| ====== | ||
| * 21 June 2022 - CLEASE is no longer considered beta. | ||
| * :class:`~clease.evaluate.Evaluate` can now properly support fitting with custom LinearRegression | ||
| schemes, even if they don't support alpha cross-validation. | ||
| * :class:`~clease.evaluate.Evaluate` now required explicit calls to | ||
| :py:meth:`~clease.evaluate.Evaluate.fit`. Calls to :py:meth:`~clease.evaluate.Evaluate.get_eci` | ||
| and :py:meth:`~clease.evaluate.Evaluate.get_eci_dict` can no longer implicitly do fitting. | ||
| This un-does a change introduced in version 0.11.6. | ||
| * Added the :py:attr:`~clease.montecarlo.montecarlo.Montecarlo.current_accept_rate` property, | ||
| and export the current accept rate in the thermodynamic quantities dictionary under the | ||
| ``accept_rate`` key. | ||
| * Removed a series of deprecated things: | ||
| * Removed the ``clease.concentration`` module. | ||
| * Removed the ``clease.new_struct`` module. | ||
| * Removed old regression imports. Regression classes must now be imported from the | ||
| ``clease.regression`` module. | ||
| * Removed the ``clease.structure_generator`` module. | ||
| * Removed the ``max_cluster_size`` settings argument. | ||
| 0.11.6 | ||
@@ -9,0 +31,0 @@ ======= |
+2
-2
| Metadata-Version: 2.1 | ||
| Name: clease | ||
| Version: 0.11.6 | ||
| Version: 1.0.0 | ||
| Summary: CLuster Expansion in Atomistic Simulation Environment | ||
@@ -12,3 +12,3 @@ Home-page: https://gitlab.com/computationalmaterials/clease/ | ||
| Keywords: Cluster Expansion,Monte Carlo,Computational materials,Materials research | ||
| Classifier: Development Status :: 4 - Beta | ||
| Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) | ||
@@ -15,0 +15,0 @@ Classifier: Programming Language :: Python :: 3.7 |
+1
-1
@@ -20,3 +20,3 @@ [metadata] | ||
| classifiers = | ||
| Development Status :: 4 - Beta | ||
| Development Status :: 5 - Production/Stable | ||
| License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) | ||
@@ -23,0 +23,0 @@ Programming Language :: Python :: 3.7 |
| from deprecated import deprecated | ||
| from . import settings | ||
| __all__ = ("Concentration",) | ||
| @deprecated(version="0.10.0", reason="Use Concentration class from clease.settings instead") | ||
| class Concentration(settings.Concentration): | ||
| """Concentration class was moved to clease.settings""" |
| """Deprecated import of new_struct. | ||
| Module has been moved to clease.structgen, so | ||
| use clease.structgen.new_struct instead """ | ||
| from warnings import warn | ||
| from deprecated import deprecated | ||
| import numpy as np | ||
| from clease.structgen import new_struct | ||
| MSG = "Import {} from clease.structgen.new_struct instead" | ||
| DEP_VERSION = "0.10.2" # Deprecation version | ||
| MODULE_DEP_MSG = f""" | ||
| The clease.new_struct module has been moved as of version {DEP_VERSION}. | ||
| Please use the clease.structgen.new_struct module instead""" | ||
| # Print the message on import | ||
| warn(MODULE_DEP_MSG, np.VisibleDeprecationWarning) | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("NewStructures")) | ||
| class NewStructures(new_struct.NewStructures): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("MaxAttemptReachedError")) | ||
| class MaxAttemptReachedError(new_struct.MaxAttemptReachedError): | ||
| pass |
| """Module for defining deprecated regression class imports""" | ||
| # pylint: skip-file | ||
| from deprecated import deprecated | ||
| from clease import regression | ||
| __all__ = ( | ||
| "LinearRegression", | ||
| "Tikhonov", | ||
| "Lasso", | ||
| "BayesianCompressiveSensing", | ||
| "ConstrainedRidge", | ||
| "GAFit", | ||
| "GeneralizedRidgeRegression", | ||
| "PhysicalRidge", | ||
| "SequentialClusterRidge", | ||
| ) | ||
| MSG = "Import {} from clease.regression instead" | ||
| DEP_VERSION = "0.10.0" # Deprecation version | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("LinearRegression")) | ||
| class LinearRegression(regression.LinearRegression): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("Tikhonov")) | ||
| class Tikhonov(regression.Tikhonov): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("Lasso")) | ||
| class Lasso(regression.Lasso): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("BayesianCompressiveSensing")) | ||
| class BayesianCompressiveSensing(regression.BayesianCompressiveSensing): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("ConstrainedRidge")) | ||
| class ConstrainedRidge(regression.ConstrainedRidge): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("GAFit")) | ||
| class GAFit(regression.GAFit): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("GeneralizedRidgeRegression")) | ||
| class GeneralizedRidgeRegression(regression.GeneralizedRidgeRegression): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("PhysicalRidge")) | ||
| class PhysicalRidge(regression.PhysicalRidge): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("SequentialClusterRidge")) | ||
| class SequentialClusterRidge(regression.SequentialClusterRidge): | ||
| pass |
| """Deprecated import of structure_generator. | ||
| Module has been moved to clease.structgen, so | ||
| use clease.structgen.structure_generator instead""" | ||
| from warnings import warn | ||
| from deprecated import deprecated | ||
| import numpy as np | ||
| from clease.structgen import structure_generator | ||
| MSG = "Import {} from clease.structgen.structure_generator instead" | ||
| DEP_VERSION = "0.10.2" # Deprecation version | ||
| MODULE_DEP_MSG = f""" | ||
| The clease.new_struct module has been moved as of version {DEP_VERSION}. | ||
| Please use the clease.structgen.structure_generator module instead""" | ||
| # Print the message on import | ||
| warn(MODULE_DEP_MSG, np.VisibleDeprecationWarning) | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("StructureGenerator")) | ||
| class StructureGenerator(structure_generator.StructureGenerator): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("GSStructure")) | ||
| class GSStructure(structure_generator.GSStructure): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("MetropolisTrajectory")) | ||
| class MetropolisTrajectory(structure_generator.MetropolisTrajectory): | ||
| pass | ||
| @deprecated(version=DEP_VERSION, reason=MSG.format("ProbeStructure")) | ||
| class ProbeStructure(structure_generator.ProbeStructure): | ||
| pass |
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
1587639
-0.31%197
-1.99%16627
-0.82%