# -*- coding: utf-8 -*-## This file is subject to the terms and conditions defined in# file 'LICENSE.txt', which is part of this source code package.#fromtypingimportOptionalimportnumpyasnpfromMuscat.TypesimportMuscatFloat,MuscatIndexfromMuscat.Helpers.LoggerimportWarning
[docs]defTruncatedSVDSymLower(matrix,epsilon:Optional[MuscatFloat]=None,nbModes:Optional[MuscatIndex]=None):""" Computes a truncated singular value decomposition of a symmetric definite matrix in scipy.sparse.csr format. Only the lower triangular part needs to be defined Parameters ---------- matrix : scipy.sparse.csr the input matrix epsilon : float the truncation tolerance, determining the number of keeps eigenvalues nbModes : int the number of keeps eigenvalues Returns ------- np.ndarray kept eigenvalues, of size (numberOfEigenvalues) np.ndarray kept eigenvectors, of size (numberOfEigenvalues, numberOfSnapshots) """ifepsilon!=NoneandnbModes!=None:# pragma: no coverraiseRuntimeError("cannot specify both epsilon and nbModes")eigenValues,eigenVectors=np.linalg.eigh(matrix,UPLO="L")idx=eigenValues.argsort()[::-1]eigenValues=eigenValues[idx]eigenVectors=eigenVectors[:,idx]ifnbModesisNone:ifepsilon==None:nbModes=matrix.shape[0]else:nbModes=MuscatIndex(0)bound=(epsilon**2)*eigenValues[0]foreineigenValues:ife>bound:nbModes+=1id_max2:MuscatIndex=MuscatIndex(0)bound=(1-epsilon**2)*np.sum(eigenValues)temp=0foreineigenValues:temp+=eiftemp<bound:id_max2+=1# pragma: no covernbModes=max(nbModes,id_max2)ifnbModes>matrix.shape[0]:Warning("nbModes taken to max possible value of "+str(matrix.shape[0])+" instead of provided value "+str(nbModes))nbModes=matrix.shape[0]index=np.where(eigenValues<0)iflen(eigenValues[index])>0:ifindex[0][0]<nbModes:# print(nbModes, index[0][0])Warning("removing numerical noise from eigenvalues, nbModes is set to "+str(index[0][0])+" instead of "+str(nbModes))nbModes=index[0][0]returneigenValues[0:nbModes],eigenVectors[:,0:nbModes]