diff --git a/rrompy/parameter/parameter_sampling/sparse_grid/sparse_grid_sampler.py b/rrompy/parameter/parameter_sampling/sparse_grid/sparse_grid_sampler.py index f130eec..6e9fda0 100644 --- a/rrompy/parameter/parameter_sampling/sparse_grid/sparse_grid_sampler.py +++ b/rrompy/parameter/parameter_sampling/sparse_grid/sparse_grid_sampler.py @@ -1,112 +1,111 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from copy import deepcopy as copy from rrompy.parameter import checkParameterList from rrompy.parameter.parameter_sampling.generic_sampler import GenericSampler from rrompy.utilities.poly_fitting.piecewise_linear import (sparsekinds, sparseMap) from rrompy.utilities.base.types import List, paramList -from rrompy.utilities.numerical import lowDiscrepancy from rrompy.utilities.exception_manager import RROMPyException __all__ = ['SparseGridSampler'] class SparseGridSampler(GenericSampler): """Generator of sparse grid sample points.""" def __init__(self, lims:paramList, kind : str = "UNIFORM", scalingExp : List[float] = None): super().__init__(lims = lims, scalingExp = scalingExp) self.kind = kind self.reset() def __str__(self) -> str: return "{}[{}_{}]_{}".format(self.name(), self.lims[0], self.lims[1], self.kind) @property def npoints(self): """Number of points.""" return len(self.points) @property def kind(self): """Value of kind.""" return self._kind @kind.setter def kind(self, kind): if kind.upper() not in [sk.split("_")[2] for sk in sparsekinds]: raise RROMPyException("Generator kind not recognized.") self._kind = kind.upper() def reset(self): centerEff = .5 * (self.lims[0] ** self.scalingExp + self.lims[1] ** self.scalingExp) self.points = checkParameterList(centerEff ** (1. / self.scalingExp), self.npar)[0] self.depth = np.zeros((1, self.npar), dtype = int) self.deltadepth = np.zeros(1, dtype = int) def refine(self, active : List[int] = None) -> List[int]: if active is None: active = np.arange(self.npoints) limsX = [self.lims(j) ** self.scalingExp[j] for j in range(self.npar)] newIdxs = [] for act in active: point, dpt = self.points[act], self.depth[act] exhausted = False while not exhausted: ddp = self.deltadepth[act] for jdelta in range(self.npar): for signdelta in [-1., 1.]: Pointj = sparseMap( point[jdelta] ** self.scalingExp[jdelta], limsX[jdelta], self.kind, False) if dpt[jdelta] == 1: Centerj = sparseMap( self.points(0, jdelta) ** self.scalingExp[jdelta], limsX[jdelta], self.kind, False) gradj = Pointj - Centerj if signdelta * gradj > 0: continue pointj = copy(point) Pointj = Pointj + .5 ** (dpt[jdelta] + ddp) * signdelta pointj[jdelta] = sparseMap(Pointj, limsX[jdelta], self.kind) ** (1. / self.scalingExp[jdelta]) dist = np.sum(np.abs(self.points.data - pointj.reshape(1, -1)), axis = 1) samePt = np.where(np.isclose(dist, 0.))[0] if len(samePt) > 0: if samePt[0] in newIdxs: exhausted = True continue newIdxs += [self.npoints] self.points.append(pointj) self.depth = np.append(self.depth, [dpt], 0) self.depth[-1, jdelta] += 1 + ddp self.deltadepth = np.append(self.deltadepth, [0]) exhausted = True self.deltadepth[act] += 1 return newIdxs def generatePoints(self, n:int, reorder = None) -> paramList: if self.npoints > n: self.reset() idx = np.arange(self.npoints) while self.npoints < n: idx = self.refine(idx) return self.points diff --git a/rrompy/reduction_methods/base/reduced_basis_utils.py b/rrompy/reduction_methods/base/reduced_basis_utils.py index 2d1b8cd..cac7a3d 100644 --- a/rrompy/reduction_methods/base/reduced_basis_utils.py +++ b/rrompy/reduction_methods/base/reduced_basis_utils.py @@ -1,69 +1,66 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np -from rrompy.utilities.base.types import (Np1D, Np2D, Tuple, List, ListAny, - sampList) +from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, sampList from rrompy.utilities.numerical import dot -from rrompy.utilities.numerical.hash_derivative import ( - hashIdxToDerivative as hashI) from rrompy.utilities.exception_manager import RROMPyAssert from rrompy.sampling import sampleList __all__ = ['projectAffineDecomposition'] def projectAffineDecomposition(As:List[Np2D], bs:List[Np1D], pMat:sampList, ARBsOld : List[Np2D] = None, bRBsOld : List[Np1D] = None, pMatOld : sampList = None)\ -> Tuple[List[Np2D], List[Np1D]]: """Project affine decomposition of linear system onto basis.""" RROMPyAssert((ARBsOld is None, bRBsOld is None), (pMatOld is None, pMatOld is None), "Old affine projected terms") if isinstance(pMat, (sampleList,)): pMat = pMat.data pMatC = pMat.conj() ARBs = [None] * len(As) bRBs = [None] * len(bs) if pMatOld is None: for j in range(max(len(As), len(bs))): if j < len(As): ARBs[j] = dot(dot(As[j], pMat).T, pMatC).T if j < len(bs): bRBs[j] = dot(pMatC.T, bs[j]) else: RROMPyAssert((len(ARBsOld), len(bRBsOld)), (len(As), len(bs)), "Old affine projected terms") if isinstance(pMatOld, (sampleList,)): pMatOld = pMatOld.data pMatOldC = pMatOld.conj() Sold = pMatOld.shape[1] Snew = pMat.shape[1] for j in range(max(len(As), len(bs))): if j < len(As): ARBs[j] = np.empty((Sold + Snew, Sold + Snew), dtype = ARBsOld[j].dtype) ARBs[j][: Sold, : Sold] = ARBsOld[j] ARBs[j][: Sold, Sold :] = dot(dot(As[j], pMat).T, pMatOldC).T ARBs[j][Sold :, : Sold] = dot(dot(As[j], pMatOld).T, pMatC).T ARBs[j][Sold :, Sold :] = dot(dot(As[j], pMat).T, pMatC).T if j < len(bs): bRBs[j] = np.empty((Sold + Snew), dtype = bRBsOld[j].dtype) bRBs[j][: Sold] = bRBsOld[j] bRBs[j][Sold :] = dot(pMatC.T, bs[j]) return ARBs, bRBs diff --git a/rrompy/utilities/numerical/compress_matrix.py b/rrompy/utilities/numerical/compress_matrix.py index a68a285..76fe175 100644 --- a/rrompy/utilities/numerical/compress_matrix.py +++ b/rrompy/utilities/numerical/compress_matrix.py @@ -1,39 +1,38 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.utilities.numerical.tensor_la import dot from rrompy.utilities.base.types import Np2D, Tuple, HFEng -from rrompy.utilities.exception_manager import RROMPyWarning, RROMPyException __all__ = ["compressMatrix"] def compressMatrix(A:Np2D, tol : float = 0., HFEngine : HFEng = None, is_state : bool = True) -> Tuple[Np2D, Np2D, float]: if HFEngine is None or not is_state: U, s, _ = np.linalg.svd(A.T.conj().dot(A)) else: U, s, _ = np.linalg.svd(HFEngine.innerProduct(A, A, is_state = is_state)) remove = np.where(s < tol * s[0])[0] ncut = len(s) if len(remove) == 0 else remove[0] sums = np.sum(s) s = s[: ncut] ** .5 R = (U[:, : ncut].conj() * s).T U = dot(A, U[:, : ncut] * s ** -1.) return U, R, 1. - np.linalg.norm(s) / sums diff --git a/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_affine.py b/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_affine.py index 0045e23..fd1c49c 100644 --- a/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_affine.py +++ b/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_affine.py @@ -1,96 +1,95 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np -from scipy.special import binom import scipy.sparse as sp from rrompy.utilities.base.types import (Np1D, Np2D, List, ListAny, Tuple, paramVal) from rrompy.utilities.numerical import dot, solve from rrompy.utilities.numerical.nonlinear_eigenproblem import eigNonlinearDense from rrompy.utilities.exception_manager import RROMPyException from rrompy.parameter import checkParameter __all__ = ['heaviside2affine', 'affine2heaviside'] def heaviside2affine(c:Np2D, poles:Np1D, mu : paramVal = [], basis : str = "MONOMIAL_HEAVISIDE", sparse : bool = False) \ -> Tuple[Np2D, List[Np2D], List[Np1D]]: mu = checkParameter(mu, 1)(0, 0) n, d = len(poles), len(c) - len(poles) basisN = basis.split("_")[0] if basisN not in ["MONOMIAL", "CHEBYSHEV", "LEGENDRE"]: raise RROMPyException("Polynomial basis not recognized.") if sparse: A0 = sp.spdiags([np.concatenate((- mu - poles, np.ones(d)))], [0], n + d, n + d) A1 = sp.spdiags([np.concatenate((np.ones(n), np.zeros(d)))], [0], n + d, n + d) else: A0 = np.diag(np.concatenate((mu - poles, np.ones(d)))) A1 = np.diag(np.concatenate((np.ones(n), np.zeros(d)))) As = [A0, A1] bs = np.zeros((d, n + d), dtype = poles.dtype) bs[0, :] = 1. if d > 0: bs[0, n + 1 :] = 0. if d > 1: bs[1, n + 1] = 1. for j in range(2, d): if basisN == "MONOMIAL": bs[j, n + j] = 1. else: alpha = - 1. if basisN == "CHEBYSHEV" else 1. / j - 1. bs[:, n + j] = alpha * bs[:, n + j - 2] bs[1 :, n + j] += (1. - alpha) * bs[: -1, n + j - 1] bs = list(bs) return c.reshape(c.shape[0], -1).T, As, bs def affine2heaviside(As:ListAny, bs:ListAny, jSupp : int = 1) -> Tuple[Np2D, Np1D, str]: if jSupp != 1 and not (isinstance(jSupp, (int,)) and jSupp.upper() == "COMPANION"): raise RROMPyException(("Affine to heaviside conversion does not allow " "nonlinear eigenproblem support outside first " "block row.")) N = len(As) M = len(bs) n = As[0].shape[0] if N == 1: poles = np.empty(0, dtype = np.complex) Q = np.eye(n) else: basis = "MONOMIAL_HEAVISIDE" poles, P, Q = eigNonlinearDense(As, jSupp = jSupp, return_inverse = True) P = P[- n :, :] Q = Q[:, : n] bEffs = np.array([dot(Q, solve(As[-1], b, np.linalg.solve, {})) for b in bs]) if N == 1: c = bEffs else: c = np.zeros((len(poles) + M - 1, As[0].shape[1]), dtype = np.complex) for l, pl in enumerate(poles): for i in range(M): c[l, :] = pl ** i * bEffs[i, l] * P[:, l] for l in range(M - 1): for i in range(l + 1, M): c[len(poles) + l, :] = dot(P, poles ** (i- 1 - l) * bEffs[i, :]) return c, poles, basis diff --git a/rrompy/utilities/poly_fitting/heaviside/vander.py b/rrompy/utilities/poly_fitting/heaviside/vander.py index 3d0a2f7..d762104 100644 --- a/rrompy/utilities/poly_fitting/heaviside/vander.py +++ b/rrompy/utilities/poly_fitting/heaviside/vander.py @@ -1,87 +1,86 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.utilities.poly_fitting.polynomial.vander import (polyvander as pvP, polyvanderTotal as pvTP) -from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, paramList +from rrompy.utilities.base.types import Np1D, Np2D, List, paramList from rrompy.parameter import checkParameterList from rrompy.utilities.exception_manager import RROMPyException __all__ = ['heavisidevander', 'polyvander', 'polyvanderTotal'] def heavisidevander(x:paramList, poles:Np1D, reorder : List[int] = None) -> Np2D: """Compute Heaviside-Vandermonde matrix.""" x = checkParameterList(x, 1)[0] x_un, idx_un = x.unique(return_inverse = True) nx = len(x) if len(x_un) < nx: raise RROMPyException("Sample points must be distinct.") del x_un x = x.data.flatten() if reorder is not None: x = x[reorder] poles = poles.flatten() Van = np.empty((len(x), len(poles)), dtype = np.complex) for j in range(len(x)): Van[j, :] = 1. / (x[j] - poles) return Van def polyvander(x:paramList, poles:Np1D, degs : List[int] = None, basis : str = "MONOMIAL_HEAVISIDE", derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, scl : Np1D = None) -> Np2D: """ Compute full Hermite-Vandermonde matrix with specified derivative directions. """ if derIdxs is not None and np.sum(np.sum(derIdxs)) > 0: raise RROMPyException(("Cannot take derivatives of heaviside " "function.")) basisp = basis.split("_")[0] VanH = heavisidevander(x, poles, reorder = reorder) if degs is None or np.sum(degs) < 0: VanP = np.empty((len(x), 0)) else: VanP = pvP(x, degs, basisp, derIdxs = derIdxs, reorder = reorder, scl = scl) return np.block([[VanH, VanP]]) def polyvanderTotal(x:paramList, poles:Np1D, deg : int = None, basis : str = "MONOMIAL_HEAVISIDE", derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, scl : Np1D = None) -> Np2D: """ Compute full total degree Hermite-Vandermonde matrix with specified derivative directions. """ if derIdxs is not None and np.sum(np.sum(derIdxs)) > 0: raise RROMPyException(("Cannot take derivatives of radial basis " "function.")) basisp = basis.split("_")[0] VanR = heavisidevander(x, poles, reorder = reorder) if deg is None or deg < 0: VanP = np.empty((len(x), 0)) - derIdxs, ordIdxs = np.zeros(0, dtype = int), np.zeros(0, dtype = int) + derIdxs, _ = np.zeros(0, dtype = int), np.zeros(0, dtype = int) else: VanP = pvTP(x, deg, basisp, derIdxs = derIdxs, reorder = reorder, scl = scl) - ordIdxsEff = np.concatenate((np.arange(len(VanR)), ordIdxs + len(VanR))) return np.block([[VanR, VanP], [VanP.T.conj(), np.zeros(tuple([VanP.shape[1]] * 2))]])