diff --git a/gext/descriptors.py b/gext/descriptors.py index 838446f5afbb73dd3eecf25691882dd8c05f02ae..728e353e1b187ba0336a58aca3b62e2ca4653c06 100644 --- a/gext/descriptors.py +++ b/gext/descriptors.py @@ -2,6 +2,7 @@ import numpy as np from scipy.spatial.distance import pdist +from scipy.linalg import eigh class BaseDescriptor: @@ -46,3 +47,17 @@ class FlattenMatrix(BaseDescriptor): def compute(self, matrix: np.ndarray) -> np.ndarray: """Compute the descriptor by flattening the matrix.""" return matrix.flatten() + +class Coreeignvector(BaseDescriptor): + + + supported_options = {} + + def __init__(self, **kwargs): + pass + + def compute(self, main_matrix: np.ndarray, basis_matrix: np.ndarray) -> np.ndarray: + """Compute the descriptor by flattening the matrix.""" + w,v=eigh(main_matrix, basis_matrix) + + return w,v diff --git a/gext/grassmann.py b/gext/grassmann.py index f99d06cc5a56fb1bc57e3d1de14d0292f0a9a6cd..0fc84196b2b8aa2ecd698620fd33c7ab8bdaeed9 100644 --- a/gext/grassmann.py +++ b/gext/grassmann.py @@ -17,6 +17,10 @@ def log(c: np.ndarray, c0: np.ndarray) -> np.ndarray: l = (np.identity(c.shape[0]) - c0 @ c0.T) @ cstar u, s, vt = np.linalg.svd(l, full_matrices=False) arcsin_s = np.diag(np.arcsin(s)) + print('s', np.isnan(c)) + + print('s', np.isnan(c0)) + return u @ arcsin_s @ vt def exp(gamma: np.ndarray, c0: np.ndarray) -> np.ndarray: diff --git a/gext/main.py b/gext/main.py index 4a16d517ca12ad82c4fbebfc534e3defa7a0436e..8cf5333717f5313c7364fd6aa90f1063023ccc9b 100644 --- a/gext/main.py +++ b/gext/main.py @@ -5,8 +5,9 @@ import numpy as np from . import grassmann from .fitting import LeastSquare, QuasiTimeReversible,DiffFitting -from .descriptors import Distance, Coulomb, FlattenMatrix +from .descriptors import Distance, Coulomb, FlattenMatrix, Coreeignvector from .buffer import CircularBuffer +from scipy.linalg import eigh class Extrapolator: @@ -22,6 +23,7 @@ class Extrapolator: "allow_partially_filled": True, "store_overlap": True, "tangent": "fixed", + "method": "lagrange_interpolation", } def __init__(self, nelectrons: int, nbasis: int, natoms: int, **kwargs): @@ -50,7 +52,7 @@ class Extrapolator: self.options = {} descriptor_options = {} fitting_options = {} - + method_options = {} # set specified options for key, value in kwargs.items(): if key in self.supported_options: @@ -59,6 +61,8 @@ class Extrapolator: descriptor_options[key[11:]] = value elif key.startswith("fitting_"): fitting_options[key[8:]] = value + #elif key.startswith("method_"): + #method_options[key[7:]] = value else: raise ValueError(f"Unsupported option: {key}") @@ -74,7 +78,7 @@ class Extrapolator: if isinstance(self.options["tangent"], int): if self.options["tangent"] < -self.options["nsteps"] or \ - self.options["tangent"] >= self.options["nsteps"]: + self.options["tangent"] > self.options["nsteps"]: raise ValueError("Unsupported tangent") else: if self.options["tangent"] != "fixed": @@ -86,9 +90,17 @@ class Extrapolator: self.descriptor_calculator = Coulomb() elif self.options["descriptor"] == "H_core": self.descriptor_calculator = FlattenMatrix() + elif self.options["descriptor"] == "H_core_and_overlap": + self.descriptor_calculator = FlattenMatrix() + elif self.options["descriptor"]=="overlap": + self.descriptor_calculator = FlattenMatrix() + elif self.options["descriptor"]=="core_eign_vector" or "log_core_eign_vector": + self.descriptor_calculator=Coreeignvector() + elif self.options["descriptor"]=="index": + pass else: raise ValueError("Unsupported descriptor") - + self.descriptor_calculator.set_options(**descriptor_options) if self.options["fitting"] == "leastsquare": @@ -115,7 +127,15 @@ class Extrapolator: self._set_tangent(coeff) # push the new data to the corresponding vectors - self.descriptors.push(self._compute_descriptor(descriptor_input)) + if self.options["descriptor"]=="core_eign_vector": + self.descriptors.push(self._compute_descriptor(descriptor_input, overlap).flatten()) + elif self.options["descriptor"]=="log_core_eign_vector": + self.descriptors.push(self._grassmann_log(self._normalize(self._compute_descriptor(descriptor_input, overlap), overlap)).flatten()) + elif self.options["descriptor"]=="index": + self.descriptors.push(descriptor_input) + else: + self.descriptors.push(self._compute_descriptor(descriptor_input)) + if self.options["store_overlap"]: self.overlaps.push(overlap) @@ -125,7 +145,7 @@ class Extrapolator: c_guess = self.guess_coefficients(coords, overlap) return c_guess @ c_guess.T - def guess_coefficients(self, descriptor_input: np.ndarray, overlap=None) -> np.ndarray: + def guess_coefficients(self, descriptor_input: np.ndarray, overlap) -> np.ndarray: """Get a new coefficient matrix to be used as a guess.""" # check if we have enough data points to perform an extrapolation @@ -145,27 +165,38 @@ class Extrapolator: # use the descriptors to find the fitting coefficients prev_descriptors= self.descriptors.get(n) - descriptor = self._compute_descriptor(descriptor_input) - fit_coefficients = self._fit(prev_descriptors, descriptor) - print(fit_coefficients) + if self.options["descriptor"]=="core_eign_vector": + descriptor =self._compute_descriptor(descriptor_input, overlap).flatten() + elif self.options["descriptor"]=="log_core_eign_vector": + descriptor =self._grassmann_log(self._normalize(self._compute_descriptor(descriptor_input, overlap), overlap)).flatten() + elif self.options["descriptor"]=="index": + descriptor =descriptor_input + else: + descriptor =self._compute_descriptor(descriptor_input) - # use the fitting coefficients and the previous gammas to - # extrapolate a new gamma coeffs=self.coeffs.get(n) - gammas=[] - for i in range(len(coeffs)): - gammas.append(self._grassmann_log(coeffs[i])) - - gamma = self.fitting_calculator.linear_combination(gammas, fit_coefficients) - if self.options["verbose"]: - fit_descriptor = self.fitting_calculator.linear_combination( - prev_descriptors, fit_coefficients) - print("error on descriptor:", \ - np.linalg.norm(fit_descriptor - descriptor, ord=np.inf)) - - # if the overlap is not given, use the coefficients to fit - # a new overlap + if self.options["method"]=="linear_interpolation": + fit_coefficients = self._fit(prev_descriptors, descriptor) + + # use the fitting coefficients and the previous gammas to + # extrapolate a new gamma + gammas=[] + for i in range(len(coeffs)): + gammas.append(self._grassmann_log(coeffs[i])) + + gamma = self.fitting_calculator.linear_combination(gammas, fit_coefficients) + + if self.options["verbose"]: + fit_descriptor = self.fitting_calculator.linear_combination( + prev_descriptors, fit_coefficients) + print("error on descriptor:", \ + np.linalg.norm(fit_descriptor - descriptor, ord=np.inf)) + + # use the overlap and gamma to find a new set of coefficients + c_guess = self._grassmann_exp(gamma) + # if the overlap is not given, use the coefficients to fit + # a new overlap if overlap is None: overlaps = self.overlaps.get(n) overlap = self.fitting_calculator.linear_combination(overlaps, fit_coefficients) @@ -173,10 +204,11 @@ class Extrapolator: else: inverse_sqrt_overlap = self._inverse_sqrt_overlap(overlap) - # use the overlap and gamma to find a new set of coefficients - c_guess = self._grassmann_exp(gamma) - return inverse_sqrt_overlap @ c_guess - + if self.options["method"]=="linear_interpolation": + return inverse_sqrt_overlap @ c_guess + else: + return inverse_sqrt_overlap @ self._grassmann_exp(self._lagrange_polynomial(prev_descriptors, coeffs, descriptor)) + def _get_tangent(self) -> np.ndarray: """Get the tangent point.""" if self.options["tangent"] == "fixed": @@ -219,11 +251,39 @@ class Extrapolator: q, s, vt = np.linalg.svd(overlap, full_matrices=False) return q @ np.diag(1.0/np.sqrt(s)) @ vt - def _compute_descriptor(self, descriptor_input) -> np.ndarray: + def _compute_descriptor(self, descriptor_input, overlap=None) -> np.ndarray: """Given an input compute the corresponding descriptor.""" - return self.descriptor_calculator.compute(descriptor_input) + if overlap is not None: + #w,v= self.descriptor_calculator.compute(descriptor_input, overlap) + w,v = eigh(descriptor_input, overlap) + print('check', np.max(descriptor_input@ v - overlap@ v@ np.diag(w))) + print('check2', np.max(self._normalize(v,overlap).T@ overlap@ self._normalize(v, overlap)-np.eye(len(self._normalize(v,overlap).T@ overlap@ self._normalize(v, overlap))))) + return self._normalize(v, overlap) + else: + return self.descriptor_calculator.compute(descriptor_input) def _fit(self, prev_descriptors, descriptor) -> np.ndarray: """Fit the current descriptor using previous descriptors and the specified fitting scheme.""" return self.fitting_calculator.fit(prev_descriptors, descriptor) + + def _lagrange_polynomial(self, prev_descriptors, coeffs, descriptor): + r = range(len(prev_descriptors)) + print('r', r) + print('descriptor', descriptor) + print('prev_descriptors', prev_descriptors) + t=np.zeros((len(prev_descriptors))) + t[0]=0 + for k in range(1,len(prev_descriptors)): + t[k] = np.linalg.norm(prev_descriptors[k]-prev_descriptors[k-1])+t[k-1] + a = [self._grassmann_log(coeffs[i]) / self._product(t[i] - t[j] for j in r if j != i) for i in r] + return sum(a[i] * self._product([ np.linalg.norm(descriptor-prev_descriptors[k])+t[k] - t[j] for j in r if j != i]) for i in r) + if self.options['descriptor']=="index": + a = [self._grassmann_log(coeffs[i]) / self._product(prev_descriptors[i] - prev_descriptors[j] for j in r if j != i) for i in r] + return sum(a[i] * self._product([(descriptor - prev_descriptors[j]) for j in r if j != i]) for i in r) + + def _product(self, a): + p = 1. + for i in a: + p *= i + return p