diff --git a/VERSION b/VERSION
index c0943d3..7208c21 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.3
\ No newline at end of file
+2.4
\ No newline at end of file
diff --git a/examples/5_anisotropic_square/anisotropic_square.py b/examples/5_anisotropic_square/anisotropic_square.py
index b32777a..d40d0ab 100644
--- a/examples/5_anisotropic_square/anisotropic_square.py
+++ b/examples/5_anisotropic_square/anisotropic_square.py
@@ -1,114 +1,114 @@
import numpy as np
import matplotlib.pyplot as plt
from itertools import product
from anisotropic_square_engine import (AnisotropicSquareEngine as engine,
AnisotropicSquareEnginePoles as plsEx)
from rrompy.sampling import SamplingEngineStandard as SES
from rrompy.reduction_methods import (NearestNeighbor as NN,
RationalInterpolantGreedyPivotedGreedy as RIGPG)
-from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
-from rrompy.parameter import localSparseGrid as LSG
+from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
+ SparseGridSampler as SGS)
zs, Ls = [10., 50.], [.2, 1.2]
z0, L0, n = np.mean(zs), np.mean(Ls), 50
murange = [[zs[0], Ls[0]], [zs[-1], Ls[-1]]]
np.random.seed(4020)
mu = [zs[0] + np.random.rand() * (zs[-1] - zs[0]),
Ls[0] + np.random.rand() * (Ls[-1] - Ls[0])]
solver = engine(z0, L0, n)
fighandles = []
params = {"POD": True, "nTestPoints": 100, "greedyTol": 1e-4, "S": 3,
"polybasisMarginal": "MONOMIAL_WENDLAND",
"polybasis": "LEGENDRE", 'samplerPivot':QS(zs, "UNIFORM"),
'trainSetGenerator':QS(zs, "UNIFORM"),
'errorEstimatorKind':"LOOK_AHEAD_RES",
"MMarginal": 2, "SMarginal": 3,
- "greedyTolMarginal": 1e-2, "samplerMarginalGrid":LSG(Ls),
+ "greedyTolMarginal": 1e-2, "samplerMarginalGrid":SGS(Ls),
"radialDirectionalWeightsMarginal": [2.], "matchingWeight": 1.}
for tol, shared in product([1., 3.], [1., 0.]):
print("Testing cutoff tolerance {} with shared ratio {}.".format(tol,
shared))
params['cutOffTolerance'] = tol
params['cutOffSharedRatio'] = shared
approx = RIGPG([0], solver, mu0 = [z0, L0], approx_state = True,
approxParameters = params, verbosity = 5)
approx.setupApprox("LAST")
print("--- Approximant ---")
approx.plotApprox(mu, name = 'u_app')
approx.plotHF(mu, name = 'u_HF')
approx.plotErr(mu, name = 'err_app')
approx.plotRes(mu, name = 'res_app')
normErr = approx.normErr(mu)[0]
normSol = approx.normHF(mu)[0]
normRes = approx.normRes(mu)[0]
normRHS = approx.normRHS(mu)[0]
print("SolNorm:\t{:.5e}\nErr_app: \t{:.5e}\nErrRel_app:\t{:.5e}".format(
normSol, normErr, normErr / normSol))
print("RHSNorm:\t{:.5e}\nRes_app: \t{:.5e}\nResRel_app:\t{:.5e}".format(
normRHS, normRes, normRes / normRHS))
print("--- Closest snapshot ---")
eng = SES(solver, verbosity = 0)
eng.nsamples = approx.samplingEngine.nsamplesCoalesced
eng.mus = approx.samplingEngine.musCoalesced
eng.samples = approx.samplingEngine.samples_fullCoalesced
paramsNN = {'S':eng.nsamples, 'POD':False,
'sampler':QS(murange, "UNIFORM")}
approxNN = NN(solver, mu0 = [z0, L0], approx_state = True,
approxParameters = paramsNN, verbosity = 0)
approxNN.setSamples(eng)
approxNN.plotApprox(mu, name = 'u_close')
approxNN.plotHF(mu, name = 'u_HF')
approxNN.plotErr(mu, name = 'err_close')
approxNN.plotRes(mu, name = 'res_close')
normErr = approxNN.normErr(mu)[0]
normSol = approxNN.normHF(mu)[0]
normRes = approxNN.normRes(mu)[0]
normRHS = approxNN.normRHS(mu)[0]
print("SolNorm:\t{:.5e}\nErr_close:\t{:.5e}\nErrRel_close:\t{:.5e}".format(
normSol, normErr, normErr / normSol))
print("RHSNorm:\t{:.5e}\nRes_close:\t{:.5e}\nResRel_close:\t{:.5e}".format(
normRHS, normRes, normRes / normRHS))
verb = approx.verbosity
approx.verbosity = 0
tspace = np.linspace(Ls[0], Ls[-1], 100)
for j, t in enumerate(tspace):
plsE = plsEx(t, 0., zs[-1])
pls = approx.getPoles([None, t])
pls[np.abs(np.imag(pls)) > 1e-5] = np.nan
if j == 0:
polesE = np.empty((len(tspace), len(plsE)))
poles = np.empty((len(tspace), len(pls)))
polesE[:] = np.nan
if len(plsE) > polesE.shape[1]:
nanR = np.empty((len(tspace), len(plsE) - polesE.shape[1]))
nanR[:] = np.nan
polesE = np.hstack((polesE, nanR))
polesE[j, : len(plsE)] = np.real(plsE)
poles[j] = np.real(pls)
approx.verbosity = verb
fighandles += [plt.figure(figsize = (17, 5))]
ax1 = fighandles[-1].add_subplot(1, 2, 1)
ax2 = fighandles[-1].add_subplot(1, 2, 2)
ax1.plot(poles, tspace)
ax1.set_ylim(Ls)
ax1.set_xlabel('mu_1')
ax1.set_ylabel('mu_2')
ax1.grid()
ax2.plot(polesE, tspace, 'k-.', linewidth = 1)
ax2.plot(poles, tspace)
for mm in approx.musMarginal:
ax2.plot(zs, [mm[0, 0]] * 2, 'k--', linewidth = 1)
ax2.set_xlim(zs)
ax2.set_ylim(Ls)
ax2.set_xlabel('mu_1')
ax2.set_ylabel('mu_2')
ax2.grid()
plt.show()
print("\n")
diff --git a/examples/7_MHD/mhd_engine.py b/examples/7_MHD/mhd_engine.py
index bac6d15..d56f9d3 100644
--- a/examples/7_MHD/mhd_engine.py
+++ b/examples/7_MHD/mhd_engine.py
@@ -1,22 +1,23 @@
import numpy as np
import scipy.io as scio
import scipy.sparse as sp
-from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBase
+from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBaseTensorized
-class MHDEngine(LinearAffineEngine, NumpyEngineBase):
+class MHDEngine(LinearAffineEngine, NumpyEngineBaseTensorized):
"""
From Matrix Market: //math.nist.gov/MatrixMarket/data/NEP/mhd/mhd.html
"""
def __init__(self, ncol : int = 1, seed : int = 31415):
super().__init__()
self.npar = 1
self.nAs, self.nbs = 2, 1
+ self.nports = ncol
A0 = sp.csr_matrix(scio.mmread("mhd4800a.mtx"), dtype = np.complex)
self.As = [A0, - scio.mmread("mhd4800b.mtx").tocsr()]
np.random.seed(seed)
B = np.random.randn(self.As[0].shape[0], ncol)
self.bs[0] = (B / np.linalg.norm(B, axis = 0)).flatten()
def getPolesExact(self, k:int, sigma:np.complex):
return sp.linalg.eigs(self.As[0], k, - self.As[1], sigma,
return_eigenvectors = False)
diff --git a/rrompy/hfengines/base/__init__.py b/rrompy/hfengines/base/__init__.py
index 12da73d..a0d375b 100644
--- a/rrompy/hfengines/base/__init__.py
+++ b/rrompy/hfengines/base/__init__.py
@@ -1,39 +1,42 @@
# 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 .
#
from .boundary_conditions import BoundaryConditions
-from .fenics_engine_base import FenicsEngineBase
+from .fenics_engine_base import FenicsEngineBase, FenicsEngineBaseTensorized
from .hfengine_base import HFEngineBase
from .linear_affine_engine import LinearAffineEngine, checkIfAffine
from .marginal_proxy_engine import MarginalProxyEngine
-from .numpy_engine_base import NumpyEngineBase
-from .vector_fenics_engine_base import VectorFenicsEngineBase
+from .numpy_engine_base import NumpyEngineBase, NumpyEngineBaseTensorized
+from .vector_fenics_engine_base import VectorFenicsEngineBase, VectorFenicsEngineBaseTensorized
__all__ = [
'BoundaryConditions',
'FenicsEngineBase',
+ 'FenicsEngineBaseTensorized',
'HFEngineBase',
'LinearAffineEngine',
'checkIfAffine',
'MarginalProxyEngine',
'NumpyEngineBase',
- 'VectorFenicsEngineBase'
+ 'NumpyEngineBaseTensorized',
+ 'VectorFenicsEngineBase',
+ 'VectorFenicsEngineBaseTensorized'
]
diff --git a/rrompy/hfengines/base/fenics_engine_base.py b/rrompy/hfengines/base/fenics_engine_base.py
index 0691f33..f435c51 100644
--- a/rrompy/hfengines/base/fenics_engine_base.py
+++ b/rrompy/hfengines/base/fenics_engine_base.py
@@ -1,402 +1,492 @@
# 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 .
#
from os import path, mkdir
import fenics as fen
import numpy as np
from matplotlib import pyplot as plt
-from .numpy_engine_base import NumpyEngineBase
+from .numpy_engine_base import NumpyEngineBase, checknports
from rrompy.utilities.base.types import (Np1D, strLst, FenFunc, Tuple, List,
FigHandle)
from rrompy.utilities.base.data_structures import purgeList, getNewFilename
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.solver.fenics import L2NormMatrix, fenplot, interp_project
from .boundary_conditions import BoundaryConditions
from rrompy.utilities.exception_manager import RROMPyException
-__all__ = ['FenicsEngineBase']
+__all__ = ['FenicsEngineBase', 'FenicsEngineBaseTensorized']
+
+def plottingBaseFen(u, fig, V, what, nRows, subplotidx, warping, name,
+ colorbar, fenplotArgs):
+ if 'ABS' in what:
+ uAb = fen.Function(V)
+ uAb.vector().set_local(np.abs(u))
+ subplotidx = subplotidx + 1
+ ax = fig.add_subplot(nRows, len(what), subplotidx)
+ p = fenplot(uAb, warping = warping, title = "|{}|".format(name),
+ **fenplotArgs)
+ if colorbar: fig.colorbar(p, ax = ax)
+ if 'PHASE' in what:
+ uPh = fen.Function(V)
+ uPh.vector().set_local(np.angle(u))
+ subplotidx = subplotidx + 1
+ ax = fig.add_subplot(nRows, len(what), subplotidx)
+ p = fenplot(uPh, warping = warping, title = "phase({})".format(name),
+ **fenplotArgs)
+ if colorbar: fig.colorbar(p, ax = ax)
+ if 'REAL' in what:
+ uRe = fen.Function(V)
+ uRe.vector().set_local(np.real(u))
+ subplotidx = subplotidx + 1
+ ax = fig.add_subplot(nRows, len(what), subplotidx)
+ p = fenplot(uRe, warping = warping, title = "Re({})".format(name),
+ **fenplotArgs)
+ if colorbar: fig.colorbar(p, ax = ax)
+ if 'IMAG' in what:
+ uIm = fen.Function(V)
+ uIm.vector().set_local(np.imag(u))
+ subplotidx = subplotidx + 1
+ ax = fig.add_subplot(nRows, len(what), subplotidx)
+ p = fenplot(uIm, warping = warping, title = "Im({})".format(name),
+ **fenplotArgs)
+ if colorbar: fig.colorbar(p, ax = ax)
class FenicsEngineBase(NumpyEngineBase):
"""Generic solver for parametric fenics problems."""
def __init__(self, degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(verbosity = verbosity, timestamp = timestamp)
self.BCManager = BoundaryConditions("Dirichlet")
self.V = fen.FunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1)
self.degree_threshold = degree_threshold
@property
def V(self):
"""Value of V."""
return self._V
@V.setter
def V(self, V):
if not type(V).__name__ == 'FunctionSpace':
raise RROMPyException("V type not recognized.")
self.dsToBeSet = True
self._V = V
self.u = fen.TrialFunction(V)
self.v = fen.TestFunction(V)
@property
def spacedim(self):
if hasattr(self, "_V"): return self.V.dim()
return super().spacedim
def autoSetDS(self):
"""Set FEniCS boundary measure based on boundary function handles."""
if self.dsToBeSet:
vbMng(self, "INIT", "Initializing boundary measures.", 20)
mesh = self.V.mesh()
NB = self.NeumannBoundary
RB = self.RobinBoundary
boundary_markers = fen.MeshFunction("size_t", mesh,
mesh.topology().dim() - 1)
NB.mark(boundary_markers, 0)
RB.mark(boundary_markers, 1)
self.ds = fen.Measure("ds", domain = mesh,
subdomain_data = boundary_markers)
self.dsToBeSet = False
vbMng(self, "DEL", "Done assembling boundary measures.", 20)
def buildEnergyNormForm(self):
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
vbMng(self, "INIT", "Assembling energy matrix.", 20)
self.energyNormMatrix = L2NormMatrix(self.V)
vbMng(self, "DEL", "Done assembling energy matrix.", 20)
def buildEnergyNormDualForm(self):
"""
Build sparse matrix (in CSR format) representative of dual scalar
product without duality.
"""
if not hasattr(self, "energyNormMatrix"):
self.buildEnergyNormForm()
self.energyNormDualMatrix = self.energyNormMatrix
def liftDirichletData(self) -> Np1D:
"""Lift Dirichlet datum."""
if not hasattr(self, "_liftedDirichletDatum"):
liftRe = interp_project(self.DirichletDatum[0], self.V)
liftIm = interp_project(self.DirichletDatum[1], self.V)
self._liftedDirichletDatum = (np.array(liftRe.vector())
+ 1.j * np.array(liftIm.vector()))
return self._liftedDirichletDatum
def reduceQuadratureDegree(self, fun:FenFunc, name:str):
"""Check whether to reduce compiler parameters to degree threshold."""
if not np.isinf(self.degree_threshold):
from ufl.algorithms.estimate_degrees import (
estimate_total_polynomial_degree as ETPD)
try:
deg = ETPD(fun)
except:
return False
if deg > self.degree_threshold:
vbMng(self, "MAIN",
("Reducing quadrature degree from {} to {} for "
"{}.").format(deg, self.degree_threshold, name), 15)
return True
return False
def iterReduceQuadratureDegree(self, funsNames:List[Tuple[FenFunc, str]]):
"""
Iterate reduceQuadratureDegree over list and define reduce compiler
parameters.
"""
if funsNames is not None:
for fun, name in funsNames:
if self.reduceQuadratureDegree(fun, name):
return {"quadrature_degree" : self.degree_threshold}
return {}
def plot(self, u:Np1D, warping : List[callable] = None,
is_state : bool = False, name : str = "u", save : str = None,
what : strLst = 'all', forceNewFile : bool = True,
saveFormat : str = "eps", saveDPI : int = 100, show : bool = True,
colorMap : str = "jet", fenplotArgs : dict = {},
**figspecs) -> Tuple[FigHandle, str]:
"""
Do some nice plots of the complex-valued function with given dofs.
Args:
u: numpy complex array with function dofs.
warping(optional): Domain warping functions.
is_state(optional): whether given u is value before multiplication
by c. Defaults to False.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
forceNewFile(optional): Whether to create new output file.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
show(optional): Whether to show figure. Defaults to True.
colorMap(optional): Pyplot colormap. Defaults to 'jet'.
fenplotArgs(optional): Optional arguments for fenplot.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
Returns:
Output filename and figure handle.
"""
if not is_state and not self.isCEye:
return super().plot(u, warping, False, name, save, what,
forceNewFile, saveFormat, saveDPI, show,
colorMap, fenplotArgs, **figspecs)
if isinstance(what, (str,)):
if what.upper() == 'ALL':
what = ['ABS', 'PHASE', 'REAL', 'IMAG']
else:
what = [what]
what = purgeList(what, ['ABS', 'PHASE', 'REAL', 'IMAG'],
listname = self.name() + ".what", baselevel = 1)
if len(what) == 0: return
if 'figsize' not in figspecs.keys():
figspecs['figsize'] = plt.figaspect(1. / len(what))
- subplotidx = 0
fig = plt.figure(**figspecs)
plt.set_cmap(colorMap)
- if 'ABS' in what:
- uAb = fen.Function(self.V)
- uAb.vector().set_local(np.abs(u))
- subplotidx = subplotidx + 1
- ax = fig.add_subplot(1, len(what), subplotidx)
- p = fenplot(uAb, warping = warping, title = "|{0}|".format(name),
- **fenplotArgs)
- if self.V.mesh().geometric_dimension() > 1:
- fig.colorbar(p, ax = ax)
- if 'PHASE' in what:
- uPh = fen.Function(self.V)
- uPh.vector().set_local(np.angle(u))
- subplotidx = subplotidx + 1
- ax = fig.add_subplot(1, len(what), subplotidx)
- p = fenplot(uPh, warping = warping,
- title = "phase({0})".format(name), **fenplotArgs)
- if self.V.mesh().geometric_dimension() > 1:
- fig.colorbar(p, ax = ax)
- if 'REAL' in what:
- uRe = fen.Function(self.V)
- uRe.vector().set_local(np.real(u))
- subplotidx = subplotidx + 1
- ax = fig.add_subplot(1, len(what), subplotidx)
- p = fenplot(uRe, warping = warping, title = "Re({0})".format(name),
- **fenplotArgs)
- if self.V.mesh().geometric_dimension() > 1:
- fig.colorbar(p, ax = ax)
- if 'IMAG' in what:
- uIm = fen.Function(self.V)
- uIm.vector().set_local(np.imag(u))
- subplotidx = subplotidx + 1
- ax = fig.add_subplot(1, len(what), subplotidx)
- p = fenplot(uIm, warping = warping, title = "Im({0})".format(name),
- **fenplotArgs)
- if self.V.mesh().geometric_dimension() > 1:
- fig.colorbar(p, ax = ax)
+ plottingBaseFen(u, fig, self.V, what, 1, 0, warping, name,
+ self.V.mesh().geometric_dimension() > 1, fenplotArgs)
plt.tight_layout()
if save is not None:
save = save.strip()
if forceNewFile:
fileOut = getNewFilename("{}_fig_".format(save), saveFormat)
else:
fileOut = "{}_fig.{}".format(save, saveFormat)
fig.savefig(fileOut, format = saveFormat, dpi = saveDPI)
else: fileOut = None
if show: plt.show()
if fileOut is None: return fig
return fig, fileOut
def plotmesh(self, warping : List[callable] = None, name : str = "Mesh",
save : str = None, forceNewFile : bool = True,
saveFormat : str = "eps", saveDPI : int = 100,
show : bool = True, fenplotArgs : dict = {},
**figspecs) -> Tuple[FigHandle, str]:
"""
Do a nice plot of the mesh.
Args:
u: numpy complex array with function dofs.
warping(optional): Domain warping functions.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
forceNewFile(optional): Whether to create new output file.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
show(optional): Whether to show figure. Defaults to True.
fenplotArgs(optional): Optional arguments for fenplot.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
Returns:
Output filename and figure handle.
"""
fig = plt.figure(**figspecs)
fenplot(self.V.mesh(), warping = warping, **fenplotArgs)
plt.tight_layout()
if save is not None:
save = save.strip()
if forceNewFile:
fileOut = getNewFilename("{}_msh_".format(save), saveFormat)
else:
fileOut = "{}_msh.{}".format(save, saveFormat)
fig.savefig(fileOut, format = saveFormat, dpi = saveDPI)
else: fileOut = None
if show: plt.show()
if fileOut is None: return fig
return fig, fileOut
def outParaview(self, u:Np1D, warping : List[callable] = None,
is_state : bool = False, name : str = "u",
filename : str = "out", time : float = 0.,
what : strLst = 'all', forceNewFile : bool = True,
folder : bool = False, filePW = None) -> str:
"""
Output complex-valued function with given dofs to ParaView file.
Args:
u: numpy complex array with function dofs.
warping(optional): Domain warping functions.
is_state(optional): whether given u is value before multiplication
by c. Defaults to False.
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
time(optional): Timestamp.
what(optional): Which plots to do. If list, can contain 'MESH',
'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard
'ALL'. Defaults to 'ALL'.
forceNewFile(optional): Whether to create new output file.
folder(optional): Whether to create an additional folder layer.
filePW(optional): Fenics File entity (for time series).
Returns:
Output filename.
"""
if not is_state and not self.isCEye:
raise RROMPyException(("Cannot output to Paraview non-state "
"object."))
if isinstance(what, (str,)):
if what.upper() == 'ALL':
what = ['MESH', 'ABS', 'PHASE', 'REAL', 'IMAG']
else:
what = [what]
what = purgeList(what, ['MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'],
listname = self.name() + ".what", baselevel = 1)
if len(what) == 0: return
if filePW is None:
if folder:
if not path.exists(filename + "/"):
mkdir(filename)
idxpath = filename.rfind("/")
filename += "/" + filename[idxpath + 1 :]
if forceNewFile:
filePW = fen.File(getNewFilename(filename, "pvd"))
else:
filePW = fen.File("{}.pvd".format(filename))
if warping is not None:
fen.ALE.move(self.V.mesh(),
interp_project(warping[0], self.V.mesh()))
if what == ['MESH']:
filePW << (self.V.mesh(), time)
if 'ABS' in what:
uAb = fen.Function(self.V, name = "{}_ABS".format(name))
uAb.vector().set_local(np.abs(u))
filePW << (uAb, time)
if 'PHASE' in what:
uPh = fen.Function(self.V, name = "{}_PHASE".format(name))
uPh.vector().set_local(np.angle(u))
filePW << (uPh, time)
if 'REAL' in what:
uRe = fen.Function(self.V, name = "{}_REAL".format(name))
uRe.vector().set_local(np.real(u))
filePW << (uRe, time)
if 'IMAG' in what:
uIm = fen.Function(self.V, name = "{}_IMAG".format(name))
uIm.vector().set_local(np.imag(u))
filePW << (uIm, time)
if warping is not None:
fen.ALE.move(self.V.mesh(),
interp_project(warping[1], self.V.mesh()))
return filePW
def outParaviewTimeDomain(self, u:Np1D, omega:float,
warping : List[callable] = None,
is_state : bool = False,
timeFinal : float = None,
periodResolution : int = 20, name : str = "u",
filename : str = "out",
forceNewFile : bool = True,
folder : bool = False) -> str:
"""
Output complex-valued function with given dofs to ParaView file,
converted to time domain.
Args:
u: numpy complex array with function dofs.
omega: frequency.
warping(optional): Domain warping functions.
is_state(optional): whether given u is value before multiplication
by c. Defaults to False.
timeFinal(optional): final time of simulation.
periodResolution(optional): number of time steps per period.
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
forceNewFile(optional): Whether to create new output file.
folder(optional): Whether to create an additional folder layer.
Returns:
Output filename.
"""
if not is_state and not self.isCEye:
raise RROMPyException(("Cannot output to Paraview non-state "
"object."))
if folder:
if not path.exists(filename + "/"):
mkdir(filename)
idxpath = filename.rfind("/")
filename += "/" + filename[idxpath + 1 :]
if forceNewFile:
filePW = fen.File(getNewFilename(filename, "pvd"))
else:
filePW = fen.File("{}.pvd".format(filename))
omega = np.abs(omega)
t = 0.
dt = 2. * np.pi / omega / periodResolution
if timeFinal is None: timeFinal = 2. * np.pi / omega - dt
if warping is not None:
fen.ALE.move(self.V.mesh(),
interp_project(warping[0], self.V.mesh()))
for j in range(int(np.ceil(timeFinal / dt)) + 1):
ut = fen.Function(self.V, name = name)
ut.vector().set_local(np.real(u) * np.cos(omega * t)
+ np.imag(u) * np.sin(omega * t))
filePW << (ut, t)
t += dt
if warping is not None:
fen.ALE.move(self.V.mesh(),
interp_project(warping[1], self.V.mesh()))
return filePW
+
+class FenicsEngineBaseTensorized(FenicsEngineBase):
+ """The number of tensorized dimensions should be assigned to nports."""
+
+ def plot(self, u:Np1D, warping : List[callable] = None,
+ is_state : bool = False, name : str = "u", save : str = None,
+ what : strLst = 'all', forceNewFile : bool = True,
+ saveFormat : str = "eps", saveDPI : int = 100, show : bool = True,
+ colorMap : str = "jet", fenplotArgs : dict = {},
+ **figspecs) -> Tuple[FigHandle, str]:
+ """
+ Do some nice plots of the complex-valued function with given dofs.
+
+ Args:
+ u: numpy complex array with function dofs.
+ warping(optional): Domain warping functions.
+ is_state(optional): whether given u is value before multiplication
+ by c. Defaults to False.
+ name(optional): Name to be shown as title of the plots. Defaults to
+ 'u'.
+ save(optional): Where to save plot(s). Defaults to None, i.e. no
+ saving.
+ what(optional): Which plots to do. If list, can contain 'ABS',
+ 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
+ Defaults to 'ALL'.
+ forceNewFile(optional): Whether to create new output file.
+ saveFormat(optional): Format for saved plot(s). Defaults to "eps".
+ saveDPI(optional): DPI for saved plot(s). Defaults to 100.
+ show(optional): Whether to show figure. Defaults to True.
+ colorMap(optional): Pyplot colormap. Defaults to 'jet'.
+ fenplotArgs(optional): Optional arguments for fenplot.
+ figspecs(optional key args): Optional arguments for matplotlib
+ figure creation.
+
+ Returns:
+ Output filename and figure handle.
+ """
+ nP = checknports(self)
+ if not is_state and not self.isCEye:
+ return super().plot(u.reshape(-1, nP), warping, False, name, save,
+ what, forceNewFile, saveFormat, saveDPI, show,
+ colorMap, fenplotArgs, **figspecs)
+ if isinstance(what, (str,)):
+ if what.upper() == 'ALL':
+ what = ['ABS', 'PHASE', 'REAL', 'IMAG']
+ else:
+ what = [what]
+ what = purgeList(what, ['ABS', 'PHASE', 'REAL', 'IMAG'],
+ listname = self.name() + ".what", baselevel = 1)
+ if len(what) == 0: return
+ if 'figsize' not in figspecs.keys():
+ figspecs['figsize'] = plt.figaspect(1. / len(what))
+ figspecs['figsize'][1] *= nP
+
+ fig = plt.figure(**figspecs)
+ plt.set_cmap(colorMap)
+ for i in range(nP):
+ plottingBaseFen(u[i :: nP], fig, self.V, what, nP, i * len(what),
+ warping, "{}_port{}".format(name, i + 1),
+ self.V.mesh().geometric_dimension() > 1,
+ fenplotArgs)
+ plt.tight_layout()
+ if save is not None:
+ save = save.strip()
+ if forceNewFile:
+ fileOut = getNewFilename("{}_fig_".format(save), saveFormat)
+ else:
+ fileOut = "{}_fig.{}".format(save, saveFormat)
+ fig.savefig(fileOut, format = saveFormat, dpi = saveDPI)
+ else: fileOut = None
+ if show: plt.show()
+ if fileOut is None: return fig
+ return fig, fileOut
+
+ def outParaview(self, u:Np1D, *args, **kwargs) -> List[str]:
+ nP = checknports(self)
+ filesOut = [None] * nP
+ for j in range(nP):
+ filesOut[j] = super().outParaview(u[j :: nP], *args, **kwargs)
+ if filesOut[0] is None: return None
+ return filesOut
+
+ def outParaviewTimeDomain(self, u:Np1D, *args, **kwargs) -> List[str]:
+ nP = checknports(self)
+ filesOut = [None] * nP
+ for j in range(nP):
+ filesOut[j] = super().outParaviewTimeDomain(u[j :: nP],
+ *args, **kwargs)
+ if filesOut[0] is None: return None
+ return filesOut
diff --git a/rrompy/hfengines/base/hfengine_base.py b/rrompy/hfengines/base/hfengine_base.py
index b7ccf2d..b786246 100644
--- a/rrompy/hfengines/base/hfengine_base.py
+++ b/rrompy/hfengines/base/hfengine_base.py
@@ -1,279 +1,270 @@
# 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 .
#
from abc import abstractmethod
import numpy as np
import scipy.sparse as scsp
from numbers import Number
from copy import copy as softcopy
from rrompy.utilities.base.decorators import nonaffine_construct
from rrompy.utilities.base.types import (Np1D, Np2D, List, DictAny, paramVal,
paramList, sampList)
from rrompy.utilities.numerical import solve as tsolve, dot, customPInv
from rrompy.utilities.numerical.rayleigh_quotient_iteration import (
rayleighQuotientIteration)
from rrompy.utilities.exception_manager import RROMPyAssert
from rrompy.parameter import checkParameter, checkParameterList
from rrompy.solver.linear_solver import setupSolver
__all__ = ['HFEngineBase']
class HFEngineBase:
"""Generic solver for parametric problems."""
def __init__(self, verbosity : int = 10, timestamp : bool = True):
self.verbosity = verbosity
self.timestamp = timestamp
self.setSolver("SPSOLVE", {"use_umfpack" : False})
self.npar = 0
self._C = None
self.outputNormMatrix = 1.
def name(self) -> str:
return self.__class__.__name__
def __str__(self) -> str:
return self.name()
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def __dir_base__(self):
return [x for x in self.__dir__() if x[:2] != "__"]
def __deepcopy__(self, memo):
return softcopy(self)
@property
def npar(self):
"""Value of npar."""
return self._npar
@npar.setter
def npar(self, npar):
nparOld = self._npar if hasattr(self, "_npar") else -1
if npar != nparOld:
self.rescalingExp = [1.] * npar
self._npar = npar
@property
def spacedim(self):
return 1
def checkParameter(self, mu:paramVal):
return checkParameter(mu, self.npar)
def checkParameterList(self, mu:paramList):
return checkParameterList(mu, self.npar)
def buildEnergyNormForm(self):
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
self.energyNormMatrix = 1.
def buildEnergyNormDualForm(self):
"""
Build sparse matrix (in CSR format) representative of dual scalar
product without duality.
"""
self.energyNormDualMatrix = 1.
def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False,
dual : bool = False, is_state : bool = True) -> Np2D:
"""Scalar product."""
if is_state or self.isCEye:
if dual:
if not hasattr(self, "energyNormDualMatrix"):
self.buildEnergyNormDualForm()
energyMat = self.energyNormDualMatrix
else:
if not hasattr(self, "energyNormMatrix"):
self.buildEnergyNormForm()
energyMat = self.energyNormMatrix
else:
energyMat = self.outputNormMatrix
if not isinstance(u, (np.ndarray,)): u = u.data
if not isinstance(v, (np.ndarray,)): v = v.data
if onlyDiag:
return np.sum(dot(energyMat, u) * v.conj(), axis = 0)
return dot(dot(energyMat, u).T, v.conj()).T
def norm(self, u:Np2D, dual : bool = False,
is_state : bool = True) -> Np1D:
return np.abs(self.innerProduct(u, u, onlyDiag = True, dual = dual,
is_state = is_state)) ** .5
def baselineA(self):
"""Return 0 of shape consistent with operator of linear system."""
- d = self.As[0].shape[0]
+ if (hasattr(self, "As") and hasattr(self.As, "__len__")
+ and self.As[0] is not None):
+ d = self.As[0].shape[0]
+ else:
+ d = self.spacedim
return scsp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(d + 1)),
shape = (d, d), dtype = np.complex)
def baselineb(self):
"""Return 0 of shape consistent with RHS of linear system."""
return np.zeros(self.spacedim, dtype = np.complex)
@nonaffine_construct
@abstractmethod
def A(self, mu : paramVal = [], der : List[int] = 0) -> Np2D:
"""
Assemble terms of operator of linear system and return it (or its
derivative) at a given parameter.
"""
return
@nonaffine_construct
@abstractmethod
def b(self, mu : paramVal = [], der : List[int] = 0) -> Np1D:
"""
Assemble terms of RHS of linear system and return it (or its
derivative) at a given parameter.
"""
return
@property
def C(self):
"""Value of C."""
if self._C is None: self._C = 1.
return self._C
@property
def isCEye(self):
return isinstance(self.C, Number)
def applyC(self, u:sampList):
"""Apply LHS of linear system."""
return dot(self.C, u)
def applyCpInv(self, u:sampList):
"""Apply pseudoinverse of LHS of linear system."""
return dot(customPInv(self.C), u)
- _isStateShiftZero = True
- def stateShift(self, mu : paramVal = []) -> Np1D:
- return np.zeros((self.spacedim, len(mu)))
-
- _isOutputShiftZero = True
- def outputShift(self, mu : paramVal = []) -> Np1D:
- return self.applyC(self.stateShift(mu))
-
def setSolver(self, solverType:str, solverArgs : DictAny = {}):
"""Choose solver type and parameters."""
self._solver, self._solverArgs = setupSolver(solverType, solverArgs)
def solve(self, mu : paramList = [], RHS : sampList = None,
return_state : bool = False, verbose : bool = False) -> sampList:
"""
Find solution of linear system.
Args:
mu: parameter value.
RHS: RHS of linear system. If None, defaults to that of parametric
system. Defaults to None.
return_state: whether to return state before multiplication by c.
Defaults to False.
verbose: whether to notify for each solution computed. Defaults to
False.
"""
from rrompy.sampling import sampleList, emptySampleList
if mu == []: mu = self.mu0
mu = self.checkParameterList(mu)[0]
if self.npar == 0: mu.reset((1, self.npar), mu.dtype)
if len(mu) == 0: return emptySampleList()
if RHS is None: RHS = [self.b(m) for m in mu]
RHS = sampleList(RHS)
mult = 0 if len(RHS) == 1 else 1
RROMPyAssert(mult * (len(mu) - 1) + 1, len(RHS), "Sample size")
for j in range(len(mu)):
u = tsolve(self.A(mu[j]), RHS[mult * j], self._solver,
self._solverArgs)
if j == 0:
sol = emptySampleList()
sol.reset((len(u), len(mu)), dtype = u.dtype)
sol[j] = u
if verbose and len(mu) > 1:
print("." * (j % 5 != 4) + "*" * (j % 5 == 4), end = "")
- if return_state:
- sol = sampleList(sol - self.stateShift(mu))
- else:
- sol = sampleList(self.applyC(sol.data) - self.outputShift(mu))
+ if not return_state:
+ sol = sampleList(self.applyC(sol.data))
if verbose and len(mu) > 1: print()
return sol
def residual(self, mu : paramList = [], u : sampList = None,
post_c : bool = True) -> sampList:
"""
Find residual of linear system for given approximate solution.
Args:
mu: parameter value.
u: numpy complex array with function dofs. If None, set to 0.
post_c: whether to post-process using c. Defaults to True.
"""
from rrompy.sampling import sampleList, emptySampleList
if mu == []: mu = self.mu0
mu = self.checkParameterList(mu)[0]
if self.npar == 0: mu.reset((1, self.npar), mu.dtype)
if len(mu) == 0: return emptySampleList()
- v = sampleList(self.stateShift(mu))
+ v = sampleList(np.zeros((self.spacedim, len(mu))))
if u is not None: v = v + sampleList(u)
for j in range(len(mu)):
r = self.b(mu[j]) - dot(self.A(mu[j]), v[j])
if post_c:
if j == 0: res = np.empty((len(r), len(mu)), dtype = r.dtype)
res[:, j] = r
else:
if j == 0:
res = emptySampleList()
res.reset((len(r), len(mu)), dtype = r.dtype)
res[j] = r
if post_c: res = sampleList(self.applyC(res))
return res
def stabilityFactor(self, mu : paramList = [], u : sampList = None,
nIterP : int = 10, nIterR : int = 10) -> sampList:
"""
Find stability factor of matrix of linear system using iterative
inverse power iteration- and Rayleigh quotient-based procedure.
Args:
mu: parameter values.
u: numpy complex arrays with function dofs.
nIterP: number of iterations of power method.
nIterR: number of iterations of Rayleigh quotient method.
"""
from rrompy.sampling import sampleList
if mu == []: mu = self.mu0
mu = self.checkParameterList(mu)[0]
if self.npar == 0: mu.reset((1, self.npar), mu.dtype)
u = sampleList(u)
- solShift = self.stateShift(mu)
- if len(u) == len(mu):
- u = u + solShift
- else:
- u = sampleList(solShift) + np.tile(u.data, (1, len(mu)))
+ if len(u) != len(mu):
+ u = sampleList(np.tile(u.data.reshape(-1, 1), (1, len(mu))))
stabFact = np.empty(len(mu), dtype = float)
if not hasattr(self, "energyNormMatrix"):
self.buildEnergyNormForm()
for j in range(len(mu)):
stabFact[j] = rayleighQuotientIteration(self.A(mu[j]), u[j],
self.energyNormMatrix,
0., nIterP, nIterR,
self._solver,
self._solverArgs)
return stabFact
diff --git a/rrompy/hfengines/base/linear_affine_engine.py b/rrompy/hfengines/base/linear_affine_engine.py
index 27b9895..61030f5 100644
--- a/rrompy/hfengines/base/linear_affine_engine.py
+++ b/rrompy/hfengines/base/linear_affine_engine.py
@@ -1,198 +1,199 @@
# 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 .
#
from abc import abstractmethod
import numpy as np
import scipy.sparse as scsp
from copy import deepcopy as copy
+from .hfengine_base import HFEngineBase
from rrompy.utilities.base.decorators import affine_construct
from rrompy.utilities.base.types import (Np1D, Np2D, List, ListAny, TupleAny,
paramVal)
from rrompy.utilities.expression import (expressionEvaluator, createMonomial,
createMonomialList)
from rrompy.utilities.numerical.hash_derivative import (
hashDerivativeToIdx as hashD)
from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['LinearAffineEngine', 'checkIfAffine']
-class LinearAffineEngine:
+class LinearAffineEngine(HFEngineBase):
"""Generic solver for affine parametric problems."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._affinePoly = True
self.nAs, self.nbs = 1, 1
@property
def affinePoly(self):
return self._affinePoly
@property
def nAs(self):
"""Value of nAs."""
return self._nAs
@nAs.setter
def nAs(self, nAs):
nAsOld = self._nAs if hasattr(self, "_nAs") else -1
if nAs != nAsOld:
self._nAs = nAs
self.resetAs()
@property
def nbs(self):
"""Value of nbs."""
return self._nbs
@nbs.setter
def nbs(self, nbs):
nbsOld = self._nbs if hasattr(self, "_nbs") else -1
if nbs != nbsOld:
self._nbs = nbs
self.resetbs()
@property
def spacedim(self):
if (hasattr(self, "bs") and hasattr(self.bs, "__len__")
and self.bs[0] is not None):
return len(self.bs[0])
return super().spacedim
def getMonomialSingleWeight(self, deg:List[int]):
return createMonomial(deg, True)
def getMonomialWeights(self, n:int):
return createMonomialList(n, self.npar, True)
def setAs(self, As:List[Np2D]):
"""Assign terms of operator of linear system."""
if len(As) != self.nAs:
raise RROMPyException(("Expected number {} of terms of As not "
"matching given list length {}.").format(self.nAs,
len(As)))
self.As = [copy(A) for A in As]
def setthAs(self, thAs:List[List[TupleAny]]):
"""Assign terms of operator of linear system."""
if len(thAs) != self.nAs:
raise RROMPyException(("Expected number {} of terms of thAs not "
"matching given list length {}.").format(self.nAs,
len(thAs)))
self.thAs = copy(thAs)
def setbs(self, bs:List[Np1D]):
"""Assign terms of RHS of linear system."""
if len(bs) != self.nbs:
raise RROMPyException(("Expected number {} of terms of bs not "
"matching given list length {}.").format(self.nbs,
len(bs)))
self.bs = [copy(b) for b in bs]
def setthbs(self, thbs:List[List[TupleAny]]):
"""Assign terms of RHS of linear system."""
if len(thbs) != self.nbs:
raise RROMPyException(("Expected number {} of terms of thbs not "
"matching given list length {}.").format(self.nbs,
len(thbs)))
self.thbs = copy(thbs)
def resetAs(self):
"""Reset (derivatives of) operator of linear system."""
if hasattr(self, "_nAs"):
self.setAs([None] * self.nAs)
self.setthAs([None] * self.nAs)
def resetbs(self):
"""Reset (derivatives of) RHS of linear system."""
if hasattr(self, "_nbs"):
self.setbs([None] * self.nbs)
self.setthbs([None] * self.nbs)
def _assembleObject(self, mu:paramVal, objs:ListAny, th:ListAny,
derI:int) -> Np2D:
"""Assemble (derivative of) object from list of derivatives."""
mu = self.checkParameter(mu)
rExp = self.rescalingExp
muE = mu ** rExp
obj = None
for j in range(len(objs)):
if len(th[j]) <= derI and th[j][-1] is not None:
raise RROMPyException(("Cannot assemble operator. Non enough "
"derivatives of theta provided."))
if len(th[j]) > derI and th[j][derI] is not None:
expr = expressionEvaluator(th[j][derI], muE)
if hasattr(expr, "__len__"):
if len(expr) > 1:
raise RROMPyException(("Size mismatch in value of "
"theta function. Only scalars "
"allowed."))
expr = expr[0]
if obj is None:
obj = expr * objs[j]
else:
obj = obj + expr * objs[j]
return obj
@abstractmethod
def buildA(self):
"""Build terms of operator of linear system."""
if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs)
if self.As[0] is None:
self.As[0] = scsp.eye(self.spacedim, dtype = np.complex,
format = "csr")
for j in range(1, self.nAs):
if self.As[j] is None: self.As[j] = self.baselineA()
@affine_construct
def A(self, mu : paramVal = [], der : List[int] = 0) -> Np2D:
"""
Assemble terms of operator of linear system and return it (or its
derivative) at a given parameter.
"""
derI = hashD(der) if hasattr(der, "__len__") else der
if derI < 0 or derI > self.nAs - 1: return self.baselineA()
self.buildA()
assembledA = self._assembleObject(mu, self.As, self.thAs, derI)
if assembledA is None: return self.baselineA()
return assembledA
@abstractmethod
def buildb(self):
"""Build terms of RHS of linear system."""
if self.thbs[0] is None: self.thbs = self.getMonomialWeights(self.nbs)
for j in range(self.nbs):
if self.bs[j] is None: self.bs[j] = self.baselineb()
@affine_construct
def b(self, mu : paramVal = [], der : List[int] = 0) -> Np1D:
"""
Assemble terms of RHS of linear system and return it (or its
derivative) at a given parameter.
"""
derI = hashD(der) if hasattr(der, "__len__") else der
if derI < 0 or derI > self.nbs - 1: return self.baselineb()
self.buildb()
assembledb = self._assembleObject(mu, self.bs, self.thbs, derI)
if assembledb is None: return self.baselineb()
return assembledb
def checkIfAffine(engine, msg : str = "apply method", noA : bool = False):
msg = ("Cannot {} because of non-affine parametric dependence{}. Consider "
"using DEIM to define a new engine.").format(msg, " of RHS" * noA)
if (not (hasattr(engine.b, "is_affine") and engine.b.is_affine)
or not (noA or (hasattr(engine.A, "is_affine") and engine.A.is_affine))):
raise RROMPyException(msg)
diff --git a/rrompy/hfengines/base/marginal_proxy_engine.py b/rrompy/hfengines/base/marginal_proxy_engine.py
index bc4c1cc..16f867c 100644
--- a/rrompy/hfengines/base/marginal_proxy_engine.py
+++ b/rrompy/hfengines/base/marginal_proxy_engine.py
@@ -1,172 +1,165 @@
# 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 inspect
import numpy as np
from copy import copy as softcopy
from rrompy.utilities.base.types import Np1D, paramVal, paramList, HFEng
from rrompy.utilities.base import freepar as fp
from rrompy.utilities.base.decorators import (affine_construct,
nonaffine_construct)
from rrompy.utilities.exception_manager import RROMPyException
from rrompy.parameter import checkParameter, checkParameterList
__all__ = ['MarginalProxyEngine']
def MarginalProxyEngine(HFEngine:HFEng, marginalized:Np1D):
Aaff = hasattr(HFEngine.A, "is_affine") and HFEngine.A.is_affine
baff = hasattr(HFEngine.b, "is_affine") and HFEngine.b.is_affine
if Aaff:
if baff:
return MarginalProxyEngineAffineAb(HFEngine, marginalized)
return MarginalProxyEngineAffineA(HFEngine, marginalized)
if baff:
return MarginalProxyEngineAffineb(HFEngine, marginalized)
return MarginalProxyEngineNonAffine(HFEngine, marginalized)
class MarginalProxyEngineNonAffine:
"""
Marginalized should prescribe fixed value for the marginalized parameters
and leave freepar/None elsewhere.
"""
_allowedMuDependencies = ["A", "b", "checkParameter", "checkParameterList",
"_assembleObject", "solve", "residual",
- "stabilityFactor", "stateShift", "outputShift"]
+ "stabilityFactor"]
def __init__(self, HFEngine:HFEng, marginalized:Np1D):
self.baseHF = HFEngine
self.marg = marginalized
for name in HFEngine.__dir_base__():
att = getattr(HFEngine, name)
if inspect.ismethod(att):
attargs = inspect.getfullargspec(att).args
if "mu" not in attargs:
setattr(self.__class__, name, getattr(HFEngine, name))
else:
if name not in self._allowedMuDependencies:
raise RROMPyException(("Function {} depends on mu "
"and was not accounted for. "
"Must override.").format(name))
@property
def affinePoly(self):
return self.nparFixed == 0 and self.baseHF.affinePoly
@property
def freeLocations(self):
return [x for x in range(self.baseHF.npar) if self.marg[x] == fp]
@property
def fixedLocations(self):
return [x for x in range(self.baseHF.npar) if self.marg[x] != fp]
@property
def _freeLocationsInsert(self):
return np.cumsum([m == fp for m in self.marg])[self.fixedLocations]
@property
def muFixed(self):
muF = checkParameter([m for m in self.marg if m != fp])
if self.baseHF.npar - self.nparFree > 0: muF = muF[0]
return muF
@property
def nparFree(self):
"""Value of nparFree."""
return len(self.freeLocations)
@property
def nparFixed(self):
"""Value of nparFixed."""
return len(self.fixedLocations)
def name(self) -> str:
return "{}-proxy for {}".format(self.freeLocations, self.baseHF.name())
def __str__(self) -> str:
return self.name()
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def __dir_base__(self):
return [x for x in self.__dir__() if x[:2] != "__"]
def __deepcopy__(self, memo):
return softcopy(self)
def completeMu(self, mu:paramVal) -> paramVal:
mu = checkParameter(mu, self.nparFree)
return np.insert(mu.data, self._freeLocationsInsert, self.muFixed,
axis = 1)
def completeMuList(self, mu:paramList) -> paramList:
mu = checkParameterList(mu, self.nparFree)[0]
return np.insert(mu.data, self._freeLocationsInsert, self.muFixed,
axis = 1)
@nonaffine_construct
def A(self, mu : paramVal = [], *args, **kwargs):
return self.baseHF.A(self.completeMu(mu), *args, **kwargs)
@nonaffine_construct
def b(self, mu : paramVal = [], *args, **kwargs):
return self.baseHF.b(self.completeMu(mu), *args, **kwargs)
def checkParameter(self, mu : paramVal = [], *args, **kwargs):
return self.baseHF.checkParameter(self.completeMu(mu), *args, **kwargs)
def checkParameterList(self, mu : paramList = [], *args, **kwargs):
return self.baseHF.checkParameterList(self.completeMuList(mu),
*args, **kwargs)
def _assembleObject(self, mu : paramVal = [], *args, **kwargs):
return self.baseHF._assembleObject(self.completeMu(mu),
*args, **kwargs)
- def stateShift(self, mu : paramVal = [], *args, **kwargs):
- return self.baseHF.stateShift(self.completeMuList(mu), *args, **kwargs)
-
- def outputShift(self, mu : paramVal = [], *args, **kwargs):
- return self.baseHF.outputShift(self.completeMuList(mu),
- *args, **kwargs)
-
def solve(self, mu : paramList = [], *args, **kwargs):
return self.baseHF.solve(self.completeMuList(mu), *args, **kwargs)
def residual(self, mu : paramList = [], *args, **kwargs):
return self.baseHF.residual(self.completeMuList(mu), *args, **kwargs)
def stabilityFactor(self, mu : paramList = [], *args, **kwargs):
return self.baseHF.stabilityFactor(self.completeMuList(mu),
*args, **kwargs)
class MarginalProxyEngineAffineA(MarginalProxyEngineNonAffine):
@affine_construct
def A(self, mu : paramVal = [], *args, **kwargs):
return self.baseHF.A(self.completeMu(mu), *args, **kwargs)
class MarginalProxyEngineAffineb(MarginalProxyEngineNonAffine):
@affine_construct
def b(self, mu : paramVal = [], *args, **kwargs):
return self.baseHF.b(self.completeMu(mu), *args, **kwargs)
class MarginalProxyEngineAffineAb(MarginalProxyEngineAffineA,
MarginalProxyEngineAffineb):
pass
diff --git a/rrompy/hfengines/base/numpy_engine_base.py b/rrompy/hfengines/base/numpy_engine_base.py
index a84f6a5..9fc509a 100644
--- a/rrompy/hfengines/base/numpy_engine_base.py
+++ b/rrompy/hfengines/base/numpy_engine_base.py
@@ -1,114 +1,123 @@
# 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 matplotlib import pyplot as plt
from .hfengine_base import HFEngineBase
from rrompy.utilities.base.types import Np1D, strLst, List, Tuple, FigHandle
from rrompy.utilities.base.data_structures import purgeList, getNewFilename
+from rrompy.utilities.exception_manager import RROMPyException
-__all__ = ['NumpyEngineBase']
+__all__ = ['NumpyEngineBase', 'NumpyEngineBaseTensorized']
+def checknports(eng) -> int:
+ if not hasattr(eng, "nports"):
+ raise RROMPyException(("Engine.nports should be assigned before using "
+ "tensorized plotting functionalities."))
+ return eng.nports
+
class NumpyEngineBase(HFEngineBase):
"""Generic solver for parametric matricial problems."""
def plot(self, u:Np1D, warping : List[callable] = None,
is_state : bool = False, name : str = "u", save : str = None,
what : strLst = 'all', forceNewFile : bool = True,
saveFormat : str = "eps", saveDPI : int = 100, show : bool = True,
colorMap : str = "jet", pyplotArgs : dict = {},
**figspecs) -> Tuple[FigHandle, str]:
"""
Do some nice plots of the complex-valued function with given dofs.
Args:
u: numpy complex array with function dofs.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
is_state(optional): whether given u is value before multiplication
by c. Defaults to False.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
forceNewFile(optional): Whether to create new output file.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
show(optional): Whether to show figure. Defaults to True.
colorMap(optional): Pyplot colormap. Defaults to 'jet'.
pyplotArgs(optional): Optional arguments for pyplot.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
Returns:
Output filename and figure handle.
"""
if isinstance(what, (str,)):
if what.upper() == 'ALL':
what = ['ABS', 'PHASE', 'REAL', 'IMAG']
else:
what = [what]
what = purgeList(what, ['ABS', 'PHASE', 'REAL', 'IMAG'],
listname = self.name() + ".what", baselevel = 1)
if len(what) == 0: return
if 'figsize' not in figspecs.keys():
figspecs['figsize'] = plt.figaspect(1. / len(what))
- if is_state or self.isCEye:
- idxs = np.arange(self.spacedim)
- else:
- idxs = np.arange(self.C.shape[0])
- uP = u.reshape(idxs[-1] + 1, -1)
+ idxs = np.arange(len(u))
if warping is not None:
- idxs = warping[0](np.arange(self.spacedim))
+ idxs = warping[0](idxs)
subplotidx = 0
fig = plt.figure(**figspecs)
plt.set_cmap(colorMap)
if 'ABS' in what:
subplotidx = subplotidx + 1
ax = fig.add_subplot(1, len(what), subplotidx)
- ax.plot(idxs, np.abs(uP), **pyplotArgs)
+ ax.plot(idxs, np.abs(u), **pyplotArgs)
ax.set_title("|{0}|".format(name))
if 'PHASE' in what:
subplotidx = subplotidx + 1
ax = fig.add_subplot(1, len(what), subplotidx)
- ax.plot(idxs, np.angle(uP), **pyplotArgs)
+ ax.plot(idxs, np.angle(u), **pyplotArgs)
ax.set_title("phase({0})".format(name))
if 'REAL' in what:
subplotidx = subplotidx + 1
ax = fig.add_subplot(1, len(what), subplotidx)
- ax.plot(idxs, np.real(uP), **pyplotArgs)
+ ax.plot(idxs, np.real(u), **pyplotArgs)
ax.set_title("Re({0})".format(name))
if 'IMAG' in what:
subplotidx = subplotidx + 1
ax = fig.add_subplot(1, len(what), subplotidx)
- ax.plot(idxs, np.imag(uP), **pyplotArgs)
+ ax.plot(idxs, np.imag(u), **pyplotArgs)
ax.set_title("Im({0})".format(name))
plt.tight_layout()
if save is not None:
save = save.strip()
if forceNewFile:
fileOut = getNewFilename("{}_fig_".format(save), saveFormat)
else:
fileOut = "{}_fig.{}".format(save, saveFormat)
fig.savefig(fileOut, format = saveFormat, dpi = saveDPI)
else: fileOut = None
if show: plt.show()
if fileOut is None: return fig
return fig, fileOut
+
+class NumpyEngineBaseTensorized(NumpyEngineBase):
+ """The number of tensorized dimensions should be assigned to nports."""
+
+ def plot(self, u:Np1D, *args, **kwargs) -> Tuple[FigHandle, str]:
+ return super().plot(u.reshape(-1, checknports(self)), *args, **kwargs)
diff --git a/rrompy/hfengines/base/vector_fenics_engine_base.py b/rrompy/hfengines/base/vector_fenics_engine_base.py
index 3b44ba9..e013a95 100644
--- a/rrompy/hfengines/base/vector_fenics_engine_base.py
+++ b/rrompy/hfengines/base/vector_fenics_engine_base.py
@@ -1,148 +1,187 @@
# 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 fenics as fen
import numpy as np
from matplotlib import pyplot as plt
-from .fenics_engine_base import FenicsEngineBase
+from .fenics_engine_base import (FenicsEngineBase, FenicsEngineBaseTensorized,
+ plottingBaseFen, checknports)
from rrompy.utilities.base.types import Np1D, List, strLst, Tuple, FigHandle
from rrompy.utilities.base.data_structures import purgeList, getNewFilename
-from rrompy.solver.fenics import fenplot
-__all__ = ['VectorFenicsEngineBase']
+__all__ = ['VectorFenicsEngineBase', 'VectorFenicsEngineBaseTensorized']
class VectorFenicsEngineBase(FenicsEngineBase):
"""Generic solver for parametric vector fenics problems."""
def __init__(self, degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.V = fen.VectorFunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1)
def plot(self, u:Np1D, warping : List[callable] = None,
is_state : bool = False, name : str = "u", save : str = None,
what : strLst = 'all', forceNewFile : bool = True,
saveFormat : str = "eps", saveDPI : int = 100,
show : bool = True, colorMap : str = "jet",
fenplotArgs : dict = {},
- **figspecs) -> Tuple[List[FigHandle], List[str]]:
+ **figspecs) -> Tuple[FigHandle, str]:
"""
Do some nice plots of the complex-valued function with given dofs.
Args:
u: numpy complex array with function dofs.
warping(optional): Domain warping functions.
is_state(optional): whether given u is value before multiplication
by c. Defaults to False.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
forceNewFile(optional): Whether to create new output file.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to.
show(optional): Whether to show figure. Defaults to True.
colorMap(optional): Pyplot colormap. Defaults to 'jet'.
fenplotArgs(optional): Optional arguments for fenplot.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
Returns:
- List of output filenames and list of figure handles.
+ Output filename and figure handle.
"""
if not is_state and not self.isCEye:
return super().plot(u, warping, False, name, save, what,
forceNewFile, saveFormat, saveDPI, show,
colorMap, fenplotArgs, **figspecs)
if isinstance(what, (str,)):
if what.upper() == 'ALL':
what = ['ABS', 'PHASE', 'REAL', 'IMAG']
else:
what = [what]
what = purgeList(what, ['ABS', 'PHASE', 'REAL', 'IMAG'],
listname = self.name() + ".what", baselevel = 1)
if len(what) == 0: return
+ nss = self.V.num_sub_spaces()
if 'figsize' not in figspecs.keys():
figspecs['figsize'] = plt.figaspect(1. / len(what))
+ figspecs['figsize'][1] *= nss
- figs = [None] * self.V.num_sub_spaces()
- fileOut = [None] * self.V.num_sub_spaces()
- for j in range(self.V.num_sub_spaces()):
- II = self.V.sub(j).dofmap().dofs()
- Vj = self.V.sub(j).collapse()
- subplotidx = 0
- figs[j] = plt.figure(**figspecs)
- plt.set_cmap(colorMap)
- if 'ABS' in what:
- uAb = fen.Function(Vj)
- uAb.vector().set_local(np.abs(u[II]))
- subplotidx = subplotidx + 1
- ax = figs[j].add_subplot(1, len(what), subplotidx)
- p = fenplot(uAb, warping = warping,
- title = "|{}_comp{}|".format(name, j),
- **fenplotArgs)
- if self.V.mesh().geometric_dimension() > 1:
- figs[j].colorbar(p, ax = ax)
- if 'PHASE' in what:
- uPh = fen.Function(Vj)
- uPh.vector().set_local(np.angle(u[II]))
- subplotidx = subplotidx + 1
- ax = figs[j].add_subplot(1, len(what), subplotidx)
- p = fenplot(uPh, warping = warping,
- title = "phase({}_comp{})".format(name, j),
- **fenplotArgs)
- if self.V.mesh().geometric_dimension() > 1:
- figs[j].colorbar(p, ax = ax)
- if 'REAL' in what:
- uRe = fen.Function(Vj)
- uRe.vector().set_local(np.real(u[II]))
- subplotidx = subplotidx + 1
- ax = figs[j].add_subplot(1, len(what), subplotidx)
- p = fenplot(uRe, warping = warping,
- title = "Re({}_comp{})".format(name, j),
- **fenplotArgs)
- if self.V.mesh().geometric_dimension() > 1:
- figs[j].colorbar(p, ax = ax)
- if 'IMAG' in what:
- uIm = fen.Function(Vj)
- uIm.vector().set_local(np.imag(u[II]))
- subplotidx = subplotidx + 1
- ax = figs[j].add_subplot(1, len(what), subplotidx)
- p = fenplot(uIm, warping = warping,
- title = "Im({}_comp{})".format(name, j),
- **fenplotArgs)
- if self.V.mesh().geometric_dimension() > 1:
- figs[j].colorbar(p, ax = ax)
- plt.tight_layout()
- if save is not None:
- save = save.strip()
- if forceNewFile:
- fileOut = getNewFilename(
- "{}_comp{}_fig_".format(save, j),
- saveFormat)
- else:
- fileOut = "{}_comp{}_fig.{}".format(save,j,saveFormat)
- figs[j].savefig(fileOut, format = saveFormat,
- dpi = saveDPI)
- if show: plt.show()
- if fileOut[0] is None: return figs
- return figs, fileOut
+ fig = plt.figure(**figspecs)
+ plt.set_cmap(colorMap)
+ for j in range(nss):
+ plottingBaseFen(u[self.V.sub(j).dofmap().dofs()], fig,
+ self.V.sub(j).collapse(), what, nss, j * len(what),
+ warping, "{}_comp{}".format(name, j + 1),
+ self.V.mesh().geometric_dimension() > 1,
+ fenplotArgs)
+ plt.tight_layout()
+ if save is not None:
+ save = save.strip()
+ if forceNewFile:
+ fileOut = getNewFilename("{}_fig_".format(save), saveFormat)
+ else:
+ fileOut = "{}_fig.{}".format(save, saveFormat)
+ fig.savefig(fileOut, format = saveFormat, dpi = saveDPI)
+ else: fileOut = None
+ if show: plt.show()
+ if fileOut is None: return fig
+ return fig, fileOut
+
+class VectorFenicsEngineBaseTensorized(VectorFenicsEngineBase,
+ FenicsEngineBaseTensorized):
+ """The number of tensorized dimensions should be assigned to nports."""
+
+ def plot(self, u:Np1D, warping : List[callable] = None,
+ is_state : bool = False, name : str = "u", save : str = None,
+ what : strLst = 'all', forceNewFile : bool = True,
+ saveFormat : str = "eps", saveDPI : int = 100,
+ show : bool = True, colorMap : str = "jet",
+ fenplotArgs : dict = {},
+ **figspecs) -> Tuple[FigHandle, str]:
+ """
+ Do some nice plots of the complex-valued function with given dofs.
+
+ Args:
+ u: numpy complex array with function dofs.
+ warping(optional): Domain warping functions.
+ is_state(optional): whether given u is value before multiplication
+ by c. Defaults to False.
+ name(optional): Name to be shown as title of the plots. Defaults to
+ 'u'.
+ save(optional): Where to save plot(s). Defaults to None, i.e. no
+ saving.
+ what(optional): Which plots to do. If list, can contain 'ABS',
+ 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
+ Defaults to 'ALL'.
+ forceNewFile(optional): Whether to create new output file.
+ saveFormat(optional): Format for saved plot(s). Defaults to "eps".
+ saveDPI(optional): DPI for saved plot(s). Defaults to.
+ show(optional): Whether to show figure. Defaults to True.
+ colorMap(optional): Pyplot colormap. Defaults to 'jet'.
+ fenplotArgs(optional): Optional arguments for fenplot.
+ figspecs(optional key args): Optional arguments for matplotlib
+ figure creation.
+
+ Returns:
+ Output filename and figure handle.
+ """
+ nP = checknports(self)
+ if not is_state and not self.isCEye:
+ return super().plot(u.reshape(-1, nP), warping, False, name, save,
+ what, forceNewFile, saveFormat, saveDPI, show,
+ colorMap, fenplotArgs, **figspecs)
+ if isinstance(what, (str,)):
+ if what.upper() == 'ALL':
+ what = ['ABS', 'PHASE', 'REAL', 'IMAG']
+ else:
+ what = [what]
+ what = purgeList(what, ['ABS', 'PHASE', 'REAL', 'IMAG'],
+ listname = self.name() + ".what", baselevel = 1)
+ if len(what) == 0: return
+ nss = self.V.num_sub_spaces()
+ if 'figsize' not in figspecs.keys():
+ figspecs['figsize'] = plt.figaspect(1. / len(what))
+ figspecs['figsize'][1] *= nss * nP
+
+ fig = plt.figure(**figspecs)
+ plt.set_cmap(colorMap)
+ for i in range(nP):
+ for j in range(nss):
+ plottingBaseFen(u[i :: nP][self.V.sub(j).dofmap().dofs()], fig,
+ self.V.sub(j).collapse(), what, nss * nP,
+ (j + i * nss) * len(what), warping,
+ "{}_port{}_comp{}".format(name, i + 1, j + 1),
+ self.V.mesh().geometric_dimension() > 1,
+ fenplotArgs)
+ plt.tight_layout()
+ if save is not None:
+ save = save.strip()
+ if forceNewFile:
+ fileOut = getNewFilename("{}_fig_".format(save), saveFormat)
+ else:
+ fileOut = "{}_fig.{}".format(save, saveFormat)
+ fig.savefig(fileOut, format = saveFormat, dpi = saveDPI)
+ else: fileOut = None
+ if show: plt.show()
+ if fileOut is None: return fig
+ return fig, fileOut
diff --git a/rrompy/parameter/__init__.py b/rrompy/parameter/__init__.py
index f7b8ab3..55e1350 100644
--- a/rrompy/parameter/__init__.py
+++ b/rrompy/parameter/__init__.py
@@ -1,31 +1,29 @@
# 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 .
#
from .parameter_list import (parameterList, emptyParameterList, checkParameter,
checkParameterList)
-from .local_sparse_grid import localSparseGrid
__all__ = [
'parameterList',
'emptyParameterList',
'checkParameter',
- 'checkParameterList',
- 'localSparseGrid'
+ 'checkParameterList'
]
diff --git a/rrompy/parameter/parameter_sampling/__init__.py b/rrompy/parameter/parameter_sampling/__init__.py
index c819043..861155c 100644
--- a/rrompy/parameter/parameter_sampling/__init__.py
+++ b/rrompy/parameter/parameter_sampling/__init__.py
@@ -1,36 +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 .
#
from .manual_sampler import ManualSampler
from .segment import QuadratureSampler, QuadratureSamplerTotal, RandomSampler
from .shape import (FFTSampler, QuadratureBoxSampler, QuadratureCircleSampler,
RandomBoxSampler, RandomCircleSampler)
+from .sparse_grid import SparseGridSampler
__all__ = [
'ManualSampler',
'QuadratureSampler',
'QuadratureSamplerTotal',
'RandomSampler',
'FFTSampler',
'QuadratureBoxSampler',
'QuadratureCircleSampler',
'RandomBoxSampler',
- 'RandomCircleSampler'
+ 'RandomCircleSampler',
+ 'SparseGridSampler'
]
diff --git a/rrompy/parameter/parameter_sampling/generic_sampler.py b/rrompy/parameter/parameter_sampling/generic_sampler.py
index 6e74eef..21dfedb 100644
--- a/rrompy/parameter/parameter_sampling/generic_sampler.py
+++ b/rrompy/parameter/parameter_sampling/generic_sampler.py
@@ -1,87 +1,94 @@
# 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 abc import abstractmethod
from rrompy.utilities.base.types import List, paramList
from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
from rrompy.parameter import checkParameterList
__all__ = ['GenericSampler']
class GenericSampler:
"""ABSTRACT. Generic generator of sample points."""
def __init__(self, lims:paramList, scalingExp : List[float] = None):
self.lims = lims
self.scalingExp = scalingExp
def name(self) -> str:
return self.__class__.__name__
def __str__(self) -> str:
return "{}[{}_{}]".format(self.name(), self.lims[0], self.lims[1])
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def __eq__(self, other) -> bool:
- return self.__dict__ == other.__dict__
-
+ if self.__dict__.keys() != other.__dict__.keys(): return False
+ for key in self.__dict__:
+ val = self.__dict__[key]
+ if isinstance(val, (np.ndarray,)):
+ if not np.allclose(val, other.__dict__[key]): return False
+ else:
+ if val != other.__dict__[key]: return False
+ return True
+
@property
def npar(self):
"""Number of parameters."""
return self._lims.shape[1]
def normalFoci(self, d : int = 0):
return [-1., 1.]
def groundPotential(self, d : int = 0):
fp = self.normalFoci(d)[1]
fpa = np.abs(fp)
if np.isclose(fpa, 0.) or np.isclose(fpa, 1.): return 1.
return (1. + np.abs(1. - fp ** 2.) ** .5) / fpa
@property
def lims(self):
"""Value of lims."""
return self._lims
@lims.setter
def lims(self, lims):
lims = checkParameterList(lims)[0]
if len(lims) != 2:
raise RROMPyException("2 limits must be specified.")
self._lims = lims
@property
def scalingExp(self):
"""Value of scalingExp."""
return self._scalingExp
@scalingExp.setter
def scalingExp(self, scalingExp):
if scalingExp is None:
scalingExp = [1.] * self.npar
if not hasattr(scalingExp, "__len__"): scalingExp = [scalingExp]
RROMPyAssert(self.npar, len(scalingExp), "Number of scaling terms")
- self._scalingExp = scalingExp
+ self._scalingExp = np.array(scalingExp)
@abstractmethod
def generatePoints(self, n:int, reorder : bool = True) -> paramList:
"""Array of points."""
pass
diff --git a/rrompy/utilities/poly_fitting/moving_least_squares/kernel.py b/rrompy/parameter/parameter_sampling/sparse_grid/__init__.py
similarity index 72%
rename from rrompy/utilities/poly_fitting/moving_least_squares/kernel.py
rename to rrompy/parameter/parameter_sampling/sparse_grid/__init__.py
index 1708271..64373b4 100644
--- a/rrompy/utilities/poly_fitting/moving_least_squares/kernel.py
+++ b/rrompy/parameter/parameter_sampling/sparse_grid/__init__.py
@@ -1,24 +1,25 @@
# 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 .
#
-from rrompy.utilities.poly_fitting.radial_basis.kernel import (radialGaussian,
- thinPlateSpline, multiQuadric, localWendland, nearestNeighbor)
+from .sparse_grid_sampler import SparseGridSampler
+
+__all__ = [
+ 'SparseGridSampler'
+ ]
-__all__ = ['radialGaussian', 'thinPlateSpline', 'multiQuadric',
- 'localWendland', 'nearestNeighbor']
diff --git a/rrompy/parameter/local_sparse_grid.py b/rrompy/parameter/parameter_sampling/sparse_grid/sparse_grid_sampler.py
similarity index 56%
rename from rrompy/parameter/local_sparse_grid.py
rename to rrompy/parameter/parameter_sampling/sparse_grid/sparse_grid_sampler.py
index 80afac6..a66fe14 100644
--- a/rrompy/parameter/local_sparse_grid.py
+++ b/rrompy/parameter/parameter_sampling/sparse_grid/sparse_grid_sampler.py
@@ -1,137 +1,137 @@
# 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.utilities.base.types import List, paramList
from rrompy.parameter import checkParameterList
-from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
+from rrompy.parameter.parameter_sampling.generic_sampler import GenericSampler
+from rrompy.utilities.base.types import List, paramList
+from rrompy.utilities.numerical import lowDiscrepancy
+from rrompy.utilities.exception_manager import RROMPyException
+
+_allowedSparseGridKinds = ["UNIFORM", "LOBATTO"]
+
+__all__ = ['SparseGridSampler']
-__all__ = ['localSparseGrid']
+def mapForward(x, kind):
+ if kind == "UNIFORM":
+ return x
+ elif kind == "LOBATTO":
+ return np.cos(.5 * np.pi * (x + 1.))
+ else:
+ raise RROMPyException("Kind not recognized.")
-class localSparseGrid:
- _allowedKinds = ["UNIFORM"]
+def mapBackwards(y, kind):
+ if kind == "UNIFORM":
+ return y
+ elif kind == "LOBATTO":
+ return 2. / np.pi * np.arccos(np.clip(y, -1., 1., y)) - 1.
+ else:
+ raise RROMPyException("Kind not recognized.")
+
+class SparseGridSampler(GenericSampler):
+ """Generator of sparse grid sample points."""
def __init__(self, lims:paramList, kind : str = "UNIFORM",
scalingExp : List[float] = None):
- self.lims = lims
+ super().__init__(lims = lims, scalingExp = scalingExp)
+ self._allowedKinds = _allowedSparseGridKinds
self.kind = kind
- self.scalingExp = scalingExp
self.reset()
- def name(self) -> str:
- return self.__class__.__name__
-
def __str__(self) -> str:
return "{}[{}_{}]_{}".format(self.name(), self.lims[0],
self.lims[1], self.kind)
- def __repr__(self) -> str:
- return self.__str__() + " at " + hex(id(self))
-
- @property
- def npar(self):
- """Number of parameters."""
- return self._lims.shape[1]
-
@property
def npoints(self):
"""Number of points."""
return len(self.points)
- @property
- def lims(self):
- """Value of lims."""
- return self._lims
- @lims.setter
- def lims(self, lims):
- lims = checkParameterList(lims)[0]
- if len(lims) != 2:
- raise RROMPyException("2 limits must be specified.")
- self._lims = lims
-
@property
def kind(self):
"""Value of kind."""
return self._kind
@kind.setter
def kind(self, kind):
if kind.upper() not in self._allowedKinds:
raise RROMPyException("Generator kind not recognized.")
self._kind = kind.upper()
- if self.kind == "UNIFORM":
- self._phiD = lambda x: x
- self._phiI = lambda x: x
-
- @property
- def scalingExp(self):
- """Value of scalingExp."""
- return self._scalingExp
- @scalingExp.setter
- def scalingExp(self, scalingExp):
- if scalingExp is None:
- scalingExp = [1.] * self.npar
- if not hasattr(scalingExp, "__len__"): scalingExp = [scalingExp]
- RROMPyAssert(self.npar, len(scalingExp), "Number of scaling terms")
- self._scalingExp = np.array(scalingExp)
def reset(self):
self.deltalims = .5 * np.abs(self.lims.data[1] ** self.scalingExp
- self.lims.data[0] ** self.scalingExp)
- self.points = checkParameterList(self._phiD(.5 * (
- self.lims[0] ** self.scalingExp
- + self.lims[1] ** self.scalingExp)
- ) ** (1. / self.scalingExp), self.npar)[0]
+ centerEff = .5 * (self.lims[0] ** self.scalingExp
+ + self.lims[1] ** self.scalingExp)
+ self.points = checkParameterList(mapForward(centerEff, self.kind)
+ ** (1. / self.scalingExp), self.npar)[0]
self.depth = np.zeros((1, self.npar))
self.deltadepth = np.zeros(1)
def refine(self, active : List[int] = None) -> List[int]:
if active is None: active = np.arange(self.npoints)
newIdxs = []
for act in active:
pnt, 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.]:
if np.isclose(dpt[jdelta], 1.):
- gradj = (
- self._phiI(pnt[jdelta] ** self.scalingExp[jdelta])
- - self._phiI(self.points[0, jdelta] ** self.scalingExp[jdelta]))
+ pntEffj = mapBackwards(
+ pnt[jdelta] ** self.scalingExp[jdelta],
+ self.kind)
+ centerEffj = mapBackwards(
+ self.points[0, jdelta] ** self.scalingExp[jdelta],
+ self.kind)
+ gradj = pntEffj - centerEffj
if signdelta * gradj > 0:
continue
pntNew = copy(pnt)
- pntNew[jdelta] = self._phiD(
- self._phiI(pntNew[jdelta] ** self.scalingExp[jdelta])
- + .5 ** (dpt[jdelta] + ddp) * self.deltalims[jdelta]
- * signdelta) ** (1. / self.scalingExp[jdelta])
+ pntEffj = (mapBackwards(
+ pntNew[jdelta] ** self.scalingExp[jdelta],
+ self.kind)
+ + .5 ** (dpt[jdelta] + ddp)
+ * self.deltalims[jdelta] * signdelta)
+ pntNew[jdelta] = (mapForward(pntEffj, self.kind)
+ ** (1. / self.scalingExp[jdelta]))
dist = np.sum(np.abs(self.points.data
- pntNew.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(pntNew)
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 : bool = True) -> paramList:
+ if self.npoints > n: self.reset()
+ idx = np.arange(self.npoints)
+ while self.npoints < n: idx = self.refine(idx)
+ x = self.points
+ if self.npoints > 1 and reorder:
+ fejerOrdering = lowDiscrepancy(self.npoints)
+ x = checkParameterList(x.data[fejerOrdering, :], self.npar)[0]
+ return x
diff --git a/rrompy/reduction_methods/__init__.py b/rrompy/reduction_methods/__init__.py
index 44e41b5..c21fae4 100644
--- a/rrompy/reduction_methods/__init__.py
+++ b/rrompy/reduction_methods/__init__.py
@@ -1,41 +1,56 @@
# 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 .
#
-from .standard import (NearestNeighbor, RationalInterpolant,
- RationalMovingLeastSquares, RationalPade, ReducedBasis)
+from .standard import (NearestNeighbor, RationalInterpolant, RationalPade,
+ ReducedBasis)
from .standard.greedy import RationalInterpolantGreedy, ReducedBasisGreedy
-from .pivoted import (RationalInterpolantPivoted,
- RationalInterpolantGreedyPivoted)
-from .pivoted.greedy import (RationalInterpolantPivotedGreedy,
- RationalInterpolantGreedyPivotedGreedy)
+from .pivoted import (RationalInterpolantPivotedNoMatch,
+ RationalInterpolantPivoted,
+ RationalInterpolantPivotedNearestNeighbor,
+ RationalInterpolantGreedyPivotedNoMatch,
+ RationalInterpolantGreedyPivoted,
+ RationalInterpolantGreedyPivotedNearestNeighbor)
+from .pivoted.greedy import (RationalInterpolantPivotedGreedyNoMatch,
+ RationalInterpolantPivotedGreedy,
+ RationalInterpolantPivotedGreedyNearestNeighbor,
+ RationalInterpolantGreedyPivotedGreedyNoMatch,
+ RationalInterpolantGreedyPivotedGreedy,
+ RationalInterpolantGreedyPivotedGreedyNearestNeighbor)
__all__ = [
'NearestNeighbor',
'RationalInterpolant',
- 'RationalMovingLeastSquares',
'RationalPade',
'ReducedBasis',
'RationalInterpolantGreedy',
'ReducedBasisGreedy',
+ 'RationalInterpolantPivotedNoMatch',
'RationalInterpolantPivoted',
+ 'RationalInterpolantPivotedNearestNeighbor',
+ 'RationalInterpolantGreedyPivotedNoMatch',
'RationalInterpolantGreedyPivoted',
+ 'RationalInterpolantGreedyPivotedNearestNeighbor',
+ 'RationalInterpolantPivotedGreedyNoMatch',
'RationalInterpolantPivotedGreedy',
- 'RationalInterpolantGreedyPivotedGreedy'
+ 'RationalInterpolantPivotedGreedyNearestNeighbor',
+ 'RationalInterpolantGreedyPivotedGreedyNoMatch',
+ 'RationalInterpolantGreedyPivotedGreedy',
+ 'RationalInterpolantGreedyPivotedGreedyNearestNeighbor'
]
diff --git a/rrompy/reduction_methods/base/generic_approximant.py b/rrompy/reduction_methods/base/generic_approximant.py
index 11b1a66..7d094c2 100644
--- a/rrompy/reduction_methods/base/generic_approximant.py
+++ b/rrompy/reduction_methods/base/generic_approximant.py
@@ -1,880 +1,880 @@
# 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 .
#
from abc import abstractmethod
import numpy as np
from itertools import product as iterprod
from copy import deepcopy as copy
from os import remove as osrm
from rrompy.sampling import SamplingEngineStandard, SamplingEngineStandardPOD
from rrompy.utilities.base.types import (Np1D, DictAny, HFEng, List, Tuple,
ListAny, strLst, paramVal, paramList,
sampList)
from rrompy.utilities.base.data_structures import purgeDict, getNewFilename
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPy_READY, RROMPy_FRAGILE)
from rrompy.utilities.base.pickle_utilities import pickleDump, pickleLoad
from rrompy.parameter import (emptyParameterList, checkParameter,
checkParameterList)
from rrompy.sampling import sampleList, emptySampleList
__all__ = ['GenericApproximant']
def addNormFieldToClass(self, fieldName):
def objFunc(self, mu:paramList, *args, **kwargs) -> Np1D:
uV = getattr(self.__class__, "get" + fieldName)(self, mu)
kwargs["is_state"] = False
val = self.HFEngine.norm(uV, *args, **kwargs)
return val
setattr(self.__class__, "norm" + fieldName, objFunc)
def addNormDualFieldToClass(self, fieldName):
def objFunc(self, mu:paramList, *args, **kwargs) -> Np1D:
uV = getattr(self.__class__, "get" + fieldName)(self, mu)
kwargs["is_state"] = True
if "dual" not in kwargs.keys(): kwargs["dual"] = True
val = self.HFEngine.norm(uV, *args, **kwargs)
return val
setattr(self.__class__, "norm" + fieldName, objFunc)
def addPlotFieldToClass(self, fieldName):
def objFunc(self, mu:paramList, *args, **kwargs):
uV = getattr(self.__class__, "get" + fieldName)(self, mu)
if "name" in kwargs.keys(): nameBase = copy(kwargs["name"])
filesOut = []
for j, u in enumerate(uV):
if "name" in kwargs.keys(): kwargs["name"] = nameBase + str(j)
filesOut += [self.HFEngine.plot(u, *args, **kwargs)]
if "name" in kwargs.keys(): kwargs["name"] = nameBase
if filesOut[0] is None: return None
return filesOut
setattr(self.__class__, "plot" + fieldName, objFunc)
def addPlotDualFieldToClass(self, fieldName):
def objFunc(self, mu:paramList, *args, **kwargs):
uV = getattr(self.__class__, "get" + fieldName)(self, mu)
if "name" in kwargs.keys(): nameBase = copy(kwargs["name"])
filesOut = []
for j, u in enumerate(uV):
if "name" in kwargs.keys(): kwargs["name"] = nameBase + str(j)
filesOut += [self.HFEngine.plot(u, *args, **kwargs)]
if "name" in kwargs.keys(): kwargs["name"] = nameBase
if filesOut[0] is None: return None
return filesOut
setattr(self.__class__, "plot" + fieldName, objFunc)
def addOutParaviewFieldToClass(self, fieldName):
def objFunc(self, mu:paramVal, *args, **kwargs):
if not hasattr(self.HFEngine, "outParaview"):
raise RROMPyException(("High fidelity engine cannot output to "
"Paraview."))
uV = getattr(self.__class__, "get" + fieldName)(self, mu)
if "name" in kwargs.keys(): nameBase = copy(kwargs["name"])
filesOut = []
for j, u in enumerate(uV):
if "name" in kwargs.keys(): kwargs["name"] = nameBase + str(j)
filesOut += [self.HFEngine.outParaview(u, *args, **kwargs)]
if "name" in kwargs.keys(): kwargs["name"] = nameBase
if filesOut[0] is None: return None
return filesOut
setattr(self.__class__, "outParaview" + fieldName, objFunc)
def addOutParaviewTimeDomainFieldToClass(self, fieldName):
def objFunc(self, mu:paramVal, *args, **kwargs):
if not hasattr(self.HFEngine, "outParaviewTimeDomain"):
raise RROMPyException(("High fidelity engine cannot output to "
"Paraview."))
uV = getattr(self.__class__, "get" + fieldName)(self, mu)
omega = args.pop(0) if len(args) > 0 else np.real(mu)
if "name" in kwargs.keys(): nameBase = copy(kwargs["name"])
filesOut = []
for j, u in enumerate(uV):
if "name" in kwargs.keys(): kwargs["name"] = nameBase + str(j)
filesOut += [self.HFEngine.outParaviewTimeDomain(u, omega, *args,
**kwargs)]
if "name" in kwargs.keys(): kwargs["name"] = nameBase
if filesOut[0] is None: return None
return filesOut
setattr(self.__class__, "outParaviewTimeDomain" + fieldName, objFunc)
class GenericApproximant:
"""
ABSTRACT
ROM approximant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'S': total number of samples current approximant relies upon.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults to
False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
trainedModel: Trained model evaluator.
mu0: Default parameter.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList{Soft,Critical}.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
S: Number of solution snapshots over which current approximant is
based upon.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
"""
__all__ += [ftype + dtype for ftype, dtype in iterprod(
["norm", "plot", "outParaview", "outParaviewTimeDomain"],
["HF", "RHS", "Approx", "Res", "Err"])]
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, approx_state : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
self._mode = RROMPy_READY
self.approx_state = approx_state
self.verbosity = verbosity
self.timestamp = timestamp
vbMng(self, "INIT",
"Initializing engine of type {}.".format(self.name()), 10)
self._HFEngine = HFEngine
self.trainedModel = None
self.lastSolvedHF = emptyParameterList()
self.uHF = emptySampleList()
self._addParametersToList(["POD", "scaleFactorDer"], [True, "AUTO"],
["S"], [1.])
if mu0 is None:
if hasattr(self.HFEngine, "mu0"):
self.mu0 = checkParameter(self.HFEngine.mu0)
else:
raise RROMPyException(("Center of approximation cannot be "
"inferred from HF engine. Parameter "
"required"))
else:
self.mu0 = checkParameter(mu0, self.HFEngine.npar)
self.resetSamples()
self.approxParameters = approxParameters
self._postInit()
### add norm{HF,Err} methods
"""
Compute norm of * at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Target norm of *.
"""
for objName in ["HF", "Err"]:
addNormFieldToClass(self, objName)
### add norm{RHS,Res} methods
"""
Compute norm of * at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Target norm of *.
"""
for objName in ["RHS", "Res"]:
addNormDualFieldToClass(self, objName)
### add plot{HF,Approx,Err} methods
"""
Do some nice plots of * at arbitrary parameter.
Args:
mu: Target parameter.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
show(optional): Whether to show figure. Defaults to True.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
for objName in ["HF", "Approx", "Err"]:
addPlotFieldToClass(self, objName)
### add plot{RHS,Res} methods
"""
Do some nice plots of * at arbitrary parameter.
Args:
mu: Target parameter.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
show(optional): Whether to show figure. Defaults to True.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
for objName in ["RHS", "Res"]:
addPlotDualFieldToClass(self, objName)
### add outParaview{HF,RHS,Approx,Res,Err} methods
"""
Output * to ParaView file.
Args:
mu: Target parameter.
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
time(optional): Timestamp.
what(optional): Which plots to do. If list, can contain 'MESH',
'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard
'ALL'. Defaults to 'ALL'.
forceNewFile(optional): Whether to create new output file.
filePW(optional): Fenics File entity (for time series).
"""
for objName in ["HF", "RHS", "Approx", "Res", "Err"]:
addOutParaviewFieldToClass(self, objName)
### add outParaviewTimeDomain{HF,RHS,Approx,Res,Err} methods
"""
Output * to ParaView file, converted to time domain.
Args:
mu: Target parameter.
omega(optional): frequency.
timeFinal(optional): final time of simulation.
periodResolution(optional): number of time steps per period.
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
forceNewFile(optional): Whether to create new output file.
"""
for objName in ["HF", "RHS", "Approx", "Res", "Err"]:
addOutParaviewTimeDomainFieldToClass(self, objName)
def _preInit(self):
if not hasattr(self, "depth"): self.depth = 0
else: self.depth += 1
@property
def tModelType(self):
raise RROMPyException("No trainedModel type assigned.")
def initializeModelData(self, datadict):
from .trained_model.trained_model_data import TrainedModelData
- return (TrainedModelData(datadict["mu0"], datadict.pop("projMat"),
+ return (TrainedModelData(datadict["mu0"], datadict["mus"],
+ datadict.pop("projMat"),
datadict["scaleFactor"],
datadict.pop("rescalingExp")),
["mu0", "scaleFactor", "mus"])
@property
def parameterList(self):
"""Value of parameterListSoft + parameterListCritical."""
return self.parameterListSoft + self.parameterListCritical
def _addParametersToList(self, whatSoft : strLst = [],
defaultSoft : ListAny = [],
whatCritical : strLst = [],
defaultCritical : ListAny = [],
toBeExcluded : strLst = []):
if not hasattr(self, "parameterToBeExcluded"):
self.parameterToBeExcluded = []
self.parameterToBeExcluded = toBeExcluded + self.parameterToBeExcluded
if not hasattr(self, "parameterListSoft"):
self.parameterListSoft = []
if not hasattr(self, "parameterDefaultSoft"):
self.parameterDefaultSoft = {}
if not hasattr(self, "parameterListCritical"):
self.parameterListCritical = []
if not hasattr(self, "parameterDefaultCritical"):
self.parameterDefaultCritical = {}
for j, what in enumerate(whatSoft):
if what not in self.parameterToBeExcluded:
self.parameterListSoft = [what] + self.parameterListSoft
self.parameterDefaultSoft[what] = defaultSoft[j]
for j, what in enumerate(whatCritical):
if what not in self.parameterToBeExcluded:
self.parameterListCritical = ([what]
+ self.parameterListCritical)
self.parameterDefaultCritical[what] = defaultCritical[j]
def _postInit(self):
if self.depth == 0:
vbMng(self, "DEL", "Done initializing.", 10)
del self.depth
else: self.depth -= 1
def name(self) -> str:
return self.__class__.__name__
def __str__(self) -> str:
return self.name()
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def setupSampling(self):
"""Setup sampling engine."""
RROMPyAssert(self._mode, message = "Cannot setup sampling engine.")
if not hasattr(self, "_POD") or self._POD is None: return
if self.POD:
SamplingEngine = SamplingEngineStandardPOD
else:
SamplingEngine = SamplingEngineStandard
self.samplingEngine = SamplingEngine(self.HFEngine,
sample_state = self.approx_state,
verbosity = self.verbosity)
self.resetSamples()
@property
def HFEngine(self):
"""Value of HFEngine."""
return self._HFEngine
@HFEngine.setter
def HFEngine(self, HFEngine):
raise RROMPyException("Cannot change HFEngine.")
@property
def mu0(self):
"""Value of mu0."""
return self._mu0
@mu0.setter
def mu0(self, mu0):
mu0 = checkParameter(mu0)
if not hasattr(self, "_mu0") or mu0 != self.mu0:
self.resetSamples()
self._mu0 = mu0
@property
def npar(self):
"""Number of parameters."""
return self.mu0.shape[1]
@property
def approxParameters(self):
"""Value of approximant parameters."""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
if not hasattr(self, "approxParameters"):
self._approxParameters = {}
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
keyList = list(approxParameters.keys())
for key in self.parameterListCritical:
if key in keyList:
setattr(self, "_" + key, self.parameterDefaultCritical[key])
for key in self.parameterListSoft:
if key in keyList:
setattr(self, "_" + key, self.parameterDefaultSoft[key])
fragile = False
for key in self.parameterListCritical:
if key in keyList:
val = approxParameters[key]
else:
val = getattr(self, "_" + key, None)
if val is None:
val = self.parameterDefaultCritical[key]
getattr(self.__class__, key, None).fset(self, val)
fragile = fragile or val is None
for key in self.parameterListSoft:
if key in keyList:
val = approxParameters[key]
else:
val = getattr(self, "_" + key, None)
if val is None:
val = self.parameterDefaultSoft[key]
getattr(self.__class__, key, None).fset(self, val)
if fragile:
self._mode = RROMPy_FRAGILE
@property
def POD(self):
"""Value of POD."""
return self._POD
@POD.setter
def POD(self, POD):
if hasattr(self, "_POD"): PODold = self.POD
else: PODold = -1
self._POD = POD
self._approxParameters["POD"] = self.POD
if PODold != self.POD:
self.samplingEngine = None
self.resetSamples()
@property
def scaleFactorDer(self):
"""Value of scaleFactorDer."""
if self._scaleFactorDer == "NONE": return 1.
if self._scaleFactorDer == "AUTO": return self.scaleFactor
return self._scaleFactorDer
@scaleFactorDer.setter
def scaleFactorDer(self, scaleFactorDer):
if hasattr(self, "_scaleFactorDer"):
scaleFactorDerold = self.scaleFactorDer
else: scaleFactorDerold = -1
if isinstance(scaleFactorDer, (str,)):
scaleFactorDer = scaleFactorDer.upper()
self._scaleFactorDer = scaleFactorDer
self._approxParameters["scaleFactorDer"] = self._scaleFactorDer
if scaleFactorDerold != self._scaleFactorDer: self.resetSamples()
@property
def scaleFactorRel(self):
"""Value of scaleFactorDer / scaleFactor."""
if self._scaleFactorDer == "AUTO": return None
try:
return np.divide(self.scaleFactorDer, self.scaleFactor)
except:
raise RROMPyException(("Error in computation of relative scaling "
"factor. Make sure that scaleFactor is "
"properly initialized."))
@property
def approx_state(self):
"""Value of approx_state."""
return self._approx_state
@approx_state.setter
def approx_state(self, approx_state):
if hasattr(self, "_approx_state"): approx_stateold = self.approx_state
else: approx_stateold = -1
self._approx_state = approx_state
if approx_stateold != self.approx_state: self.resetSamples()
@property
def S(self):
"""Value of S."""
return self._S
@S.setter
def S(self, S):
if S <= 0: raise RROMPyException("S must be positive.")
if hasattr(self, "_S") and self._S is not None: Sold = self.S
else: Sold = -1
self._S = S
self._approxParameters["S"] = self.S
if Sold != self.S: self.resetSamples()
@property
def trainedModel(self):
"""Value of trainedModel."""
return self._trainedModel
@trainedModel.setter
def trainedModel(self, trainedModel):
self._trainedModel = trainedModel
if self._trainedModel is not None:
self._trainedModel.reset()
self.lastSolvedApproxReduced = emptyParameterList()
self.lastSolvedApprox = emptyParameterList()
self.uApproxReduced = emptySampleList()
self.uApprox = emptySampleList()
def resetSamples(self):
if hasattr(self, "samplingEngine") and self.samplingEngine is not None:
self.samplingEngine.resetHistory()
else:
self.setupSampling()
self._mode = RROMPy_READY
def plotSamples(self, *args, **kwargs) -> List[str]:
"""
Do some nice plots of the samples.
Returns:
Output filenames.
"""
RROMPyAssert(self._mode, message = "Cannot plot samples.")
return self.samplingEngine.plotSamples(*args, **kwargs)
def outParaviewSamples(self, *args, **kwargs) -> List[str]:
"""
Output samples to ParaView file.
Returns:
Output filenames.
"""
RROMPyAssert(self._mode, message = "Cannot output samples.")
return self.samplingEngine.outParaviewSamples(*args, **kwargs)
def outParaviewTimeDomainSamples(self, *args, **kwargs) -> List[str]:
"""
Output samples to ParaView file, converted to time domain.
Returns:
Output filenames.
"""
RROMPyAssert(self._mode, message = "Cannot output samples.")
return self.samplingEngine.outParaviewTimeDomainSamples(*args,
**kwargs)
def setSamples(self, samplingEngine):
"""Copy samplingEngine and samples."""
vbMng(self, "INIT", "Transfering samples.", 10)
self.samplingEngine = copy(samplingEngine)
vbMng(self, "DEL", "Done transfering samples.", 10)
def setTrainedModel(self, model):
"""Deepcopy approximation from trained model."""
if hasattr(model, "storeTrainedModel"):
verb = model.verbosity
model.verbosity = 0
fileOut = model.storeTrainedModel()
model.verbosity = verb
else:
try:
fileOut = getNewFilename("trained_model", "pkl")
pickleDump(model.data.__dict__, fileOut)
except:
raise RROMPyException(("Failed to store model data. Parameter "
"model must have either "
"storeTrainedModel or "
"data.__dict__ properties."))
self.loadTrainedModel(fileOut)
osrm(fileOut)
@abstractmethod
def setupApprox(self) -> int:
"""
Setup approximant. (ABSTRACT)
Any specialization should include something like
self.trainedModel = ...
self.trainedModel.data = ...
self.trainedModel.data.approxParameters = copy(
self.approxParameters)
Returns > 0 if error was encountered, < 0 if no computation was
necessary.
"""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5)
pass
vbMng(self, "DEL", "Done setting up approximant.", 5)
return 0
def checkComputedApprox(self) -> bool:
"""
Check if setup of new approximant is not needed.
Returns:
True if new setup is not needed. False otherwise.
"""
return self._mode == RROMPy_FRAGILE or (self.trainedModel is not None
and self.trainedModel.data.approxParameters == self.approxParameters)
def _pruneBeforeEval(self, mu:paramList, field:str, append:bool,
prune:bool) -> Tuple[paramList, Np1D, Np1D, bool]:
mu = checkParameterList(mu, self.npar)[0]
idx = np.empty(len(mu), dtype = np.int)
if prune:
jExtra = np.zeros(len(mu), dtype = bool)
muExtra = emptyParameterList()
lastSolvedMus = getattr(self, "lastSolved" + field)
if (len(mu) > 0 and len(mu) == len(lastSolvedMus)
and mu == lastSolvedMus):
idx = np.arange(len(mu), dtype = np.int)
return muExtra, jExtra, idx, True
muKeep = copy(muExtra)
for j in range(len(mu)):
jPos = lastSolvedMus.find(mu[j])
if jPos is not None:
idx[j] = jPos
muKeep.append(mu[j])
else:
jExtra[j] = True
muExtra.append(mu[j])
if len(muKeep) > 0 and not append:
lastSolvedu = getattr(self, "u" + field)
idx[~jExtra] = getattr(self.__class__, "set" + field)(self,
muKeep, lastSolvedu[idx[~jExtra]], append)
append = True
else:
jExtra = np.ones(len(mu), dtype = bool)
muExtra = mu
return muExtra, jExtra, idx, append
def _setObject(self, mu:paramList, field:str, object:sampList,
append:bool) -> List[int]:
newMus = checkParameterList(mu, self.npar)[0]
newObj = sampleList(object)
if append:
getattr(self, "lastSolved" + field).append(newMus)
getattr(self, "u" + field).append(newObj)
Ltot = len(getattr(self, "u" + field))
return list(range(Ltot - len(newObj), Ltot))
setattr(self, "lastSolved" + field, copy(newMus))
setattr(self, "u" + field, copy(newObj))
return list(range(len(getattr(self, "u" + field))))
def setHF(self, muHF:paramList, uHF:sampleList,
append : bool = False) -> List[int]:
"""Assign high fidelity solution."""
return self._setObject(muHF, "HF", uHF, append)
def evalHF(self, mu:paramList, append : bool = False,
prune : bool = True) -> List[int]:
"""
Find high fidelity solution with original parameters and arbitrary
parameter.
Args:
mu: Target parameter.
append(optional): Whether to append new HF solutions to old ones.
prune(optional): Whether to remove duplicates of already appearing
HF solutions.
"""
muExtra, jExtra, idx, append = self._pruneBeforeEval(mu, "HF", append,
prune)
if len(muExtra) > 0:
vbMng(self, "INIT", "Solving HF model for mu = {}.".format(mu),
15)
newuHFs = self.HFEngine.solve(muExtra)
vbMng(self, "DEL", "Done solving HF model.", 15)
idx[jExtra] = self.setHF(muExtra, newuHFs, append)
return list(idx)
def setApproxReduced(self, muApproxR:paramList, uApproxR:sampleList,
append : bool = False) -> List[int]:
"""Assign high fidelity solution."""
return self._setObject(muApproxR, "ApproxReduced", uApproxR, append)
def evalApproxReduced(self, mu:paramList, append : bool = False,
prune : bool = True) -> List[int]:
"""
Evaluate reduced representation of approximant at arbitrary parameter.
Args:
mu: Target parameter.
append(optional): Whether to append new HF solutions to old ones.
prune(optional): Whether to remove duplicates of already appearing
HF solutions.
"""
self.setupApprox()
muExtra, jExtra, idx, append = self._pruneBeforeEval(mu,
"ApproxReduced",
append, prune)
if len(muExtra) > 0:
newuApproxs = self.trainedModel.getApproxReduced(muExtra)
idx[jExtra] = self.setApproxReduced(muExtra, newuApproxs, append)
return list(idx)
def setApprox(self, muApprox:paramList, uApprox:sampleList,
append : bool = False) -> List[int]:
"""Assign high fidelity solution."""
return self._setObject(muApprox, "Approx", uApprox, append)
def evalApprox(self, mu:paramList, append : bool = False,
prune : bool = True) -> List[int]:
"""
Evaluate approximant at arbitrary parameter.
Args:
mu: Target parameter.
append(optional): Whether to append new HF solutions to old ones.
prune(optional): Whether to remove duplicates of already appearing
HF solutions.
"""
self.setupApprox()
muExtra, jExtra, idx, append = self._pruneBeforeEval(mu, "Approx",
append, prune)
if len(muExtra) > 0:
newuApproxs = self.trainedModel.getApprox(muExtra)
idx[jExtra] = self.setApprox(muExtra, newuApproxs, append)
return list(idx)
def getHF(self, mu:paramList, append : bool = False,
prune : bool = True) -> sampList:
"""
Get HF solution at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
HFsolution.
"""
mu = checkParameterList(mu, self.npar)[0]
idx = self.evalHF(mu, append = append, prune = prune)
return self.uHF(idx)
def getRHS(self, mu:paramList) -> sampList:
"""
Get linear system RHS at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Linear system RHS.
"""
return self.HFEngine.residual(mu, None)
def getApproxReduced(self, mu:paramList, append : bool = False,
prune : bool = True) -> sampList:
"""
Get approximant at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Reduced approximant.
"""
mu = checkParameterList(mu, self.npar)[0]
idx = self.evalApproxReduced(mu, append = append, prune = prune)
return self.uApproxReduced(idx)
def getApprox(self, mu:paramList, append : bool = False,
prune : bool = True) -> sampList:
"""
Get approximant at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Approximant.
"""
mu = checkParameterList(mu, self.npar)[0]
idx = self.evalApprox(mu, append = append, prune = prune)
return self.uApprox(idx)
def getRes(self, mu:paramList) -> sampList:
"""
Get residual at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Approximant residual.
"""
if not self.HFEngine.isCEye:
raise RROMPyException(("Residual of solution with non-scalar C "
"not computable."))
return self.HFEngine.residual(mu, self.getApprox(mu) / self.HFEngine.C)
def getErr(self, mu:paramList, append : bool = False,
prune : bool = True) -> sampList:
"""
Get error at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Approximant error.
"""
return (self.getApprox(mu, append = append, prune =prune)
- self.getHF(mu, append = append, prune = prune))
def normApprox(self, mu:paramList) -> float:
"""
Compute norm of approximant at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Target norm of approximant.
"""
if not (self.POD and self.HFEngine.isCEye):
return self.HFEngine.norm(self.getApprox(mu), is_state = False)
return np.linalg.norm(self.HFEngine.C * self.getApproxReduced(mu).data,
axis = 0)
def getPoles(self, *args, **kwargs) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
self.setupApprox()
vbMng(self, "INIT", "Computing poles of model.", 20)
poles = self.trainedModel.getPoles(*args, **kwargs)
vbMng(self, "DEL", "Done computing poles.", 20)
return poles
def storeTrainedModel(self, filenameBase : str = "trained_model",
forceNewFile : bool = True) -> str:
"""Store trained reduced model to file."""
self.setupApprox()
vbMng(self, "INIT", "Storing trained model to file.", 20)
if forceNewFile:
filename = getNewFilename(filenameBase, "pkl")
else:
filename = "{}.pkl".format(filenameBase)
pickleDump(self.trainedModel.data.__dict__, filename)
vbMng(self, "DEL", "Done storing trained model.", 20)
return filename
def loadTrainedModel(self, filename:str):
"""Load trained reduced model from file."""
vbMng(self, "INIT", "Loading pre-trained model from file.", 20)
datadict = pickleLoad(filename)
self.mu0 = datadict["mu0"]
self.scaleFactor = datadict["scaleFactor"]
self.mus = datadict["mus"]
- trainedModel = self.tModelType()
- trainedModel.verbosity = self.verbosity
- trainedModel.timestamp = self.timestamp
+ self.trainedModel = self.tModelType()
+ self.trainedModel.verbosity = self.verbosity
+ self.trainedModel.timestamp = self.timestamp
data, selfkeys = self.initializeModelData(datadict)
for key in selfkeys: setattr(self, key, datadict.pop(key))
approxParameters = datadict.pop("approxParameters")
data.approxParameters = copy(approxParameters)
for apkey in data.approxParameters.keys():
self._approxParameters[apkey] = approxParameters.pop(apkey)
setattr(self, "_" + apkey, self._approxParameters[apkey])
for key in datadict: setattr(data, key, datadict[key])
- trainedModel.data = data
- self.trainedModel = trainedModel
+ self.trainedModel.data = data
self._mode = RROMPy_FRAGILE
vbMng(self, "DEL", "Done loading pre-trained model.", 20)
diff --git a/rrompy/reduction_methods/base/trained_model/trained_model.py b/rrompy/reduction_methods/base/trained_model/trained_model.py
index 832e37a..90b7245 100644
--- a/rrompy/reduction_methods/base/trained_model/trained_model.py
+++ b/rrompy/reduction_methods/base/trained_model/trained_model.py
@@ -1,95 +1,109 @@
# 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 .
#
from abc import abstractmethod
from rrompy.utilities.base.types import Np1D, paramList, sampList
from rrompy.parameter import checkParameterList
from rrompy.sampling import emptySampleList
__all__ = ['TrainedModel']
class TrainedModel:
"""
ABSTRACT
ROM approximant evaluation.
Attributes:
Data: dictionary with all that can be pickled.
"""
def name(self) -> str:
return self.__class__.__name__
def __str__(self) -> str:
return self.name()
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def reset(self):
self.lastSolvedApproxReduced = None
self.lastSolvedApprox = None
+ def collapse(self):
+ self.data._nmus = self.data.projMat.shape[1]
+ self.data.projMat = 1.
+ self.data._collapsed = True
+
@property
def npar(self):
"""Number of parameters."""
return self.data.mu0.shape[1]
+ @property
+ def nmus(self):
+ """Number of samples."""
+ if self.data._collapsed: return self.data._nmus
+ return self.data.projMat.shape[1]
+
@abstractmethod
def getApproxReduced(self, mu : paramList = []) -> sampList:
"""
Evaluate reduced representation of approximant at arbitrary parameter.
(ABSTRACT)
Args:
mu: Target parameter.
"""
pass
def getApprox(self, mu : paramList = []) -> sampList:
"""
Evaluate approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
mu = checkParameterList(mu, self.data.npar)[0]
if (not hasattr(self, "lastSolvedApprox")
or self.lastSolvedApprox != mu):
uApproxR = self.getApproxReduced(mu)
- self.uApprox = emptySampleList()
- for i in range(len(mu)):
- uApp = self.data.projMat[:, : uApproxR.shape[0]].dot(
+ if self.data._collapsed:
+ self.uApprox = uApproxR
+ else:
+ self.uApprox = emptySampleList()
+ for i in range(len(mu)):
+ uApp = self.data.projMat[:, : uApproxR.shape[0]].dot(
uApproxR[i])
- if i == 0:
- self.uApprox.reset((len(uApp), len(mu)),
- dtype = uApp.dtype)
- self.uApprox[i] = uApp
+ if i == 0:
+ self.uApprox.reset((len(uApp), len(mu)),
+ dtype = uApp.dtype)
+ self.uApprox[i] = uApp
self.lastSolvedApprox = mu
return self.uApprox
@abstractmethod
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
pass
diff --git a/rrompy/reduction_methods/base/trained_model/trained_model_data.py b/rrompy/reduction_methods/base/trained_model/trained_model_data.py
index 00dd780..08485ba 100644
--- a/rrompy/reduction_methods/base/trained_model/trained_model_data.py
+++ b/rrompy/reduction_methods/base/trained_model/trained_model_data.py
@@ -1,36 +1,37 @@
# 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 .
#
from copy import deepcopy as copy
from rrompy.utilities.base.types import Np2D, List, paramVal
from rrompy.utilities.exception_manager import RROMPyAssert
__all__ = ['TrainedModelData']
class TrainedModelData:
"""ROM approximant evaluation data (must be pickle-able)."""
- def __init__(self, mu0:paramVal, projMat:Np2D,
+ def __init__(self, mu0:paramVal, mus:paramVal, projMat:Np2D,
scaleFactor : List[float] = [1.],
rescalingExp : List[float] = [1.]):
self.npar = len(rescalingExp)
RROMPyAssert(mu0.shape[1], self.npar, "Number of parameters")
self.mu0 = mu0
+ self.mus = mus
self.projMat = copy(projMat)
self.scaleFactor = scaleFactor
self.rescalingExp = rescalingExp
-
+ self._collapsed = False
diff --git a/rrompy/reduction_methods/pivoted/__init__.py b/rrompy/reduction_methods/pivoted/__init__.py
index 32a578a..547f5fd 100644
--- a/rrompy/reduction_methods/pivoted/__init__.py
+++ b/rrompy/reduction_methods/pivoted/__init__.py
@@ -1,27 +1,35 @@
# 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 .
#
-from .rational_interpolant_pivoted import RationalInterpolantPivoted
-from .rational_interpolant_greedy_pivoted import RationalInterpolantGreedyPivoted
+from .rational_interpolant_pivoted import (RationalInterpolantPivotedNoMatch,
+ RationalInterpolantPivoted,
+ RationalInterpolantPivotedNearestNeighbor)
+from .rational_interpolant_greedy_pivoted import (RationalInterpolantGreedyPivotedNoMatch,
+ RationalInterpolantGreedyPivoted,
+ RationalInterpolantGreedyPivotedNearestNeighbor)
__all__ = [
+ 'RationalInterpolantPivotedNoMatch',
'RationalInterpolantPivoted',
- 'RationalInterpolantGreedyPivoted'
+ 'RationalInterpolantPivotedNearestNeighbor',
+ 'RationalInterpolantGreedyPivotedNoMatch',
+ 'RationalInterpolantGreedyPivoted',
+ 'RationalInterpolantGreedyPivotedNearestNeighbor'
]
diff --git a/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py b/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py
index 5d71dde..be80ec0 100644
--- a/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py
+++ b/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py
@@ -1,493 +1,712 @@
# 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.reduction_methods.base.generic_approximant import (
GenericApproximant)
-from rrompy.utilities.poly_fitting.polynomial import polybases as ppb
-from rrompy.utilities.poly_fitting.radial_basis import polybases as rbpb
-from rrompy.utilities.poly_fitting.moving_least_squares import (
- polybases as mlspb)
from rrompy.sampling import (SamplingEnginePivoted, SamplingEnginePivotedPOD,
SamplingEnginePivotedPODGlobal)
+from rrompy.utilities.poly_fitting.polynomial import polybases as ppb
+from rrompy.utilities.poly_fitting.radial_basis import polybases as rbpb
from rrompy.utilities.base.types import paramList, ListAny
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical.degree import reduceDegreeN
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPyWarning)
-__all__ = ['GenericPivotedApproximant', 'PODGlobal']
+__all__ = ['GenericPivotedApproximantNoMatch', 'GenericPivotedApproximant',
+ 'GenericPivotedApproximantNearestNeighbor', 'PODGlobal']
PODGlobal = 2
-class GenericPivotedApproximant(GenericApproximant):
- """
- ROM pivoted approximant (with pole matching) computation for parametric
- problems (ABSTRACT).
-
- Args:
- HFEngine: HF problem solver.
- mu0(optional): Default parameter. Defaults to 0.
- directionPivot(optional): Pivot components. Defaults to [0].
- approxParameters(optional): Dictionary containing values for main
- parameters of approximant. Recognized keys are:
- - 'POD': whether to compute POD of snapshots; defaults to True;
- - 'scaleFactorDer': scaling factors for derivative computation;
- defaults to 'AUTO';
- - 'matchingWeight': weight for pole matching optimization; defaults
- to 1;
- - 'cutOffTolerance': tolerance for ignoring parasitic poles;
- defaults to np.inf;
- - 'cutOffSharedRatio': required ratio of marginal points to share
- resonance in cut off strategy; defaults to 1.;
- - 'S': total number of pivot samples current approximant relies
- upon;
- - 'samplerPivot': pivot sample point generator;
- - 'SMarginal': total number of marginal samples current approximant
- relies upon;
- - 'samplerMarginal': marginal sample point generator;
- - 'polybasisMarginal': type of polynomial basis for marginal
- interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
- and 'LEGENDRE'; defaults to 'MONOMIAL';
- - 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
- i.e. maximum allowed;
- - 'polydegreetypeMarginal': type of polynomial degree for marginal;
- defaults to 'TOTAL';
- - 'radialDirectionalWeightsMarginal': radial basis weights for
- marginal interpolant; defaults to 1;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows; defaults to -1;
- - 'interpRcondMarginal': tolerance for marginal interpolation;
- defaults to None.
- Defaults to empty dict.
- approx_state(optional): Whether to approximate state. Defaults to
- False.
- verbosity(optional): Verbosity level. Defaults to 10.
-
- Attributes:
- HFEngine: HF problem solver.
- mu0: Default parameter.
- directionPivot: Pivot components.
- mus: Array of snapshot parameters.
- musMarginal: Array of marginal snapshot parameters.
- approxParameters: Dictionary containing values for main parameters of
- approximant. Recognized keys are in parameterList.
- parameterListSoft: Recognized keys of soft approximant parameters:
- - 'POD': whether to compute POD of snapshots;
- - 'scaleFactorDer': scaling factors for derivative computation;
- - 'matchingWeight': weight for pole matching optimization;
- - 'cutOffTolerance': tolerance for ignoring parasitic poles;
- - 'cutOffSharedRatio': required ratio of marginal points to share
- resonance in cut off strategy;
- - 'polybasisMarginal': type of polynomial basis for marginal
- interpolation;
- - 'MMarginal': degree of marginal interpolant;
- - 'polydegreetypeMarginal': type of polynomial degree for marginal;
- - 'radialDirectionalWeightsMarginal': radial basis weights for
- marginal interpolant;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows;
- - 'interpRcondMarginal': tolerance for marginal interpolation.
- parameterListCritical: Recognized keys of critical approximant
- parameters:
- - 'S': total number of pivot samples current approximant relies
- upon;
- - 'samplerPivot': pivot sample point generator;
- - 'SMarginal': total number of marginal samples current approximant
- relies upon;
- - 'samplerMarginal': marginal sample point generator.
- approx_state: Whether to approximate state.
- verbosity: Verbosity level.
- POD: Whether to compute POD of snapshots.
- scaleFactorDer: Scaling factors for derivative computation.
- matchingWeight: Weight for pole matching optimization.
- cutOffTolerance: Tolerance for ignoring parasitic poles.
- cutOffSharedRatio: Required ratio of marginal points to share resonance
- in cut off strategy.
- S: Total number of pivot samples current approximant relies upon.
- samplerPivot: Pivot sample point generator.
- SMarginal: Total number of marginal samples current approximant relies
- upon.
- samplerMarginal: Marginal sample point generator.
- polybasisMarginal: Type of polynomial basis for marginal interpolation.
- MMarginal: Degree of marginal interpolant.
- polydegreetypeMarginal: Type of polynomial degree for marginal.
- radialDirectionalWeightsMarginal: Radial basis weights for marginal
- interpolant.
- nNearestNeighborMarginal: Number of marginal nearest neighbors
- considered if polybasisMarginal allows.
- interpRcondMarginal: Tolerance for marginal interpolation.
- muBounds: list of bounds for pivot parameter values.
- muBoundsMarginal: list of bounds for marginal parameter values.
- samplingEngine: Sampling engine.
- uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
- sampleList.
- lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
- solution(s) as parameterList.
- uApproxReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApprox as sampleList.
- lastSolvedApproxReduced: Parameter(s) corresponding to last computed
- reduced approximate solution(s) as parameterList.
- uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
- sampleList.
- lastSolvedApprox: Parameter(s) corresponding to last computed
- approximate solution(s) as parameterList.
- """
-
+class GenericPivotedApproximantBase(GenericApproximant):
def __init__(self, directionPivot:ListAny, *args, **kwargs):
self._preInit()
if len(directionPivot) > 1:
raise RROMPyException(("Exactly 1 pivot parameter allowed in pole "
"matching."))
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
QSBase = QS([[0], [1]], "UNIFORM")
- self._addParametersToList(["matchingWeight", "cutOffTolerance",
- "cutOffSharedRatio", "polybasisMarginal",
- "MMarginal", "polydegreetypeMarginal",
+ self._addParametersToList(["cutOffTolerance",
"radialDirectionalWeightsMarginal",
- "nNearestNeighborMarginal",
- "interpRcondMarginal"],
- [1., np.inf, 1., "MONOMIAL", "AUTO", "TOTAL",
- [1.], -1, -1], ["samplerPivot", "SMarginal",
- "samplerMarginal"],
- [QSBase, [1], QSBase])
+ "interpRcondMarginal"], [np.inf, [1.], -1],
+ ["samplerPivot", "SMarginal",
+ "samplerMarginal"], [QSBase, [1], QSBase])
del QS
self._directionPivot = directionPivot
super().__init__(*args, **kwargs)
self._postInit()
- @property
- def tModelType(self):
- from .trained_model.trained_model_pivoted import TrainedModelPivoted
- return TrainedModelPivoted
-
def setupSampling(self):
"""Setup sampling engine."""
RROMPyAssert(self._mode, message = "Cannot setup sampling engine.")
if not hasattr(self, "_POD") or self._POD is None: return
if self.POD:
if self.POD == PODGlobal:
SamplingEngine = SamplingEnginePivotedPODGlobal
else:
SamplingEngine = SamplingEnginePivotedPOD
else:
SamplingEngine = SamplingEnginePivoted
self.samplingEngine = SamplingEngine(self.HFEngine,
self.directionPivot,
sample_state = self.approx_state,
verbosity = self.verbosity)
def initializeModelData(self, datadict):
if "directionPivot" in datadict.keys():
from .trained_model.trained_model_pivoted_data import (
TrainedModelPivotedData)
- return (TrainedModelPivotedData(datadict["mu0"],
+ return (TrainedModelPivotedData(datadict["mu0"], datadict["mus"],
datadict.pop("projMat"),
datadict["scaleFactor"],
datadict.pop("rescalingExp"),
datadict["directionPivot"]),
["mu0", "scaleFactor", "directionPivot", "mus"])
else:
return super().initializeModelData(datadict)
@property
def npar(self):
"""Number of parameters."""
if hasattr(self, "_temporaryPivot"): return self.nparPivot
return super().npar
@property
def mus(self):
"""Value of mus. Its assignment may reset snapshots."""
return self._mus
@mus.setter
def mus(self, mus):
musOld = copy(self.mus) if hasattr(self, '_mus') else None
if (musOld is None or len(mus) != len(musOld) or not mus == musOld):
self.resetSamples()
self._mus = mus
- @property
- def matchingWeight(self):
- """Value of matchingWeight."""
- return self._matchingWeight
- @matchingWeight.setter
- def matchingWeight(self, matchingWeight):
- self._matchingWeight = matchingWeight
- self._approxParameters["matchingWeight"] = self.matchingWeight
-
@property
def cutOffTolerance(self):
"""Value of cutOffTolerance."""
return self._cutOffTolerance
@cutOffTolerance.setter
def cutOffTolerance(self, cutOffTolerance):
self._cutOffTolerance = cutOffTolerance
self._approxParameters["cutOffTolerance"] = self.cutOffTolerance
- @property
- def cutOffSharedRatio(self):
- """Value of cutOffSharedRatio."""
- return self._cutOffSharedRatio
- @cutOffSharedRatio.setter
- def cutOffSharedRatio(self, cutOffSharedRatio):
- if cutOffSharedRatio > 1.:
- RROMPyWarning("Cut off shared ratio too large. Clipping to 1.")
- cutOffSharedRatio = 1.
- elif cutOffSharedRatio < 0.:
- RROMPyWarning("Cut off shared ratio too small. Clipping to 0.")
- cutOffSharedRatio = 0.
- self._cutOffSharedRatio = cutOffSharedRatio
- self._approxParameters["cutOffSharedRatio"] = self.cutOffSharedRatio
-
@property
def SMarginal(self):
"""Value of SMarginal."""
return self._SMarginal
@SMarginal.setter
def SMarginal(self, SMarginal):
if SMarginal <= 0:
raise RROMPyException("SMarginal must be positive.")
if hasattr(self, "_SMarginal") and self._SMarginal is not None:
Sold = self.SMarginal
else: Sold = -1
self._SMarginal = SMarginal
self._approxParameters["SMarginal"] = self.SMarginal
if Sold != self.SMarginal: self.resetSamples()
- @property
- def polybasisMarginal(self):
- """Value of polybasisMarginal."""
- return self._polybasisMarginal
- @polybasisMarginal.setter
- def polybasisMarginal(self, polybasisMarginal):
- try:
- polybasisMarginal = polybasisMarginal.upper().strip().replace(" ",
- "")
- if polybasisMarginal not in ppb + rbpb + mlspb:
- raise RROMPyException(
- "Prescribed marginal polybasis not recognized.")
- self._polybasisMarginal = polybasisMarginal
- except:
- RROMPyWarning(("Prescribed marginal polybasis not recognized. "
- "Overriding to 'MONOMIAL'."))
- self._polybasisMarginal = "MONOMIAL"
- self._approxParameters["polybasisMarginal"] = self.polybasisMarginal
-
- @property
- def MMarginal(self):
- """Value of MMarginal."""
- return self._MMarginal
- @MMarginal.setter
- def MMarginal(self, MMarginal):
- if isinstance(MMarginal, str):
- MMarginal = MMarginal.strip().replace(" ","")
- if "-" not in MMarginal: MMarginal = MMarginal + "-0"
- self._MMarginal_isauto = True
- self._MMarginal_shift = int(MMarginal.split("-")[-1])
- MMarginal = 0
- if MMarginal < 0:
- raise RROMPyException("MMarginal must be non-negative.")
- self._MMarginal = MMarginal
- self._approxParameters["MMarginal"] = self.MMarginal
-
- def _setMMarginalAuto(self):
- self.MMarginal = max(0, reduceDegreeN(
- len(self.musMarginal), len(self.musMarginal),
- self.nparMarginal, self.polydegreetypeMarginal
- ) - self._MMarginal_shift)
- vbMng(self, "MAIN", ("Automatically setting MMarginal to "
- "{}.").format(self.MMarginal), 25)
-
- @property
- def polydegreetypeMarginal(self):
- """Value of polydegreetypeMarginal."""
- return self._polydegreetypeMarginal
- @polydegreetypeMarginal.setter
- def polydegreetypeMarginal(self, polydegreetypeM):
- try:
- polydegreetypeM = polydegreetypeM.upper().strip().replace(" ","")
- if polydegreetypeM not in ["TOTAL", "FULL"]:
- raise RROMPyException(("Prescribed polydegreetypeMarginal not "
- "recognized."))
- self._polydegreetypeMarginal = polydegreetypeM
- except:
- RROMPyWarning(("Prescribed polydegreetypeMarginal not recognized. "
- "Overriding to 'TOTAL'."))
- self._polydegreetypeMarginal = "TOTAL"
- self._approxParameters["polydegreetypeMarginal"] = (
- self.polydegreetypeMarginal)
-
@property
def radialDirectionalWeightsMarginal(self):
"""Value of radialDirectionalWeightsMarginal."""
return self._radialDirectionalWeightsMarginal
@radialDirectionalWeightsMarginal.setter
def radialDirectionalWeightsMarginal(self, radialDirWeightsMarginal):
if not hasattr(radialDirWeightsMarginal, "__len__"):
radialDirWeightsMarginal = [radialDirWeightsMarginal]
self._radialDirectionalWeightsMarginal = radialDirWeightsMarginal
self._approxParameters["radialDirectionalWeightsMarginal"] = (
self.radialDirectionalWeightsMarginal)
- @property
- def nNearestNeighborMarginal(self):
- """Value of nNearestNeighborMarginal."""
- return self._nNearestNeighborMarginal
- @nNearestNeighborMarginal.setter
- def nNearestNeighborMarginal(self, nNearestNeighborMarginal):
- self._nNearestNeighborMarginal = nNearestNeighborMarginal
- self._approxParameters["nNearestNeighborMarginal"] = (
- self.nNearestNeighborMarginal)
-
@property
def interpRcondMarginal(self):
"""Value of interpRcondMarginal."""
return self._interpRcondMarginal
@interpRcondMarginal.setter
def interpRcondMarginal(self, interpRcondMarginal):
self._interpRcondMarginal = interpRcondMarginal
self._approxParameters["interpRcondMarginal"] = (
self.interpRcondMarginal)
@property
def directionPivot(self):
"""Value of directionPivot. Its assignment may reset snapshots."""
return self._directionPivot
@directionPivot.setter
def directionPivot(self, directionPivot):
if hasattr(self, '_directionPivot'):
directionPivotOld = copy(self.directionPivot)
else:
directionPivotOld = None
if (directionPivotOld is None
or len(directionPivot) != len(directionPivotOld)
or not directionPivot == directionPivotOld):
self.resetSamples()
self._directionPivot = directionPivot
@property
def directionMarginal(self):
return [x for x in range(self.HFEngine.npar) \
if x not in self.directionPivot]
@property
def nparPivot(self):
return len(self.directionPivot)
@property
def nparMarginal(self):
return self.npar - self.nparPivot
@property
def rescalingExpPivot(self):
return [self.HFEngine.rescalingExp[x] for x in self.directionPivot]
@property
def rescalingExpMarginal(self):
return [self.HFEngine.rescalingExp[x] for x in self.directionMarginal]
@property
def muBounds(self):
"""Value of muBounds."""
return self.samplerPivot.lims
@property
def muBoundsMarginal(self):
"""Value of muBoundsMarginal."""
return self.samplerMarginal.lims
+ @property
+ def sampler(self):
+ """Proxy of samplerPivot."""
+ return self._samplerPivot
+
@property
def samplerPivot(self):
"""Value of samplerPivot."""
return self._samplerPivot
@samplerPivot.setter
def samplerPivot(self, samplerPivot):
if 'generatePoints' not in dir(samplerPivot):
raise RROMPyException("Pivot sampler type not recognized.")
if hasattr(self, '_samplerPivot') and self._samplerPivot is not None:
samplerOld = self.samplerPivot
self._samplerPivot = samplerPivot
- self._approxParameters["samplerPivot"] = self.samplerPivot.__str__()
+ self._approxParameters["samplerPivot"] = self.samplerPivot
if not 'samplerOld' in locals() or samplerOld != self.samplerPivot:
self.resetSamples()
@property
def samplerMarginal(self):
"""Value of samplerMarginal."""
return self._samplerMarginal
@samplerMarginal.setter
def samplerMarginal(self, samplerMarginal):
if 'generatePoints' not in dir(samplerMarginal):
raise RROMPyException("Marginal sampler type not recognized.")
if (hasattr(self, '_samplerMarginal')
and self._samplerMarginal is not None):
samplerOld = self.samplerMarginal
self._samplerMarginal = samplerMarginal
- self._approxParameters["samplerMarginal"] = (
- self.samplerMarginal.__str__())
+ self._approxParameters["samplerMarginal"] = self.samplerMarginal
if not 'samplerOld' in locals() or samplerOld != self.samplerMarginal:
self.resetSamples()
def setSamples(self, samplingEngine):
"""Copy samplingEngine and samples."""
self.mus = copy(samplingEngine.mus[0])
for sEj in samplingEngine.mus[1:]:
self.mus.append(sEj)
super().setSamples(samplingEngine)
- def _finalizeMarginalization(self):
- vbMng(self, "INIT", "Recompressing by cut off.", 10)
- msg = self.trainedModel.recompressByCutOff(self.cutOffTolerance,
- self.cutOffSharedRatio,
- self.samplerPivot.normalFoci(),
- self.samplerPivot.groundPotential())
- vbMng(self, "DEL", "Done recompressing." + msg, 10)
- interpPars = [self.verbosity >= 5,
- self.polydegreetypeMarginal == "TOTAL", {}]
- if self.polybasisMarginal not in ppb:
- interpPars[-1]["nNearestNeighbor"] = self.nNearestNeighborMarginal
- if self.polybasisMarginal in ppb + rbpb:
- interpPars += [{"rcond": self.interpRcondMarginal}]
- self.trainedModel.setupMarginalInterp(self, interpPars,
- hasattr(self, "_MMarginal_isauto"),
- self.radialDirectionalWeightsMarginal,
- hasattr(self, "_reduceDegreeNNoWarn"))
- self.trainedModel.data.approxParameters = copy(self.approxParameters)
-
def computeScaleFactor(self):
"""Compute parameter rescaling factor."""
RROMPyAssert(self._mode, message = "Cannot compute rescaling factor.")
self.scaleFactorPivot = .5 * np.abs(
self.muBounds[0] ** self.rescalingExpPivot
- self.muBounds[1] ** self.rescalingExpPivot)
self.scaleFactorMarginal = .5 * np.abs(
self.muBoundsMarginal[0] ** self.rescalingExpMarginal
- self.muBoundsMarginal[1] ** self.rescalingExpMarginal)
self.scaleFactor = np.empty(self.npar)
self.scaleFactor[self.directionPivot] = self.scaleFactorPivot
self.scaleFactor[self.directionMarginal] = self.scaleFactorMarginal
def normApprox(self, mu:paramList) -> float:
_PODOld = self.POD
self._POD = self.POD == PODGlobal
result = super().normApprox(mu)
self._POD = _PODOld
return result
-
+
+class GenericPivotedApproximantNoMatch(GenericPivotedApproximantBase):
+ """
+ ROM pivoted approximant (without pole matching) computation for parametric
+ problems (ABSTRACT).
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcondMarginal': tolerance for marginal interpolation.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginal: Marginal sample point generator.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
+
+ @property
+ def tModelType(self):
+ from .trained_model.trained_model_pivoted_rational_nomatch import (
+ TrainedModelPivotedRationalNoMatch)
+ return TrainedModelPivotedRationalNoMatch
+ def _finalizeMarginalization(self):
+ vbMng(self, "INIT", "Recompressing by cut off.", 10)
+ msg = self.trainedModel.recompressByCutOff(self.cutOffTolerance,
+ self.samplerPivot.normalFoci(),
+ self.samplerPivot.groundPotential())
+ vbMng(self, "DEL", "Done recompressing." + msg, 10)
+ self.trainedModel.setupMarginalInterp(
+ self.radialDirectionalWeightsMarginal)
+ self.trainedModel.data.approxParameters = copy(self.approxParameters)
+
+class GenericPivotedApproximant(GenericPivotedApproximantBase):
+ """
+ ROM pivoted approximant (with pole matching) computation for parametric
+ problems (ABSTRACT).
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator;
+ - 'polybasisMarginal': type of polynomial basis for marginal
+ interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
+ and 'LEGENDRE'; defaults to 'MONOMIAL';
+ - 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
+ i.e. maximum allowed;
+ - 'polydegreetypeMarginal': type of polynomial degree for marginal;
+ defaults to 'TOTAL';
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'polybasisMarginal': type of polynomial basis for marginal
+ interpolation;
+ - 'MMarginal': degree of marginal interpolant;
+ - 'polydegreetypeMarginal': type of polynomial degree for marginal;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcondMarginal': tolerance for marginal interpolation.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginal: Marginal sample point generator.
+ polybasisMarginal: Type of polynomial basis for marginal interpolation.
+ MMarginal: Degree of marginal interpolant.
+ polydegreetypeMarginal: Type of polynomial degree for marginal.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
+
+ def __init__(self, *args, **kwargs):
+ self._preInit()
+ self._addParametersToList(["matchingWeight", "cutOffSharedRatio",
+ "polybasisMarginal", "MMarginal",
+ "polydegreetypeMarginal"],
+ [1., 1., "MONOMIAL", "AUTO", "TOTAL"])
+ super().__init__(*args, **kwargs)
+ self._postInit()
+
+ @property
+ def tModelType(self):
+ from .trained_model.trained_model_pivoted_rational import (
+ TrainedModelPivotedRational)
+ return TrainedModelPivotedRational
+
+ @property
+ def matchingWeight(self):
+ """Value of matchingWeight."""
+ return self._matchingWeight
+ @matchingWeight.setter
+ def matchingWeight(self, matchingWeight):
+ self._matchingWeight = matchingWeight
+ self._approxParameters["matchingWeight"] = self.matchingWeight
+
+ @property
+ def cutOffSharedRatio(self):
+ """Value of cutOffSharedRatio."""
+ return self._cutOffSharedRatio
+ @cutOffSharedRatio.setter
+ def cutOffSharedRatio(self, cutOffSharedRatio):
+ if cutOffSharedRatio > 1.:
+ RROMPyWarning("Cut off shared ratio too large. Clipping to 1.")
+ cutOffSharedRatio = 1.
+ elif cutOffSharedRatio < 0.:
+ RROMPyWarning("Cut off shared ratio too small. Clipping to 0.")
+ cutOffSharedRatio = 0.
+ self._cutOffSharedRatio = cutOffSharedRatio
+ self._approxParameters["cutOffSharedRatio"] = self.cutOffSharedRatio
+
+ @property
+ def polybasisMarginal(self):
+ """Value of polybasisMarginal."""
+ return self._polybasisMarginal
+ @polybasisMarginal.setter
+ def polybasisMarginal(self, polybasisMarginal):
+ try:
+ polybasisMarginal = polybasisMarginal.upper().strip().replace(" ",
+ "")
+ if polybasisMarginal not in ppb + rbpb:
+ raise RROMPyException(
+ "Prescribed marginal polybasis not recognized.")
+ self._polybasisMarginal = polybasisMarginal
+ except:
+ RROMPyWarning(("Prescribed marginal polybasis not recognized. "
+ "Overriding to 'MONOMIAL'."))
+ self._polybasisMarginal = "MONOMIAL"
+ self._approxParameters["polybasisMarginal"] = self.polybasisMarginal
+
+ @property
+ def MMarginal(self):
+ """Value of MMarginal."""
+ return self._MMarginal
+ @MMarginal.setter
+ def MMarginal(self, MMarginal):
+ if isinstance(MMarginal, str):
+ MMarginal = MMarginal.strip().replace(" ","")
+ if "-" not in MMarginal: MMarginal = MMarginal + "-0"
+ self._MMarginal_isauto = True
+ self._MMarginal_shift = int(MMarginal.split("-")[-1])
+ MMarginal = 0
+ if MMarginal < 0:
+ raise RROMPyException("MMarginal must be non-negative.")
+ self._MMarginal = MMarginal
+ self._approxParameters["MMarginal"] = self.MMarginal
+
+ def _setMMarginalAuto(self):
+ self.MMarginal = max(0, reduceDegreeN(
+ len(self.musMarginal), len(self.musMarginal),
+ self.nparMarginal, self.polydegreetypeMarginal
+ ) - self._MMarginal_shift)
+ vbMng(self, "MAIN", ("Automatically setting MMarginal to "
+ "{}.").format(self.MMarginal), 25)
+
+ @property
+ def polydegreetypeMarginal(self):
+ """Value of polydegreetypeMarginal."""
+ return self._polydegreetypeMarginal
+ @polydegreetypeMarginal.setter
+ def polydegreetypeMarginal(self, polydegreetypeM):
+ try:
+ polydegreetypeM = polydegreetypeM.upper().strip().replace(" ","")
+ if polydegreetypeM not in ["TOTAL", "FULL"]:
+ raise RROMPyException(("Prescribed polydegreetypeMarginal not "
+ "recognized."))
+ self._polydegreetypeMarginal = polydegreetypeM
+ except:
+ RROMPyWarning(("Prescribed polydegreetypeMarginal not recognized. "
+ "Overriding to 'TOTAL'."))
+ self._polydegreetypeMarginal = "TOTAL"
+ self._approxParameters["polydegreetypeMarginal"] = (
+ self.polydegreetypeMarginal)
+
+ def _finalizeMarginalization(self):
+ vbMng(self, "INIT", "Recompressing by cut off.", 10)
+ msg = self.trainedModel.recompressByCutOff(self.cutOffTolerance,
+ self.cutOffSharedRatio,
+ self.samplerPivot.normalFoci(),
+ self.samplerPivot.groundPotential())
+ vbMng(self, "DEL", "Done recompressing." + msg, 10)
+ interpPars = [self.verbosity >= 5,
+ self.polydegreetypeMarginal == "TOTAL", {}]
+ if self.polybasisMarginal in ppb + rbpb:
+ interpPars += [{"rcond": self.interpRcondMarginal}]
+ self.trainedModel.setupMarginalInterp(self, interpPars,
+ hasattr(self, "_MMarginal_isauto"),
+ self.radialDirectionalWeightsMarginal,
+ hasattr(self, "_reduceDegreeNNoWarn"))
+ self.trainedModel.data.approxParameters = copy(self.approxParameters)
+
+class GenericPivotedApproximantNearestNeighbor(GenericPivotedApproximant):
+ """
+ ROM pivoted approximant (with pole matching) computation for parametric
+ problems via local average (ABSTRACT).
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator;
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ defaults to 1;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginal: Marginal sample point generator.
+ nNeighborsMarginal: Number of marginal nearest neighbors.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
+
+ def __init__(self, *args, **kwargs):
+ self._preInit()
+ self._addParametersToList(["nNeighborsMarginal"], [1],
+ toBeExcluded = ["polybasisMarginal",
+ "MMarginal", "polydegreetypeMarginal",
+ "interpRcondMarginal"])
+ super().__init__(*args, **kwargs)
+ self._postInit()
+
+ @property
+ def tModelType(self):
+ from .trained_model.trained_model_pivoted_rational_nearest_neighbor \
+ import TrainedModelPivotedRationalNearestNeighbor
+ return TrainedModelPivotedRationalNearestNeighbor
+
+ @property
+ def nNeighborsMarginal(self):
+ """Value of nNeighborsMarginal."""
+ return self._nNeighborsMarginal
+ @nNeighborsMarginal.setter
+ def nNeighborsMarginal(self, nNeighborsMarginal):
+ self._nNeighborsMarginal = max(1, nNeighborsMarginal)
+ self._approxParameters["nNeighborsMarginal"] = self.nNeighborsMarginal
+
+ def _finalizeMarginalization(self):
+ vbMng(self, "INIT", "Recompressing by cut off.", 10)
+ msg = self.trainedModel.recompressByCutOff(self.cutOffTolerance,
+ self.cutOffSharedRatio,
+ self.samplerPivot.normalFoci(),
+ self.samplerPivot.groundPotential())
+ vbMng(self, "DEL", "Done recompressing." + msg, 10)
+ self.trainedModel.setupMarginalInterp(self.nNeighborsMarginal,
+ self.radialDirectionalWeightsMarginal)
+ self.trainedModel.data.approxParameters = copy(self.approxParameters)
+
diff --git a/rrompy/reduction_methods/pivoted/greedy/__init__.py b/rrompy/reduction_methods/pivoted/greedy/__init__.py
index 2caf41a..f1dcb30 100644
--- a/rrompy/reduction_methods/pivoted/greedy/__init__.py
+++ b/rrompy/reduction_methods/pivoted/greedy/__init__.py
@@ -1,27 +1,35 @@
# 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 .
#
-from .rational_interpolant_pivoted_greedy import RationalInterpolantPivotedGreedy
-from .rational_interpolant_greedy_pivoted_greedy import RationalInterpolantGreedyPivotedGreedy
+from .rational_interpolant_pivoted_greedy import (RationalInterpolantPivotedGreedyNoMatch,
+ RationalInterpolantPivotedGreedy,
+ RationalInterpolantPivotedGreedyNearestNeighbor)
+from .rational_interpolant_greedy_pivoted_greedy import (RationalInterpolantGreedyPivotedGreedyNoMatch,
+ RationalInterpolantGreedyPivotedGreedy,
+ RationalInterpolantGreedyPivotedGreedyNearestNeighbor)
__all__ = [
+ 'RationalInterpolantPivotedGreedyNoMatch',
'RationalInterpolantPivotedGreedy',
- 'RationalInterpolantGreedyPivotedGreedy'
+ 'RationalInterpolantPivotedGreedyNearestNeighbor',
+ 'RationalInterpolantGreedyPivotedGreedyNoMatch',
+ 'RationalInterpolantGreedyPivotedGreedy',
+ 'RationalInterpolantGreedyPivotedGreedyNearestNeighbor'
]
diff --git a/rrompy/reduction_methods/pivoted/greedy/generic_pivoted_greedy_approximant.py b/rrompy/reduction_methods/pivoted/greedy/generic_pivoted_greedy_approximant.py
index debfe17..e38b5e0 100644
--- a/rrompy/reduction_methods/pivoted/greedy/generic_pivoted_greedy_approximant.py
+++ b/rrompy/reduction_methods/pivoted/greedy/generic_pivoted_greedy_approximant.py
@@ -1,724 +1,986 @@
# 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 .
#
from abc import abstractmethod
from copy import deepcopy as copy
import numpy as np
from matplotlib import pyplot as plt
from rrompy.reduction_methods.pivoted.generic_pivoted_approximant import (
- GenericPivotedApproximant, PODGlobal)
+ GenericPivotedApproximantBase,
+ GenericPivotedApproximantNoMatch,
+ GenericPivotedApproximant, PODGlobal,
+ GenericPivotedApproximantNearestNeighbor)
from rrompy.utilities.base.types import (Np1D, Tuple, List, paramVal,
paramList, ListAny)
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical import dot
from rrompy.utilities.numerical.point_matching import (pointMatching,
chordalMetricAdjusted, potential)
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPyWarning)
from rrompy.parameter import checkParameterList, emptyParameterList
-__all__ = ['GenericPivotedGreedyApproximant']
-
-class GenericPivotedGreedyApproximant(GenericPivotedApproximant):
- """
- ROM pivoted greedy interpolant computation for parametric problems
- (ABSTRACT).
-
- Args:
- HFEngine: HF problem solver.
- mu0(optional): Default parameter. Defaults to 0.
- directionPivot(optional): Pivot components. Defaults to [0].
- approxParameters(optional): Dictionary containing values for main
- parameters of approximant. Recognized keys are:
- - 'POD': whether to compute POD of snapshots; defaults to True;
- - 'scaleFactorDer': scaling factors for derivative computation;
- defaults to 'AUTO';
- - 'matchingWeight': weight for pole matching optimization; defaults
- to 1;
- - 'cutOffTolerance': tolerance for ignoring parasitic poles;
- defaults to np.inf;
- - 'cutOffSharedRatio': required ratio of marginal points to share
- resonance in cut off strategy; defaults to 1.;
- - 'matchingWeightError': weight for pole matching optimization in
- error estimation; defaults to 0;
- - 'cutOffToleranceError': tolerance for ignoring parasitic poles
- in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
- - 'S': total number of pivot samples current approximant relies
- upon;
- - 'samplerPivot': pivot sample point generator;
- - 'SMarginal': number of starting marginal samples;
- - 'samplerMarginalGrid': marginal sample point generator via sparse
- grid;
- - 'errorEstimatorKindMarginal': kind of marginal error estimator;
- available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD', and
- 'LOOK_AHEAD_RECOVER'; defaults to 'LEAVE_ONE_OUT';
- - 'polybasisMarginal': type of polynomial basis for marginal
- interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
- and 'LEGENDRE'; defaults to 'MONOMIAL';
- - 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
- i.e. maximum allowed;
- - 'greedyTolMarginal': uniform error tolerance for marginal greedy
- algorithm; defaults to 1e-1;
- - 'maxIterMarginal': maximum number of marginal greedy steps;
- defaults to 1e2;
- - 'polydegreetypeMarginal': type of polynomial degree for marginal;
- defaults to 'TOTAL';
- - 'radialDirectionalWeightsMarginal': radial basis weights for
- marginal interpolant; defaults to 1;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows; defaults to -1;
- - 'interpRcondMarginal': tolerance for marginal interpolation;
- defaults to None.
- Defaults to empty dict.
- approx_state(optional): Whether to approximate state. Defaults to
- False.
- verbosity(optional): Verbosity level. Defaults to 10.
-
- Attributes:
- HFEngine: HF problem solver.
- mu0: Default parameter.
- directionPivot: Pivot components.
- mus: Array of snapshot parameters.
- musMarginal: Array of marginal snapshot parameters.
- approxParameters: Dictionary containing values for main parameters of
- approximant. Recognized keys are in parameterList.
- parameterListSoft: Recognized keys of soft approximant parameters:
- - 'POD': whether to compute POD of snapshots;
- - 'scaleFactorDer': scaling factors for derivative computation;
- - 'matchingWeight': weight for pole matching optimization;
- - 'cutOffTolerance': tolerance for ignoring parasitic poles;
- - 'cutOffSharedRatio': required ratio of marginal points to share
- resonance in cut off strategy;
- - 'matchingWeightError': weight for pole matching optimization in
- error estimation;
- - 'cutOffToleranceError': tolerance for ignoring parasitic poles
- in error estimation;
- - 'errorEstimatorKindMarginal': kind of marginal error estimator;
- - 'polybasisMarginal': type of polynomial basis for marginal
- interpolation;
- - 'MMarginal': degree of marginal interpolant;
- - 'greedyTolMarginal': uniform error tolerance for marginal greedy
- algorithm;
- - 'maxIterMarginal': maximum number of marginal greedy steps;
- - 'polydegreetypeMarginal': type of polynomial degree for marginal;
- - 'radialDirectionalWeightsMarginal': radial basis weights for
- marginal interpolant;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows;
- - 'interpRcondMarginal': tolerance for marginal interpolation.
- parameterListCritical: Recognized keys of critical approximant
- parameters:
- - 'S': total number of pivot samples current approximant relies
- upon;
- - 'samplerPivot': pivot sample point generator;
- - 'SMarginal': total number of marginal samples current approximant
- relies upon;
- - 'samplerMarginalGrid': marginal sample point generator via sparse
- grid.
- approx_state: Whether to approximate state.
- verbosity: Verbosity level.
- POD: Whether to compute POD of snapshots.
- scaleFactorDer: Scaling factors for derivative computation.
- matchingWeight: Weight for pole matching optimization.
- cutOffTolerance: Tolerance for ignoring parasitic poles.
- cutOffSharedRatio: Required ratio of marginal points to share resonance
- in cut off strategy.
- matchingWeightError: Weight for pole matching optimization in error
- estimation.
- cutOffToleranceError: Tolerance for ignoring parasitic poles in error
- estimation.
- S: Total number of pivot samples current approximant relies upon.
- samplerPivot: Pivot sample point generator.
- SMarginal: Total number of marginal samples current approximant relies
- upon.
- samplerMarginalGrid: Marginal sample point generator via sparse grid.
- errorEstimatorKindMarginal: Kind of marginal error estimator.
- polybasisMarginal: Type of polynomial basis for marginal interpolation.
- MMarginal: Degree of marginal interpolant.
- greedyTolMarginal: Uniform error tolerance for marginal greedy
- algorithm.
- maxIterMarginal: Maximum number of marginal greedy steps.
- polydegreetypeMarginal: Type of polynomial degree for marginal.
- radialDirectionalWeightsMarginal: Radial basis weights for marginal
- interpolant.
- nNearestNeighborMarginal: Number of marginal nearest neighbors
- considered if polybasisMarginal allows.
- interpRcondMarginal: Tolerance for marginal interpolation.
- muBounds: list of bounds for pivot parameter values.
- muBoundsMarginal: list of bounds for marginal parameter values.
- samplingEngine: Sampling engine.
- uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
- sampleList.
- lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
- solution(s) as parameterList.
- uApproxReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApprox as sampleList.
- lastSolvedApproxReduced: Parameter(s) corresponding to last computed
- reduced approximate solution(s) as parameterList.
- uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
- sampleList.
- lastSolvedApprox: Parameter(s) corresponding to last computed
- approximate solution(s) as parameterList.
- """
+__all__ = ['GenericPivotedGreedyApproximantNoMatch',
+ 'GenericPivotedGreedyApproximant',
+ 'GenericPivotedGreedyApproximantNearestNeighbor']
+class GenericPivotedGreedyApproximantBase(GenericPivotedApproximantBase):
_allowedEstimatorKindsMarginal = ["LEAVE_ONE_OUT", "LOOK_AHEAD",
- "LOOK_AHEAD_RECOVER"]
+ "LOOK_AHEAD_RECOVER", "NONE"]
def __init__(self, *args, **kwargs):
self._preInit()
- from rrompy.parameter import localSparseGrid as SG
+ from rrompy.parameter.parameter_sampling import SparseGridSampler as SG
SGBase = SG([[0.], [1.]], "UNIFORM")
self._addParametersToList(["matchingWeightError",
"cutOffToleranceError",
"errorEstimatorKindMarginal",
"greedyTolMarginal",
"maxIterMarginal"],
- [0., "AUTO", "LEAVE_ONE_OUT", 1e-1, 1e2],
+ [0., "AUTO", "NONE", 1e-1, 1e2],
["samplerMarginalGrid"], [SGBase],
toBeExcluded = ["samplerMarginal"])
super().__init__(*args, **kwargs)
self._postInit()
@property
def muBoundsMarginal(self):
"""Value of muBoundsMarginal."""
return self.samplerMarginalGrid.lims
@property
def scaleFactorDer(self):
"""Value of scaleFactorDer."""
if self._scaleFactorDer == "NONE": return 1.
if self._scaleFactorDer == "AUTO": return self._scaleFactorOldPivot
return self._scaleFactorDer
@scaleFactorDer.setter
def scaleFactorDer(self, scaleFactorDer):
if hasattr(self, "_scaleFactorDer"):
scaleFactorDerold = self.scaleFactorDer
else: scaleFactorDerold = -1
if isinstance(scaleFactorDer, (str,)):
scaleFactorDer = scaleFactorDer.upper()
self._scaleFactorDer = scaleFactorDer
self._approxParameters["scaleFactorDer"] = self._scaleFactorDer
if scaleFactorDerold != self._scaleFactorDer: self.resetSamples()
@property
def samplerMarginalGrid(self):
"""Value of samplerMarginalGrid."""
return self._samplerMarginalGrid
@samplerMarginalGrid.setter
def samplerMarginalGrid(self, samplerMarginalGrid):
if 'refine' not in dir(samplerMarginalGrid):
raise RROMPyException("Marginal sampler type not recognized.")
if (hasattr(self, '_samplerMarginalGrid')
and self._samplerMarginalGrid is not None):
samplerOld = self.samplerMarginalGrid
self._samplerMarginalGrid = samplerMarginalGrid
self._approxParameters["samplerMarginalGrid"] = (
- self.samplerMarginalGrid.__str__())
+ self.samplerMarginalGrid)
if (not 'samplerOld' in locals()
or samplerOld != self.samplerMarginalGrid):
self.resetSamples()
@property
def errorEstimatorKindMarginal(self):
"""Value of errorEstimatorKindMarginal."""
return self._errorEstimatorKindMarginal
@errorEstimatorKindMarginal.setter
def errorEstimatorKindMarginal(self, errorEstimatorKindMarginal):
errorEstimatorKindMarginal = errorEstimatorKindMarginal.upper()
if errorEstimatorKindMarginal not in (
self._allowedEstimatorKindsMarginal):
RROMPyWarning(("Marginal error estimator kind not recognized. "
- "Overriding to 'LEAVE_ONE_OUT'."))
- errorEstimatorKindMarginal = "LEAVE_ONE_OUT"
+ "Overriding to 'NONE'."))
+ errorEstimatorKindMarginal = "NONE"
self._errorEstimatorKindMarginal = errorEstimatorKindMarginal
self._approxParameters["errorEstimatorKindMarginal"] = (
self.errorEstimatorKindMarginal)
@property
def matchingWeightError(self):
"""Value of matchingWeightError."""
return self._matchingWeightError
@matchingWeightError.setter
def matchingWeightError(self, matchingWeightError):
self._matchingWeightError = matchingWeightError
self._approxParameters["matchingWeightError"] = (
self.matchingWeightError)
@property
def cutOffToleranceError(self):
"""Value of cutOffToleranceError."""
return self._cutOffToleranceError
@cutOffToleranceError.setter
def cutOffToleranceError(self, cutOffToleranceError):
if isinstance(cutOffToleranceError, (str,)):
cutOffToleranceError = cutOffToleranceError.upper()\
.strip().replace(" ","")
if cutOffToleranceError != "AUTO":
RROMPyWarning(("String value of cutOffToleranceError not "
"recognized. Overriding to 'AUTO'."))
cutOffToleranceError == "AUTO"
self._cutOffToleranceError = cutOffToleranceError
self._approxParameters["cutOffToleranceError"] = (
self.cutOffToleranceError)
@property
def greedyTolMarginal(self):
"""Value of greedyTolMarginal."""
return self._greedyTolMarginal
@greedyTolMarginal.setter
def greedyTolMarginal(self, greedyTolMarginal):
if greedyTolMarginal < 0:
raise RROMPyException("greedyTolMarginal must be non-negative.")
if (hasattr(self, "_greedyTolMarginal")
and self.greedyTolMarginal is not None):
greedyTolMarginalold = self.greedyTolMarginal
else:
greedyTolMarginalold = -1
self._greedyTolMarginal = greedyTolMarginal
self._approxParameters["greedyTolMarginal"] = self.greedyTolMarginal
if greedyTolMarginalold != self.greedyTolMarginal:
self.resetSamples()
@property
def maxIterMarginal(self):
"""Value of maxIterMarginal."""
return self._maxIterMarginal
@maxIterMarginal.setter
def maxIterMarginal(self, maxIterMarginal):
if maxIterMarginal <= 0:
raise RROMPyException("maxIterMarginal must be positive.")
if (hasattr(self, "_maxIterMarginal")
and self.maxIterMarginal is not None):
maxIterMarginalold = self.maxIterMarginal
else:
maxIterMarginalold = -1
self._maxIterMarginal = maxIterMarginal
self._approxParameters["maxIterMarginal"] = self.maxIterMarginal
if maxIterMarginalold != self.maxIterMarginal:
self.resetSamples()
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
if not hasattr(self, "_temporaryPivot"):
self._mus = emptyParameterList()
self.musMarginal = emptyParameterList()
if hasattr(self, "samplerMarginalGrid"):
self.samplerMarginalGrid.reset()
if hasattr(self, "samplingEngine") and self.samplingEngine is not None:
self.samplingEngine.resetHistory()
- def getErrorEstimatorMarginalLookAhead(self) -> Np1D:
- if not hasattr(self.trainedModel, "_musMExcl"):
- err = np.zeros(0)
+ def getErrorEstimatorMarginalLeaveOneOut(self) -> Np1D:
+ err = np.zeros(len(self.trainedModel.data.musMarginal))
+ self._musMarginalTestIdxs = np.arange(len(err))
+ if len(err) <= 1:
err[:] = np.inf
- self._musMarginalTestIdxs = np.zeros(0, dtype = int)
return err
- err = np.zeros(len(self.trainedModel._musMExcl))
- self._musMarginalTestIdxs = np.array(self.trainedModel._idxExcl,
- dtype = int)
- self.verbosity -= 35
- self.trainedModel.verbosity -= 35
+ _tMdataFull = copy(self.trainedModel.data)
if self.cutOffToleranceError == "AUTO":
cutOffTolErr = self.cutOffTolerance
else:
cutOffTolErr = self.cutOffToleranceError
+ _musMExcl = None
+ self.verbosity -= 35
+ self.trainedModel.verbosity -= 35
foci = self.samplerPivot.normalFoci()
ground = self.samplerPivot.groundPotential()
- for j, (muTest, HITest) in enumerate(zip(self.trainedModel._musMExcl,
- self.trainedModel._HIsExcl)):
- polesEx = HITest.poles
+ for j in range(len(err)):
+ jEff = j - (j > 0)
+ muTest = self.trainedModel.data.musMarginal[jEff]
+ polesEx = self.trainedModel.data.HIs[jEff].poles
idxExEff = np.where(potential(polesEx, foci) - ground
<= cutOffTolErr * ground)[0]
polesEx = polesEx[idxExEff]
if self.matchingWeightError != 0:
- resEx = HITest.coeffs[idxExEff]
+ resEx = self.trainedModel.data.HIs[jEff].coeffs[idxExEff]
else:
resEx = None
+ if j > 0: self.musMarginal.insert(_musMExcl, j - 1)
+ _musMExcl = self.musMarginal[j]
+ self.musMarginal.pop(j)
if len(polesEx) == 0: continue
+ self._updateTrainedModelMarginalSamples([j])
+ self._finalizeMarginalization()
polesAp = self.trainedModel.interpolateMarginalPoles(muTest)[...,
0]
idxApEff = np.where(potential(polesAp, foci) - ground
<= cutOffTolErr * ground)[0]
polesAp = polesAp[idxApEff]
if self.matchingWeightError != 0:
resAp = self.trainedModel.interpolateMarginalCoeffs(
muTest)[idxApEff, :, 0]
if self.POD != PODGlobal:
resEx = self.trainedModel.data.projMat[:,
: resEx.shape[1]].dot(resEx.T)
resAp = self.trainedModel.data.projMat[:,
: resAp.shape[1]].dot(resAp.T)
else:
resAp = None
dist = chordalMetricAdjusted(polesEx, polesAp,
self.matchingWeightError, resEx,
resAp, self.HFEngine, False)
pmR, pmC = pointMatching(dist)
err[j] = np.mean(dist[pmR, pmC])
+ self._updateTrainedModelMarginalSamples()
+ self.musMarginal.append(_musMExcl)
self.verbosity += 35
self.trainedModel.verbosity += 35
+ self.trainedModel.data = _tMdataFull
return err
-
- def getErrorEstimatorMarginalLeaveOneOut(self) -> Np1D:
- err = np.zeros(len(self.trainedModel.data.musMarginal))
- self._musMarginalTestIdxs = np.arange(len(err))
- if len(err) <= 1:
+
+ def getErrorEstimatorMarginalLookAhead(self) -> Np1D:
+ if not hasattr(self.trainedModel, "_musMExcl"):
+ err = np.zeros(0)
err[:] = np.inf
+ self._musMarginalTestIdxs = np.zeros(0, dtype = int)
return err
- _tMdataFull = copy(self.trainedModel.data)
+ err = np.zeros(len(self.trainedModel._musMExcl))
+ self._musMarginalTestIdxs = np.array(self.trainedModel._idxExcl,
+ dtype = int)
+ self.verbosity -= 35
+ self.trainedModel.verbosity -= 35
if self.cutOffToleranceError == "AUTO":
cutOffTolErr = self.cutOffTolerance
else:
cutOffTolErr = self.cutOffToleranceError
- if not hasattr(self, "_MMarginal_isauto"):
- if not hasattr(self, "_MMarginalOriginal"):
- self._MMarginalOriginal = self.MMarginal
- self.MMarginal = self._MMarginalOriginal
- _musMExcl = None
- self.verbosity -= 35
- self.trainedModel.verbosity -= 35
foci = self.samplerPivot.normalFoci()
ground = self.samplerPivot.groundPotential()
- for j in range(len(err)):
- jEff = j - (j > 0)
- muTest = self.trainedModel.data.musMarginal[jEff]
- polesEx = self.trainedModel.data.HIs[jEff].poles
+ for j, (muTest, HITest) in enumerate(zip(self.trainedModel._musMExcl,
+ self.trainedModel._HIsExcl)):
+ polesEx = HITest.poles
idxExEff = np.where(potential(polesEx, foci) - ground
<= cutOffTolErr * ground)[0]
polesEx = polesEx[idxExEff]
if self.matchingWeightError != 0:
- resEx = self.trainedModel.data.HIs[jEff].coeffs[idxExEff]
+ resEx = HITest.coeffs[idxExEff]
else:
resEx = None
- if j > 0: self.musMarginal.insert(_musMExcl, j - 1)
- _musMExcl = self.musMarginal[j]
- self.musMarginal.pop(j)
if len(polesEx) == 0: continue
- self.trainedModel.updateEffectiveSamples(self.HFEngine, [j],
- self.matchingWeight,
- self.POD == PODGlobal, False)
- self._reduceDegreeNNoWarn = 1
- self._finalizeMarginalization()
polesAp = self.trainedModel.interpolateMarginalPoles(muTest)[...,
0]
idxApEff = np.where(potential(polesAp, foci) - ground
<= cutOffTolErr * ground)[0]
polesAp = polesAp[idxApEff]
if self.matchingWeightError != 0:
resAp = self.trainedModel.interpolateMarginalCoeffs(
muTest)[idxApEff, :, 0]
if self.POD != PODGlobal:
resEx = self.trainedModel.data.projMat[:,
: resEx.shape[1]].dot(resEx.T)
resAp = self.trainedModel.data.projMat[:,
: resAp.shape[1]].dot(resAp.T)
else:
resAp = None
dist = chordalMetricAdjusted(polesEx, polesAp,
self.matchingWeightError, resEx,
resAp, self.HFEngine, False)
pmR, pmC = pointMatching(dist)
err[j] = np.mean(dist[pmR, pmC])
- self.trainedModel.updateEffectiveSamples(self.HFEngine, [],
- self.matchingWeight,
- self.POD == PODGlobal, False)
- if not hasattr(self, "_MMarginal_isauto"):
- self.MMarginal = self._MMarginalOriginal
- self.musMarginal.append(_musMExcl)
self.verbosity += 35
self.trainedModel.verbosity += 35
- self.trainedModel.data = _tMdataFull
- if hasattr(self, "_reduceDegreeNNoWarn"): del self._reduceDegreeNNoWarn
return err
+ def getErrorEstimatorMarginalNone(self) -> Np1D:
+ nErr = len(self.trainedModel.data.musMarginal)
+ self._musMarginalTestIdxs = np.arange(nErr)
+ return (1. + self.greedyTolMarginal) * np.ones(nErr)
+
def errorEstimatorMarginal(self, return_max : bool = False) -> Np1D:
vbMng(self.trainedModel, "INIT",
"Evaluating error estimator at mu = {}.".format(
self.trainedModel.data.musMarginal), 10)
if self.errorEstimatorKindMarginal == "LEAVE_ONE_OUT":
err = self.getErrorEstimatorMarginalLeaveOneOut()
- else:#if self.errorEstimatorKindMarginal[: 10] == "LOOK_AHEAD":
+ elif self.errorEstimatorKindMarginal[: 10] == "LOOK_AHEAD":
err = self.getErrorEstimatorMarginalLookAhead()
+ else:#if self.errorEstimatorKindMarginal == "NONE":
+ err = self.getErrorEstimatorMarginalNone()
vbMng(self.trainedModel, "DEL", "Done evaluating error estimator", 10)
if not return_max: return err
idxMaxEst = np.where(err > self.greedyTolMarginal)[0]
- return err, idxMaxEst, err[idxMaxEst]
+ maxErr = err[idxMaxEst]
+ if self.errorEstimatorKindMarginal == "NONE": maxErr = None
+ return err, idxMaxEst, maxErr
def plotEstimatorMarginal(self, est:Np1D, idxMax:List[int],
estMax:List[float]):
+ if self.errorEstimatorKindMarginal == "NONE": return
if not (np.any(np.isnan(est)) or np.any(np.isinf(est))):
fig = plt.figure(figsize = plt.figaspect(1. / self.nparMarginal))
for jpar in range(self.nparMarginal):
ax = fig.add_subplot(1, self.nparMarginal, 1 + jpar)
if self.errorEstimatorKindMarginal == "LEAVE_ONE_OUT":
musre = copy(self.trainedModel.data.musMarginal.re.data)
else:#if self.errorEstimatorKindMarginal[: 10] == "LOOK_AHEAD":
musre = np.real(self.trainedModel._musMExcl)
if len(idxMax) > 0 and estMax is not None:
maxrej = musre[idxMax, jpar]
errCP = copy(est)
idx = np.delete(np.arange(self.nparMarginal), jpar)
while len(musre) > 0:
if self.nparMarginal == 1:
currIdx = np.arange(len(musre))
else:
currIdx = np.where(np.isclose(np.sum(
np.abs(musre[:, idx] - musre[0, idx]), 1), 0.))[0]
currIdxSorted = currIdx[np.argsort(musre[currIdx, jpar])]
ax.semilogy(musre[currIdxSorted, jpar],
errCP[currIdxSorted], 'k.-', linewidth = 1)
musre = np.delete(musre, currIdx, 0)
errCP = np.delete(errCP, currIdx)
ax.semilogy(self.musMarginal.re(jpar),
(self.greedyTolMarginal,) * len(self.musMarginal),
'*m')
if len(idxMax) > 0 and estMax is not None:
ax.semilogy(maxrej, estMax, 'xr')
ax.grid()
plt.tight_layout()
plt.show()
def _addMarginalSample(self, mus:paramList):
mus = checkParameterList(mus, self.nparMarginal)[0]
if len(mus) == 0: return
nmus = len(mus)
vbMng(self, "MAIN",
("Adding marginal sample point{} no. {}{} at {} to training "
"set.").format("s" * (nmus > 1), len(self.musMarginal) + 1,
"--{}".format(len(self.musMarginal) + nmus) * (nmus > 1),
mus), 3)
self.musMarginal.append(mus)
self.setupApproxPivoted(mus)
- vbMng(self, "INIT", "Matching poles.", 10)
- self.trainedModel.initializeFromRational(self.HFEngine,
- self.matchingWeight,
- self.POD == PODGlobal, False)
- vbMng(self, "DEL", "Done matching poles.", 10)
+ self._poleMatching()
if (self.errorEstimatorKindMarginal[: 10] == "LOOK_AHEAD"
and not self.firstGreedyIterM):
ubRange = len(self.trainedModel.data.musMarginal)
if hasattr(self.trainedModel, "_idxExcl"):
shRange = len(self.trainedModel._musMExcl)
else:
shRange = 0
testIdxs = list(range(ubRange + shRange - len(mus),
ubRange + shRange))
for j in testIdxs[::-1]:
self.musMarginal.pop(j - shRange)
if hasattr(self.trainedModel, "_idxExcl"):
testIdxs = self.trainedModel._idxExcl + testIdxs
- self.trainedModel.updateEffectiveSamples(self.HFEngine, testIdxs,
- self.matchingWeight,
- self.POD == PODGlobal, False)
+ self._updateTrainedModelMarginalSamples(testIdxs)
self._finalizeMarginalization()
self._SMarginal = len(self.musMarginal)
self._approxParameters["SMarginal"] = self.SMarginal
self.trainedModel.data.approxParameters["SMarginal"] = self.SMarginal
def greedyNextSampleMarginal(self, muidx:List[int],
plotEst : str = "NONE") \
-> Tuple[Np1D, List[int], float, paramVal]:
RROMPyAssert(self._mode, message = "Cannot add greedy sample.")
if (self.errorEstimatorKindMarginal[: 10] == "LOOK_AHEAD"
and not self.firstGreedyIterM):
if not hasattr(self.trainedModel, "_idxExcl"):
raise RROMPyException(("Sample index to be added not present "
"in trained model."))
testIdxs = copy(self.trainedModel._idxExcl)
skippedIdx = 0
for cj, j in enumerate(self.trainedModel._idxExcl):
if j in muidx:
testIdxs.pop(skippedIdx)
self.musMarginal.insert(self.trainedModel._musMExcl[cj],
j - skippedIdx)
else:
skippedIdx += 1
if len(self.trainedModel._idxExcl) < (len(muidx)
+ len(testIdxs)):
raise RROMPyException(("Sample index to be added not present "
"in trained model."))
- self.trainedModel.updateEffectiveSamples(self.HFEngine, testIdxs,
- self.matchingWeight,
- self.POD == PODGlobal, False)
+ self._updateTrainedModelMarginalSamples(testIdxs)
self._SMarginal = len(self.musMarginal)
self._approxParameters["SMarginal"] = self.SMarginal
self.trainedModel.data.approxParameters["SMarginal"] = (
self.SMarginal)
self.firstGreedyIterM = False
idxAdded = self.samplerMarginalGrid.refine(muidx)
self._addMarginalSample(self.samplerMarginalGrid.points[idxAdded])
errorEstTest, muidx, maxErrorEst = self.errorEstimatorMarginal(True)
if plotEst == "ALL":
self.plotEstimatorMarginal(errorEstTest, muidx, maxErrorEst)
return (errorEstTest, self._musMarginalTestIdxs[muidx], maxErrorEst,
self.samplerMarginalGrid.points[muidx])
def _preliminaryTrainingMarginal(self):
"""Initialize starting snapshots of solution map."""
RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
if np.sum(self.samplingEngine.nsamples) > 0: return
self.resetSamples()
- idx = [0]
- while self.samplerMarginalGrid.npoints < self.SMarginal:
- idx = self.samplerMarginalGrid.refine(idx)
- self._addMarginalSample(self.samplerMarginalGrid.points)
+ self._addMarginalSample(self.samplerMarginalGrid.generatePoints(
+ self.SMarginal))
def _finalizeSnapshots(self):
self.samplingEngine = self._samplingEngineOld
for muM, sEN in zip(self.musMargLoc, self.samplingEngs):
self.samplingEngine.samples += [sEN.samples]
self.samplingEngine.nsamples += [sEN.nsamples]
self.samplingEngine.mus += [sEN.mus]
self.samplingEngine.musMarginal.append(muM)
self.samplingEngine._derIdxs += [[(0,) * self.npar]
for _ in range(sEN.nsamples)]
if self.POD:
self.samplingEngine.RPOD += [sEN.RPOD]
self.samplingEngine.samples_full += [copy(sEN.samples_full)]
if self.POD == PODGlobal:
self.samplingEngine.coalesceSamples(self.interpRcondMarginal)
else:
self.samplingEngine.coalesceSamples()
def _preSetupApproxPivoted(self, mus:paramList) -> Tuple[ListAny, ListAny]:
self.computeScaleFactor()
if self.trainedModel is None:
self.trainedModel = self.tModelType()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
- datadict = {"mu0": self.mu0, "projMat": np.zeros((0, 0)),
+ datadict = {"mu0": self.mu0, "mus": None,
+ "projMat": np.zeros((0, 0)),
"scaleFactor": self.scaleFactor,
"rescalingExp": self.HFEngine.rescalingExp,
"directionPivot": self.directionPivot}
self.trainedModel.data = self.initializeModelData(datadict)[0]
self.trainedModel.data.Qs, self.trainedModel.data.Ps = [], []
self._trainedModelOld = copy(self.trainedModel)
self._scaleFactorOldPivot = copy(self.scaleFactor)
self.scaleFactor = self.scaleFactorPivot
self._temporaryPivot = 1
self._samplingEngineOld = copy(self.samplingEngine)
self.musMargLoc, self.samplingEngs = [], [None] * len(mus)
Qs, Ps = [None] * len(mus), [None] * len(mus)
self.verbosity -= 15
return Qs, Ps
def _postSetupApproxPivoted(self, mus:paramList, Qs:ListAny, Ps:ListAny):
self.scaleFactor = self._scaleFactorOldPivot
del self._scaleFactorOldPivot, self._temporaryPivot
self._finalizeSnapshots()
del self._samplingEngineOld, self.musMargLoc, self.samplingEngs
self._mus = self.samplingEngine.musCoalesced
self.trainedModel = self._trainedModelOld
del self._trainedModelOld
self.trainedModel.data.mus = copy(self.mus)
self.trainedModel.data.musMarginal = copy(self.musMarginal)
- padRight = (self.samplingEngine.nsamplesTot
- - self.trainedModel.data.projMat.shape[1])
+ padRight = self.samplingEngine.nsamplesTot - self.trainedModel.nmus
nmusOld = len(self.trainedModel.data.Ps)
for j in range(nmusOld):
self.trainedModel.data.Ps[j].pad(0, padRight)
self.trainedModel.data.HIs[j].pad(0, padRight)
if hasattr(self.trainedModel, "_PsExcl"):
nmusOldExcl = len(self.trainedModel._PsExcl)
for j in range(nmusOldExcl):
self.trainedModel._PsExcl[j].pad(0, padRight)
self.trainedModel._HIsExcl[j].pad(0, padRight)
nmusOld += nmusOldExcl
- padLeft = self.trainedModel.data.projMat.shape[1]
+ padLeft = self.trainedModel.nmus
for j in range(len(mus)):
nsj = self.samplingEngine.nsamples[nmusOld + j]
if self.POD == PODGlobal:
rRightj = self.samplingEngine.RPODCPart[:,
padLeft : padLeft + nsj]
Ps[j].postmultiplyTensorize(rRightj.T)
else:
padRight -= nsj
Ps[j].pad(padLeft, padRight)
padLeft += nsj
pMat = self.samplingEngine.samplesCoalesced.data
pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat
self.trainedModel.data.projMat = pMatEff
self.trainedModel.data.Qs += Qs
self.trainedModel.data.Ps += Ps
self.trainedModel.data.approxParameters = copy(self.approxParameters)
self.verbosity += 15
@abstractmethod
def setupApproxPivoted(self, mus:paramList) -> int:
if self.checkComputedApproxPivoted(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up pivoted approximant.", 10)
Qs, Ps = self._preSetupApproxPivoted()
pass
self._postSetupApproxPivoted(mus, Qs, Ps)
vbMng(self, "DEL", "Done setting up pivoted approximant.", 10)
return 0
def setupApprox(self, plotEst : str = "NONE") -> int:
"""Compute greedy snapshots of solution map."""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
vbMng(self, "INIT", "Starting computation of snapshots.", 3)
max2ErrorEst, self.firstGreedyIterM = np.inf, True
self._preliminaryTrainingMarginal()
- if self.errorEstimatorKindMarginal == "LEAVE_ONE_OUT":
- muidx = []
- else:#if self.errorEstimatorKindMarginal[: 10] == "LOOK_AHEAD":
+ if self.errorEstimatorKindMarginal[: 10] == "LOOK_AHEAD":
muidx = np.arange(len(self.trainedModel.data.musMarginal))
+ else:#if self.errorEstimatorKindMarginal in ["LEAVE_ONE_OUT", "NONE"]:
+ muidx = []
while self.firstGreedyIterM or (max2ErrorEst > self.greedyTolMarginal
and self.samplerMarginalGrid.npoints < self.maxIterMarginal):
errorEstTest, muidx, maxErrorEst, mu = \
self.greedyNextSampleMarginal(muidx, plotEst)
- if len(maxErrorEst) > 0:
- max2ErrorEst = np.max(maxErrorEst)
- vbMng(self, "MAIN", ("Uniform testing error estimate "
- "{:.4e}.").format(max2ErrorEst), 3)
+ if maxErrorEst is None:
+ max2ErrorEst = 1. + self.greedyTolMarginal
else:
- max2ErrorEst = 0.
+ if len(maxErrorEst) > 0:
+ max2ErrorEst = np.max(maxErrorEst)
+ vbMng(self, "MAIN", ("Uniform testing error estimate "
+ "{:.4e}.").format(max2ErrorEst), 3)
+ else:
+ max2ErrorEst = 0.
if plotEst == "LAST":
self.plotEstimatorMarginal(errorEstTest, muidx, maxErrorEst)
vbMng(self, "DEL",
("Done computing snapshots (final snapshot count: "
"{}).").format(np.sum(self.samplingEngine.nsamples)), 3)
if (self.errorEstimatorKindMarginal == "LOOK_AHEAD_RECOVER"
and len(self.trainedModel._idxExcl) > 0):
vbMng(self, "INIT", "Recovering {} test models.".format(
len(self.trainedModel._idxExcl)), 7)
for j, mu in zip(self.trainedModel._idxExcl,
self.trainedModel._musMExcl):
self.musMarginal.insert(mu, j)
- self.trainedModel.updateEffectiveSamples(self.HFEngine, [],
- self.matchingWeight,
- self.POD == PODGlobal, False)
+ self._updateTrainedModelMarginalSamples()
self._finalizeMarginalization()
self._SMarginal = len(self.musMarginal)
self._approxParameters["SMarginal"] = self.SMarginal
self.trainedModel.data.approxParameters["SMarginal"] = (
self.SMarginal)
vbMng(self, "DEL", "Done recovering test models.", 7)
return 0
def checkComputedApprox(self) -> bool:
return (super().checkComputedApprox()
and len(self.mus) == len(self.trainedModel.data.mus))
def checkComputedApproxPivoted(self) -> bool:
return (super().checkComputedApprox()
and len(self.musMarginal) == len(self.trainedModel.data.musMarginal))
+
+class GenericPivotedGreedyApproximantNoMatch(
+ GenericPivotedGreedyApproximantBase,
+ GenericPivotedApproximantNoMatch):
+ """
+ ROM pivoted greedy interpolant computation for parametric problems (without
+ pole matching) (ABSTRACT).
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation; defaults to 0;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': number of starting marginal samples;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD',
+ 'LOOK_AHEAD_RECOVER', and 'NONE'; defaults to 'NONE';
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm; defaults to 1e-1;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ defaults to 1e2;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcondMarginal': tolerance for marginal interpolation.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ matchingWeightError: Weight for pole matching optimization in error
+ estimation.
+ cutOffToleranceError: Tolerance for ignoring parasitic poles in error
+ estimation.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginalGrid: Marginal sample point generator via sparse grid.
+ errorEstimatorKindMarginal: Kind of marginal error estimator.
+ greedyTolMarginal: Uniform error tolerance for marginal greedy
+ algorithm.
+ maxIterMarginal: Maximum number of marginal greedy steps.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
+
+ def _poleMatching(self):
+ vbMng(self, "INIT", "Compressing poles.", 10)
+ self.trainedModel.initializeFromRational()
+ vbMng(self, "DEL", "Done compressing poles.", 10)
+
+ def _updateTrainedModelMarginalSamples(self, idx : ListAny = []):
+ self.trainedModel.updateEffectiveSamples(idx)
+
+class GenericPivotedGreedyApproximant(GenericPivotedGreedyApproximantBase,
+ GenericPivotedApproximant):
+ """
+ ROM pivoted greedy interpolant computation for parametric problems (with
+ pole matching) (ABSTRACT).
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation; defaults to 0;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': number of starting marginal samples;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD',
+ 'LOOK_AHEAD_RECOVER', and 'NONE'; defaults to 'NONE';
+ - 'polybasisMarginal': type of polynomial basis for marginal
+ interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
+ and 'LEGENDRE'; defaults to 'MONOMIAL';
+ - 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
+ i.e. maximum allowed;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm; defaults to 1e-1;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ defaults to 1e2;
+ - 'polydegreetypeMarginal': type of polynomial degree for marginal;
+ defaults to 'TOTAL';
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ - 'polybasisMarginal': type of polynomial basis for marginal
+ interpolation;
+ - 'MMarginal': degree of marginal interpolant;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ - 'polydegreetypeMarginal': type of polynomial degree for marginal;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcondMarginal': tolerance for marginal interpolation.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ matchingWeightError: Weight for pole matching optimization in error
+ estimation.
+ cutOffToleranceError: Tolerance for ignoring parasitic poles in error
+ estimation.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginalGrid: Marginal sample point generator via sparse grid.
+ errorEstimatorKindMarginal: Kind of marginal error estimator.
+ polybasisMarginal: Type of polynomial basis for marginal interpolation.
+ MMarginal: Degree of marginal interpolant.
+ greedyTolMarginal: Uniform error tolerance for marginal greedy
+ algorithm.
+ maxIterMarginal: Maximum number of marginal greedy steps.
+ polydegreetypeMarginal: Type of polynomial degree for marginal.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
+
+ def _poleMatching(self):
+ vbMng(self, "INIT", "Compressing and matching poles.", 10)
+ self.trainedModel.initializeFromRational(self.HFEngine,
+ self.matchingWeight,
+ self.POD == PODGlobal, False)
+ vbMng(self, "DEL", "Done compressing and matching poles.", 10)
+
+ def _updateTrainedModelMarginalSamples(self, idx : ListAny = []):
+ self.trainedModel.updateEffectiveSamples(idx, self.HFEngine,
+ self.matchingWeight,
+ self.POD == PODGlobal, False)
+
+ def getErrorEstimatorMarginalLeaveOneOut(self) -> Np1D:
+ if not hasattr(self, "_MMarginal_isauto"):
+ if not hasattr(self, "_MMarginalOriginal"):
+ self._MMarginalOriginal = self.MMarginal
+ self.MMarginal = self._MMarginalOriginal
+ self._reduceDegreeNNoWarn = 1
+ err = super().getErrorEstimatorMarginalLeaveOneOut()
+ del self._reduceDegreeNNoWarn
+ return err
+
+class GenericPivotedGreedyApproximantNearestNeighbor(
+ GenericPivotedGreedyApproximant,
+ GenericPivotedApproximantNearestNeighbor):
+ """
+ ROM pivoted greedy interpolant computation for parametric problems (with
+ pole matching) via local average (ABSTRACT).
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation; defaults to 0;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': number of starting marginal samples;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD',
+ 'LOOK_AHEAD_RECOVER', and 'NONE'; defaults to 'NONE';
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ defaults to 1;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm; defaults to 1e-1;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ defaults to 1e2;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ matchingWeightError: Weight for pole matching optimization in error
+ estimation.
+ cutOffToleranceError: Tolerance for ignoring parasitic poles in error
+ estimation.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginalGrid: Marginal sample point generator via sparse grid.
+ errorEstimatorKindMarginal: Kind of marginal error estimator.
+ nNeighborsMarginal: Number of marginal nearest neighbors.
+ greedyTolMarginal: Uniform error tolerance for marginal greedy
+ algorithm.
+ maxIterMarginal: Maximum number of marginal greedy steps.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
+
+ def getErrorEstimatorMarginalLeaveOneOut(self) -> Np1D:
+ return super(GenericPivotedGreedyApproximant,
+ self).getErrorEstimatorMarginalLeaveOneOut(self)
diff --git a/rrompy/reduction_methods/pivoted/greedy/rational_interpolant_greedy_pivoted_greedy.py b/rrompy/reduction_methods/pivoted/greedy/rational_interpolant_greedy_pivoted_greedy.py
index 9305ec1..e781dbd 100644
--- a/rrompy/reduction_methods/pivoted/greedy/rational_interpolant_greedy_pivoted_greedy.py
+++ b/rrompy/reduction_methods/pivoted/greedy/rational_interpolant_greedy_pivoted_greedy.py
@@ -1,316 +1,675 @@
#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 .
#
from copy import deepcopy as copy
import numpy as np
-from .generic_pivoted_greedy_approximant import GenericPivotedGreedyApproximant
+from .generic_pivoted_greedy_approximant import (
+ GenericPivotedGreedyApproximantBase,
+ GenericPivotedGreedyApproximantNoMatch,
+ GenericPivotedGreedyApproximant,
+ GenericPivotedGreedyApproximantNearestNeighbor)
from rrompy.reduction_methods.standard.greedy import RationalInterpolantGreedy
from rrompy.reduction_methods.standard.greedy.generic_greedy_approximant \
import pruneSamples
-from rrompy.reduction_methods.pivoted import RationalInterpolantGreedyPivoted
+from rrompy.reduction_methods.pivoted import (
+ RationalInterpolantGreedyPivotedNoMatch,
+ RationalInterpolantGreedyPivoted,
+ RationalInterpolantGreedyPivotedNearestNeighbor)
from rrompy.utilities.base.types import Np1D, Tuple, paramVal, paramList
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.exception_manager import RROMPyAssert
from rrompy.parameter import emptyParameterList
-__all__ = ['RationalInterpolantGreedyPivotedGreedy']
+__all__ = ['RationalInterpolantGreedyPivotedGreedyNoMatch',
+ 'RationalInterpolantGreedyPivotedGreedy',
+ 'RationalInterpolantGreedyPivotedGreedyNearestNeighbor']
-class RationalInterpolantGreedyPivotedGreedy(GenericPivotedGreedyApproximant,
- RationalInterpolantGreedyPivoted):
+class RationalInterpolantGreedyPivotedGreedyBase(
+ GenericPivotedGreedyApproximantBase):
+ @property
+ def sampleBatchSize(self):
+ """Value of sampleBatchSize."""
+ return 1
+
+ @property
+ def sampleBatchIdx(self):
+ """Value of sampleBatchIdx."""
+ return self.S
+
+ def greedyNextSample(self, muidx:int, plotEst : str = "NONE")\
+ -> Tuple[Np1D, int, float, paramVal]:
+ """Compute next greedy snapshot of solution map."""
+ RROMPyAssert(self._mode, message = "Cannot add greedy sample.")
+ mus = copy(self.muTest[muidx])
+ self.muTest.pop(muidx)
+ for j, mu in enumerate(mus):
+ vbMng(self, "MAIN",
+ ("Adding sample point no. {} at {} to training "
+ "set.").format(len(self.mus) + 1, mu), 3)
+ self.mus.append(mu)
+ self._S = len(self.mus)
+ self._approxParameters["S"] = self.S
+ if (self.samplingEngine.nsamples <= len(mus) - j - 1
+ or not np.allclose(mu,
+ self.samplingEngine.mus.data[j - len(mus)])):
+ self.samplingEngine.nextSample(mu)
+ if self._isLastSampleCollinear():
+ vbMng(self, "MAIN",
+ ("Collinearity above tolerance detected. Starting "
+ "preemptive greedy loop termination."), 3)
+ self._collinearityFlag = 1
+ errorEstTest = np.empty(len(self.muTest))
+ errorEstTest[:] = np.nan
+ return errorEstTest, [-1], np.nan, np.nan
+ errorEstTest, muidx, maxErrorEst = self.errorEstimator(self.muTest,
+ True)
+ if plotEst == "ALL":
+ self.plotEstimator(errorEstTest, muidx, maxErrorEst)
+ return errorEstTest, muidx, maxErrorEst, self.muTest[muidx]
+
+ def _preliminaryTraining(self):
+ """Initialize starting snapshots of solution map."""
+ RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
+ if self.samplingEngine.nsamples > 0: return
+ self.resetSamples()
+ self.samplingEngine.scaleFactor = self.scaleFactorDer
+ musPivot = self.trainSetGenerator.generatePoints(self.S)
+ while len(musPivot) > self.S: musPivot.pop()
+ muTestBasePivot = self.samplerPivot.generatePoints(self.nTestPoints,
+ False)
+ idxPop = pruneSamples(
+ muTestBasePivot ** self.HFEngine.rescalingExp[self.directionPivot[0]],
+ musPivot ** self.HFEngine.rescalingExp[self.directionPivot[0]],
+ 1e-10 * self.scaleFactor[0])
+ muTestBasePivot.pop(idxPop)
+ self.mus = emptyParameterList()
+ self.mus.reset((self.S - 1, self.HFEngine.npar))
+ self.muTest = emptyParameterList()
+ self.muTest.reset((len(muTestBasePivot) + 1, self.HFEngine.npar))
+ for k in range(self.S - 1):
+ self.mus.data[k, self.directionPivot] = musPivot[k].data
+ self.mus.data[k, self.directionMarginal] = self.musMargLoc[-1].data
+ for k in range(len(muTestBasePivot)):
+ self.muTest.data[k, self.directionPivot] = muTestBasePivot[k].data
+ self.muTest.data[k, self.directionMarginal] = (
+ self.musMargLoc[-1].data)
+ self.muTest.data[-1, self.directionPivot] = musPivot[-1].data
+ self.muTest.data[-1, self.directionMarginal] = self.musMargLoc[-1].data
+ if len(self.mus) > 0:
+ vbMng(self, "MAIN",
+ ("Adding first {} sample point{} at {} to training "
+ "set.").format(self.S - 1, "" + "s" * (self.S > 2),
+ self.mus), 3)
+ self.samplingEngine.iterSample(self.mus)
+ self._S = len(self.mus)
+ self._approxParameters["S"] = self.S
+ self.M, self.N = ("AUTO",) * 2
+
+ def setupApproxPivoted(self, mus:paramList) -> int:
+ if self.checkComputedApproxPivoted(): return -1
+ RROMPyAssert(self._mode, message = "Cannot setup approximant.")
+ vbMng(self, "INIT", "Setting up pivoted approximant.", 10)
+ if not hasattr(self, "_plotEstPivot"): self._plotEstPivot = "NONE"
+ Qs, Ps = self._preSetupApproxPivoted(mus)
+ S0 = copy(self.S)
+ for j, mu in enumerate(mus):
+ RationalInterpolantGreedy.setupSampling(self)
+ self.trainedModel = None
+ self.musMargLoc += [mu]
+ RationalInterpolantGreedy.setupApprox(self, self._plotEstPivot)
+ self.samplingEngs[j] = copy(self.samplingEngine)
+ Qs[j] = copy(self.trainedModel.data.Q)
+ Ps[j] = copy(self.trainedModel.data.P)
+ self._S = S0
+ self._postSetupApproxPivoted(mus, Qs, Ps)
+ vbMng(self, "DEL", "Done setting up pivoted approximant.", 10)
+ return 0
+
+ def setupApprox(self, plotEst : str = "NONE") -> int:
+ if self.checkComputedApprox(): return -1
+ if '_' not in plotEst: plotEst = plotEst + "_NONE"
+ plotEstM, self._plotEstPivot = plotEst.split("_")
+ val = super().setupApprox(plotEstM)
+ return val
+
+class RationalInterpolantGreedyPivotedGreedyNoMatch(
+ RationalInterpolantGreedyPivotedGreedyBase,
+ GenericPivotedGreedyApproximantNoMatch,
+ RationalInterpolantGreedyPivotedNoMatch):
+ """
+ ROM greedy pivoted greedy rational interpolant computation for parametric
+ problems (without pole matching).
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation; defaults to 0;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': number of starting marginal samples;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD', and
+ 'LOOK_AHEAD_RECOVER'; defaults to 'LEAVE_ONE_OUT';
+ - 'polybasis': type of polynomial basis for pivot interpolation;
+ defaults to 'MONOMIAL';
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ defaults to 1e-2;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ defaults to 0.;
+ - 'maxIter': maximum number of greedy steps; defaults to 1e2;
+ - 'nTestPoints': number of test points; defaults to 5e2;
+ - 'trainSetGenerator': training sample points generator; defaults
+ to sampler;
+ - 'errorEstimatorKind': kind of error estimator; available values
+ include 'AFFINE', 'DISCREPANCY', 'LOOK_AHEAD',
+ 'LOOK_AHEAD_RES', and 'NONE'; defaults to 'NONE';
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm; defaults to 1e-1;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ defaults to 1e2;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ - 'polybasis': type of polynomial basis for pivot interpolation;
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ - 'maxIter': maximum number of greedy steps;
+ - 'nTestPoints': number of test points;
+ - 'trainSetGenerator': training sample points generator;
+ - 'errorEstimatorKind': kind of error estimator;
+ - 'MMarginal': degree of marginal interpolant;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ matchingWeightError: Weight for pole matching optimization in error
+ estimation.
+ cutOffToleranceError: Tolerance for ignoring parasitic poles in error
+ estimation.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginalGrid: Marginal sample point generator via sparse grid.
+ errorEstimatorKindMarginal: Kind of marginal error estimator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ greedyTol: uniform error tolerance for greedy algorithm.
+ collinearityTol: Collinearity tolerance for greedy algorithm.
+ maxIter: maximum number of greedy steps.
+ nTestPoints: number of starting training points.
+ trainSetGenerator: training sample points generator.
+ errorEstimatorKind: kind of error estimator.
+ greedyTolMarginal: Uniform error tolerance for marginal greedy
+ algorithm.
+ maxIterMarginal: Maximum number of marginal greedy steps.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
+
+class RationalInterpolantGreedyPivotedGreedy(
+ RationalInterpolantGreedyPivotedGreedyBase,
+ GenericPivotedGreedyApproximant,
+ RationalInterpolantGreedyPivoted):
"""
ROM greedy pivoted greedy rational interpolant computation for parametric
- problems.
+ problems (with pole matching).
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
directionPivot(optional): Pivot components. Defaults to [0].
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'matchingWeight': weight for pole matching optimization; defaults
to 1;
- 'cutOffTolerance': tolerance for ignoring parasitic poles;
defaults to np.inf;
- 'cutOffSharedRatio': required ratio of marginal points to share
resonance in cut off strategy; defaults to 1.;
- 'matchingWeightError': weight for pole matching optimization in
error estimation; defaults to 0;
- 'cutOffToleranceError': tolerance for ignoring parasitic poles
in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
- 'S': total number of pivot samples current approximant relies
upon;
- 'samplerPivot': pivot sample point generator;
- 'SMarginal': number of starting marginal samples;
- 'samplerMarginalGrid': marginal sample point generator via sparse
grid;
- 'errorEstimatorKindMarginal': kind of marginal error estimator;
available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD', and
'LOOK_AHEAD_RECOVER'; defaults to 'LEAVE_ONE_OUT';
- 'polybasis': type of polynomial basis for pivot interpolation;
defaults to 'MONOMIAL';
- 'polybasisMarginal': type of polynomial basis for marginal
interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
and 'LEGENDRE'; defaults to 'MONOMIAL';
- 'greedyTol': uniform error tolerance for greedy algorithm;
defaults to 1e-2;
- 'collinearityTol': collinearity tolerance for greedy algorithm;
defaults to 0.;
- 'maxIter': maximum number of greedy steps; defaults to 1e2;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator; defaults
to sampler;
- 'errorEstimatorKind': kind of error estimator; available values
include 'AFFINE', 'DISCREPANCY', 'LOOK_AHEAD',
'LOOK_AHEAD_RES', and 'NONE'; defaults to 'NONE';
- 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
i.e. maximum allowed;
- 'greedyTolMarginal': uniform error tolerance for marginal greedy
algorithm; defaults to 1e-1;
- 'maxIterMarginal': maximum number of marginal greedy steps;
defaults to 1e2;
- 'polydegreetypeMarginal': type of polynomial degree for marginal;
defaults to 'TOTAL';
- 'radialDirectionalWeightsMarginal': radial basis weights for
marginal interpolant; defaults to 1;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows; defaults to -1;
- 'interpRcond': tolerance for pivot interpolation; defaults to
None;
- 'interpRcondMarginal': tolerance for marginal interpolation;
defaults to None;
- 'robustTol': tolerance for robust rational denominator
- management; defaults to 0.
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults to
False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
directionPivot: Pivot components.
mus: Array of snapshot parameters.
musMarginal: Array of marginal snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation;
- 'matchingWeight': weight for pole matching optimization;
- 'cutOffTolerance': tolerance for ignoring parasitic poles;
- 'cutOffSharedRatio': required ratio of marginal points to share
resonance in cut off strategy;
- 'matchingWeightError': weight for pole matching optimization in
error estimation;
- 'cutOffToleranceError': tolerance for ignoring parasitic poles
in error estimation;
- 'errorEstimatorKindMarginal': kind of marginal error estimator;
- 'polybasis': type of polynomial basis for pivot interpolation;
- 'polybasisMarginal': type of polynomial basis for marginal
interpolation;
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'collinearityTol': collinearity tolerance for greedy algorithm;
- 'maxIter': maximum number of greedy steps;
- 'nTestPoints': number of test points;
- 'trainSetGenerator': training sample points generator;
- 'errorEstimatorKind': kind of error estimator;
- 'MMarginal': degree of marginal interpolant;
- 'greedyTolMarginal': uniform error tolerance for marginal greedy
algorithm;
- 'maxIterMarginal': maximum number of marginal greedy steps;
- 'polydegreetypeMarginal': type of polynomial degree for marginal;
- 'radialDirectionalWeightsMarginal': radial basis weights for
marginal interpolant;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows;
- 'interpRcond': tolerance for pivot interpolation;
- 'interpRcondMarginal': tolerance for marginal interpolation;
- 'robustTol': tolerance for robust rational denominator
- management.
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of pivot samples current approximant relies
upon;
- 'samplerPivot': pivot sample point generator;
- 'SMarginal': total number of marginal samples current approximant
relies upon;
- 'samplerMarginalGrid': marginal sample point generator via sparse
grid.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
matchingWeight: Weight for pole matching optimization.
cutOffTolerance: Tolerance for ignoring parasitic poles.
cutOffSharedRatio: Required ratio of marginal points to share resonance
in cut off strategy.
matchingWeightError: Weight for pole matching optimization in error
estimation.
cutOffToleranceError: Tolerance for ignoring parasitic poles in error
estimation.
S: Total number of pivot samples current approximant relies upon.
samplerPivot: Pivot sample point generator.
SMarginal: Total number of marginal samples current approximant relies
upon.
samplerMarginalGrid: Marginal sample point generator via sparse grid.
errorEstimatorKindMarginal: Kind of marginal error estimator.
polybasis: Type of polynomial basis for pivot interpolation.
polybasisMarginal: Type of polynomial basis for marginal interpolation.
greedyTol: uniform error tolerance for greedy algorithm.
collinearityTol: Collinearity tolerance for greedy algorithm.
maxIter: maximum number of greedy steps.
nTestPoints: number of starting training points.
trainSetGenerator: training sample points generator.
errorEstimatorKind: kind of error estimator.
MMarginal: Degree of marginal interpolant.
greedyTolMarginal: Uniform error tolerance for marginal greedy
algorithm.
maxIterMarginal: Maximum number of marginal greedy steps.
polydegreetypeMarginal: Type of polynomial degree for marginal.
radialDirectionalWeightsMarginal: Radial basis weights for marginal
interpolant.
- nNearestNeighborMarginal: Number of marginal nearest neighbors
- considered if polybasisMarginal allows.
interpRcond: Tolerance for pivot interpolation.
interpRcondMarginal: Tolerance for marginal interpolation.
robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
muBounds: list of bounds for pivot parameter values.
muBoundsMarginal: list of bounds for marginal parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
"""
-
- @property
- def sampleBatchSize(self):
- """Value of sampleBatchSize."""
- return 1
-
- @property
- def sampleBatchIdx(self):
- """Value of sampleBatchIdx."""
- return self.S
-
- def greedyNextSample(self, muidx:int, plotEst : str = "NONE")\
- -> Tuple[Np1D, int, float, paramVal]:
- """Compute next greedy snapshot of solution map."""
- RROMPyAssert(self._mode, message = "Cannot add greedy sample.")
- mus = copy(self.muTest[muidx])
- self.muTest.pop(muidx)
- for j, mu in enumerate(mus):
- vbMng(self, "MAIN",
- ("Adding sample point no. {} at {} to training "
- "set.").format(len(self.mus) + 1, mu), 3)
- self.mus.append(mu)
- self._S = len(self.mus)
- self._approxParameters["S"] = self.S
- if (self.samplingEngine.nsamples <= len(mus) - j - 1
- or not np.allclose(mu,
- self.samplingEngine.mus.data[j - len(mus)])):
- self.samplingEngine.nextSample(mu)
- if self._isLastSampleCollinear():
- vbMng(self, "MAIN",
- ("Collinearity above tolerance detected. Starting "
- "preemptive greedy loop termination."), 3)
- self._collinearityFlag = 1
- errorEstTest = np.empty(len(self.muTest))
- errorEstTest[:] = np.nan
- return errorEstTest, [-1], np.nan, np.nan
- errorEstTest, muidx, maxErrorEst = self.errorEstimator(self.muTest,
- True)
- if plotEst == "ALL":
- self.plotEstimator(errorEstTest, muidx, maxErrorEst)
- return errorEstTest, muidx, maxErrorEst, self.muTest[muidx]
-
- def _preliminaryTraining(self):
- """Initialize starting snapshots of solution map."""
- RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
- if self.samplingEngine.nsamples > 0: return
- self.resetSamples()
- self.samplingEngine.scaleFactor = self.scaleFactorDer
- musPivot = self.trainSetGenerator.generatePoints(self.S)
- while len(musPivot) > self.S: musPivot.pop()
- muTestBasePivot = self.samplerPivot.generatePoints(self.nTestPoints,
- False)
- idxPop = pruneSamples(
- muTestBasePivot ** self.HFEngine.rescalingExp[self.directionPivot[0]],
- musPivot ** self.HFEngine.rescalingExp[self.directionPivot[0]],
- 1e-10 * self.scaleFactor[0])
- muTestBasePivot.pop(idxPop)
- self.mus = emptyParameterList()
- self.mus.reset((self.S - 1, self.HFEngine.npar))
- self.muTest = emptyParameterList()
- self.muTest.reset((len(muTestBasePivot) + 1, self.HFEngine.npar))
- for k in range(self.S - 1):
- self.mus.data[k, self.directionPivot] = musPivot[k].data
- self.mus.data[k, self.directionMarginal] = self.musMargLoc[-1].data
- for k in range(len(muTestBasePivot)):
- self.muTest.data[k, self.directionPivot] = muTestBasePivot[k].data
- self.muTest.data[k, self.directionMarginal] = (
- self.musMargLoc[-1].data)
- self.muTest.data[-1, self.directionPivot] = musPivot[-1].data
- self.muTest.data[-1, self.directionMarginal] = self.musMargLoc[-1].data
- if len(self.mus) > 0:
- vbMng(self, "MAIN",
- ("Adding first {} sample point{} at {} to training "
- "set.").format(self.S - 1, "" + "s" * (self.S > 2),
- self.mus), 3)
- self.samplingEngine.iterSample(self.mus)
- self._S = len(self.mus)
- self._approxParameters["S"] = self.S
- self.M, self.N = ("AUTO",) * 2
- def setupApproxPivoted(self, mus:paramList) -> int:
- if self.checkComputedApproxPivoted(): return -1
- RROMPyAssert(self._mode, message = "Cannot setup approximant.")
- vbMng(self, "INIT", "Setting up pivoted approximant.", 10)
- if not hasattr(self, "_plotEstPivot"): self._plotEstPivot = "NONE"
- Qs, Ps = self._preSetupApproxPivoted(mus)
- S0 = copy(self.S)
- for j, mu in enumerate(mus):
- RationalInterpolantGreedy.setupSampling(self)
- self.trainedModel = None
- self.musMargLoc += [mu]
- RationalInterpolantGreedy.setupApprox(self, self._plotEstPivot)
- self.samplingEngs[j] = copy(self.samplingEngine)
- Qs[j] = copy(self.trainedModel.data.Q)
- Ps[j] = copy(self.trainedModel.data.P)
- self._S = S0
- self._postSetupApproxPivoted(mus, Qs, Ps)
- vbMng(self, "DEL", "Done setting up pivoted approximant.", 10)
- return 0
+class RationalInterpolantGreedyPivotedGreedyNearestNeighbor(
+ RationalInterpolantGreedyPivotedGreedy,
+ GenericPivotedGreedyApproximantNearestNeighbor,
+ RationalInterpolantGreedyPivotedNearestNeighbor):
+ """
+ ROM greedy pivoted greedy rational interpolant computation for parametric
+ problems (with pole matching) via local average.
- def setupApprox(self, plotEst : str = "NONE") -> int:
- if self.checkComputedApprox(): return -1
- if '_' not in plotEst: plotEst = plotEst + "_NONE"
- plotEstM, self._plotEstPivot = plotEst.split("_")
- val = super().setupApprox(plotEstM)
- return val
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation; defaults to 0;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': number of starting marginal samples;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD', and
+ 'LOOK_AHEAD_RECOVER'; defaults to 'LEAVE_ONE_OUT';
+ - 'polybasis': type of polynomial basis for pivot interpolation;
+ defaults to 'MONOMIAL';
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ defaults to 1e-2;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ defaults to 0.;
+ - 'maxIter': maximum number of greedy steps; defaults to 1e2;
+ - 'nTestPoints': number of test points; defaults to 5e2;
+ - 'trainSetGenerator': training sample points generator; defaults
+ to sampler;
+ - 'errorEstimatorKind': kind of error estimator; available values
+ include 'AFFINE', 'DISCREPANCY', 'LOOK_AHEAD',
+ 'LOOK_AHEAD_RES', and 'NONE'; defaults to 'NONE';
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ defaults to 1;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm; defaults to 1e-1;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ defaults to 1e2;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ - 'polybasis': type of polynomial basis for pivot interpolation;
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ - 'maxIter': maximum number of greedy steps;
+ - 'nTestPoints': number of test points;
+ - 'trainSetGenerator': training sample points generator;
+ - 'errorEstimatorKind': kind of error estimator;
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ matchingWeightError: Weight for pole matching optimization in error
+ estimation.
+ cutOffToleranceError: Tolerance for ignoring parasitic poles in error
+ estimation.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginalGrid: Marginal sample point generator via sparse grid.
+ errorEstimatorKindMarginal: Kind of marginal error estimator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ greedyTol: uniform error tolerance for greedy algorithm.
+ collinearityTol: Collinearity tolerance for greedy algorithm.
+ maxIter: maximum number of greedy steps.
+ nTestPoints: number of starting training points.
+ trainSetGenerator: training sample points generator.
+ errorEstimatorKind: kind of error estimator.
+ nNeighborsMarginal: Number of marginal nearest neighbors.
+ greedyTolMarginal: Uniform error tolerance for marginal greedy
+ algorithm.
+ maxIterMarginal: Maximum number of marginal greedy steps.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
diff --git a/rrompy/reduction_methods/pivoted/greedy/rational_interpolant_pivoted_greedy.py b/rrompy/reduction_methods/pivoted/greedy/rational_interpolant_pivoted_greedy.py
index bec9970..c672d56 100644
--- a/rrompy/reduction_methods/pivoted/greedy/rational_interpolant_pivoted_greedy.py
+++ b/rrompy/reduction_methods/pivoted/greedy/rational_interpolant_pivoted_greedy.py
@@ -1,262 +1,583 @@
# 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 .
#
from copy import deepcopy as copy
-from .generic_pivoted_greedy_approximant import GenericPivotedGreedyApproximant
+from .generic_pivoted_greedy_approximant import (
+ GenericPivotedGreedyApproximantBase,
+ GenericPivotedGreedyApproximantNoMatch,
+ GenericPivotedGreedyApproximant,
+ GenericPivotedGreedyApproximantNearestNeighbor)
from rrompy.reduction_methods.standard import RationalInterpolant
-from rrompy.reduction_methods.pivoted import RationalInterpolantPivoted
+from rrompy.reduction_methods.pivoted import (
+ RationalInterpolantPivotedNoMatch,
+ RationalInterpolantPivoted,
+ RationalInterpolantPivotedNearestNeighbor)
from rrompy.utilities.base.types import paramList
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.exception_manager import RROMPyAssert
from rrompy.parameter import checkParameterList, emptyParameterList
-__all__ = ['RationalInterpolantPivotedGreedy']
+__all__ = ['RationalInterpolantPivotedGreedyNoMatch',
+ 'RationalInterpolantPivotedGreedy',
+ 'RationalInterpolantPivotedGreedyNearestNeighbor']
-class RationalInterpolantPivotedGreedy(GenericPivotedGreedyApproximant,
+class RationalInterpolantPivotedGreedyBase(
+ GenericPivotedGreedyApproximantBase):
+
+ def computeSnapshots(self):
+ """Compute snapshots of solution map."""
+ RROMPyAssert(self._mode,
+ message = "Cannot start snapshot computation.")
+ vbMng(self, "INIT", "Starting computation of snapshots.", 5)
+ self.samplingEngine.scaleFactor = self.scaleFactorDer
+ self.musPivot = self.samplerPivot.generatePoints(self.S)
+ while len(self.musPivot) > self.S: self.musPivot.pop()
+ self.mus = emptyParameterList()
+ self.mus.reset((self.S, self.HFEngine.npar))
+ self.samplingEngine.resetHistory()
+ for k in range(self.S):
+ self.mus.data[k, self.directionPivot] = self.musPivot[k].data
+ self.mus.data[k, self.directionMarginal] = self.musMargLoc[-1].data
+ self.samplingEngine.iterSample(self.mus)
+ vbMng(self, "DEL", "Done computing snapshots.", 5)
+ self._m_selfmus = copy(self.mus)
+ self._mus = self.musPivot
+ self._m_mu0 = copy(self.mu0)
+ self._m_HFErescalingExp = copy(self.HFEngine.rescalingExp)
+ self._mu0 = checkParameterList(self.mu0(self.directionPivot), 1)[0]
+ self.HFEngine.rescalingExp = [self.HFEngine.rescalingExp[
+ self.directionPivot[0]]]
+
+ def setupApproxPivoted(self, mus:paramList) -> int:
+ if self.checkComputedApproxPivoted(): return -1
+ RROMPyAssert(self._mode, message = "Cannot setup approximant.")
+ vbMng(self, "INIT", "Setting up pivoted approximant.", 10)
+ Qs, Ps = self._preSetupApproxPivoted(mus)
+ for j, mu in enumerate(mus):
+ RationalInterpolant.setupSampling(self)
+ self.trainedModel = None
+ self.musMargLoc += [mu]
+ RationalInterpolant.setupApprox(self)
+ self._mu0 = self._m_mu0
+ self._mus = self._m_selfmus
+ self.HFEngine.rescalingExp = self._m_HFErescalingExp
+ del self._m_mu0, self._m_selfmus, self._m_HFErescalingExp
+ self.samplingEngs[j] = copy(self.samplingEngine)
+ Qs[j] = copy(self.trainedModel.data.Q)
+ Ps[j] = copy(self.trainedModel.data.P)
+ self._postSetupApproxPivoted(mus, Qs, Ps)
+ vbMng(self, "DEL", "Done setting up pivoted approximant.", 10)
+ return 0
+
+class RationalInterpolantPivotedGreedyNoMatch(
+ RationalInterpolantPivotedGreedyBase,
+ GenericPivotedGreedyApproximantNoMatch,
+ RationalInterpolantPivotedNoMatch):
+ """
+ ROM pivoted greedy rational interpolant computation for parametric
+ problems (without pole matching).
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation; defaults to 0;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': number of starting marginal samples;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD', and
+ 'LOOK_AHEAD_RECOVER'; defaults to 'LEAVE_ONE_OUT';
+ - 'polybasis': type of polynomial basis for pivot interpolation;
+ defaults to 'MONOMIAL';
+ - 'M': degree of rational interpolant numerator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'N': degree of rational interpolant denominator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm; defaults to 1e-1;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ defaults to 1e2;
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator; defaults to 1;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musPivot: Array of pivot snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ - 'polybasis': type of polynomial basis for pivot interpolation;
+ - 'M': degree of rational interpolant numerator;
+ - 'N': degree of rational interpolant denominator;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ matchingWeightError: Weight for pole matching optimization in error
+ estimation.
+ cutOffToleranceError: Tolerance for ignoring parasitic poles in error
+ estimation.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginalGrid: Marginal sample point generator via sparse grid.
+ errorEstimatorKindMarginal: Kind of marginal error estimator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ M: Degree of rational interpolant numerator.
+ N: Degree of rational interpolant denominator.
+ greedyTolMarginal: Uniform error tolerance for marginal greedy
+ algorithm.
+ maxIterMarginal: Maximum number of marginal greedy steps.
+ radialDirectionalWeights: Radial basis weights for pivot numerator.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
+
+class RationalInterpolantPivotedGreedy(RationalInterpolantPivotedGreedyBase,
+ GenericPivotedGreedyApproximant,
RationalInterpolantPivoted):
"""
ROM pivoted greedy rational interpolant computation for parametric
- problems.
+ problems (with pole matching).
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
directionPivot(optional): Pivot components. Defaults to [0].
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'matchingWeight': weight for pole matching optimization; defaults
to 1;
- 'cutOffTolerance': tolerance for ignoring parasitic poles;
defaults to np.inf;
- 'cutOffSharedRatio': required ratio of marginal points to share
resonance in cut off strategy; defaults to 1.;
- 'matchingWeightError': weight for pole matching optimization in
error estimation; defaults to 0;
- 'cutOffToleranceError': tolerance for ignoring parasitic poles
in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
- 'S': total number of pivot samples current approximant relies
upon;
- 'samplerPivot': pivot sample point generator;
- 'SMarginal': number of starting marginal samples;
- 'samplerMarginalGrid': marginal sample point generator via sparse
grid;
- 'errorEstimatorKindMarginal': kind of marginal error estimator;
available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD', and
'LOOK_AHEAD_RECOVER'; defaults to 'LEAVE_ONE_OUT';
- 'polybasis': type of polynomial basis for pivot interpolation;
defaults to 'MONOMIAL';
- 'polybasisMarginal': type of polynomial basis for marginal
interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
and 'LEGENDRE'; defaults to 'MONOMIAL';
- 'M': degree of rational interpolant numerator; defaults to
'AUTO', i.e. maximum allowed;
- 'N': degree of rational interpolant denominator; defaults to
'AUTO', i.e. maximum allowed;
- 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
i.e. maximum allowed;
- 'greedyTolMarginal': uniform error tolerance for marginal greedy
algorithm; defaults to 1e-1;
- 'maxIterMarginal': maximum number of marginal greedy steps;
defaults to 1e2;
- 'polydegreetypeMarginal': type of polynomial degree for marginal;
defaults to 'TOTAL';
- 'radialDirectionalWeights': radial basis weights for pivot
numerator; defaults to 1;
- 'radialDirectionalWeightsMarginal': radial basis weights for
marginal interpolant; defaults to 1;
- - 'nNearestNeighbor': number of pivot nearest neighbors considered
- if polybasis allows; defaults to -1;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows; defaults to -1;
- 'interpRcond': tolerance for pivot interpolation; defaults to
None;
- 'interpRcondMarginal': tolerance for marginal interpolation;
defaults to None;
- 'robustTol': tolerance for robust rational denominator
management; defaults to 0;
- 'correctorForce': whether corrector should forcefully delete bad
poles; defaults to False;
- 'correctorTol': tolerance for corrector step; defaults to 0.,
i.e. no bad poles;
- 'correctorMaxIter': maximum number of corrector iterations;
defaults to 1.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults to
False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
directionPivot: Pivot components.
mus: Array of snapshot parameters.
musPivot: Array of pivot snapshot parameters.
musMarginal: Array of marginal snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation;
- 'matchingWeight': weight for pole matching optimization;
- 'cutOffTolerance': tolerance for ignoring parasitic poles;
- 'cutOffSharedRatio': required ratio of marginal points to share
resonance in cut off strategy;
- 'matchingWeightError': weight for pole matching optimization in
error estimation;
- 'cutOffToleranceError': tolerance for ignoring parasitic poles
in error estimation;
- 'errorEstimatorKindMarginal': kind of marginal error estimator;
- 'polybasis': type of polynomial basis for pivot interpolation;
- 'polybasisMarginal': type of polynomial basis for marginal
interpolation;
- 'M': degree of rational interpolant numerator;
- 'N': degree of rational interpolant denominator;
- 'MMarginal': degree of marginal interpolant;
- 'greedyTolMarginal': uniform error tolerance for marginal greedy
algorithm;
- 'maxIterMarginal': maximum number of marginal greedy steps;
- 'polydegreetypeMarginal': type of polynomial degree for marginal;
- 'radialDirectionalWeights': radial basis weights for pivot
numerator;
- 'radialDirectionalWeightsMarginal': radial basis weights for
marginal interpolant;
- - 'nNearestNeighbor': number of pivot nearest neighbors considered
- if polybasis allows;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows;
- 'interpRcond': tolerance for pivot interpolation;
- 'interpRcondMarginal': tolerance for marginal interpolation;
- 'robustTol': tolerance for robust rational denominator
management;
- 'correctorForce': whether corrector should forcefully delete bad
poles;
- 'correctorTol': tolerance for corrector step;
- 'correctorMaxIter': maximum number of corrector iterations.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of pivot samples current approximant relies
upon;
- 'samplerPivot': pivot sample point generator;
- 'SMarginal': total number of marginal samples current approximant
relies upon;
- 'samplerMarginalGrid': marginal sample point generator via sparse
grid.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
matchingWeight: Weight for pole matching optimization.
cutOffTolerance: Tolerance for ignoring parasitic poles.
cutOffSharedRatio: Required ratio of marginal points to share resonance
in cut off strategy.
matchingWeightError: Weight for pole matching optimization in error
estimation.
cutOffToleranceError: Tolerance for ignoring parasitic poles in error
estimation.
S: Total number of pivot samples current approximant relies upon.
samplerPivot: Pivot sample point generator.
SMarginal: Total number of marginal samples current approximant relies
upon.
samplerMarginalGrid: Marginal sample point generator via sparse grid.
errorEstimatorKindMarginal: Kind of marginal error estimator.
polybasis: Type of polynomial basis for pivot interpolation.
polybasisMarginal: Type of polynomial basis for marginal interpolation.
M: Degree of rational interpolant numerator.
N: Degree of rational interpolant denominator.
MMarginal: Degree of marginal interpolant.
greedyTolMarginal: Uniform error tolerance for marginal greedy
algorithm.
maxIterMarginal: Maximum number of marginal greedy steps.
polydegreetypeMarginal: Type of polynomial degree for marginal.
radialDirectionalWeights: Radial basis weights for pivot numerator.
radialDirectionalWeightsMarginal: Radial basis weights for marginal
interpolant.
- nNearestNeighbor: Number of pivot nearest neighbors considered if
- polybasis allows.
- nNearestNeighborMarginal: Number of marginal nearest neighbors
- considered if polybasisMarginal allows.
interpRcond: Tolerance for pivot interpolation.
interpRcondMarginal: Tolerance for marginal interpolation.
robustTol: Tolerance for robust rational denominator management.
correctorForce: Whether corrector should forcefully delete bad poles.
correctorTol: Tolerance for corrector step.
correctorMaxIter: Maximum number of corrector iterations.
muBounds: list of bounds for pivot parameter values.
muBoundsMarginal: list of bounds for marginal parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
"""
-
- def computeSnapshots(self):
- """Compute snapshots of solution map."""
- RROMPyAssert(self._mode,
- message = "Cannot start snapshot computation.")
- vbMng(self, "INIT", "Starting computation of snapshots.", 5)
- self.samplingEngine.scaleFactor = self.scaleFactorDer
- self.musPivot = self.samplerPivot.generatePoints(self.S)
- while len(self.musPivot) > self.S: self.musPivot.pop()
- self.mus = emptyParameterList()
- self.mus.reset((self.S, self.HFEngine.npar))
- self.samplingEngine.resetHistory()
- for k in range(self.S):
- self.mus.data[k, self.directionPivot] = self.musPivot[k].data
- self.mus.data[k, self.directionMarginal] = self.musMargLoc[-1].data
- self.samplingEngine.iterSample(self.mus)
- vbMng(self, "DEL", "Done computing snapshots.", 5)
- self._m_selfmus = copy(self.mus)
- self._mus = self.musPivot
- self._m_mu0 = copy(self.mu0)
- self._m_HFErescalingExp = copy(self.HFEngine.rescalingExp)
- self._mu0 = checkParameterList(self.mu0(self.directionPivot), 1)[0]
- self.HFEngine.rescalingExp = [self.HFEngine.rescalingExp[
- self.directionPivot[0]]]
- def setupApproxPivoted(self, mus:paramList) -> int:
- if self.checkComputedApproxPivoted(): return -1
- RROMPyAssert(self._mode, message = "Cannot setup approximant.")
- vbMng(self, "INIT", "Setting up pivoted approximant.", 10)
- Qs, Ps = self._preSetupApproxPivoted(mus)
- for j, mu in enumerate(mus):
- RationalInterpolant.setupSampling(self)
- self.trainedModel = None
- self.musMargLoc += [mu]
- RationalInterpolant.setupApprox(self)
- self._mu0 = self._m_mu0
- self._mus = self._m_selfmus
- self.HFEngine.rescalingExp = self._m_HFErescalingExp
- del self._m_mu0, self._m_selfmus, self._m_HFErescalingExp
- self.samplingEngs[j] = copy(self.samplingEngine)
- Qs[j] = copy(self.trainedModel.data.Q)
- Ps[j] = copy(self.trainedModel.data.P)
- self._postSetupApproxPivoted(mus, Qs, Ps)
- vbMng(self, "DEL", "Done setting up pivoted approximant.", 10)
- return 0
+class RationalInterpolantPivotedGreedyNearestNeighbor(
+ RationalInterpolantPivotedGreedy,
+ GenericPivotedGreedyApproximantNearestNeighbor,
+ RationalInterpolantPivotedNearestNeighbor):
+ """
+ ROM pivoted greedy rational interpolant computation for parametric
+ problems (with pole matching) via local average.
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation; defaults to 0;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation; defaults to 'AUTO', i.e. cutOffTolerance;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': number of starting marginal samples;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ available values include 'LEAVE_ONE_OUT', 'LOOK_AHEAD', and
+ 'LOOK_AHEAD_RECOVER'; defaults to 'LEAVE_ONE_OUT';
+ - 'polybasis': type of polynomial basis for pivot interpolation;
+ defaults to 'MONOMIAL';
+ - 'M': degree of rational interpolant numerator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'N': degree of rational interpolant denominator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ defaults to 1;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm; defaults to 1e-1;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ defaults to 1e2;
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator; defaults to 1;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musPivot: Array of pivot snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'matchingWeightError': weight for pole matching optimization in
+ error estimation;
+ - 'cutOffToleranceError': tolerance for ignoring parasitic poles
+ in error estimation;
+ - 'errorEstimatorKindMarginal': kind of marginal error estimator;
+ - 'polybasis': type of polynomial basis for pivot interpolation;
+ - 'M': degree of rational interpolant numerator;
+ - 'N': degree of rational interpolant denominator;
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ - 'greedyTolMarginal': uniform error tolerance for marginal greedy
+ algorithm;
+ - 'maxIterMarginal': maximum number of marginal greedy steps;
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginalGrid': marginal sample point generator via sparse
+ grid.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ matchingWeightError: Weight for pole matching optimization in error
+ estimation.
+ cutOffToleranceError: Tolerance for ignoring parasitic poles in error
+ estimation.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginalGrid: Marginal sample point generator via sparse grid.
+ errorEstimatorKindMarginal: Kind of marginal error estimator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ M: Degree of rational interpolant numerator.
+ N: Degree of rational interpolant denominator.
+ nNeighborsMarginal: Number of marginal nearest neighbors.
+ greedyTolMarginal: Uniform error tolerance for marginal greedy
+ algorithm.
+ maxIterMarginal: Maximum number of marginal greedy steps.
+ radialDirectionalWeights: Radial basis weights for pivot numerator.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ """
diff --git a/rrompy/reduction_methods/pivoted/rational_interpolant_greedy_pivoted.py b/rrompy/reduction_methods/pivoted/rational_interpolant_greedy_pivoted.py
index 6d3dc94..581d02c 100644
--- a/rrompy/reduction_methods/pivoted/rational_interpolant_greedy_pivoted.py
+++ b/rrompy/reduction_methods/pivoted/rational_interpolant_greedy_pivoted.py
@@ -1,447 +1,783 @@
# 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 .
#
from copy import deepcopy as copy
import numpy as np
-from .generic_pivoted_approximant import GenericPivotedApproximant, PODGlobal
+from .generic_pivoted_approximant import (GenericPivotedApproximantBase,
+ GenericPivotedApproximantNoMatch,
+ GenericPivotedApproximant, PODGlobal,
+ GenericPivotedApproximantNearestNeighbor)
from rrompy.reduction_methods.standard.greedy.rational_interpolant_greedy \
import RationalInterpolantGreedy
from rrompy.reduction_methods.standard.greedy.generic_greedy_approximant \
import pruneSamples
from rrompy.utilities.base.types import Np1D
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical import dot
from rrompy.utilities.numerical.degree import totalDegreeN
from rrompy.utilities.poly_fitting.polynomial import polyvander as pv
-from rrompy.utilities.exception_manager import RROMPyAssert
+from rrompy.utilities.exception_manager import RROMPyAssert, RROMPyWarning
from rrompy.parameter import emptyParameterList, checkParameterList
-__all__ = ['RationalInterpolantGreedyPivoted']
+__all__ = ['RationalInterpolantGreedyPivotedNoMatch',
+ 'RationalInterpolantGreedyPivoted',
+ 'RationalInterpolantGreedyPivotedNearestNeighbor']
-class RationalInterpolantGreedyPivoted(GenericPivotedApproximant,
- RationalInterpolantGreedy):
- """
- ROM pivoted rational interpolant (with pole matching) computation for
- parametric problems.
-
- Args:
- HFEngine: HF problem solver.
- mu0(optional): Default parameter. Defaults to 0.
- directionPivot(optional): Pivot components. Defaults to [0].
- approxParameters(optional): Dictionary containing values for main
- parameters of approximant. Recognized keys are:
- - 'POD': whether to compute POD of snapshots; defaults to True;
- - 'scaleFactorDer': scaling factors for derivative computation;
- defaults to 'AUTO';
- - 'matchingWeight': weight for pole matching optimization; defaults
- to 1;
- - 'cutOffTolerance': tolerance for ignoring parasitic poles;
- defaults to np.inf;
- - 'cutOffSharedRatio': required ratio of marginal points to share
- resonance in cut off strategy; defaults to 1.;
- - 'S': total number of pivot samples current approximant relies
- upon;
- - 'samplerPivot': pivot sample point generator;
- - 'SMarginal': total number of marginal samples current approximant
- relies upon;
- - 'samplerMarginal': marginal sample point generator;
- - 'polybasis': type of polynomial basis for pivot
- interpolation; defaults to 'MONOMIAL';
- - 'polybasisMarginal': type of polynomial basis for marginal
- interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
- and 'LEGENDRE'; defaults to 'MONOMIAL';
- - 'greedyTol': uniform error tolerance for greedy algorithm;
- defaults to 1e-2;
- - 'collinearityTol': collinearity tolerance for greedy algorithm;
- defaults to 0.;
- - 'maxIter': maximum number of greedy steps; defaults to 1e2;
- - 'nTestPoints': number of test points; defaults to 5e2;
- - 'trainSetGenerator': training sample points generator; defaults
- to sampler;
- - 'errorEstimatorKind': kind of error estimator; available values
- include 'AFFINE', 'DISCREPANCY', 'LOOK_AHEAD',
- 'LOOK_AHEAD_RES', and 'NONE'; defaults to 'NONE';
- - 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
- i.e. maximum allowed;
- - 'polydegreetypeMarginal': type of polynomial degree for marginal;
- defaults to 'TOTAL';
- - 'radialDirectionalWeightsMarginal': radial basis weights for
- marginal interpolant; defaults to 1;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows; defaults to -1;
- - 'interpRcond': tolerance for pivot interpolation; defaults to
- None;
- - 'interpRcondMarginal': tolerance for marginal interpolation;
- defaults to None;
- - 'robustTol': tolerance for robust rational denominator
- management; defaults to 0.
- Defaults to empty dict.
- approx_state(optional): Whether to approximate state. Defaults to
- False.
- verbosity(optional): Verbosity level. Defaults to 10.
-
- Attributes:
- HFEngine: HF problem solver.
- mu0: Default parameter.
- directionPivot: Pivot components.
- mus: Array of snapshot parameters.
- musMarginal: Array of marginal snapshot parameters.
- approxParameters: Dictionary containing values for main parameters of
- approximant. Recognized keys are in parameterList.
- parameterListSoft: Recognized keys of soft approximant parameters:
- - 'POD': whether to compute POD of snapshots;
- - 'scaleFactorDer': scaling factors for derivative computation;
- - 'matchingWeight': weight for pole matching optimization;
- - 'cutOffTolerance': tolerance for ignoring parasitic poles;
- - 'cutOffSharedRatio': required ratio of marginal points to share
- resonance in cut off strategy;
- - 'polybasis': type of polynomial basis for pivot
- interpolation;
- - 'polybasisMarginal': type of polynomial basis for marginal
- interpolation;
- - 'greedyTol': uniform error tolerance for greedy algorithm;
- - 'collinearityTol': collinearity tolerance for greedy algorithm;
- - 'maxIter': maximum number of greedy steps;
- - 'nTestPoints': number of test points;
- - 'trainSetGenerator': training sample points generator;
- - 'errorEstimatorKind': kind of error estimator;
- - 'MMarginal': degree of marginal interpolant;
- - 'polydegreetypeMarginal': type of polynomial degree for marginal;
- - 'radialDirectionalWeightsMarginal': radial basis weights for
- marginal interpolant;
- - 'nNearestNeighbor': number of pivot nearest neighbors considered
- if polybasis allows;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows;
- - 'interpRcond': tolerance for pivot interpolation;
- - 'interpRcondMarginal': tolerance for marginal interpolation;
- - 'robustTol': tolerance for robust rational denominator
- management;
- - 'correctorForce': whether corrector should forcefully delete bad
- poles;
- - 'correctorTol': tolerance for corrector step;
- - 'correctorMaxIter': maximum number of corrector iterations.
- parameterListCritical: Recognized keys of critical approximant
- parameters:
- - 'S': total number of pivot samples current approximant relies
- upon;
- - 'samplerPivot': pivot sample point generator;
- - 'SMarginal': total number of marginal samples current approximant
- relies upon;
- - 'samplerMarginal': marginal sample point generator.
- approx_state: Whether to approximate state.
- verbosity: Verbosity level.
- POD: Whether to compute POD of snapshots.
- scaleFactorDer: Scaling factors for derivative computation.
- matchingWeight: Weight for pole matching optimization.
- cutOffTolerance: Tolerance for ignoring parasitic poles.
- cutOffSharedRatio: Required ratio of marginal points to share resonance
- in cut off strategy.
- S: Total number of pivot samples current approximant relies upon.
- samplerPivot: Pivot sample point generator.
- SMarginal: Total number of marginal samples current approximant relies
- upon.
- samplerMarginal: Marginal sample point generator.
- polybasis: Type of polynomial basis for pivot interpolation.
- polybasisMarginal: Type of polynomial basis for marginal interpolation.
- greedyTol: uniform error tolerance for greedy algorithm.
- collinearityTol: Collinearity tolerance for greedy algorithm.
- maxIter: maximum number of greedy steps.
- nTestPoints: number of starting training points.
- trainSetGenerator: training sample points generator.
- errorEstimatorKind: kind of error estimator.
- MMarginal: Degree of marginal interpolant.
- polydegreetypeMarginal: Type of polynomial degree for marginal.
- radialDirectionalWeightsMarginal: Radial basis weights for marginal
- interpolant.
- nNearestNeighborMarginal: Number of marginal nearest neighbors
- considered if polybasisMarginal allows.
- interpRcond: Tolerance for pivot interpolation.
- interpRcondMarginal: Tolerance for marginal interpolation.
- robustTol: Tolerance for robust rational denominator management.
- muBounds: list of bounds for pivot parameter values.
- muBoundsMarginal: list of bounds for marginal parameter values.
- samplingEngine: Sampling engine.
- uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
- sampleList.
- lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
- solution(s) as parameterList.
- uApproxReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApprox as sampleList.
- lastSolvedApproxReduced: Parameter(s) corresponding to last computed
- reduced approximate solution(s) as parameterList.
- uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
- sampleList.
- lastSolvedApprox: Parameter(s) corresponding to last computed
- approximate solution(s) as parameterList.
- Q: Numpy 1D vector containing complex coefficients of approximant
- denominator.
- P: Numpy 2D vector whose columns are FE dofs of coefficients of
- approximant numerator.
- """
-
+class RationalInterpolantGreedyPivotedBase(GenericPivotedApproximantBase,
+ RationalInterpolantGreedy):
def __init__(self, *args, **kwargs):
self._preInit()
self._addParametersToList(toBeExcluded = ["sampler"])
super().__init__(*args, **kwargs)
self._postInit()
@property
def tModelType(self):
if hasattr(self, "_temporaryPivot"):
return RationalInterpolantGreedy.tModelType.fget(self)
return super().tModelType
@property
def polybasis0(self):
if "_" in self.polybasis:
return self.polybasis.split("_")[0]
return self.polybasis
+ @property
+ def correctorTol(self):
+ """Value of correctorTol."""
+ return self._correctorTol
+ @correctorTol.setter
+ def correctorTol(self, correctorTol):
+ if correctorTol < 0. or (correctorTol > 0. and self.nparPivot > 1):
+ RROMPyWarning(("Overriding prescribed corrector tolerance "
+ "to 0."))
+ correctorTol = 0.
+ self._correctorTol = correctorTol
+ self._approxParameters["correctorTol"] = self.correctorTol
+
+ @property
+ def correctorMaxIter(self):
+ """Value of correctorMaxIter."""
+ return self._correctorMaxIter
+ @correctorMaxIter.setter
+ def correctorMaxIter(self, correctorMaxIter):
+ if correctorMaxIter < 1 or (correctorMaxIter > 1
+ and self.nparPivot > 1):
+ RROMPyWarning(("Overriding prescribed max number of corrector "
+ "iterations to 1."))
+ correctorMaxIter = 1
+ self._correctorMaxIter = correctorMaxIter
+ self._approxParameters["correctorMaxIter"] = self.correctorMaxIter
+
def _polyvanderAuxiliary(self, mus, deg, *args):
degEff = [0] * self.npar
degEff[self.directionPivot[0]] = deg
return pv(mus, degEff, *args)
def _marginalizeMiscellanea(self, forward:bool):
if forward:
self._m_mu0 = copy(self.mu0)
self._m_selfmus = copy(self.mus)
self._m_HFErescalingExp = copy(self.HFEngine.rescalingExp)
self._mu0 = checkParameterList(self.mu0(self.directionPivot), 1)[0]
self._mus = checkParameterList(self.mus(self.directionPivot), 1)[0]
self.HFEngine.rescalingExp = [self.HFEngine.rescalingExp[
self.directionPivot[0]]]
else:
self._mu0 = self._m_mu0
self._mus = self._m_selfmus
self.HFEngine.rescalingExp = self._m_HFErescalingExp
del self._m_mu0, self._m_selfmus, self._m_HFErescalingExp
def _marginalizeTrainedModel(self, forward:bool):
if forward:
del self._temporaryPivot
self.trainedModel.data.mu0 = self.mu0
self.trainedModel.data.scaleFactor = [1.] * self.npar
self.trainedModel.data.scaleFactor[self.directionPivot[0]] = (
self.scaleFactor[0])
self.trainedModel.data.rescalingExp = self.HFEngine.rescalingExp
- Qc = np.zeros((len(self.trainedModel.data.Q.coeffs),) * self.npar,
+ Qc = np.zeros((1,) * self.directionPivot[0]
+ + (len(self.trainedModel.data.Q.coeffs),)
+ + (1,) * (self.npar - self.directionPivot[0] - 1),
dtype = self.trainedModel.data.Q.coeffs.dtype)
- Pc = np.zeros((len(self.trainedModel.data.P.coeffs),) * self.npar
+ Pc = np.zeros((1,) * self.directionPivot[0]
+ + (len(self.trainedModel.data.P.coeffs),)
+ + (1,) * (self.npar - self.directionPivot[0] - 1)
+ (self.trainedModel.data.P.coeffs.shape[1],),
dtype = self.trainedModel.data.P.coeffs.dtype)
for j in range(len(self.trainedModel.data.Q.coeffs)):
Qc[(0,) * self.directionPivot[0] + (j,)
+ (0,) * (self.npar - self.directionPivot[0] - 1)] = (
self.trainedModel.data.Q.coeffs[j])
for j in range(len(self.trainedModel.data.P.coeffs)):
for k in range(self.trainedModel.data.P.coeffs.shape[1]):
Pc[(0,) * self.directionPivot[0] + (j,)
+ (0,) * (self.npar - self.directionPivot[0] - 1)
+ (k,)] = self.trainedModel.data.P.coeffs[j, k]
self.trainedModel.data.Q.coeffs = Qc
self.trainedModel.data.P.coeffs = Pc
self._m_musUniqueCN = copy(self._musUniqueCN)
musUniqueCNAux = np.zeros((self.S, self.npar),
dtype = self._musUniqueCN.dtype)
musUniqueCNAux[:, self.directionPivot[0]] = self._musUniqueCN(0)
self._musUniqueCN = checkParameterList(musUniqueCNAux,
self.npar)[0]
self._m_derIdxs = copy(self._derIdxs)
for j in range(len(self._derIdxs)):
for l in range(len(self._derIdxs[j])):
derjl = self._derIdxs[j][l][0]
self._derIdxs[j][l] = [0] * self.npar
self._derIdxs[j][l][self.directionPivot[0]] = derjl
else:
self._temporaryPivot = 1
self.trainedModel.data.mu0 = checkParameterList(
self.mu0(self.directionPivot), 1)[0]
self.trainedModel.data.scaleFactor = self.scaleFactor
self.trainedModel.data.rescalingExp = self.HFEngine.rescalingExp[
self.directionPivot[0]]
self.trainedModel.data.Q.coeffs = self.trainedModel.data.Q.coeffs[
(0,) * self.directionPivot[0]
+ (slice(None),)
+ (0,) * (self.HFEngine.npar - 1
- self.directionPivot[0])]
self.trainedModel.data.P.coeffs = self.trainedModel.data.P.coeffs[
(0,) * self.directionPivot[0]
+ (slice(None),)
+ (0,) * (self.HFEngine.npar - 1
- self.directionPivot[0])]
self._musUniqueCN = copy(self._m_musUniqueCN)
self._derIdxs = copy(self._m_derIdxs)
del self._m_musUniqueCN, self._m_derIdxs
self.trainedModel.data.npar = self.npar
self.trainedModel.data.Q.npar = self.npar
self.trainedModel.data.P.npar = self.npar
def errorEstimator(self, mus:Np1D, return_max : bool = False) -> Np1D:
"""Standard residual-based error estimator."""
self._marginalizeMiscellanea(True)
setupOK = self.setupApproxLocal()
self._marginalizeMiscellanea(False)
if setupOK > 0:
err = np.empty(len(mus))
err[:] = np.nan
if not return_max: return err
return err, [- setupOK], np.nan
self._marginalizeTrainedModel(True)
errRes = super().errorEstimator(mus, return_max)
self._marginalizeTrainedModel(False)
return errRes
def _preliminaryTraining(self):
"""Initialize starting snapshots of solution map."""
RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
S = self.S
self.sampleBatchIdx, self.sampleBatchSize, self._S = -1, 0, 0
nextBatchSize = 1
while self._S + nextBatchSize <= S:
self.sampleBatchIdx += 1
self.sampleBatchSize = nextBatchSize
self._S += self.sampleBatchSize
nextBatchSize = totalDegreeN(self.npar - 1,
self.sampleBatchIdx + 1)
self.resetSamples()
self.samplingEngine.scaleFactor = self.scaleFactorDer
musPivot = self.trainSetGenerator.generatePoints(self.S)
while len(musPivot) > self.S: musPivot.pop()
muTestPivot = self.samplerPivot.generatePoints(self.nTestPoints, False)
idxPop = pruneSamples(muTestPivot ** self.HFEngine.rescalingExp[
self.directionPivot[0]],
musPivot ** self.HFEngine.rescalingExp[
self.directionPivot[0]],
1e-10 * self.scaleFactor[0])
self.mus = emptyParameterList()
self.mus.reset((self.S, self.npar + len(self.musMargLoc)))
muTestBase = emptyParameterList()
muTestBase.reset((len(muTestPivot), self.npar + len(self.musMargLoc)))
for k in range(self.S):
self.mus.data[k, self.directionPivot] = musPivot[k].data
self.mus.data[k, self.directionMarginal] = self.musMargLoc.data
for k in range(len(muTestPivot)):
muTestBase.data[k, self.directionPivot] = muTestPivot[k].data
muTestBase.data[k, self.directionMarginal] = self.musMargLoc.data
muTestBase.pop(idxPop)
muLast = copy(self.mus[-1])
self.mus.pop()
if len(self.mus) > 0:
vbMng(self, "MAIN",
("Adding first {} sample point{} at {} to training "
"set.").format(self.S - 1, "" + "s" * (self.S > 2),
self.mus), 3)
self.samplingEngine.iterSample(self.mus)
self._S = len(self.mus)
self._approxParameters["S"] = self.S
self.muTest = emptyParameterList()
self.muTest.reset((len(muTestBase) + 1, self.mus.shape[1]))
self.muTest.data[: -1] = muTestBase.data
self.muTest.data[-1] = muLast.data
self.M, self.N = ("AUTO",) * 2
def _finalizeSnapshots(self):
self.setupSampling()
self.samplingEngine.resetHistory(len(self.musMarginal))
for j in range(len(self.musMarginal)):
self.samplingEngine.setsample(self.samplingEngs[j].samples,
j, False)
self.samplingEngine.mus[j] = copy(self.samplingEngs[j].mus)
self.samplingEngine.musMarginal[j] = copy(self.musMarginal[j])
self.samplingEngine.nsamples[j] = self.samplingEngs[j].nsamples
if self.POD:
self.samplingEngine.RPOD[j] = self.samplingEngs[j].RPOD
self.samplingEngine.samples_full[j].data = (
self.samplingEngs[j].samples_full.data)
if self.POD == PODGlobal:
self.samplingEngine.coalesceSamples(self.interpRcondMarginal)
else:
self.samplingEngine.coalesceSamples()
def setupApprox(self, *args, **kwargs) -> int:
"""Compute rational interpolant."""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5)
self.musMarginal = self.samplerMarginal.generatePoints(self.SMarginal)
while len(self.musMarginal) > self.SMarginal: self.musMarginal.pop()
S0 = copy(self.S)
Qs, Ps = [None] * len(self.musMarginal), [None] * len(self.musMarginal)
self.samplingEngs = [None] * len(self.musMarginal)
self.computeScaleFactor()
self._scaleFactorOldPivot = copy(self.scaleFactor)
self.scaleFactor = self.scaleFactorPivot
self._temporaryPivot = 1
for j in range(len(self.musMarginal)):
vbMng(self, "MAIN",
"Building marginal model no. {} at {}.".format(j + 1,
self.musMarginal[j]), 5)
self._S = S0
self.musMargLoc = self.musMarginal[j]
RationalInterpolantGreedy.setupSampling(self)
self.trainedModel = None
self.verbosity -= 5
self.samplingEngine.verbosity -= 5
super().setupApprox(*args, **kwargs)
self.verbosity += 5
self.samplingEngine.verbosity += 5
self.samplingEngs[j] = copy(self.samplingEngine)
Qs[j] = copy(self.trainedModel.data.Q)
Ps[j] = copy(self.trainedModel.data.P)
self.scaleFactor = self._scaleFactorOldPivot
del self._scaleFactorOldPivot, self._temporaryPivot
self._finalizeSnapshots()
del self.musMargLoc, self.samplingEngs
self._mus = self.samplingEngine.musCoalesced
padLeft = 0
if self.POD != PODGlobal: padRight = self.samplingEngine.nsamplesTot
for j in range(len(self.musMarginal)):
nsj = self.samplingEngine.nsamples[j]
if self.POD == PODGlobal:
rRightj = self.samplingEngine.RPODCPart[:,
padLeft : padLeft + nsj]
Ps[j].postmultiplyTensorize(rRightj.T)
else:
padRight -= nsj
Ps[j].pad(padLeft, padRight)
padLeft += nsj
pMat = self.samplingEngine.samplesCoalesced.data
pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat
self.trainedModel = self.tModelType()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
- datadict = {"mu0": self.mu0, "projMat": pMatEff,
+ datadict = {"mu0": self.mu0, "mus": copy(self.mus), "projMat": pMatEff,
"scaleFactor": self.scaleFactor,
"rescalingExp": self.HFEngine.rescalingExp,
"directionPivot": self.directionPivot}
self.trainedModel.data = self.initializeModelData(datadict)[0]
- self.trainedModel.data.mus = copy(self.mus)
self.trainedModel.data.musMarginal = copy(self.musMarginal)
self.trainedModel.data.Qs, self.trainedModel.data.Ps = Qs, Ps
- vbMng(self, "INIT", "Matching poles.", 10)
- self.trainedModel.initializeFromRational(self.HFEngine,
- self.matchingWeight,
- self.POD == PODGlobal, False)
- vbMng(self, "DEL", "Done matching poles.", 10)
+ self._poleMatching()
self._finalizeMarginalization()
vbMng(self, "DEL", "Done setting up approximant.", 5)
return 0
+
+class RationalInterpolantGreedyPivotedNoMatch(
+ RationalInterpolantGreedyPivotedBase,
+ GenericPivotedApproximantNoMatch):
+ """
+ ROM pivoted rational interpolant (without pole matching) computation for
+ parametric problems.
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation; defaults to 'MONOMIAL';
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ defaults to 1e-2;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ defaults to 0.;
+ - 'maxIter': maximum number of greedy steps; defaults to 1e2;
+ - 'nTestPoints': number of test points; defaults to 5e2;
+ - 'trainSetGenerator': training sample points generator; defaults
+ to sampler;
+ - 'errorEstimatorKind': kind of error estimator; available values
+ include 'AFFINE', 'DISCREPANCY', 'LOOK_AHEAD',
+ 'LOOK_AHEAD_RES', and 'NONE'; defaults to 'NONE';
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation;
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ - 'maxIter': maximum number of greedy steps;
+ - 'nTestPoints': number of test points;
+ - 'trainSetGenerator': training sample points generator;
+ - 'errorEstimatorKind': kind of error estimator;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginal: Marginal sample point generator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ greedyTol: uniform error tolerance for greedy algorithm.
+ collinearityTol: Collinearity tolerance for greedy algorithm.
+ maxIter: maximum number of greedy steps.
+ nTestPoints: number of starting training points.
+ trainSetGenerator: training sample points generator.
+ errorEstimatorKind: kind of error estimator.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ Q: Numpy 1D vector containing complex coefficients of approximant
+ denominator.
+ P: Numpy 2D vector whose columns are FE dofs of coefficients of
+ approximant numerator.
+ """
+
+ def _poleMatching(self):
+ vbMng(self, "INIT", "Compressing poles.", 10)
+ self.trainedModel.initializeFromRational()
+ vbMng(self, "DEL", "Done compressing poles.", 10)
+
+class RationalInterpolantGreedyPivoted(RationalInterpolantGreedyPivotedBase,
+ GenericPivotedApproximant):
+ """
+ ROM pivoted rational interpolant (with pole matching) computation for
+ parametric problems.
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation; defaults to 'MONOMIAL';
+ - 'polybasisMarginal': type of polynomial basis for marginal
+ interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
+ and 'LEGENDRE'; defaults to 'MONOMIAL';
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ defaults to 1e-2;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ defaults to 0.;
+ - 'maxIter': maximum number of greedy steps; defaults to 1e2;
+ - 'nTestPoints': number of test points; defaults to 5e2;
+ - 'trainSetGenerator': training sample points generator; defaults
+ to sampler;
+ - 'errorEstimatorKind': kind of error estimator; available values
+ include 'AFFINE', 'DISCREPANCY', 'LOOK_AHEAD',
+ 'LOOK_AHEAD_RES', and 'NONE'; defaults to 'NONE';
+ - 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
+ i.e. maximum allowed;
+ - 'polydegreetypeMarginal': type of polynomial degree for marginal;
+ defaults to 'TOTAL';
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation;
+ - 'polybasisMarginal': type of polynomial basis for marginal
+ interpolation;
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ - 'maxIter': maximum number of greedy steps;
+ - 'nTestPoints': number of test points;
+ - 'trainSetGenerator': training sample points generator;
+ - 'errorEstimatorKind': kind of error estimator;
+ - 'MMarginal': degree of marginal interpolant;
+ - 'polydegreetypeMarginal': type of polynomial degree for marginal;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginal: Marginal sample point generator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ polybasisMarginal: Type of polynomial basis for marginal interpolation.
+ greedyTol: uniform error tolerance for greedy algorithm.
+ collinearityTol: Collinearity tolerance for greedy algorithm.
+ maxIter: maximum number of greedy steps.
+ nTestPoints: number of starting training points.
+ trainSetGenerator: training sample points generator.
+ errorEstimatorKind: kind of error estimator.
+ MMarginal: Degree of marginal interpolant.
+ polydegreetypeMarginal: Type of polynomial degree for marginal.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ Q: Numpy 1D vector containing complex coefficients of approximant
+ denominator.
+ P: Numpy 2D vector whose columns are FE dofs of coefficients of
+ approximant numerator.
+ """
+
+ def _poleMatching(self):
+ vbMng(self, "INIT", "Compressing and matching poles.", 10)
+ self.trainedModel.initializeFromRational(self.HFEngine,
+ self.matchingWeight,
+ self.POD == PODGlobal, False)
+ vbMng(self, "DEL", "Done compressing and matching poles.", 10)
+
+class RationalInterpolantGreedyPivotedNearestNeighbor(
+ RationalInterpolantGreedyPivoted,
+ GenericPivotedApproximantNearestNeighbor):
+ """
+ ROM pivoted rational interpolant (with pole matching) computation for
+ parametric problems via local average.
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation; defaults to 'MONOMIAL';
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ defaults to 1e-2;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ defaults to 0.;
+ - 'maxIter': maximum number of greedy steps; defaults to 1e2;
+ - 'nTestPoints': number of test points; defaults to 5e2;
+ - 'trainSetGenerator': training sample points generator; defaults
+ to sampler;
+ - 'errorEstimatorKind': kind of error estimator; available values
+ include 'AFFINE', 'DISCREPANCY', 'LOOK_AHEAD',
+ 'LOOK_AHEAD_RES', and 'NONE'; defaults to 'NONE';
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ defaults to 1;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation;
+ - 'greedyTol': uniform error tolerance for greedy algorithm;
+ - 'collinearityTol': collinearity tolerance for greedy algorithm;
+ - 'maxIter': maximum number of greedy steps;
+ - 'nTestPoints': number of test points;
+ - 'trainSetGenerator': training sample points generator;
+ - 'errorEstimatorKind': kind of error estimator;
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginal: Marginal sample point generator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ greedyTol: uniform error tolerance for greedy algorithm.
+ collinearityTol: Collinearity tolerance for greedy algorithm.
+ maxIter: maximum number of greedy steps.
+ nTestPoints: number of starting training points.
+ trainSetGenerator: training sample points generator.
+ errorEstimatorKind: kind of error estimator.
+ nNeighborsMarginal: Number of marginal nearest neighbors.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ Q: Numpy 1D vector containing complex coefficients of approximant
+ denominator.
+ P: Numpy 2D vector whose columns are FE dofs of coefficients of
+ approximant numerator.
+ """
diff --git a/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py b/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py
index a8ef072..50128fa 100644
--- a/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py
+++ b/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py
@@ -1,389 +1,661 @@
# 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 .
#
from copy import deepcopy as copy
import numpy as np
-from .generic_pivoted_approximant import GenericPivotedApproximant, PODGlobal
+from .generic_pivoted_approximant import (GenericPivotedApproximantBase,
+ GenericPivotedApproximantNoMatch,
+ GenericPivotedApproximant, PODGlobal,
+ GenericPivotedApproximantNearestNeighbor)
from rrompy.reduction_methods.standard.rational_interpolant import (
RationalInterpolant)
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical import dot
from rrompy.utilities.numerical.hash_derivative import nextDerivativeIndices
from rrompy.utilities.exception_manager import RROMPyAssert, RROMPyWarning
from rrompy.parameter import emptyParameterList
-__all__ = ['RationalInterpolantPivoted']
+__all__ = ['RationalInterpolantPivotedNoMatch', 'RationalInterpolantPivoted',
+ 'RationalInterpolantPivotedNearestNeighbor']
-class RationalInterpolantPivoted(GenericPivotedApproximant,
- RationalInterpolant):
- """
- ROM pivoted rational interpolant (with pole matching) computation for
- parametric problems.
-
- Args:
- HFEngine: HF problem solver.
- mu0(optional): Default parameter. Defaults to 0.
- directionPivot(optional): Pivot components. Defaults to [0].
- approxParameters(optional): Dictionary containing values for main
- parameters of approximant. Recognized keys are:
- - 'POD': whether to compute POD of snapshots; defaults to True;
- - 'scaleFactorDer': scaling factors for derivative computation;
- defaults to 'AUTO';
- - 'matchingWeight': weight for pole matching optimization; defaults
- to 1;
- - 'cutOffTolerance': tolerance for ignoring parasitic poles;
- defaults to np.inf;
- - 'cutOffSharedRatio': required ratio of marginal points to share
- resonance in cut off strategy; defaults to 1.;
- - 'S': total number of pivot samples current approximant relies
- upon;
- - 'samplerPivot': pivot sample point generator;
- - 'SMarginal': total number of marginal samples current approximant
- relies upon;
- - 'samplerMarginal': marginal sample point generator;
- - 'polybasis': type of polynomial basis for pivot
- interpolation; defaults to 'MONOMIAL';
- - 'polybasisMarginal': type of polynomial basis for marginal
- interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
- and 'LEGENDRE'; defaults to 'MONOMIAL';
- - 'M': degree of rational interpolant numerator; defaults to
- 'AUTO', i.e. maximum allowed;
- - 'N': degree of rational interpolant denominator; defaults to
- 'AUTO', i.e. maximum allowed;
- - 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
- i.e. maximum allowed;
- - 'polydegreetypeMarginal': type of polynomial degree for marginal;
- defaults to 'TOTAL';
- - 'radialDirectionalWeights': radial basis weights for pivot
- numerator; defaults to 1;
- - 'radialDirectionalWeightsMarginal': radial basis weights for
- marginal interpolant; defaults to 1;
- - 'nNearestNeighbor': number of pivot nearest neighbors considered
- if polybasis allows; defaults to -1;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows; defaults to -1;
- - 'interpRcond': tolerance for pivot interpolation; defaults to
- None;
- - 'interpRcondMarginal': tolerance for marginal interpolation;
- defaults to None;
- - 'robustTol': tolerance for robust rational denominator
- management; defaults to 0;
- - 'correctorForce': whether corrector should forcefully delete bad
- poles; defaults to False;
- - 'correctorTol': tolerance for corrector step; defaults to 0.,
- i.e. no bad poles;
- - 'correctorMaxIter': maximum number of corrector iterations;
- defaults to 1.
- Defaults to empty dict.
- approx_state(optional): Whether to approximate state. Defaults to
- False.
- verbosity(optional): Verbosity level. Defaults to 10.
-
- Attributes:
- HFEngine: HF problem solver.
- mu0: Default parameter.
- directionPivot: Pivot components.
- mus: Array of snapshot parameters.
- musPivot: Array of pivot snapshot parameters.
- musMarginal: Array of marginal snapshot parameters.
- approxParameters: Dictionary containing values for main parameters of
- approximant. Recognized keys are in parameterList.
- parameterListSoft: Recognized keys of soft approximant parameters:
- - 'POD': whether to compute POD of snapshots;
- - 'scaleFactorDer': scaling factors for derivative computation;
- - 'matchingWeight': weight for pole matching optimization;
- - 'cutOffTolerance': tolerance for ignoring parasitic poles;
- - 'cutOffSharedRatio': required ratio of marginal points to share
- resonance in cut off strategy;
- - 'polybasis': type of polynomial basis for pivot
- interpolation;
- - 'polybasisMarginal': type of polynomial basis for marginal
- interpolation;
- - 'M': degree of rational interpolant numerator;
- - 'N': degree of rational interpolant denominator;
- - 'MMarginal': degree of marginal interpolant;
- - 'polydegreetypeMarginal': type of polynomial degree for marginal;
- - 'radialDirectionalWeights': radial basis weights for pivot
- numerator;
- - 'radialDirectionalWeightsMarginal': radial basis weights for
- marginal interpolant;
- - 'nNearestNeighbor': number of pivot nearest neighbors considered
- if polybasis allows;
- - 'nNearestNeighborMarginal': number of marginal nearest neighbors
- considered if polybasisMarginal allows;
- - 'interpRcond': tolerance for pivot interpolation;
- - 'interpRcondMarginal': tolerance for marginal interpolation;
- - 'robustTol': tolerance for robust rational denominator
- management;
- - 'correctorForce': whether corrector should forcefully delete bad
- poles;
- - 'correctorTol': tolerance for corrector step;
- - 'correctorMaxIter': maximum number of corrector iterations.
- parameterListCritical: Recognized keys of critical approximant
- parameters:
- - 'S': total number of pivot samples current approximant relies
- upon;
- - 'samplerPivot': pivot sample point generator;
- - 'SMarginal': total number of marginal samples current approximant
- relies upon;
- - 'samplerMarginal': marginal sample point generator.
- approx_state: Whether to approximate state.
- verbosity: Verbosity level.
- POD: Whether to compute POD of snapshots.
- scaleFactorDer: Scaling factors for derivative computation.
- matchingWeight: Weight for pole matching optimization.
- cutOffTolerance: Tolerance for ignoring parasitic poles.
- cutOffSharedRatio: Required ratio of marginal points to share resonance
- in cut off strategy.
- S: Total number of pivot samples current approximant relies upon.
- samplerPivot: Pivot sample point generator.
- SMarginal: Total number of marginal samples current approximant relies
- upon.
- samplerMarginal: Marginal sample point generator.
- polybasis: Type of polynomial basis for pivot interpolation.
- polybasisMarginal: Type of polynomial basis for marginal interpolation.
- M: Numerator degree of approximant.
- N: Denominator degree of approximant.
- MMarginal: Degree of marginal interpolant.
- polydegreetypeMarginal: Type of polynomial degree for marginal.
- radialDirectionalWeights: Radial basis weights for pivot numerator.
- radialDirectionalWeightsMarginal: Radial basis weights for marginal
- interpolant.
- nNearestNeighbor: Number of pivot nearest neighbors considered if
- polybasis allows.
- nNearestNeighborMarginal: Number of marginal nearest neighbors
- considered if polybasisMarginal allows.
- interpRcond: Tolerance for pivot interpolation.
- interpRcondMarginal: Tolerance for marginal interpolation.
- robustTol: Tolerance for robust rational denominator management.
- correctorForce: Whether corrector should forcefully delete bad poles.
- correctorTol: Tolerance for corrector step.
- correctorMaxIter: Maximum number of corrector iterations.
- muBounds: list of bounds for pivot parameter values.
- muBoundsMarginal: list of bounds for marginal parameter values.
- samplingEngine: Sampling engine.
- uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
- sampleList.
- lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
- solution(s) as parameterList.
- uApproxReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApprox as sampleList.
- lastSolvedApproxReduced: Parameter(s) corresponding to last computed
- reduced approximate solution(s) as parameterList.
- uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
- sampleList.
- lastSolvedApprox: Parameter(s) corresponding to last computed
- approximate solution(s) as parameterList.
- Q: Numpy 1D vector containing complex coefficients of approximant
- denominator.
- P: Numpy 2D vector whose columns are FE dofs of coefficients of
- approximant numerator.
- """
-
+class RationalInterpolantPivotedBase(GenericPivotedApproximantBase,
+ RationalInterpolant):
def __init__(self, *args, **kwargs):
self._preInit()
self._addParametersToList(toBeExcluded = ["polydegreetype", "sampler"])
super().__init__(*args, **kwargs)
self._postInit()
@property
def scaleFactorDer(self):
"""Value of scaleFactorDer."""
if self._scaleFactorDer == "NONE": return 1.
if self._scaleFactorDer == "AUTO": return self.scaleFactorPivot
return self._scaleFactorDer
@scaleFactorDer.setter
def scaleFactorDer(self, scaleFactorDer):
if hasattr(self, "_scaleFactorDer"):
scaleFactorDerold = self.scaleFactorDer
else: scaleFactorDerold = -1
if isinstance(scaleFactorDer, (str,)):
scaleFactorDer = scaleFactorDer.upper()
self._scaleFactorDer = scaleFactorDer
self._approxParameters["scaleFactorDer"] = self._scaleFactorDer
if scaleFactorDerold != self._scaleFactorDer: self.resetSamples()
@property
def polydegreetype(self):
"""Value of polydegreetype."""
return "TOTAL"
@polydegreetype.setter
def polydegreetype(self, polydegreetype):
RROMPyWarning(("polydegreetype is used just to simplify inheritance, "
"and its value cannot be changed from 'TOTAL'."))
@property
def polybasis0(self):
if "_" in self.polybasis:
return self.polybasis.split("_")[0]
return self.polybasis
@property
def correctorTol(self):
"""Value of correctorTol."""
return self._correctorTol
@correctorTol.setter
def correctorTol(self, correctorTol):
if correctorTol < 0. or (correctorTol > 0. and self.nparPivot > 1):
RROMPyWarning(("Overriding prescribed corrector tolerance "
"to 0."))
correctorTol = 0.
self._correctorTol = correctorTol
self._approxParameters["correctorTol"] = self.correctorTol
@property
def correctorMaxIter(self):
"""Value of correctorMaxIter."""
return self._correctorMaxIter
@correctorMaxIter.setter
def correctorMaxIter(self, correctorMaxIter):
if correctorMaxIter < 1 or (correctorMaxIter > 1
and self.nparPivot > 1):
RROMPyWarning(("Overriding prescribed max number of corrector "
"iterations to 1."))
correctorMaxIter = 1
self._correctorMaxIter = correctorMaxIter
self._approxParameters["correctorMaxIter"] = self.correctorMaxIter
def _setupInterpolationIndices(self):
"""Setup parameters for polyvander."""
RROMPyAssert(self._mode,
message = "Cannot setup interpolation indices.")
if (self._musUniqueCN is None
or len(self._reorder) != len(self.musPivot)):
try:
muPC = self.trainedModel.centerNormalizePivot(self.musPivot)
except:
muPC = self.trainedModel.centerNormalize(self.musPivot)
self._musUniqueCN, musIdxsTo, musIdxs, musCount = (muPC.unique(
return_index = True, return_inverse = True,
return_counts = True))
self._musUnique = self.musPivot[musIdxsTo]
self._derIdxs = [None] * len(self._musUniqueCN)
self._reorder = np.empty(len(musIdxs), dtype = int)
filled = 0
for j, cnt in enumerate(musCount):
self._derIdxs[j] = nextDerivativeIndices([], self.nparPivot,
cnt)
jIdx = np.nonzero(musIdxs == j)[0]
self._reorder[jIdx] = np.arange(filled, filled + cnt)
filled += cnt
def computeSnapshots(self):
"""Compute snapshots of solution map."""
RROMPyAssert(self._mode,
message = "Cannot start snapshot computation.")
self.computeScaleFactor()
if self.samplingEngine.nsamplesTot != self.S * self.SMarginal:
self.resetSamples()
self.samplingEngine.scaleFactor = self.scaleFactorDer
vbMng(self, "INIT", "Starting computation of snapshots.", 5)
self.musPivot = self.samplerPivot.generatePoints(self.S)
while len(self.musPivot) > self.S: self.musPivot.pop()
self.musMarginal = self.samplerMarginal.generatePoints(
self.SMarginal)
while len(self.musMarginal) > self.SMarginal:
self.musMarginal.pop()
self.mus = emptyParameterList()
self.mus.reset((self.S * self.SMarginal, self.HFEngine.npar))
self.samplingEngine.resetHistory(self.SMarginal)
for j, muMarg in enumerate(self.musMarginal):
for k in range(j * self.S, (j + 1) * self.S):
self.mus.data[k, self.directionPivot] = (
self.musPivot[k - j * self.S].data)
self.mus.data[k, self.directionMarginal] = muMarg.data
self.samplingEngine.iterSample(self.musPivot, self.musMarginal)
self._finalizeSnapshots()
vbMng(self, "DEL", "Done computing snapshots.", 5)
def _finalizeSnapshots(self):
if self.POD == PODGlobal:
self.samplingEngine.coalesceSamples(self.interpRcondMarginal)
else:
self.samplingEngine.coalesceSamples()
def setupApprox(self) -> int:
"""Compute rational interpolant."""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5)
self.computeSnapshots()
pMat = self.samplingEngine.samplesCoalesced.data
pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat
if self.trainedModel is None:
self.trainedModel = self.tModelType()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
- datadict = {"mu0": self.mu0, "projMat": pMatEff,
- "scaleFactor": self.scaleFactor,
+ datadict = {"mu0": self.mu0, "mus": copy(self.mus),
+ "projMat": pMatEff, "scaleFactor": self.scaleFactor,
"rescalingExp": self.HFEngine.rescalingExp,
"directionPivot": self.directionPivot}
self.trainedModel.data = self.initializeModelData(datadict)[0]
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(pMatEff)
+ self.trainedModel.data.mus = copy(self.mus)
N0 = copy(self.N)
Qs, Ps = [None] * len(self.musMarginal), [None] * len(self.musMarginal)
self._temporaryPivot = 1
padLeft = 0
if self.POD:
self._RPODOldPivot = copy(self.samplingEngine.RPODCoalesced)
else:
self._samplesOldPivot = copy(self.samplingEngine.samples)
padRight = self.samplingEngine.nsamplesTot
self._scaleFactorOldPivot = copy(self.scaleFactor)
self.scaleFactor = self.scaleFactorPivot
for j in range(len(self.musMarginal)):
vbMng(self, "MAIN",
"Building marginal model no. {} at {}.".format(j + 1,
self.musMarginal[j]), 5)
self.N = N0
if self.POD:
self.samplingEngine.RPOD = (
self._RPODOldPivot[:, padLeft : padLeft + self.S])
else:
self.samplingEngine.samples = self._samplesOldPivot[j]
padRight -= self.S
self.verbosity -= 5
self._iterCorrector()
self.verbosity += 5
Qs[j] = copy(self.trainedModel.data.Q)
Ps[j] = copy(self.trainedModel.data.P)
del self.trainedModel.data.Q, self.trainedModel.data.P
if not self.POD: Ps[j].pad(padLeft, padRight)
padLeft += self.S
if self.POD:
self.samplingEngine.RPODCoalesced = copy(self._RPODOldPivot)
del self._RPODOldPivot
else:
self.samplingEngine.samples = copy(self._samplesOldPivot)
del self._samplesOldPivot
self.scaleFactor = self._scaleFactorOldPivot
del self._temporaryPivot, self._scaleFactorOldPivot
- self.trainedModel.data.mus = copy(self.mus)
self.trainedModel.data.musPivot = copy(self.musPivot)
self.trainedModel.data.musMarginal = copy(self.musMarginal)
self.trainedModel.data.Qs, self.trainedModel.data.Ps = Qs, Ps
- vbMng(self, "INIT", "Matching poles.", 10)
- self.trainedModel.initializeFromRational(self.HFEngine,
- self.matchingWeight,
- self.POD == PODGlobal, False)
- vbMng(self, "DEL", "Done matching poles.", 10)
+ self._poleMatching()
self._finalizeMarginalization()
vbMng(self, "DEL", "Done setting up approximant.", 5)
return 0
+
+class RationalInterpolantPivotedNoMatch(RationalInterpolantPivotedBase,
+ GenericPivotedApproximantNoMatch):
+ """
+ ROM pivoted rational interpolant (without pole matching) computation for
+ parametric problems.
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation; defaults to 'MONOMIAL';
+ - 'M': degree of rational interpolant numerator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'N': degree of rational interpolant denominator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator; defaults to 1;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musPivot: Array of pivot snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation;
+ - 'M': degree of rational interpolant numerator;
+ - 'N': degree of rational interpolant denominator;
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginal: Marginal sample point generator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ M: Numerator degree of approximant.
+ N: Denominator degree of approximant.
+ radialDirectionalWeights: Radial basis weights for pivot numerator.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ Q: Numpy 1D vector containing complex coefficients of approximant
+ denominator.
+ P: Numpy 2D vector whose columns are FE dofs of coefficients of
+ approximant numerator.
+ """
+
+ def _poleMatching(self):
+ vbMng(self, "INIT", "Compressing poles.", 10)
+ self.trainedModel.initializeFromRational()
+ vbMng(self, "DEL", "Done compressing poles.", 10)
+
+class RationalInterpolantPivoted(RationalInterpolantPivotedBase,
+ GenericPivotedApproximant):
+ """
+ ROM pivoted rational interpolant (with pole matching) computation for
+ parametric problems.
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation; defaults to 'MONOMIAL';
+ - 'polybasisMarginal': type of polynomial basis for marginal
+ interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV'
+ and 'LEGENDRE'; defaults to 'MONOMIAL';
+ - 'M': degree of rational interpolant numerator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'N': degree of rational interpolant denominator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'MMarginal': degree of marginal interpolant; defaults to 'AUTO',
+ i.e. maximum allowed;
+ - 'polydegreetypeMarginal': type of polynomial degree for marginal;
+ defaults to 'TOTAL';
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator; defaults to 1;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ defaults to None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musPivot: Array of pivot snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation;
+ - 'polybasisMarginal': type of polynomial basis for marginal
+ interpolation;
+ - 'M': degree of rational interpolant numerator;
+ - 'N': degree of rational interpolant denominator;
+ - 'MMarginal': degree of marginal interpolant;
+ - 'polydegreetypeMarginal': type of polynomial degree for marginal;
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'interpRcondMarginal': tolerance for marginal interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginal: Marginal sample point generator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ polybasisMarginal: Type of polynomial basis for marginal interpolation.
+ M: Numerator degree of approximant.
+ N: Denominator degree of approximant.
+ MMarginal: Degree of marginal interpolant.
+ polydegreetypeMarginal: Type of polynomial degree for marginal.
+ radialDirectionalWeights: Radial basis weights for pivot numerator.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ interpRcondMarginal: Tolerance for marginal interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ Q: Numpy 1D vector containing complex coefficients of approximant
+ denominator.
+ P: Numpy 2D vector whose columns are FE dofs of coefficients of
+ approximant numerator.
+ """
+
+ def _poleMatching(self):
+ vbMng(self, "INIT", "Compressing and matching poles.", 10)
+ self.trainedModel.initializeFromRational(self.HFEngine,
+ self.matchingWeight,
+ self.POD == PODGlobal, False)
+ vbMng(self, "DEL", "Done compressing and matching poles.", 10)
+
+class RationalInterpolantPivotedNearestNeighbor(RationalInterpolantPivoted,
+ GenericPivotedApproximantNearestNeighbor):
+ """
+ ROM pivoted rational interpolant (with pole matching) computation for
+ parametric problems via local average.
+
+ Args:
+ HFEngine: HF problem solver.
+ mu0(optional): Default parameter. Defaults to 0.
+ directionPivot(optional): Pivot components. Defaults to [0].
+ approxParameters(optional): Dictionary containing values for main
+ parameters of approximant. Recognized keys are:
+ - 'POD': whether to compute POD of snapshots; defaults to True;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ defaults to 'AUTO';
+ - 'matchingWeight': weight for pole matching optimization; defaults
+ to 1;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ defaults to np.inf;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy; defaults to 1.;
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation; defaults to 'MONOMIAL';
+ - 'M': degree of rational interpolant numerator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'N': degree of rational interpolant denominator; defaults to
+ 'AUTO', i.e. maximum allowed;
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ defaults to 1;
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator; defaults to 1;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant; defaults to 1;
+ - 'interpRcond': tolerance for pivot interpolation; defaults to
+ None;
+ - 'robustTol': tolerance for robust rational denominator
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
+ Defaults to empty dict.
+ approx_state(optional): Whether to approximate state. Defaults to
+ False.
+ verbosity(optional): Verbosity level. Defaults to 10.
+
+ Attributes:
+ HFEngine: HF problem solver.
+ mu0: Default parameter.
+ directionPivot: Pivot components.
+ mus: Array of snapshot parameters.
+ musPivot: Array of pivot snapshot parameters.
+ musMarginal: Array of marginal snapshot parameters.
+ approxParameters: Dictionary containing values for main parameters of
+ approximant. Recognized keys are in parameterList.
+ parameterListSoft: Recognized keys of soft approximant parameters:
+ - 'POD': whether to compute POD of snapshots;
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'matchingWeight': weight for pole matching optimization;
+ - 'cutOffTolerance': tolerance for ignoring parasitic poles;
+ - 'cutOffSharedRatio': required ratio of marginal points to share
+ resonance in cut off strategy;
+ - 'polybasis': type of polynomial basis for pivot
+ interpolation;
+ - 'M': degree of rational interpolant numerator;
+ - 'N': degree of rational interpolant denominator;
+ - 'nNeighborsMarginal': number of marginal nearest neighbors;
+ - 'radialDirectionalWeights': radial basis weights for pivot
+ numerator;
+ - 'radialDirectionalWeightsMarginal': radial basis weights for
+ marginal interpolant;
+ - 'interpRcond': tolerance for pivot interpolation;
+ - 'robustTol': tolerance for robust rational denominator
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
+ parameterListCritical: Recognized keys of critical approximant
+ parameters:
+ - 'S': total number of pivot samples current approximant relies
+ upon;
+ - 'samplerPivot': pivot sample point generator;
+ - 'SMarginal': total number of marginal samples current approximant
+ relies upon;
+ - 'samplerMarginal': marginal sample point generator.
+ approx_state: Whether to approximate state.
+ verbosity: Verbosity level.
+ POD: Whether to compute POD of snapshots.
+ scaleFactorDer: Scaling factors for derivative computation.
+ matchingWeight: Weight for pole matching optimization.
+ cutOffTolerance: Tolerance for ignoring parasitic poles.
+ cutOffSharedRatio: Required ratio of marginal points to share resonance
+ in cut off strategy.
+ S: Total number of pivot samples current approximant relies upon.
+ samplerPivot: Pivot sample point generator.
+ SMarginal: Total number of marginal samples current approximant relies
+ upon.
+ samplerMarginal: Marginal sample point generator.
+ polybasis: Type of polynomial basis for pivot interpolation.
+ M: Numerator degree of approximant.
+ N: Denominator degree of approximant.
+ nNeighborsMarginal: Number of marginal nearest neighbors.
+ radialDirectionalWeights: Radial basis weights for pivot numerator.
+ radialDirectionalWeightsMarginal: Radial basis weights for marginal
+ interpolant.
+ interpRcond: Tolerance for pivot interpolation.
+ robustTol: Tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
+ muBounds: list of bounds for pivot parameter values.
+ muBoundsMarginal: list of bounds for marginal parameter values.
+ samplingEngine: Sampling engine.
+ uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
+ sampleList.
+ lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
+ solution(s) as parameterList.
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
+ reduced approximate solution(s) as parameterList.
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
+ sampleList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ Q: Numpy 1D vector containing complex coefficients of approximant
+ denominator.
+ P: Numpy 2D vector whose columns are FE dofs of coefficients of
+ approximant numerator.
+ """
diff --git a/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted.py b/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted.py
deleted file mode 100644
index 0db6052..0000000
--- a/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted.py
+++ /dev/null
@@ -1,466 +0,0 @@
-# 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 factorial as fact
-from copy import deepcopy as copy
-from itertools import combinations
-from rrompy.reduction_methods.standard.trained_model.trained_model_rational \
- import TrainedModelRational
-from rrompy.utilities.base.types import (Np1D, Np2D, List, ListAny, paramVal,
- paramList, sampList, HFEng)
-from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp
-from rrompy.utilities.numerical.point_matching import (pointMatching,
- chordalMetricAdjusted, potential)
-from rrompy.utilities.numerical.degree import reduceDegreeN
-from rrompy.utilities.poly_fitting.polynomial import (polybases as ppb,
- PolynomialInterpolator as PI)
-from rrompy.utilities.poly_fitting.radial_basis import (polybases as rbpb,
- RadialBasisInterpolator as RBI)
-from rrompy.utilities.poly_fitting.moving_least_squares import (
- MovingLeastSquaresInterpolator as MLSI)
-from rrompy.utilities.poly_fitting.heaviside import (heavisideUniformShape,
- rational2heaviside,
- HeavisideInterpolator as HI)
-from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
- RROMPyWarning)
-from rrompy.parameter import checkParameter, checkParameterList
-from rrompy.sampling import emptySampleList
-
-__all__ = ['TrainedModelPivoted']
-
-class TrainedModelPivoted(TrainedModelRational):
- """
- ROM approximant evaluation for pivoted approximants (with pole matching).
-
- Attributes:
- Data: dictionary with all that can be pickled.
- """
-
- def centerNormalizePivot(self, mu : paramList = [],
- mu0 : paramVal = None) -> paramList:
- """
- Compute normalized parameter to be plugged into approximant.
-
- Args:
- mu: Parameter(s) 1.
- mu0: Parameter(s) 2. If None, set to self.data.mu0Pivot.
-
- Returns:
- Normalized parameter.
- """
- mu = checkParameterList(mu, self.data.nparPivot)[0]
- if mu0 is None: mu0 = self.data.mu0Pivot
- rad = ((mu ** self.data.rescalingExpPivot
- - mu0 ** self.data.rescalingExpPivot)
- / self.data.scaleFactorPivot)
- return rad
-
- def centerNormalizeMarginal(self, mu : paramList = [],
- mu0 : paramVal = None) -> paramList:
- """
- Compute normalized parameter to be plugged into approximant.
-
- Args:
- mu: Parameter(s) 1.
- mu0: Parameter(s) 2. If None, set to self.data.mu0Marginal.
-
- Returns:
- Normalized parameter.
- """
- mu = checkParameterList(mu, self.data.nparMarginal)[0]
- if mu0 is None: mu0 = self.data.mu0Marginal
- rad = ((mu ** self.data.rescalingExpMarginal
- - mu0 ** self.data.rescalingExpMarginal)
- / self.data.scaleFactorMarginal)
- return rad
-
- def updateEffectiveSamples(self, HFEngine:HFEng, exclude : List[int] = [],
- matchingWeight : float = 1., POD : bool = True,
- is_state : bool = True):
- if hasattr(self, "_idxExcl"):
- for j, excl in enumerate(self._idxExcl):
- self.data.musMarginal.insert(self._musMExcl[j], excl)
- self.data.HIs.insert(excl, self._HIsExcl[j])
- self.data.Ps.insert(excl, self._PsExcl[j])
- self.data.Qs.insert(excl, self._QsExcl[j])
- self._idxExcl, self._musMExcl = list(np.sort(exclude)), []
- self._HIsExcl, self._PsExcl, self._QsExcl = [], [], []
- for excl in self._idxExcl[::-1]:
- self._musMExcl = [self.data.musMarginal[excl]] + self._musMExcl
- self.data.musMarginal.pop(excl)
- self._HIsExcl = [self.data.HIs.pop(excl)] + self._HIsExcl
- self._PsExcl = [self.data.Ps.pop(excl)] + self._PsExcl
- self._QsExcl = [self.data.Qs.pop(excl)] + self._QsExcl
- poles = [hi.poles for hi in self.data.HIs]
- coeffs = [hi.coeffs for hi in self.data.HIs]
- self.initializeFromLists(poles, coeffs, self.data.HIs[0].polybasis,
- HFEngine, matchingWeight, POD, is_state)
-
- def setupMarginalInterp(self, approx, interpPars:ListAny,
- MMAuto:bool, rDWM : Np1D = None,
- noWarnReduceAuto : bool = True):
- vbMng(self, "INIT", "Starting computation of marginal interpolator.",
- 12)
- musMCN = self.centerNormalizeMarginal(self.data.musMarginal)
- pbM = approx.polybasisMarginal
- self.data.marginalInterp = []
- for ipts, pts in enumerate(self.data.suppEffPts):
- mI = [None] * len(musMCN)
- if len(pts) > 0:
- musMCNEff = musMCN[pts]
- if MMAuto:
- if ipts > 0:
- verb = approx.verbosity
- approx.verbosity = 0
- _musM = approx.musMarginal
- approx.musMarginal = musMCNEff
- approx._setMMarginalAuto()
- if ipts > 0:
- approx.musMarginal = _musM
- approx.verbosity = verb
- if ipts == 0: _MMarginalEffective = approx.MMarginal
- if not MMAuto:
- approx.MMarginal = _MMarginalEffective
- MM = reduceDegreeN(approx.MMarginal, len(musMCNEff),
- self.data.nparMarginal,
- approx.polydegreetypeMarginal)
- if MM < approx.MMarginal:
- if ipts == 0 and not noWarnReduceAuto:
- RROMPyWarning(("MMarginal too large compared to "
- "SMarginal. Reducing MMarginal by "
- "{}").format(approx.MMarginal - MM))
- approx.MMarginal = MM
- for j in range(len(musMCNEff)):
- MMEff = approx.MMarginal
- canonicalj = 1. * (np.arange(len(musMCNEff)) == j)
- while MMEff >= 0:
- pParRest = copy(interpPars)
- if pbM in ppb:
- p = PI()
- else:
- pParRest = [rDWM] + pParRest
- p = RBI() if pbM in rbpb else MLSI()
- wellCond, msg = p.setupByInterpolation(musMCNEff,
- canonicalj, MMEff,
- pbM, *pParRest)
- vbMng(self, "MAIN", msg, 30)
- if wellCond: break
- vbMng(self, "MAIN",
- ("Polyfit is poorly conditioned. Reducing "
- "MMarginal by 1."), 35)
- MMEff -= 1
- if MMEff < 0:
- raise RROMPyException(("Instability in computation of "
- "interpolant. Aborting."))
- mI[pts[j]] = copy(p)
- self.data.marginalInterp += [mI]
- approx.MMarginal = _MMarginalEffective
- vbMng(self, "DEL", "Done computing marginal interpolator.", 12)
-
- def initializeFromLists(self, poles:ListAny, coeffs:ListAny, basis:str,
- HFEngine:HFEng, matchingWeight : float = 1.,
- POD : bool = True, is_state : bool = True):
- """Initialize Heaviside representation."""
- musM = self.data.musMarginal
- margAbsDist = np.sum(np.abs(np.repeat(musM.data, len(musM), 0)
- - np.tile(musM.data, [len(musM), 1])
- ), axis = 1).reshape(len(musM), len(musM))
- explored = [0]
- unexplored = list(range(1, len(musM)))
- poles, coeffs = heavisideUniformShape(poles, coeffs)
- N = len(poles[0])
- for _ in range(1, len(musM)):
- minIdx = np.argmin(np.concatenate([margAbsDist[ex, unexplored] \
- for ex in explored]))
- minIex = explored[minIdx // len(unexplored)]
- minIunex = unexplored[minIdx % len(unexplored)]
- resex = coeffs[minIex][: N]
- resunex = coeffs[minIunex][: N]
- if matchingWeight != 0 and not POD:
- resex = self.data.projMat[:, : resex.shape[1]].dot(resex.T)
- resunex = self.data.projMat[:, : resunex.shape[1]].dot(
- resunex.T)
- dist = chordalMetricAdjusted(poles[minIex], poles[minIunex],
- matchingWeight, resex, resunex,
- HFEngine, is_state)
- reordering = pointMatching(dist)[1]
- poles[minIunex] = poles[minIunex][reordering]
- coeffs[minIunex][: N] = coeffs[minIunex][reordering]
- explored += [minIunex]
- unexplored.remove(minIunex)
- HIs = []
- for pls, cfs in zip(poles, coeffs):
- hsi = HI()
- hsi.poles = pls
- hsi.coeffs = cfs
- hsi.npar = 1
- hsi.polybasis = basis
- HIs += [hsi]
- self.data.HIs = HIs
- self.data.suppEffPts = [np.arange(len(self.data.HIs))]
- self.data.suppEffIdx = np.zeros(N, dtype = int)
-
- def initializeFromRational(self, HFEngine:HFEng,
- matchingWeight : float = 1., POD : bool = True,
- is_state : bool = True):
- """Initialize Heaviside representation."""
- RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
- poles, coeffs = [], []
- for Q, P in zip(self.data.Qs, self.data.Ps):
- cfs, pls, basis = rational2heaviside(P, Q)
- poles += [pls]
- coeffs += [cfs]
- self.initializeFromLists(poles, coeffs, basis, HFEngine,
- matchingWeight, POD, is_state)
-
- def recompressByCutOff(self, tol:float, shared:float,
- foci:List[np.complex], ground:float) -> str:
- N = len(self.data.HIs[0].poles)
- M = len(self.data.HIs)
- mu0 = np.mean(foci)
- goodLocPoles = np.array([potential(hi.poles, foci) - ground
- <= tol * ground for hi in self.data.HIs])
- self.data.suppEffPts = [np.arange(len(self.data.HIs))]
- self.data.suppEffIdx = np.zeros(N, dtype = int)
- if np.all(goodLocPoles): return " No poles erased."
- goodGlobPoles = np.sum(goodLocPoles, axis = 0)
- goodEnoughPoles = goodGlobPoles >= max(1., 1. * shared * M)
- keepPole = np.where(goodEnoughPoles)[0]
- halfPole = np.where(np.logical_and(goodEnoughPoles,
- goodGlobPoles < M))[0]
- removePole = np.where(np.logical_not(goodEnoughPoles))[0]
- if len(removePole) > 0:
- keepCoeff = np.append(keepPole, np.append([N],
- np.arange(N + 1, len(self.data.HIs[0].coeffs))))
- for hi in self.data.HIs:
- polyCorrection = np.zeros_like(hi.coeffs[0, :])
- for j in removePole:
- if not np.isinf(hi.poles[j]):
- polyCorrection += hi.coeffs[j, :] / (mu0 - hi.poles[j])
- if len(hi.coeffs) == N:
- hi.coeffs = np.vstack((hi.coeffs, polyCorrection))
- else:
- hi.coeffs[N, :] += polyCorrection
- hi.poles = hi.poles[keepPole]
- hi.coeffs = hi.coeffs[keepCoeff, :]
- for idxR in halfPole:
- pts = np.where(goodLocPoles[:, idxR])[0]
- idxEff = len(self.data.suppEffPts)
- for idEff, prevPts in enumerate(self.data.suppEffPts):
- if len(prevPts) == len(pts):
- if np.allclose(prevPts, pts):
- idxEff = idEff
- break
- if idxEff == len(self.data.suppEffPts):
- self.data.suppEffPts += [pts]
- self.data.suppEffIdx[idxR] = idxEff
- self.data.suppEffIdx = self.data.suppEffIdx[keepPole]
- return (" Hard-erased {} pole".format(len(removePole))
- + "s" * (len(removePole) != 1)
- + " and soft-erased {} pole".format(len(halfPole))
- + "s" * (len(halfPole) != 1) + ".")
-
- def _interpolateMarginal(self, muC : paramList, objs : ListAny) -> Np2D:
- res = np.zeros(objs[0].shape + (len(muC),), dtype = objs[0].dtype)
- for suppIdx in range(len(self.data.suppEffPts)):
- i = np.where(self.data.suppEffIdx == suppIdx)[0]
- if suppIdx == 0:
- i = np.append(i, np.arange(len(self.data.suppEffIdx),
- len(res)))
- if len(i) > 0:
- for mIj, obj in zip(self.data.marginalInterp[suppIdx], objs):
- if mIj is not None:
- res[i] += np.expand_dims(obj[i], - 1) * mIj(muC)
- return res
-
- def interpolateMarginalInterpolator(self, mu : paramVal = []) -> Np1D:
- """Obtain interpolated approximant interpolator."""
- mu = checkParameter(mu, self.data.nparMarginal)[0]
- hsi = HI()
- hsi.poles = self.interpolateMarginalPoles(mu)[..., 0]
- hsi.coeffs = self.interpolateMarginalCoeffs(mu)[..., 0]
- hsi.npar = 1
- hsi.polybasis = self.data.HIs[0].polybasis
- return hsi
-
- def interpolateMarginalPoles(self, mu : paramList = []) -> Np1D:
- """Obtain interpolated approximant poles."""
- mu = checkParameterList(mu, self.data.nparMarginal)[0]
- muC = self.centerNormalizeMarginal(mu)
- vbMng(self, "INIT",
- "Interpolating marginal poles at mu = {}.".format(mu), 95)
- intMPoles = self._interpolateMarginal(muC,
- [hi.poles for hi in self.data.HIs])
- vbMng(self, "DEL", "Done interpolating marginal poles.", 95)
- return intMPoles
-
- def interpolateMarginalCoeffs(self, mu : paramList = []) -> Np1D:
- """Obtain interpolated approximant coefficients."""
- mu = checkParameterList(mu, self.data.nparMarginal)[0]
- muC = self.centerNormalizeMarginal(mu)
- vbMng(self, "INIT",
- "Interpolating marginal coefficients at mu = {}.".format(mu), 95)
- intMCoeffs = self._interpolateMarginal(muC,
- [hi.coeffs for hi in self.data.HIs])
- vbMng(self, "DEL", "Done interpolating marginal coefficients.", 95)
- return intMCoeffs
-
- def getApproxReduced(self, mu : paramList = []) -> sampList:
- """
- Evaluate reduced representation of approximant at arbitrary parameter.
-
- Args:
- mu: Target parameter.
- """
- RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
- mu = checkParameterList(mu, self.data.npar)[0]
- if (not hasattr(self, "lastSolvedApproxReduced")
- or self.lastSolvedApproxReduced != mu):
- vbMng(self, "INIT",
- "Evaluating approximant at mu = {}.".format(mu), 12)
- self.uApproxReduced = emptySampleList()
- for i, muPL in enumerate(mu):
- muL = self.centerNormalizePivot([muPL(0, x) \
- for x in self.data.directionPivot])
- muM = [muPL(0, x) for x in self.data.directionMarginal]
- vbMng(self, "INIT",
- "Assembling reduced model for mu = {}.".format(muPL), 87)
- hsL = self.interpolateMarginalInterpolator(muM)
- vbMng(self, "DEL", "Done assembling reduced model.", 87)
- uAppR = hsL(muL)
- if i == 0:
- self.uApproxReduced.reset((len(uAppR), len(mu)),
- dtype = uAppR.dtype)
- self.uApproxReduced[i] = uAppR
- vbMng(self, "DEL", "Done evaluating approximant.", 12)
- self.lastSolvedApproxReduced = mu
- return self.uApproxReduced
-
- def getPVal(self, mu : paramList = []) -> sampList:
- """
- Evaluate rational numerator at arbitrary parameter.
-
- Args:
- mu: Target parameter.
- """
- RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
- mu = checkParameterList(mu, self.data.npar)[0]
- p = emptySampleList()
- p.reset((len(self.data.HIs[0].coeffs.shape[1]), len(mu)))
- for i, muPL in enumerate(mu):
- muL = self.centerNormalizePivot([muPL(0, x) \
- for x in self.data.directionPivot])
- muM = [muPL(0, x) for x in self.data.directionMarginal]
- hsL = self.interpolateMarginalInterpolator(muM)
- p[i] = hsL(muL) * np.prod(muL(0, 0) - hsL.poles)
- return p
-
- def getQVal(self, mu:Np1D, der : List[int] = None,
- scl : Np1D = None) -> Np1D:
- """
- Evaluate rational denominator at arbitrary parameter.
-
- Args:
- mu: Target parameter.
- der(optional): Derivatives to take before evaluation.
- """
- RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
- mu = checkParameterList(mu, self.data.npar)[0]
- muP = self.centerNormalizePivot(checkParameterList(
- mu.data[:, self.data.directionPivot],
- self.data.nparPivot)[0])
- muM = checkParameterList(mu.data[:, self.data.directionMarginal],
- self.data.nparMarginal)[0]
- if der is None:
- derP, derM = 0, [0]
- else:
- derP = der[self.data.directionPivot[0]]
- derM = [der[x] for x in self.data.directionMarginal]
- if np.any(np.array(derM) != 0):
- raise RROMPyException(("Derivatives of Q with respect to marginal "
- "parameters not allowed."))
- sclP = 1 if scl is None else scl[self.data.directionPivot[0]]
- derVal = np.zeros(len(mu), dtype = np.complex)
- N = len(self.data.HIs[0].poles)
- if derP == N: derVal[:] = 1.
- elif derP >= 0 and derP < N:
- pls = self.interpolateMarginalPoles(muM).T
- plsDist = muP.data.reshape(-1, 1) - pls
- for terms in combinations(np.arange(N), N - derP):
- derVal += np.prod(plsDist[:, list(terms)], axis = 1)
- return sclP ** derP * fact(derP) * derVal
-
- def getPoles(self, *args, **kwargs) -> Np1D:
- """
- Obtain approximant poles.
-
- Returns:
- Numpy complex vector of poles.
- """
- RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
- if len(args) + len(kwargs) > 1:
- raise RROMPyException(("Wrong number of parameters passed. "
- "Only 1 available."))
- elif len(args) + len(kwargs) == 1:
- if len(args) == 1:
- mVals = args[0]
- else:
- mVals = kwargs["marginalVals"]
- if not hasattr(mVals, "__len__"): mVals = [mVals]
- mVals = list(mVals)
- else:
- mVals = [fp]
- try:
- rDim = mVals.index(fp)
- if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]:
- raise
- except:
- raise RROMPyException(("Exactly 1 'freepar' entry in "
- "marginalVals must be provided."))
- if rDim != self.data.directionPivot[0]:
- raise RROMPyException(("'freepar' entry in marginalVals must "
- "coincide with pivot direction."))
- mVals[rDim] = self.data.mu0(rDim)
- mMarg = [mVals[j] for j in range(len(mVals)) if j != rDim]
- roots = self.interpolateMarginalPoles(mMarg)[..., 0]
- return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim]
- + self.data.scaleFactor[rDim] * roots,
- 1. / self.data.rescalingExp[rDim])
-
- def getResidues(self, *args, **kwargs) -> Np1D:
- """
- Obtain approximant residues.
-
- Returns:
- Numpy matrix with residues as columns.
- """
- pls = self.getPoles(*args, **kwargs)
- if len(args) == 1:
- mVals = args[0]
- elif len(args) == 0:
- mVals = [None]
- else:
- mVals = kwargs["marginalVals"]
- if not hasattr(mVals, "__len__"): mVals = [mVals]
- mVals = list(mVals)
- rDim = mVals.index(fp)
- mMarg = [mVals[j] for j in range(len(mVals)) if j != rDim]
- residues = self.interpolateMarginalCoeffs(mMarg)[: len(pls), :, 0]
- res = self.data.projMat[:, : residues.shape[1]].dot(residues.T)
- return pls, res
diff --git a/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_data.py b/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_data.py
index e465334..a4f80e6 100644
--- a/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_data.py
+++ b/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_data.py
@@ -1,73 +1,73 @@
# 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 .
#
from rrompy.reduction_methods.base.trained_model.trained_model_data import (
TrainedModelData)
from rrompy.utilities.base.types import Np2D, List, ListAny, paramVal
from rrompy.parameter import checkParameterList
__all__ = ['TrainedModelPivotedData']
class TrainedModelPivotedData(TrainedModelData):
"""ROM approximant evaluation data (must be pickle-able)."""
- def __init__(self, mu0:paramVal, projMat:Np2D,
+ def __init__(self, mu0:paramVal, mus:paramVal, projMat:Np2D,
scaleFactor : ListAny = [1.],
rescalingExp : List[float] = [1.],
directionPivot : ListAny = [0]):
- super().__init__(mu0, projMat, scaleFactor, rescalingExp)
+ super().__init__(mu0, mus, projMat, scaleFactor, rescalingExp)
self.directionPivot = directionPivot
@property
def directionMarginal(self):
return tuple([x for x in range(self.npar) \
if x not in self.directionPivot])
@property
def mu0Pivot(self):
return checkParameterList(self.mu0(0, self.directionPivot),
self.nparPivot)[0]
@property
def mu0Marginal(self):
return checkParameterList(self.mu0(0, self.directionMarginal),
self.nparMarginal)[0]
@property
def nparPivot(self):
return len(self.directionPivot)
@property
def nparMarginal(self):
return self.npar - self.nparPivot
@property
def rescalingExpPivot(self):
return [self.rescalingExp[x] for x in self.directionPivot]
@property
def rescalingExpMarginal(self):
return [self.rescalingExp[x] for x in self.directionMarginal]
@property
def scaleFactorPivot(self):
return [self.scaleFactor[x] for x in self.directionPivot]
@property
def scaleFactorMarginal(self):
return [self.scaleFactor[x] for x in self.directionMarginal]
diff --git a/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_rational.py b/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_rational.py
new file mode 100644
index 0000000..ba20b1e
--- /dev/null
+++ b/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_rational.py
@@ -0,0 +1,283 @@
+# 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 .trained_model_pivoted_rational_nomatch import (
+ TrainedModelPivotedRationalNoMatch)
+from rrompy.utilities.base.types import (Np1D, Np2D, List, ListAny, paramVal,
+ paramList, HFEng)
+from rrompy.utilities.base import verbosityManager as vbMng
+from rrompy.utilities.numerical.point_matching import (pointMatching,
+ chordalMetricAdjusted, potential)
+from rrompy.utilities.numerical.degree import reduceDegreeN
+from rrompy.utilities.poly_fitting.polynomial import (polybases as ppb,
+ PolynomialInterpolator as PI)
+from rrompy.utilities.poly_fitting.radial_basis import (polybases as rbpb,
+ RadialBasisInterpolator as RBI)
+from rrompy.utilities.poly_fitting.heaviside import (heavisideUniformShape,
+ rational2heaviside,
+ HeavisideInterpolator as HI)
+from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
+ RROMPyWarning)
+from rrompy.parameter import checkParameterList
+
+__all__ = ['TrainedModelPivotedRational']
+
+class TrainedModelPivotedRational(TrainedModelPivotedRationalNoMatch):
+ """
+ ROM approximant evaluation for pivoted approximants based on interpolation
+ of rational approximants (with pole matching).
+
+ Attributes:
+ Data: dictionary with all that can be pickled.
+ """
+
+ def centerNormalizeMarginal(self, mu : paramList = [],
+ mu0 : paramVal = None) -> paramList:
+ """
+ Compute normalized parameter to be plugged into approximant.
+
+ Args:
+ mu: Parameter(s) 1.
+ mu0: Parameter(s) 2. If None, set to self.data.mu0Marginal.
+
+ Returns:
+ Normalized parameter.
+ """
+ mu = checkParameterList(mu, self.data.nparMarginal)[0]
+ if mu0 is None: mu0 = self.data.mu0Marginal
+ rad = ((mu ** self.data.rescalingExpMarginal
+ - mu0 ** self.data.rescalingExpMarginal)
+ / self.data.scaleFactorMarginal)
+ return rad
+
+ def setupMarginalInterp(self, approx, interpPars:ListAny,
+ MMAuto:bool, rDWM : Np1D = None,
+ noWarnReduceAuto : bool = True):
+ vbMng(self, "INIT", "Starting computation of marginal interpolator.",
+ 12)
+ musMCN = self.centerNormalizeMarginal(self.data.musMarginal)
+ pbM = approx.polybasisMarginal
+ self.data.marginalInterp = []
+ for ipts, pts in enumerate(self.data.suppEffPts):
+ if pbM in ppb:
+ p = PI()
+ else:
+ p = RBI()
+ if len(pts) == 0:
+ wellCond, msg = p.setupByInterpolation(musMCN[[0]],
+ np.ones((1, 0)), 0, pbM)
+ else:
+ musMCNEff = musMCN[pts]
+ if MMAuto:
+ if ipts > 0:
+ verb = approx.verbosity
+ approx.verbosity = 0
+ _musM = approx.musMarginal
+ approx.musMarginal = musMCNEff
+ approx._setMMarginalAuto()
+ if ipts > 0:
+ approx.musMarginal = _musM
+ approx.verbosity = verb
+ if ipts == 0: _MMarginalEffective = approx.MMarginal
+ if not MMAuto:
+ approx.MMarginal = _MMarginalEffective
+ MM = reduceDegreeN(approx.MMarginal, len(musMCNEff),
+ self.data.nparMarginal,
+ approx.polydegreetypeMarginal)
+ if MM < approx.MMarginal:
+ if ipts == 0 and not noWarnReduceAuto:
+ RROMPyWarning(("MMarginal too large compared to "
+ "SMarginal. Reducing MMarginal by "
+ "{}").format(approx.MMarginal - MM))
+ approx.MMarginal = MM
+ MMEff = approx.MMarginal
+ while MMEff >= 0:
+ pParRest = copy(interpPars)
+ if pbM in rbpb:
+ pParRest = [rDWM] + pParRest
+ wellCond, msg = p.setupByInterpolation(musMCNEff,
+ np.eye(len(musMCNEff)),
+ MMEff, pbM, *pParRest)
+ vbMng(self, "MAIN", msg, 30)
+ if wellCond: break
+ vbMng(self, "MAIN",
+ ("Polyfit is poorly conditioned. Reducing "
+ "MMarginal by 1."), 35)
+ MMEff -= 1
+ if MMEff < 0:
+ raise RROMPyException(("Instability in computation of "
+ "interpolant. Aborting."))
+ self.data.marginalInterp += [copy(p)]
+ approx.MMarginal = _MMarginalEffective
+ vbMng(self, "DEL", "Done computing marginal interpolator.", 12)
+
+ def initializeFromLists(self, poles:ListAny, coeffs:ListAny, basis:str,
+ HFEngine:HFEng, matchingWeight : float = 1.,
+ POD : bool = True, is_state : bool = True):
+ """Initialize Heaviside representation."""
+ musM = self.data.musMarginal
+ margAbsDist = np.sum(np.abs(np.repeat(musM.data, len(musM), 0)
+ - np.tile(musM.data, [len(musM), 1])
+ ), axis = 1).reshape(len(musM), len(musM))
+ explored = [0]
+ unexplored = list(range(1, len(musM)))
+ poles, coeffs = heavisideUniformShape(poles, coeffs)
+ N = len(poles[0])
+ for _ in range(1, len(musM)):
+ minIdx = np.argmin(np.concatenate([margAbsDist[ex, unexplored] \
+ for ex in explored]))
+ minIex = explored[minIdx // len(unexplored)]
+ minIunex = unexplored[minIdx % len(unexplored)]
+ resex = coeffs[minIex][: N].T
+ resunex = coeffs[minIunex][: N].T
+ if matchingWeight != 0 and not POD and not self.data._collapsed:
+ resex = self.data.projMat[:, : resex.shape[0]].dot(resex)
+ resunex = self.data.projMat[:, : resunex.shape[0]].dot(resunex)
+ dist = chordalMetricAdjusted(poles[minIex], poles[minIunex],
+ matchingWeight, resex, resunex,
+ HFEngine, is_state)
+ reordering = pointMatching(dist)[1]
+ poles[minIunex] = poles[minIunex][reordering]
+ coeffs[minIunex][: N] = coeffs[minIunex][reordering]
+ explored += [minIunex]
+ unexplored.remove(minIunex)
+ super().initializeFromLists(poles, coeffs, basis)
+ self.data.suppEffPts = [np.arange(len(self.data.HIs))]
+ self.data.suppEffIdx = np.zeros(N, dtype = int)
+
+ def initializeFromRational(self, HFEngine:HFEng,
+ matchingWeight : float = 1., POD : bool = True,
+ is_state : bool = True):
+ """Initialize Heaviside representation."""
+ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
+ poles, coeffs = [], []
+ for Q, P in zip(self.data.Qs, self.data.Ps):
+ cfs, pls, basis = rational2heaviside(P, Q)
+ poles += [pls]
+ coeffs += [cfs]
+ self.initializeFromLists(poles, coeffs, basis, HFEngine,
+ matchingWeight, POD, is_state)
+
+ def recompressByCutOff(self, tol:float, shared:float,
+ foci:List[np.complex], ground:float) -> str:
+ N = len(self.data.HIs[0].poles)
+ M = len(self.data.HIs)
+ mu0 = np.mean(foci)
+ goodLocPoles = np.array([potential(hi.poles, foci) - ground
+ <= tol * ground for hi in self.data.HIs])
+ self.data.suppEffPts = [np.arange(len(self.data.HIs))]
+ self.data.suppEffIdx = np.zeros(N, dtype = int)
+ if np.all(np.all(goodLocPoles)): return " No poles erased."
+ goodGlobPoles = np.sum(goodLocPoles, axis = 0)
+ goodEnoughPoles = goodGlobPoles >= max(1., 1. * shared * M)
+ keepPole = np.where(goodEnoughPoles)[0]
+ halfPole = np.where(np.logical_and(goodEnoughPoles,
+ goodGlobPoles < M))[0]
+ removePole = np.where(np.logical_not(goodEnoughPoles))[0]
+ if len(removePole) > 0:
+ keepCoeff = np.append(keepPole, np.append([N],
+ np.arange(N + 1, len(self.data.HIs[0].coeffs))))
+ for hi in self.data.HIs:
+ polyCorrection = np.zeros_like(hi.coeffs[0, :])
+ for j in removePole:
+ if not np.isinf(hi.poles[j]):
+ polyCorrection += hi.coeffs[j, :] / (mu0 - hi.poles[j])
+ if len(hi.coeffs) == N:
+ hi.coeffs = np.vstack((hi.coeffs, polyCorrection))
+ else:
+ hi.coeffs[N, :] += polyCorrection
+ hi.poles = hi.poles[keepPole]
+ hi.coeffs = hi.coeffs[keepCoeff, :]
+ for idxR in halfPole:
+ pts = np.where(goodLocPoles[:, idxR])[0]
+ idxEff = len(self.data.suppEffPts)
+ for idEff, prevPts in enumerate(self.data.suppEffPts):
+ if len(prevPts) == len(pts):
+ if np.allclose(prevPts, pts):
+ idxEff = idEff
+ break
+ if idxEff == len(self.data.suppEffPts):
+ self.data.suppEffPts += [pts]
+ self.data.suppEffIdx[idxR] = idxEff
+ self.data.suppEffIdx = self.data.suppEffIdx[keepPole]
+ return (" Hard-erased {} pole".format(len(removePole))
+ + "s" * (len(removePole) != 1)
+ + " and soft-erased {} pole".format(len(halfPole))
+ + "s" * (len(halfPole) != 1) + ".")
+
+ def _interpolateMarginal(self, mu:paramList, objs:ListAny,
+ mIvals : List[Np2D] = None) -> Np2D:
+ res = np.zeros(objs[0].shape + (len(mu),), dtype = objs[0].dtype)
+ if mIvals is None: muC = self.centerNormalizeMarginal(mu)
+ for suppIdx, suppEffPt in enumerate(self.data.suppEffPts):
+ i = np.where(self.data.suppEffIdx == suppIdx)[0]
+ if suppIdx == 0:
+ i = np.append(i, np.arange(len(self.data.suppEffIdx),
+ len(res)))
+ if len(i) > 0:
+ if mIvals is None:
+ mIval = self.data.marginalInterp[suppIdx](muC)
+ else:
+ mIval = mIvals[suppIdx]
+ for ij, j in enumerate(suppEffPt):
+ res[i] += np.expand_dims(objs[j][i], - 1) * mIval[ij]
+ return res
+
+ def interpolateMarginalInterpolator(self, mu : paramList = []):
+ """Obtain interpolated approximant interpolator."""
+ mu = checkParameterList(mu, self.data.nparMarginal)[0]
+ muC = self.centerNormalizeMarginal(mu)
+ mIvals = [None] * len(self.data.suppEffPts)
+ for suppIdx in range(len(self.data.suppEffPts)):
+ mIvals[suppIdx] = self.data.marginalInterp[suppIdx](muC)
+ poless = self.interpolateMarginalPoles(mu, mIvals)
+ coeffss = self.interpolateMarginalCoeffs(mu, mIvals)
+ his = [None] * len(mu)
+ for j in range(len(mu)):
+ his[j] = HI()
+ his[j].poles = poless[..., j]
+ his[j].coeffs = coeffss[..., j]
+ his[j].npar = 1
+ his[j].polybasis = self.data.HIs[0].polybasis
+ return his
+
+ def interpolateMarginalPoles(self, mu : paramList = [],
+ mIvals : Np2D = None) -> Np1D:
+ """Obtain interpolated approximant poles."""
+ mu = checkParameterList(mu, self.data.nparMarginal)[0]
+ vbMng(self, "INIT",
+ "Interpolating marginal poles at mu = {}.".format(mu), 95)
+ intMPoles = self._interpolateMarginal(mu,
+ [hi.poles for hi in self.data.HIs],
+ mIvals)
+ vbMng(self, "DEL", "Done interpolating marginal poles.", 95)
+ return intMPoles
+
+ def interpolateMarginalCoeffs(self, mu : paramList = [],
+ mIvals : Np2D = None) -> Np1D:
+ """Obtain interpolated approximant coefficients."""
+ mu = checkParameterList(mu, self.data.nparMarginal)[0]
+ vbMng(self, "INIT",
+ "Interpolating marginal coefficients at mu = {}.".format(mu), 95)
+ intMCoeffs = self._interpolateMarginal(mu,
+ [hi.coeffs for hi in self.data.HIs],
+ mIvals)
+ vbMng(self, "DEL", "Done interpolating marginal coefficients.", 95)
+ return intMCoeffs
diff --git a/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_rational_nearest_neighbor.py b/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_rational_nearest_neighbor.py
new file mode 100644
index 0000000..86f41c0
--- /dev/null
+++ b/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_rational_nearest_neighbor.py
@@ -0,0 +1,57 @@
+# 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 .trained_model_pivoted_rational import TrainedModelPivotedRational
+from rrompy.utilities.base.types import Np1D, paramVal, paramList
+from rrompy.utilities.base import verbosityManager as vbMng
+from rrompy.utilities.poly_fitting.nearest_neighbor import (
+ NearestNeighborInterpolator as NNI)
+from rrompy.parameter import checkParameterList
+
+__all__ = ['TrainedModelPivotedRationalNearestNeighbor']
+
+class TrainedModelPivotedRationalNearestNeighbor(TrainedModelPivotedRational):
+ """
+ ROM approximant evaluation for pivoted approximants based on interpolation
+ of rational approximants (with pole matching) via local average.
+
+ Attributes:
+ Data: dictionary with all that can be pickled.
+ """
+
+ def centerNormalizeMarginal(self, mu : paramList = [],
+ mu0 : paramVal = None) -> paramList:
+ return checkParameterList(mu, self.data.nparMarginal)[0]
+
+ def setupMarginalInterp(self, nN:bool, rDWM : Np1D = None):
+ vbMng(self, "INIT", "Starting computation of marginal interpolator.",
+ 12)
+ self.data.marginalInterp = []
+ for ipts, pts in enumerate(self.data.suppEffPts):
+ nn = NNI()
+ if len(pts) == 0:
+ nn.setupByInterpolation(self.data.musMarginal[[0]],
+ np.ones((1, 0)), 1, rDWM)
+ else:
+ nn.setupByInterpolation(self.data.musMarginal[pts],
+ np.eye(len(pts)), nN, rDWM)
+ self.data.marginalInterp += [copy(nn)]
+ vbMng(self, "DEL", "Done computing marginal interpolator.", 12)
+
diff --git a/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_rational_nomatch.py b/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_rational_nomatch.py
new file mode 100644
index 0000000..063b467
--- /dev/null
+++ b/rrompy/reduction_methods/pivoted/trained_model/trained_model_pivoted_rational_nomatch.py
@@ -0,0 +1,308 @@
+# 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 factorial as fact
+from itertools import combinations
+from rrompy.reduction_methods.standard.trained_model.trained_model_rational \
+ import TrainedModelRational
+from rrompy.utilities.base.types import (Np1D, List, ListAny, paramVal,
+ paramList, sampList)
+from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp
+from rrompy.utilities.numerical.point_matching import potential
+from rrompy.utilities.poly_fitting.heaviside import (rational2heaviside,
+ HeavisideInterpolator as HI)
+from rrompy.utilities.poly_fitting.nearest_neighbor import (
+ NearestNeighborInterpolator as NNI)
+from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
+from rrompy.parameter import checkParameterList
+from rrompy.sampling import emptySampleList
+
+__all__ = ['TrainedModelPivotedRationalNoMatch']
+
+class TrainedModelPivotedRationalNoMatch(TrainedModelRational):
+ """
+ ROM approximant evaluation for pivoted approximants based on interpolation
+ of rational approximants (without pole matching).
+
+ Attributes:
+ Data: dictionary with all that can be pickled.
+ """
+
+ def collapse(self):
+ for obj in self.data.HIs:
+ obj.postmultiplyTensorize(self.data.projMat.T)
+ if hasattr(self, "_HIsExcl"):
+ for obj in self._HIsExcl:
+ obj.postmultiplyTensorize(self.data.projMat.T)
+ if hasattr(self.data, "Ps"):
+ for obj in self.data.Ps:
+ obj.postmultiplyTensorize(self.data.projMat.T)
+ if hasattr(self, "_PsExcl"):
+ for obj in self._PsExcl:
+ obj.postmultiplyTensorize(self.data.projMat.T)
+ super(TrainedModelRational, self).collapse()
+
+ def centerNormalizePivot(self, mu : paramList = [],
+ mu0 : paramVal = None) -> paramList:
+ """
+ Compute normalized parameter to be plugged into approximant.
+
+ Args:
+ mu: Parameter(s) 1.
+ mu0: Parameter(s) 2. If None, set to self.data.mu0Pivot.
+
+ Returns:
+ Normalized parameter.
+ """
+ mu = checkParameterList(mu, self.data.nparPivot)[0]
+ if mu0 is None: mu0 = self.data.mu0Pivot
+ rad = ((mu ** self.data.rescalingExpPivot
+ - mu0 ** self.data.rescalingExpPivot)
+ / self.data.scaleFactorPivot)
+ return rad
+
+ def setupMarginalInterp(self, rDWM : Np1D = None):
+ self.data.NN = NNI()
+ self.data.NN.setupByInterpolation(self.data.musMarginal,
+ np.arange(len(self.data.musMarginal)),
+ 1, rDWM)
+
+ def updateEffectiveSamples(self, exclude:List[int], *args, **kwargs):
+ if hasattr(self, "_idxExcl"):
+ for j, excl in enumerate(self._idxExcl):
+ self.data.musMarginal.insert(self._musMExcl[j], excl)
+ self.data.HIs.insert(excl, self._HIsExcl[j])
+ self.data.Ps.insert(excl, self._PsExcl[j])
+ self.data.Qs.insert(excl, self._QsExcl[j])
+ self._idxExcl, self._musMExcl = list(np.sort(exclude)), []
+ self._HIsExcl, self._PsExcl, self._QsExcl = [], [], []
+ for excl in self._idxExcl[::-1]:
+ self._musMExcl = [self.data.musMarginal[excl]] + self._musMExcl
+ self.data.musMarginal.pop(excl)
+ self._HIsExcl = [self.data.HIs.pop(excl)] + self._HIsExcl
+ self._PsExcl = [self.data.Ps.pop(excl)] + self._PsExcl
+ self._QsExcl = [self.data.Qs.pop(excl)] + self._QsExcl
+ poles = [hi.poles for hi in self.data.HIs]
+ coeffs = [hi.coeffs for hi in self.data.HIs]
+ self.initializeFromLists(poles, coeffs, self.data.HIs[0].polybasis,
+ *args, **kwargs)
+
+ def initializeFromLists(self, poles:ListAny, coeffs:ListAny, basis:str):
+ """Initialize Heaviside representation."""
+ self.data.HIs = []
+ for pls, cfs in zip(poles, coeffs):
+ hsi = HI()
+ hsi.poles = pls
+ hsi.coeffs = cfs
+ hsi.npar = 1
+ hsi.polybasis = basis
+ self.data.HIs += [hsi]
+
+ def initializeFromRational(self):
+ """Initialize Heaviside representation."""
+ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
+ poles, coeffs = [], []
+ for Q, P in zip(self.data.Qs, self.data.Ps):
+ cfs, pls, basis = rational2heaviside(P, Q)
+ poles += [pls]
+ coeffs += [cfs]
+ self.initializeFromLists(poles, coeffs, basis)
+
+ def recompressByCutOff(self, tol:float, foci:List[np.complex],
+ ground:float) -> str:
+ mu0 = np.mean(foci)
+ gLocPoles = [potential(hi.poles, foci) - ground <= tol * ground
+ for hi in self.data.HIs]
+ nRemPole = np.sum([np.sum(np.logical_not(gLPi)) for gLPi in gLocPoles])
+ if nRemPole == 0: return " No poles erased."
+ for hi, gLocPolesi in zip(self.data.HIs, gLocPoles):
+ N = len(hi.poles)
+ polyCorrection = np.zeros_like(hi.coeffs[0, :])
+ for j, goodj in enumerate(gLocPolesi):
+ if not goodj and not np.isinf(hi.poles[j]):
+ polyCorrection += hi.coeffs[j, :] / (mu0 - hi.poles[j])
+ if len(hi.coeffs) == N:
+ hi.coeffs = np.vstack((hi.coeffs, polyCorrection))
+ else:
+ hi.coeffs[N, :] += polyCorrection
+ hi.poles = hi.poles[gLocPolesi]
+ gLocCoeffi = np.append(gLocPolesi,
+ np.ones(len(hi.coeffs) - N, dtype = bool))
+ hi.coeffs = hi.coeffs[gLocCoeffi, :]
+ return " Erased {} pole{}.".format(nRemPole, "s" * (nRemPole != 1))
+
+ def interpolateMarginalInterpolator(self, mu : paramList = []) -> Np1D:
+ """Obtain interpolated approximant interpolator."""
+ mu = checkParameterList(mu, self.data.nparMarginal)[0]
+ vbMng(self, "INIT", "Finding nearest neighbor to mu = {}.".format(mu),
+ 95)
+ intM = np.array(self.data.NN(mu), dtype = int)
+ vbMng(self, "DEL", "Done finding nearest neighbor.", 95)
+ return [self.data.HIs[i] for i in intM]
+
+ def interpolateMarginalPoles(self, mu : paramList = []) -> Np1D:
+ """Obtain interpolated approximant poles."""
+ interps = self.interpolateMarginalInterpolator(mu)
+ return np.moveaxis(np.array([interp.poles for interp in interps]),
+ 0, -1)
+
+ def interpolateMarginalCoeffs(self, mu : paramList = []) -> Np1D:
+ """Obtain interpolated approximant poles."""
+ interps = self.interpolateMarginalInterpolator(mu)
+ return np.moveaxis(np.array([interp.coeffs for interp in interps]),
+ 0, -1)
+
+ def getApproxReduced(self, mu : paramList = []) -> sampList:
+ """
+ Evaluate reduced representation of approximant at arbitrary parameter.
+
+ Args:
+ mu: Target parameter.
+ """
+ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
+ mu = checkParameterList(mu, self.data.npar)[0]
+ if (not hasattr(self, "lastSolvedApproxReduced")
+ or self.lastSolvedApproxReduced != mu):
+ vbMng(self, "INIT",
+ "Evaluating approximant at mu = {}.".format(mu), 12)
+ self.uApproxReduced = emptySampleList()
+ muP = self.centerNormalizePivot(mu.data[:,
+ self.data.directionPivot])
+ muM = mu.data[:, self.data.directionMarginal]
+ his = self.interpolateMarginalInterpolator(muM)
+ for i, (mP, hi) in enumerate(zip(muP, his)):
+ uAppR = hi(mP)
+ if i == 0:
+ self.uApproxReduced.reset((len(uAppR), len(mu)),
+ dtype = uAppR.dtype)
+ self.uApproxReduced[i] = uAppR
+ vbMng(self, "DEL", "Done evaluating approximant.", 12)
+ self.lastSolvedApproxReduced = mu
+ return self.uApproxReduced
+
+ def getPVal(self, mu : paramList = []) -> sampList:
+ """
+ Evaluate rational numerator at arbitrary parameter.
+
+ Args:
+ mu: Target parameter.
+ """
+ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
+ mu = checkParameterList(mu, self.data.npar)[0]
+ p = emptySampleList()
+ p.reset((len(self.data.HIs[0].coeffs.shape[1]), len(mu)))
+ muP = self.centerNormalizePivot(mu.data[:, self.data.directionPivot])
+ muM = mu.data[:, self.data.directionMarginal]
+ his = self.interpolateMarginalInterpolator(muM)
+ for i, (mP, hi) in enumerate(zip(muP, his)):
+ p[i] = hi(mP) * np.prod(mP[0] - hi.poles)
+ return p
+
+ def getQVal(self, mu:Np1D, der : List[int] = None,
+ scl : Np1D = None) -> Np1D:
+ """
+ Evaluate rational denominator at arbitrary parameter.
+
+ Args:
+ mu: Target parameter.
+ der(optional): Derivatives to take before evaluation.
+ """
+ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
+ mu = checkParameterList(mu, self.data.npar)[0]
+ muP = self.centerNormalizePivot(mu.data[:, self.data.directionPivot])
+ muM = mu.data[:, self.data.directionMarginal]
+ if der is None:
+ derP, derM = 0, [0]
+ else:
+ derP = der[self.data.directionPivot[0]]
+ derM = [der[x] for x in self.data.directionMarginal]
+ if np.any(np.array(derM) != 0):
+ raise RROMPyException(("Derivatives of Q with respect to marginal "
+ "parameters not allowed."))
+ sclP = 1 if scl is None else scl[self.data.directionPivot[0]]
+ derVal = np.zeros(len(mu), dtype = np.complex)
+ pls = self.interpolateMarginalPoles(muM)
+ for i, (mP, pl) in enumerate(zip(muP, pls)):
+ N = len(pl)
+ if derP == N: derVal[i] = 1.
+ elif derP >= 0 and derP < N:
+ plDist = muP[0] - pl
+ for terms in combinations(np.arange(N), N - derP):
+ derVal[i] += np.prod(plDist[list(terms)], axis = 1)
+ return sclP ** derP * fact(derP) * derVal
+
+ def getPoles(self, *args, **kwargs) -> Np1D:
+ """
+ Obtain approximant poles.
+
+ Returns:
+ Numpy complex vector of poles.
+ """
+ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters")
+ if len(args) + len(kwargs) > 1:
+ raise RROMPyException(("Wrong number of parameters passed. "
+ "Only 1 available."))
+ elif len(args) + len(kwargs) == 1:
+ if len(args) == 1:
+ mVals = args[0]
+ else:
+ mVals = kwargs["marginalVals"]
+ if not hasattr(mVals, "__len__"): mVals = [mVals]
+ mVals = list(mVals)
+ else:
+ mVals = [fp]
+ try:
+ rDim = mVals.index(fp)
+ if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]:
+ raise
+ except:
+ raise RROMPyException(("Exactly 1 'freepar' entry in "
+ "marginalVals must be provided."))
+ if rDim != self.data.directionPivot[0]:
+ raise RROMPyException(("'freepar' entry in marginalVals must "
+ "coincide with pivot direction."))
+ mVals[rDim] = self.data.mu0(rDim)
+ mMarg = [mVals[j] for j in range(len(mVals)) if j != rDim]
+ roots = self.interpolateMarginalPoles(mMarg)[..., 0]
+ return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim]
+ + self.data.scaleFactor[rDim] * roots,
+ 1. / self.data.rescalingExp[rDim])
+
+ def getResidues(self, *args, **kwargs) -> Np1D:
+ """
+ Obtain approximant residues.
+
+ Returns:
+ Numpy matrix with residues as columns.
+ """
+ pls = self.getPoles(*args, **kwargs)
+ if len(args) == 1:
+ mVals = args[0]
+ elif len(args) == 0:
+ mVals = [None]
+ else:
+ mVals = kwargs["marginalVals"]
+ if not hasattr(mVals, "__len__"): mVals = [mVals]
+ mVals = list(mVals)
+ rDim = mVals.index(fp)
+ mMarg = [mVals[j] for j in range(len(mVals)) if j != rDim]
+ res = self.interpolateMarginalCoeffs(mMarg)[: len(pls), :, 0].T
+ if not self.data._collapsed:
+ res = self.data.projMat[:, : res.shape[0]].dot(res)
+ return pls, res
diff --git a/rrompy/reduction_methods/standard/__init__.py b/rrompy/reduction_methods/standard/__init__.py
index 5155dab..eb87cda 100644
--- a/rrompy/reduction_methods/standard/__init__.py
+++ b/rrompy/reduction_methods/standard/__init__.py
@@ -1,33 +1,31 @@
# 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 .
#
from .nearest_neighbor import NearestNeighbor
from .rational_interpolant import RationalInterpolant
-from .rational_moving_least_squares import RationalMovingLeastSquares
from .rational_pade import RationalPade
from .reduced_basis import ReducedBasis
__all__ = [
'NearestNeighbor',
'RationalInterpolant',
- 'RationalMovingLeastSquares',
'RationalPade',
'ReducedBasis'
]
diff --git a/rrompy/reduction_methods/standard/generic_standard_approximant.py b/rrompy/reduction_methods/standard/generic_standard_approximant.py
index a3a72ac..a8489d1 100644
--- a/rrompy/reduction_methods/standard/generic_standard_approximant.py
+++ b/rrompy/reduction_methods/standard/generic_standard_approximant.py
@@ -1,148 +1,148 @@
# 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.reduction_methods.base.generic_approximant import (
GenericApproximant)
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
from rrompy.parameter import checkParameterList
__all__ = ['GenericStandardApproximant']
class GenericStandardApproximant(GenericApproximant):
"""
ROM interpolant computation for parametric problems (ABSTRACT).
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults to
False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
"""
def __init__(self, *args, **kwargs):
self._preInit()
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
self._addParametersToList([], [], ["sampler"],
[QS([[0.], [1.]], "UNIFORM")])
del QS
super().__init__(*args, **kwargs)
self._postInit()
@property
def mus(self):
"""Value of mus. Its assignment may reset snapshots."""
return self._mus
@mus.setter
def mus(self, mus):
mus = checkParameterList(mus, self.npar)[0]
musOld = copy(self.mus) if hasattr(self, '_mus') else None
if (musOld is None or len(mus) != len(musOld) or not mus == musOld):
self.resetSamples()
self._mus = mus
@property
def muBounds(self):
"""Value of muBounds."""
return self.sampler.lims
@property
def sampler(self):
"""Value of sampler."""
return self._sampler
@sampler.setter
def sampler(self, sampler):
if 'generatePoints' not in dir(sampler):
raise RROMPyException("Sampler type not recognized.")
if hasattr(self, '_sampler') and self._sampler is not None:
samplerOld = self.sampler
self._sampler = sampler
- self._approxParameters["sampler"] = self.sampler.__str__()
+ self._approxParameters["sampler"] = self.sampler
if not 'samplerOld' in locals() or samplerOld != self.sampler:
self.resetSamples()
def setSamples(self, samplingEngine):
"""Copy samplingEngine and samples."""
self.mus = copy(samplingEngine.mus)
super().setSamples(samplingEngine)
def computeSnapshots(self):
"""Compute snapshots of solution map."""
RROMPyAssert(self._mode,
message = "Cannot start snapshot computation.")
self.computeScaleFactor()
self.samplingEngine.scaleFactor = self.scaleFactorDer
if self.samplingEngine.nsamples != self.S:
vbMng(self, "INIT", "Starting computation of snapshots.", 5)
self.mus = self.sampler.generatePoints(self.S)
while len(self.mus) > self.S: self.mus.pop()
self.samplingEngine.iterSample(self.mus)
vbMng(self, "DEL", "Done computing snapshots.", 5)
def computeScaleFactor(self):
"""Compute parameter rescaling factor."""
RROMPyAssert(self._mode, message = "Cannot compute rescaling factor.")
self.scaleFactor = .5 * np.abs(
self.muBounds[0] ** self.HFEngine.rescalingExp
- self.muBounds[1] ** self.HFEngine.rescalingExp)
diff --git a/rrompy/reduction_methods/standard/greedy/generic_greedy_approximant.py b/rrompy/reduction_methods/standard/greedy/generic_greedy_approximant.py
index 8f18a43..259cb59 100644
--- a/rrompy/reduction_methods/standard/greedy/generic_greedy_approximant.py
+++ b/rrompy/reduction_methods/standard/greedy/generic_greedy_approximant.py
@@ -1,655 +1,655 @@
# 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 .
#
from abc import abstractmethod
from copy import deepcopy as copy
import numpy as np
from matplotlib import pyplot as plt
from rrompy.hfengines.base.linear_affine_engine import checkIfAffine
from rrompy.reduction_methods.standard.generic_standard_approximant import (
GenericStandardApproximant)
from rrompy.utilities.base.types import (Np1D, Np2D, Tuple, List, normEng,
paramVal, paramList, sampList)
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical import dot
from rrompy.utilities.expression import expressionEvaluator
from rrompy.solver import normEngine
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPyWarning)
from rrompy.parameter import checkParameterList, emptyParameterList
__all__ = ['GenericGreedyApproximant']
def localL2Distance(mus:Np2D, badmus:Np2D) -> Np2D:
return np.linalg.norm(np.tile(mus[..., np.newaxis], [1, 1, len(badmus)])
- badmus[..., np.newaxis].T, axis = 1)
def pruneSamples(mus:paramList, badmus:paramList,
tol : float = 1e-8) -> Np1D:
"""Remove from mus all the elements which are too close to badmus."""
if len(badmus) == 0: return mus
proximity = np.min(localL2Distance(mus.data, badmus.data), axis = 1)
return np.arange(len(mus))[proximity <= tol]
class GenericGreedyApproximant(GenericStandardApproximant):
"""
ROM greedy interpolant computation for parametric problems
(ABSTRACT).
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'S': number of starting training points;
- 'sampler': sample point generator;
- 'greedyTol': uniform error tolerance for greedy algorithm;
defaults to 1e-2;
- 'collinearityTol': collinearity tolerance for greedy algorithm;
defaults to 0.;
- 'maxIter': maximum number of greedy steps; defaults to 1e2;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator; defaults
to sampler.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults to
False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation;
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'collinearityTol': collinearity tolerance for greedy algorithm;
- 'maxIter': maximum number of greedy steps;
- 'nTestPoints': number of test points;
- 'trainSetGenerator': training sample points generator.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
S: number of test points.
sampler: Sample point generator.
greedyTol: Uniform error tolerance for greedy algorithm.
collinearityTol: Collinearity tolerance for greedy algorithm.
maxIter: maximum number of greedy steps.
nTestPoints: number of starting training points.
trainSetGenerator: training sample points generator.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
estimatorNormEngine: Engine for estimator norm computation.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
"""
def __init__(self, *args, **kwargs):
self._preInit()
self._addParametersToList(["greedyTol", "collinearityTol", "maxIter",
"nTestPoints"], [1e-2, 0., 1e2, 5e2],
["trainSetGenerator"], ["AUTO"])
super().__init__(*args, **kwargs)
self._postInit()
@property
def greedyTol(self):
"""Value of greedyTol."""
return self._greedyTol
@greedyTol.setter
def greedyTol(self, greedyTol):
if greedyTol < 0:
raise RROMPyException("greedyTol must be non-negative.")
if hasattr(self, "_greedyTol") and self.greedyTol is not None:
greedyTolold = self.greedyTol
else:
greedyTolold = -1
self._greedyTol = greedyTol
self._approxParameters["greedyTol"] = self.greedyTol
if greedyTolold != self.greedyTol:
self.resetSamples()
@property
def collinearityTol(self):
"""Value of collinearityTol."""
return self._collinearityTol
@collinearityTol.setter
def collinearityTol(self, collinearityTol):
if collinearityTol < 0:
raise RROMPyException("collinearityTol must be non-negative.")
if (hasattr(self, "_collinearityTol")
and self.collinearityTol is not None):
collinearityTolold = self.collinearityTol
else:
collinearityTolold = -1
self._collinearityTol = collinearityTol
self._approxParameters["collinearityTol"] = self.collinearityTol
if collinearityTolold != self.collinearityTol:
self.resetSamples()
@property
def maxIter(self):
"""Value of maxIter."""
return self._maxIter
@maxIter.setter
def maxIter(self, maxIter):
if maxIter <= 0: raise RROMPyException("maxIter must be positive.")
if hasattr(self, "_maxIter") and self.maxIter is not None:
maxIterold = self.maxIter
else:
maxIterold = -1
self._maxIter = maxIter
self._approxParameters["maxIter"] = self.maxIter
if maxIterold != self.maxIter:
self.resetSamples()
@property
def nTestPoints(self):
"""Value of nTestPoints."""
return self._nTestPoints
@nTestPoints.setter
def nTestPoints(self, nTestPoints):
if nTestPoints <= 0:
raise RROMPyException("nTestPoints must be positive.")
if not np.isclose(nTestPoints, np.int(nTestPoints)):
raise RROMPyException("nTestPoints must be an integer.")
nTestPoints = np.int(nTestPoints)
if hasattr(self, "_nTestPoints") and self.nTestPoints is not None:
nTestPointsold = self.nTestPoints
else:
nTestPointsold = -1
self._nTestPoints = nTestPoints
self._approxParameters["nTestPoints"] = self.nTestPoints
if nTestPointsold != self.nTestPoints:
self.resetSamples()
@property
def trainSetGenerator(self):
"""Value of trainSetGenerator."""
return self._trainSetGenerator
@trainSetGenerator.setter
def trainSetGenerator(self, trainSetGenerator):
if (isinstance(trainSetGenerator, (str,))
and trainSetGenerator.upper() == "AUTO"):
trainSetGenerator = self.sampler
if 'generatePoints' not in dir(trainSetGenerator):
raise RROMPyException("trainSetGenerator type not recognized.")
if (hasattr(self, '_trainSetGenerator')
and self.trainSetGenerator not in [None, "AUTO"]):
trainSetGeneratorOld = self.trainSetGenerator
self._trainSetGenerator = trainSetGenerator
self._approxParameters["trainSetGenerator"] = self.trainSetGenerator
if (not 'trainSetGeneratorOld' in locals()
or trainSetGeneratorOld != self.trainSetGenerator):
self.resetSamples()
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
self._mus = emptyParameterList()
def initEstimatorNormEngine(self, normEngn : normEng = None):
"""Initialize estimator norm engine."""
if (normEngn is not None or not hasattr(self, "estimatorNormEngine")
or self.estimatorNormEngine is None):
if normEngn is None:
if self.approx_state:
if not hasattr(self.HFEngine, "energyNormDualMatrix"):
self.HFEngine.buildEnergyNormDualForm()
estimatorEnergyMatrix = self.HFEngine.energyNormDualMatrix
else:
estimatorEnergyMatrix = self.HFEngine.outputNormMatrix
else:
if hasattr(normEngn, "buildEnergyNormDualForm"):
if not hasattr(normEngn, "energyNormDualMatrix"):
normEngn.buildEnergyNormDualForm()
estimatorEnergyMatrix = normEngn.energyNormDualMatrix
else:
estimatorEnergyMatrix = normEngn
self.estimatorNormEngine = normEngine(estimatorEnergyMatrix)
def _affineResidualMatricesContraction(self, rb:Np2D, rA : Np2D = None) \
-> Tuple[Np1D, Np1D, Np1D]:
self.assembleReducedResidualBlocks(full = rA is not None)
# 'ij,jk,ik->k', resbb, radiusb, radiusb.conj()
ff = np.sum(self.trainedModel.data.resbb.dot(rb) * rb.conj(), axis = 0)
if rA is None: return ff
# 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj()
Lf = np.sum(np.tensordot(self.trainedModel.data.resAb, rA, 2)
* rb.conj(), axis = 0)
# 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj()
LL = np.sum(np.tensordot(self.trainedModel.data.resAA, rA, 2)
* rA.conj(), axis = (0, 1))
return ff, Lf, LL
def getErrorEstimatorAffine(self, mus:Np1D) -> Np1D:
"""Standard residual estimator."""
checkIfAffine(self.HFEngine, "apply affinity-based error estimator")
self.HFEngine.buildA()
self.HFEngine.buildb()
mus = checkParameterList(mus, self.npar)[0]
verb = self.trainedModel.verbosity
self.trainedModel.verbosity = 0
uApproxRs = self.getApproxReduced(mus)
muTestEff = mus ** self.HFEngine.rescalingExp
radiusA = np.empty((len(self.HFEngine.thAs), len(mus)),
dtype = np.complex)
radiusb = np.empty((len(self.HFEngine.thbs), len(mus)),
dtype = np.complex)
for j, thA in enumerate(self.HFEngine.thAs):
radiusA[j] = expressionEvaluator(thA[0], muTestEff)
for j, thb in enumerate(self.HFEngine.thbs):
radiusb[j] = expressionEvaluator(thb[0], muTestEff)
radiusA = np.expand_dims(uApproxRs.data, 1) * radiusA
ff, Lf, LL = self._affineResidualMatricesContraction(radiusb, radiusA)
err = np.abs((LL - 2. * np.real(Lf) + ff) / ff) ** .5
self.trainedModel.verbosity = verb
return err
def errorEstimator(self, mus:Np1D, return_max : bool = False) -> Np1D:
setupOK = self.setupApproxLocal()
if setupOK > 0:
err = np.empty(len(mus))
err[:] = np.nan
if not return_max: return err
return err, [- setupOK], np.nan
mus = checkParameterList(mus, self.npar)[0]
vbMng(self.trainedModel, "INIT",
"Evaluating error estimator at mu = {}.".format(mus), 10)
err = self.getErrorEstimatorAffine(mus)
vbMng(self.trainedModel, "DEL", "Done evaluating error estimator", 10)
if not return_max: return err
idxMaxEst = [np.argmax(err)]
return err, idxMaxEst, err[idxMaxEst]
def _isLastSampleCollinear(self) -> bool:
"""Check collinearity of last sample."""
if self.collinearityTol <= 0.: return False
if self.POD:
reff = self.samplingEngine.RPOD[:, -1]
else:
RROMPyWarning(("Repeated orthogonalization of the samples for "
"collinearity check. Consider setting POD to "
"True."))
if not hasattr(self, "_PODEngine"):
from rrompy.sampling.base.pod_engine import PODEngine
self._PODEngine = PODEngine(self.HFEngine)
reff = self._PODEngine.generalizedQR(self.samplingEngine.samples,
only_R = True,
is_state = True)[:, -1]
cLevel = np.abs(reff[-1]) / np.linalg.norm(reff)
cLevel = np.inf if np.isclose(cLevel, 0.) else cLevel ** -1.
vbMng(self, "MAIN", "Collinearity indicator {:.4e}.".format(cLevel), 3)
return cLevel > self.collinearityTol
def plotEstimator(self, est:Np1D, idxMax:List[int], estMax:List[float]):
if not (np.any(np.isnan(est)) or np.any(np.isinf(est))):
fig = plt.figure(figsize = plt.figaspect(1. / self.npar))
for jpar in range(self.npar):
ax = fig.add_subplot(1, self.npar, 1 + jpar)
musre = copy(self.muTest.re.data)
errCP = copy(est)
idx = np.delete(np.arange(self.npar), jpar)
while len(musre) > 0:
if self.npar == 1:
currIdx = np.arange(len(musre))
else:
currIdx = np.where(np.isclose(np.sum(
np.abs(musre[:, idx] - musre[0, idx]), 1), 0.))[0]
ax.semilogy(musre[currIdx, jpar], errCP[currIdx], 'k',
linewidth = 1)
musre = np.delete(musre, currIdx, 0)
errCP = np.delete(errCP, currIdx)
ax.semilogy([self.muBounds.re(0, jpar),
self.muBounds.re(-1, jpar)],
[self.greedyTol] * 2, 'r--')
ax.semilogy(self.mus.re(jpar),
2. * self.greedyTol * np.ones(len(self.mus)), '*m')
if len(idxMax) > 0 and estMax is not None:
ax.semilogy(self.muTest.re(idxMax, jpar), estMax, 'xr')
ax.grid()
plt.tight_layout()
plt.show()
def greedyNextSample(self, muidx:int, plotEst : str = "NONE")\
-> Tuple[Np1D, int, float, paramVal]:
"""Compute next greedy snapshot of solution map."""
RROMPyAssert(self._mode, message = "Cannot add greedy sample.")
mus = copy(self.muTest[muidx])
self.muTest.pop(muidx)
for j, mu in enumerate(mus):
vbMng(self, "MAIN",
("Adding sample point no. {} at {} to training "
"set.").format(len(self.mus) + 1, mu), 3)
self.mus.append(mu)
self._S = len(self.mus)
self._approxParameters["S"] = self.S
if (self.samplingEngine.nsamples <= len(mus) - j - 1
or not np.allclose(mu,
self.samplingEngine.mus.data[j - len(mus)])):
self.samplingEngine.nextSample(mu)
if self._isLastSampleCollinear():
vbMng(self, "MAIN",
("Collinearity above tolerance detected. Starting "
"preemptive greedy loop termination."), 3)
self._collinearityFlag = 1
errorEstTest = np.empty(len(self.muTest))
errorEstTest[:] = np.nan
return errorEstTest, [-1], np.nan, np.nan
errorEstTest, muidx, maxErrorEst = self.errorEstimator(self.muTest,
True)
if plotEst == "ALL":
self.plotEstimator(errorEstTest, muidx, maxErrorEst)
return errorEstTest, muidx, maxErrorEst, self.muTest[muidx]
def _preliminaryTraining(self):
"""Initialize starting snapshots of solution map."""
RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
self.computeScaleFactor()
if self.samplingEngine.nsamples > 0: return
self.resetSamples()
self.samplingEngine.scaleFactor = self.scaleFactorDer
self.mus = self.trainSetGenerator.generatePoints(self.S)
while len(self.mus) > self.S: self.mus.pop()
muTestBase = self.sampler.generatePoints(self.nTestPoints, False)
idxPop = pruneSamples(muTestBase ** self.HFEngine.rescalingExp,
self.mus ** self.HFEngine.rescalingExp,
1e-10 * self.scaleFactor[0])
muTestBase.pop(idxPop)
muLast = copy(self.mus[-1])
self.mus.pop()
if len(self.mus) > 0:
vbMng(self, "MAIN",
("Adding first {} sample point{} at {} to training "
"set.").format(self.S - 1, "" + "s" * (self.S > 2),
self.mus), 3)
self.samplingEngine.iterSample(self.mus)
self._S = len(self.mus)
self._approxParameters["S"] = self.S
self.muTest = emptyParameterList()
self.muTest.reset((len(muTestBase) + 1, self.mus.shape[1]))
self.muTest[: -1] = muTestBase.data
self.muTest[-1] = muLast.data
@abstractmethod
def setupApproxLocal(self) -> int:
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up local approximant.", 5)
pass
vbMng(self, "DEL", "Done setting up local approximant.", 5)
return 0
def setupApprox(self, plotEst : str = "NONE") -> int:
"""Compute greedy snapshots of solution map."""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
vbMng(self, "INIT", "Starting computation of snapshots.", 3)
self._collinearityFlag = 0
self._preliminaryTraining()
muidx, self.firstGreedyIter = [len(self.muTest) - 1], True
errorEstTest, maxErrorEst = [np.inf], np.inf
max2ErrorEst, trainedModelOld = np.inf, None
while self.firstGreedyIter or (len(self.muTest) > 0
and (maxErrorEst is None or max2ErrorEst > self.greedyTol)
and self.samplingEngine.nsamples < self.maxIter):
muTestOld, errorEstTestOld = self.muTest, errorEstTest
muidxOld, maxErrorEstOld = muidx, maxErrorEst
errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample(
muidx, plotEst)
if maxErrorEst is not None and (np.any(np.isnan(maxErrorEst))
or np.any(np.isinf(maxErrorEst))):
if self._collinearityFlag == 0 and not self.firstGreedyIter:
RROMPyWarning(("Instability in a posteriori "
"estimator. Starting preemptive greedy "
"loop termination."))
self.muTest, errorEstTest = muTestOld, errorEstTestOld
if self.firstGreedyIter:
self.mus.pop(-1)
self.samplingEngine.popSample()
if muidx[0] < 0:
self.trainedModel = None
raise RROMPyException(("Instability in approximant "
"computation. Aborting greedy "
"iterations."))
else:
self._approxParameters = (
trainedModelOld.data.approxParameters)
self._S = trainedModelOld.data.approxParameters["S"]
self._approxParameters["S"] = self.S
self.trainedModel.data = copy(trainedModelOld.data)
muidx, maxErrorEst = muidxOld, maxErrorEstOld
break
if maxErrorEst is not None:
max2ErrorEst = np.max(maxErrorEst)
vbMng(self, "MAIN", ("Uniform testing error estimate "
"{:.4e}.").format(max2ErrorEst), 3)
if self.firstGreedyIter:
trainedModelOld = copy(self.trainedModel)
else:
trainedModelOld.data = copy(self.trainedModel.data)
self.firstGreedyIter = False
if (maxErrorEst is None or max2ErrorEst <= self.greedyTol
or np.any(np.isnan(maxErrorEst)) or np.any(np.isinf(maxErrorEst))):
while self.samplingEngine.nsamples > self.S:
self.samplingEngine.popSample()
while len(self.mus) > self.S: self.mus.pop(-1)
else:
self._S = self.samplingEngine.nsamples
self._approxParameters["S"] = self.S
while len(self.mus) < self.S:
self.mus.append(self.samplingEngine.mus[len(self.mus)])
self.setupApproxLocal()
if plotEst == "LAST":
self.plotEstimator(errorEstTest, muidx, maxErrorEst)
vbMng(self, "DEL",
("Done computing snapshots (final snapshot count: "
"{}).").format(self.samplingEngine.nsamples), 3)
return 0
def checkComputedApprox(self) -> bool:
"""
Check if setup of new approximant is not needed.
Returns:
True if new setup is not needed. False otherwise.
"""
return (super().checkComputedApprox()
- and len(self.mus) == self.trainedModel.data.projMat.shape[1])
+ and len(self.mus) == self.trainedModel.nmus)
def assembleReducedResidualGramian(self, pMat:sampList):
"""
Build residual gramian of reduced linear system through projections.
"""
self.initEstimatorNormEngine()
if (not hasattr(self.trainedModel.data, "gramian")
or self.trainedModel.data.gramian is None):
gramian = self.estimatorNormEngine.innerProduct(pMat, pMat)
else:
Sold = self.trainedModel.data.gramian.shape[0]
S = len(self.mus)
if Sold > S:
gramian = self.trainedModel.data.gramian[: S, : S]
else:
idxOld = list(range(Sold))
idxNew = list(range(Sold, S))
gramian = np.empty((S, S), dtype = np.complex)
gramian[: Sold, : Sold] = self.trainedModel.data.gramian
gramian[: Sold, Sold :] = (
self.estimatorNormEngine.innerProduct(pMat(idxNew),
pMat(idxOld)))
gramian[Sold :, : Sold] = gramian[: Sold, Sold :].T.conj()
gramian[Sold :, Sold :] = (
self.estimatorNormEngine.innerProduct(pMat(idxNew),
pMat(idxNew)))
self.trainedModel.data.gramian = gramian
def assembleReducedResidualBlocksbb(self, bs:List[Np1D]):
"""
Build blocks (of type bb) of reduced linear system through projections.
"""
self.initEstimatorNormEngine()
nbs = len(bs)
if (not hasattr(self.trainedModel.data, "resbb")
or self.trainedModel.data.resbb is None):
resbb = np.empty((nbs, nbs), dtype = np.complex)
for i in range(nbs):
Mbi = bs[i]
resbb[i, i] = self.estimatorNormEngine.innerProduct(Mbi, Mbi)
for j in range(i):
Mbj = bs[j]
resbb[i, j] = self.estimatorNormEngine.innerProduct(Mbj,
Mbi)
for i in range(nbs):
for j in range(i + 1, nbs):
resbb[i, j] = resbb[j, i].conj()
self.trainedModel.data.resbb = resbb
def assembleReducedResidualBlocksAb(self, As:List[Np2D], bs:List[Np1D],
pMat:sampList):
"""
Build blocks (of type Ab) of reduced linear system through projections.
"""
self.initEstimatorNormEngine()
nAs = len(As)
nbs = len(bs)
S = len(self.mus)
if (not hasattr(self.trainedModel.data, "resAb")
or self.trainedModel.data.resAb is None):
if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data
resAb = np.empty((nbs, S, nAs), dtype = np.complex)
for j in range(nAs):
MAj = dot(As[j], pMat)
for i in range(nbs):
Mbi = bs[i]
resAb[i, :, j] = self.estimatorNormEngine.innerProduct(MAj,
Mbi)
else:
Sold = self.trainedModel.data.resAb.shape[1]
if Sold == S: return
if Sold > S:
resAb = self.trainedModel.data.resAb[:, : S, :]
else:
if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data
resAb = np.empty((nbs, S, nAs), dtype = np.complex)
resAb[:, : Sold, :] = self.trainedModel.data.resAb
for j in range(nAs):
MAj = dot(As[j], pMat[:, Sold :])
for i in range(nbs):
Mbi = bs[i]
resAb[i, Sold :, j] = (
self.estimatorNormEngine.innerProduct(MAj, Mbi))
self.trainedModel.data.resAb = resAb
def assembleReducedResidualBlocksAA(self, As:List[Np2D], pMat:sampList):
"""
Build blocks (of type AA) of reduced linear system through projections.
"""
self.initEstimatorNormEngine()
nAs = len(As)
S = len(self.mus)
if (not hasattr(self.trainedModel.data, "resAA")
or self.trainedModel.data.resAA is None):
if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data
resAA = np.empty((S, nAs, S, nAs), dtype = np.complex)
for i in range(nAs):
MAi = dot(As[i], pMat)
resAA[:, i, :, i] = (
self.estimatorNormEngine.innerProduct(MAi, MAi))
for j in range(i):
MAj = dot(As[j], pMat)
resAA[:, i, :, j] = (
self.estimatorNormEngine.innerProduct(MAj, MAi))
for i in range(nAs):
for j in range(i + 1, nAs):
resAA[:, i, :, j] = resAA[:, j, :, i].T.conj()
else:
Sold = self.trainedModel.data.resAA.shape[0]
if Sold == S: return
if Sold > S:
resAA = self.trainedModel.data.resAA[: S, :, : S, :]
else:
if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data
resAA = np.empty((S, nAs, S, nAs), dtype = np.complex)
resAA[: Sold, :, : Sold, :] = self.trainedModel.data.resAA
for i in range(nAs):
MAi = dot(As[i], pMat)
resAA[: Sold, i, Sold :, i] = (
self.estimatorNormEngine.innerProduct(MAi[:, Sold :],
MAi[:, : Sold]))
resAA[Sold :, i, : Sold, i] = resAA[: Sold, i,
Sold :, i].T.conj()
resAA[Sold :, i, Sold :, i] = (
self.estimatorNormEngine.innerProduct(MAi[:, Sold :],
MAi[:, Sold :]))
for j in range(i):
MAj = dot(As[j], pMat)
resAA[: Sold, i, Sold :, j] = (
self.estimatorNormEngine.innerProduct(MAj[:, Sold :],
MAi[:, : Sold]))
resAA[Sold :, i, : Sold, j] = (
self.estimatorNormEngine.innerProduct(MAj[:, : Sold],
MAi[:, Sold :]))
resAA[Sold :, i, Sold :, j] = (
self.estimatorNormEngine.innerProduct(MAj[:, Sold :],
MAi[:, Sold :]))
for i in range(nAs):
for j in range(i + 1, nAs):
resAA[: Sold, i, Sold :, j] = (
resAA[Sold :, j, : Sold, i].T.conj())
resAA[Sold :, i, : Sold, j] = (
resAA[: Sold, j, Sold :, i].T.conj())
resAA[Sold :, i, Sold :, j] = (
resAA[Sold :, j, Sold :, i].T.conj())
self.trainedModel.data.resAA = resAA
def assembleReducedResidualBlocks(self, full : bool = False):
"""Build affine blocks of affine decomposition of residual."""
if full:
checkIfAffine(self.HFEngine, "assemble reduced residual blocks")
else:
checkIfAffine(self.HFEngine, "assemble reduced RHS blocks", True)
self.HFEngine.buildb()
self.assembleReducedResidualBlocksbb(self.HFEngine.bs)
if full:
pMat = self.samplingEngine.samples
self.HFEngine.buildA()
self.assembleReducedResidualBlocksAb(self.HFEngine.As,
self.HFEngine.bs, pMat)
self.assembleReducedResidualBlocksAA(self.HFEngine.As, pMat)
diff --git a/rrompy/reduction_methods/standard/greedy/rational_interpolant_greedy.py b/rrompy/reduction_methods/standard/greedy/rational_interpolant_greedy.py
index 068b5e1..76dab43 100644
--- a/rrompy/reduction_methods/standard/greedy/rational_interpolant_greedy.py
+++ b/rrompy/reduction_methods/standard/greedy/rational_interpolant_greedy.py
@@ -1,552 +1,562 @@
# 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 .
#
from copy import deepcopy as copy
import numpy as np
from rrompy.hfengines.base.linear_affine_engine import checkIfAffine
from .generic_greedy_approximant import GenericGreedyApproximant
-from rrompy.utilities.poly_fitting.polynomial import (polybases,
+from rrompy.utilities.poly_fitting.polynomial import (polybases, polyfitname,
PolynomialInterpolator as PI,
polyvanderTotal as pvT)
from rrompy.utilities.numerical import dot
from rrompy.utilities.numerical.degree import totalDegreeN
from rrompy.utilities.expression import expressionEvaluator
from rrompy.reduction_methods.standard import RationalInterpolant
from rrompy.utilities.base.types import Np1D, Tuple, paramVal, List
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.poly_fitting import customFit
from rrompy.utilities.exception_manager import (RROMPyWarning, RROMPyException,
RROMPyAssert, RROMPy_FRAGILE)
from rrompy.parameter import checkParameterList
from rrompy.sampling import sampleList, emptySampleList
__all__ = ['RationalInterpolantGreedy']
class RationalInterpolantGreedy(GenericGreedyApproximant, RationalInterpolant):
"""
ROM greedy rational interpolant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'S': number of starting training points;
- 'sampler': sample point generator;
- 'greedyTol': uniform error tolerance for greedy algorithm;
defaults to 1e-2;
- 'collinearityTol': collinearity tolerance for greedy algorithm;
defaults to 0.;
- 'maxIter': maximum number of greedy steps; defaults to 1e2;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator; defaults
to sampler;
- 'polybasis': type of basis for interpolation; defaults to
'MONOMIAL';
- 'errorEstimatorKind': kind of error estimator; available values
include 'AFFINE', 'DISCREPANCY', 'LOOK_AHEAD',
'LOOK_AHEAD_RES', 'LOOK_AHEAD_OUTPUT', and 'NONE'; defaults to
'NONE';
- 'interpRcond': tolerance for interpolation; defaults to None;
- 'robustTol': tolerance for robust rational denominator
- management; defaults to 0.
+ management; defaults to 0;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles; defaults to False;
+ - 'correctorTol': tolerance for corrector step; defaults to 0.,
+ i.e. no bad poles;
+ - 'correctorMaxIter': maximum number of corrector iterations;
+ defaults to 1.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults and must
be True.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation;
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'collinearityTol': collinearity tolerance for greedy algorithm;
- 'maxIter': maximum number of greedy steps;
- 'nTestPoints': number of test points;
- 'trainSetGenerator': training sample points generator;
- 'errorEstimatorKind': kind of error estimator;
- 'interpRcond': tolerance for interpolation;
- 'robustTol': tolerance for robust rational denominator
- management.
+ management;
+ - 'correctorForce': whether corrector should forcefully delete bad
+ poles;
+ - 'correctorTol': tolerance for corrector step;
+ - 'correctorMaxIter': maximum number of corrector iterations.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
S: number of test points.
sampler: Sample point generator.
greedyTol: uniform error tolerance for greedy algorithm.
collinearityTol: Collinearity tolerance for greedy algorithm.
maxIter: maximum number of greedy steps.
nTestPoints: number of starting training points.
trainSetGenerator: training sample points generator.
robustTol: tolerance for robust rational denominator management.
+ correctorForce: Whether corrector should forcefully delete bad poles.
+ correctorTol: Tolerance for corrector step.
+ correctorMaxIter: Maximum number of corrector iterations.
errorEstimatorKind: kind of error estimator.
interpRcond: tolerance for interpolation.
robustTol: tolerance for robust rational denominator management.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
estimatorNormEngine: Engine for estimator norm computation.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
"""
_allowedEstimatorKinds = ["AFFINE", "DISCREPANCY", "LOOK_AHEAD",
"LOOK_AHEAD_RES", "LOOK_AHEAD_OUTPUT", "NONE"]
def __init__(self, *args, **kwargs):
self._preInit()
self._addParametersToList(["errorEstimatorKind"], ["DISCREPANCY"],
toBeExcluded = ["M", "N", "polydegreetype",
- "radialDirectionalWeights",
- "nNearestNeighbor"])
+ "radialDirectionalWeights"])
super().__init__(*args, **kwargs)
if not self.approx_state and self.errorEstimatorKind not in [
"LOOK_AHEAD", "LOOK_AHEAD_OUTPUT", "NONE"]:
raise RROMPyException(("Must compute greedy approximation of "
"state, unless error estimator allows "
"otherwise."))
self._postInit()
@property
def approx_state(self):
"""Value of approx_state."""
return self._approx_state
@approx_state.setter
def approx_state(self, approx_state):
RationalInterpolant.approx_state.fset(self, approx_state)
if (not self.approx_state and hasattr(self, "_errorEstimatorKind")
and self.errorEstimatorKind not in [
"LOOK_AHEAD", "LOOK_AHEAD_OUTPUT", "NONE"]):
raise RROMPyException(("Must compute greedy approximation of "
"state, unless error estimator allows "
"otherwise."))
@property
def E(self):
"""Value of E."""
self._E = self.sampleBatchIdx - 1
return self._E
@E.setter
def E(self, E):
RROMPyWarning(("E is used just to simplify inheritance, and its value "
"cannot be changed from that of sampleBatchIdx - 1."))
def _setMAuto(self):
self.M = self.E
def _setNAuto(self):
self.N = self.E
@property
def polydegreetype(self):
"""Value of polydegreetype."""
return "TOTAL"
@polydegreetype.setter
def polydegreetype(self, polydegreetype):
RROMPyWarning(("polydegreetype is used just to simplify inheritance, "
"and its value cannot be changed from 'TOTAL'."))
@property
def polybasis(self):
"""Value of polybasis."""
return self._polybasis
@polybasis.setter
def polybasis(self, polybasis):
try:
polybasis = polybasis.upper().strip().replace(" ","")
if polybasis not in polybases:
raise RROMPyException("Sample type not recognized.")
self._polybasis = polybasis
except:
RROMPyWarning(("Prescribed polybasis not recognized. Overriding "
"to 'MONOMIAL'."))
self._polybasis = "MONOMIAL"
self._approxParameters["polybasis"] = self.polybasis
@property
def errorEstimatorKind(self):
"""Value of errorEstimatorKind."""
return self._errorEstimatorKind
@errorEstimatorKind.setter
def errorEstimatorKind(self, errorEstimatorKind):
errorEstimatorKind = errorEstimatorKind.upper()
if errorEstimatorKind not in self._allowedEstimatorKinds:
RROMPyWarning(("Error estimator kind not recognized. Overriding "
"to 'NONE'."))
errorEstimatorKind = "NONE"
self._errorEstimatorKind = errorEstimatorKind
self._approxParameters["errorEstimatorKind"] = self.errorEstimatorKind
if (self.errorEstimatorKind not in [
"LOOK_AHEAD", "LOOK_AHEAD_OUTPUT", "NONE"]
and hasattr(self, "_approx_state") and not self.approx_state):
raise RROMPyException(("Must compute greedy approximation of "
"state, unless error estimator allows "
"otherwise."))
def _polyvanderAuxiliary(self, mus, deg, *args):
return pvT(mus, deg, *args)
def getErrorEstimatorDiscrepancy(self, mus:Np1D) -> Np1D:
"""Discrepancy-based residual estimator."""
checkIfAffine(self.HFEngine, "apply discrepancy-based error estimator")
mus = checkParameterList(mus, self.npar)[0]
muCTest = self.trainedModel.centerNormalize(mus)
verb = self.trainedModel.verbosity
self.trainedModel.verbosity = 0
QTest = self.trainedModel.getQVal(mus)
QTzero = np.where(QTest == 0.)[0]
if len(QTzero) > 0:
RROMPyWarning(("Adjusting estimator to avoid division by "
"numerically zero denominator."))
QTest[QTzero] = np.finfo(np.complex).eps / (1. + self.N)
self.HFEngine.buildA()
self.HFEngine.buildb()
nAs, nbs = self.HFEngine.nAs, self.HFEngine.nbs
muTrainEff = self.mus ** self.HFEngine.rescalingExp
muTestEff = mus ** self.HFEngine.rescalingExp
PTrain = self.trainedModel.getPVal(self.mus).data.T
QTrain = self.trainedModel.getQVal(self.mus)
QTzero = np.where(QTrain == 0.)[0]
if len(QTzero) > 0:
RROMPyWarning(("Adjusting estimator to avoid division by "
"numerically zero denominator."))
QTrain[QTzero] = np.finfo(np.complex).eps / (1. + self.N)
PTest = self.trainedModel.getPVal(mus).data
radiusAbTrain = np.empty((self.S, nAs * self.S + nbs),
dtype = np.complex)
radiusA = np.empty((self.S, nAs, len(mus)), dtype = np.complex)
radiusb = np.empty((nbs, len(mus)), dtype = np.complex)
for j, thA in enumerate(self.HFEngine.thAs):
idxs = j * self.S + np.arange(self.S)
radiusAbTrain[:, idxs] = expressionEvaluator(thA[0], muTrainEff,
(self.S, 1)) * PTrain
radiusA[:, j] = PTest * expressionEvaluator(thA[0], muTestEff,
(len(mus),))
for j, thb in enumerate(self.HFEngine.thbs):
idx = nAs * self.S + j
radiusAbTrain[:, idx] = QTrain * expressionEvaluator(thb[0],
muTrainEff, (self.S,))
radiusb[j] = QTest * expressionEvaluator(thb[0], muTestEff,
(len(mus),))
QRHSNorm2 = self._affineResidualMatricesContraction(radiusb)
vanTrain = self._polyvanderAuxiliary(self._musUniqueCN, self.E,
self.polybasis0, self._derIdxs,
self._reorder)
interpPQ = customFit(vanTrain, radiusAbTrain,
rcond = self.interpRcond)
- vanTest = self._polyvanderAuxiliary(muCTest, self.E,
- self.polybasis0)
+ vanTest = self._polyvanderAuxiliary(muCTest, self.E, self.polybasis0)
DradiusAb = vanTest.dot(interpPQ)
radiusA = (radiusA
- DradiusAb[:, : - nbs].reshape(len(mus), -1, self.S).T)
radiusb = radiusb - DradiusAb[:, - nbs :].T
ff, Lf, LL = self._affineResidualMatricesContraction(radiusb, radiusA)
err = np.abs((LL - 2. * np.real(Lf) + ff) / QRHSNorm2) ** .5
self.trainedModel.verbosity = verb
return err
def getErrorEstimatorLookAhead(self, mus:Np1D,
what : str = "") -> Tuple[Np1D, List[int]]:
"""Residual estimator based on look-ahead idea."""
errTest, QTest, idxMaxEst = self._EIMStep(mus)
_approx_state_old = self.approx_state
if what == "OUTPUT" and _approx_state_old: self._approx_state = False
self.initEstimatorNormEngine()
self._approx_state = _approx_state_old
mu_muTestSample = mus[idxMaxEst]
app_muTestSample = self.getApproxReduced(mu_muTestSample)
if self._mode == RROMPy_FRAGILE:
if what == "RES" and not self.HFEngine.isCEye:
raise RROMPyException(("Cannot compute LOOK_AHEAD_RES "
"estimator in fragile mode for "
"non-scalar C."))
app_muTestSample = dot(self.trainedModel.data.projMat[:,
: app_muTestSample.shape[0]],
app_muTestSample.data)
else:
app_muTestSample = dot(self.samplingEngine.samples,
app_muTestSample)
if what == "RES":
errmu = self.HFEngine.residual(mu_muTestSample, app_muTestSample,
post_c = False)
solmu = self.HFEngine.residual(mu_muTestSample, None,
post_c = False)
else:
for j, mu in enumerate(mu_muTestSample):
uEx = self.samplingEngine.nextSample(mu)
if hasattr(self.samplingEngine, "samples_full"):
uEx = self.samplingEngine.samples_full[-1]
if j == 0:
solmu = emptySampleList()
solmu.reset((len(uEx), len(mu_muTestSample)),
dtype = uEx.dtype)
solmu[j] = uEx
if what == "OUTPUT" and self.approx_state:
solmu = sampleList(self.HFEngine.applyC(solmu.data))
app_muTestSample = sampleList(self.HFEngine.applyC(
app_muTestSample.data))
errmu = solmu - app_muTestSample
errsamples = (self.estimatorNormEngine.norm(errmu)
/ self.estimatorNormEngine.norm(solmu))
musT = copy(self.mus)
musT.append(mu_muTestSample)
musT = self.trainedModel.centerNormalize(musT)
musC = self.trainedModel.centerNormalize(mus)
- errT = np.zeros(len(musT), dtype = np.complex)
- err = np.zeros(len(mus))
- for l in range(len(mu_muTestSample)):
- errT[len(self.mus) + l] = errsamples[l] * QTest[idxMaxEst[l]]
- p = PI()
- wellCond, msg = p.setupByInterpolation(musT, errT, self.E + 1,
- self.polybasis, self.verbosity >= 15,
- True, {}, {"rcond": self.interpRcond})
- err += np.abs(p(musC))
- errT[len(self.mus) + l] = 0.
- err /= QTest
- vbMng(self, "MAIN", msg, 15)
+ errT = np.zeros((len(musT), len(mu_muTestSample)), dtype = np.complex)
+ errT[np.arange(len(self.mus), len(musT)),
+ np.arange(len(mu_muTestSample))] = errsamples * QTest[idxMaxEst]
+ vanT = self._polyvanderAuxiliary(musT, self.E + 1, self.polybasis)
+ fitOut = customFit(vanT, errT, full = True, rcond = self.interpRcond)
+ vbMng(self, "MAIN",
+ ("Fitting {} samples with degree {} through {}... Conditioning "
+ "of LS system: {:.4e}.").format(len(vanT), self.E + 1,
+ polyfitname(self.polybasis),
+ fitOut[1][2][0] / fitOut[1][2][-1]), 15)
+ vanC = self._polyvanderAuxiliary(musC, self.E + 1, self.polybasis)
+ err = np.sum(np.abs(vanC.dot(fitOut[0])), axis = -1) / QTest
return err, idxMaxEst
def getErrorEstimatorNone(self, mus:Np1D) -> Np1D:
"""EIM-based residual estimator."""
err = np.max(self._EIMStep(mus, True), axis = 1)
err *= self.greedyTol / np.mean(err)
return err
def _EIMStep(self, mus:Np1D,
only_one : bool = False) -> Tuple[Np1D, Np1D, List[int]]:
"""Residual estimator based on look-ahead idea."""
mus = checkParameterList(mus, self.npar)[0]
verb = self.trainedModel.verbosity
self.trainedModel.verbosity = 0
QTest = self.trainedModel.getQVal(mus)
QTzero = np.where(QTest == 0.)[0]
if len(QTzero) > 0:
RROMPyWarning(("Adjusting estimator to avoid division by "
"numerically zero denominator."))
QTest[QTzero] = np.finfo(np.complex).eps / (1. + self.N)
QTest = np.abs(QTest)
muCTest = self.trainedModel.centerNormalize(mus)
muCTrain = self.trainedModel.centerNormalize(self.mus)
vanTest = self._polyvanderAuxiliary(muCTest, self.E, self.polybasis)
vanTestNext = self._polyvanderAuxiliary(muCTest, self.E + 1,
self.polybasis)[:,
vanTest.shape[1] :]
idxsTest = np.arange(vanTestNext.shape[1])
basis = np.zeros((len(idxsTest), 0), dtype = float)
idxMaxEst = []
while len(idxsTest) > 0:
vanTrial = self._polyvanderAuxiliary(muCTrain, self.E,
self.polybasis)
vanTrialNext = self._polyvanderAuxiliary(muCTrain, self.E + 1,
self.polybasis)[:,
vanTrial.shape[1] :]
vanTrial = np.hstack((vanTrial, vanTrialNext.dot(basis).reshape(
len(vanTrialNext), basis.shape[1])))
valuesTrial = vanTrialNext[:, idxsTest]
vanTestEff = np.hstack((vanTest, vanTestNext.dot(basis).reshape(
len(vanTestNext), basis.shape[1])))
vanTestNextEff = vanTestNext[:, idxsTest]
try:
coeffTest = np.linalg.solve(vanTrial, valuesTrial)
except np.linalg.LinAlgError as e:
raise RROMPyException(e)
errTest = (np.abs(vanTestNextEff - vanTestEff.dot(coeffTest))
/ np.expand_dims(QTest, 1))
if only_one:
self.trainedModel.verbosity = verb
return errTest
idxMaxErr = np.unravel_index(np.argmax(errTest), errTest.shape)
idxMaxEst += [idxMaxErr[0]]
muCTrain.append(muCTest[idxMaxErr[0]])
basis = np.pad(basis, [(0, 0), (0, 1)], "constant")
basis[idxsTest[idxMaxErr[1]], -1] = 1.
idxsTest = np.delete(idxsTest, idxMaxErr[1])
self.trainedModel.verbosity = verb
return errTest, QTest, idxMaxEst
def errorEstimator(self, mus:Np1D, return_max : bool = False) -> Np1D:
"""Standard residual-based error estimator."""
setupOK = self.setupApproxLocal()
if setupOK > 0:
err = np.empty(len(mus))
err[:] = np.nan
if not return_max: return err
return err, [- setupOK], np.nan
mus = checkParameterList(mus, self.npar)[0]
vbMng(self.trainedModel, "INIT",
"Evaluating error estimator at mu = {}.".format(mus), 10)
if self.errorEstimatorKind == "AFFINE":
err = self.getErrorEstimatorAffine(mus)
else:
self._setupInterpolationIndices()
if self.errorEstimatorKind == "DISCREPANCY":
err = self.getErrorEstimatorDiscrepancy(mus)
elif self.errorEstimatorKind[: 10] == "LOOK_AHEAD":
err, idxMaxEst = self.getErrorEstimatorLookAhead(mus,
self.errorEstimatorKind[11 :])
else: #if self.errorEstimatorKind == "NONE":
err = self.getErrorEstimatorNone(mus)
vbMng(self.trainedModel, "DEL", "Done evaluating error estimator", 10)
if not return_max: return err
if self.errorEstimatorKind[: 10] != "LOOK_AHEAD":
idxMaxEst = np.empty(self.sampleBatchSize, dtype = int)
errCP = copy(err)
for j in range(self.sampleBatchSize):
k = np.argmax(errCP)
idxMaxEst[j] = k
if j + 1 < self.sampleBatchSize:
musZero = self.trainedModel.centerNormalize(mus, mus[k])
errCP *= np.linalg.norm(musZero.data, axis = 1)
return err, idxMaxEst, err[idxMaxEst]
def plotEstimator(self, *args, **kwargs):
super().plotEstimator(*args, **kwargs)
if self.errorEstimatorKind == "NONE":
vbMng(self, "MAIN",
("Warning! Error estimator has been arbitrarily normalized "
"before plotting."), 15)
def greedyNextSample(self, *args,
**kwargs) -> Tuple[Np1D, int, float, paramVal]:
"""Compute next greedy snapshot of solution map."""
RROMPyAssert(self._mode, message = "Cannot add greedy sample.")
self.sampleBatchIdx += 1
self.sampleBatchSize = totalDegreeN(self.npar - 1, self.sampleBatchIdx)
err, muidx, maxErr, muNext = super().greedyNextSample(*args, **kwargs)
if maxErr is not None and (np.any(np.isnan(maxErr))
or np.any(np.isinf(maxErr))):
self.sampleBatchIdx -= 1
self.sampleBatchSize = totalDegreeN(self.npar - 1,
self.sampleBatchIdx)
if (self.errorEstimatorKind == "NONE" and not np.isnan(maxErr)
and not np.isinf(maxErr)):
maxErr = None
return err, muidx, maxErr, muNext
def _preliminaryTraining(self):
"""Initialize starting snapshots of solution map."""
RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
if self.samplingEngine.nsamples > 0:
return
S = self.S
self.sampleBatchIdx, self.sampleBatchSize, self._S = -1, 0, 0
nextBatchSize = 1
while self._S + nextBatchSize <= S:
self.sampleBatchIdx += 1
self.sampleBatchSize = nextBatchSize
self._S += self.sampleBatchSize
nextBatchSize = totalDegreeN(self.npar - 1,
self.sampleBatchIdx + 1)
super()._preliminaryTraining()
self.M, self.N = ("AUTO",) * 2
def setupApproxLocal(self) -> int:
"""Compute rational interpolant."""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
self.verbosity -= 10
vbMng(self, "INIT", "Setting up local approximant.", 5)
pMat = self.samplingEngine.samples.data
pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat
if self.trainedModel is None:
self.trainedModel = self.tModelType()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
- datadict = {"mu0": self.mu0, "projMat": pMatEff,
- "scaleFactor": self.scaleFactor,
+ datadict = {"mu0": self.mu0, "mus": copy(self.mus),
+ "projMat": pMatEff, "scaleFactor": self.scaleFactor,
"rescalingExp": self.HFEngine.rescalingExp}
self.trainedModel.data = self.initializeModelData(datadict)[0]
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(pMatEff)
self.trainedModel.data.mus = copy(self.mus)
- self.trainedModel.data.mus = copy(self.mus)
self.catchInstability = 2
unstable = False
if self.E > 0:
try:
Q = self._setupDenominator()[0]
except RROMPyException as RE:
RROMPyWarning("Downgraded {}: {}".format(RE.__class__.__name__,
RE))
vbMng(self, "DEL", "", 7)
unstable = True
else:
Q = PI()
Q.coeffs = np.ones((1,) * self.npar, dtype = np.complex)
Q.npar = self.npar
Q.polybasis = self.polybasis
if not unstable:
self.trainedModel.data.Q = copy(Q)
try:
P = copy(self._setupNumerator())
except RROMPyException as RE:
RROMPyWarning("Downgraded {}: {}".format(RE.__class__.__name__,
RE))
vbMng(self, "DEL", "", 7)
unstable = True
if not unstable:
self.trainedModel.data.P = copy(P)
self.trainedModel.data.approxParameters = copy(
self.approxParameters)
vbMng(self, "DEL", "Done setting up local approximant.", 5)
self.catchInstability = 0
self.verbosity += 10
return 1 * unstable
def setupApprox(self, plotEst : str = "NONE") -> int:
val = super().setupApprox(plotEst)
if val == 0:
self._iterCorrector()
self.trainedModel.data.approxParameters = copy(
self.approxParameters)
return val
def loadTrainedModel(self, filename:str):
"""Load trained reduced model from file."""
super().loadTrainedModel(filename)
self.sampleBatchIdx, self.sampleBatchSize, _S = -1, 0, 0
nextBatchSize = 1
while _S + nextBatchSize <= self.S + 1:
self.sampleBatchIdx += 1
self.sampleBatchSize = nextBatchSize
_S += self.sampleBatchSize
nextBatchSize = totalDegreeN(self.npar - 1,
self.sampleBatchIdx + 1)
diff --git a/rrompy/reduction_methods/standard/greedy/reduced_basis_greedy.py b/rrompy/reduction_methods/standard/greedy/reduced_basis_greedy.py
index 70b532d..9693dd4 100644
--- a/rrompy/reduction_methods/standard/greedy/reduced_basis_greedy.py
+++ b/rrompy/reduction_methods/standard/greedy/reduced_basis_greedy.py
@@ -1,165 +1,165 @@
# 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 .
#
from copy import deepcopy as copy
from .generic_greedy_approximant import GenericGreedyApproximant
from rrompy.reduction_methods.standard import ReducedBasis
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical import dot
from rrompy.utilities.exception_manager import RROMPyWarning, RROMPyAssert
__all__ = ['ReducedBasisGreedy']
class ReducedBasisGreedy(GenericGreedyApproximant, ReducedBasis):
"""
ROM greedy RB approximant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'S': number of starting training points;
- 'sampler': sample point generator;
- 'greedyTol': uniform error tolerance for greedy algorithm;
defaults to 1e-2;
- 'collinearityTol': collinearity tolerance for greedy algorithm;
defaults to 0.;
- 'maxIter': maximum number of greedy steps; defaults to 1e2;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator; defaults
to sampler.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults and must
be True.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation;
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'collinearityTol': collinearity tolerance for greedy algorithm;
- 'maxIter': maximum number of greedy steps;
- 'nTestPoints': number of test points;
- 'trainSetGenerator': training sample points generator.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
S: number of test points.
sampler: Sample point generator.
greedyTol: uniform error tolerance for greedy algorithm.
collinearityTol: Collinearity tolerance for greedy algorithm.
maxIter: maximum number of greedy steps.
nTestPoints: number of starting training points.
trainSetGenerator: training sample points generator.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
estimatorNormEngine: Engine for estimator norm computation.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
As: List of sparse matrices (in CSC format) representing coefficients
of linear system matrix.
bs: List of numpy vectors representing coefficients of linear system
RHS.
ARBs: List of sparse matrices (in CSC format) representing coefficients
of compressed linear system matrix.
bRBs: List of numpy vectors representing coefficients of compressed
linear system RHS.
"""
def __init__(self, *args, **kwargs):
self._preInit()
self._addParametersToList(toBeExcluded = ["R", "PODTolerance"])
super().__init__(*args, **kwargs)
self.R = "AUTO"
self._postInit()
def _setRAuto(self):
self.R = self.S
@property
def PODTolerance(self):
"""Value of PODTolerance."""
self._PODTolerance = -1
return self._PODTolerance
@PODTolerance.setter
def PODTolerance(self, PODTolerance):
RROMPyWarning(("PODTolerance is used just to simplify inheritance, "
"and its value cannot be changed from -1."))
def setupApproxLocal(self) -> int:
"""Compute RB projection matrix."""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
self.verbosity -= 10
vbMng(self, "INIT", "Setting up local approximant.", 5)
vbMng(self, "INIT", "Computing projection matrix.", 7)
pMat = self.samplingEngine.samples.data
pMatEff = dot(self.HFEngine.C, pMat)
if self.trainedModel is None:
self.trainedModel = self.tModelType()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
- datadict = {"mu0": self.mu0, "projMat": pMatEff,
- "scaleFactor": self.scaleFactor,
+ datadict = {"mu0": self.mu0, "mus": copy(self.mus),
+ "projMat": pMatEff, "scaleFactor": self.scaleFactor,
"rescalingExp": self.HFEngine.rescalingExp}
data = self.initializeModelData(datadict)[0]
ARBs, bRBs = self.assembleReducedSystem(pMat)
data.affinePoly = self.HFEngine.affinePoly
self.HFEngine.buildA()
self.HFEngine.buildb()
data.thAs, data.thbs = self.HFEngine.thAs, self.HFEngine.thbs
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
- Sold = self.trainedModel.data.projMat.shape[1]
+ Sold = self.trainedModel.nmus
ARBs, bRBs = self.assembleReducedSystem(pMat[:, Sold :],
pMat[:, : Sold])
self.trainedModel.data.projMat = copy(pMatEff)
- self.trainedModel.data.mus = copy(self.mus)
+ self.trainedModel.data.mus = copy(self.mus)
self.trainedModel.data.ARBs = ARBs
self.trainedModel.data.bRBs = bRBs
vbMng(self, "DEL", "Done computing projection matrix.", 7)
self.trainedModel.data.approxParameters = copy(self.approxParameters)
vbMng(self, "DEL", "Done setting up local approximant.", 5)
self.verbosity += 10
return 0
diff --git a/rrompy/reduction_methods/standard/nearest_neighbor.py b/rrompy/reduction_methods/standard/nearest_neighbor.py
index 08ff30f..c95043b 100644
--- a/rrompy/reduction_methods/standard/nearest_neighbor.py
+++ b/rrompy/reduction_methods/standard/nearest_neighbor.py
@@ -1,114 +1,156 @@
# 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 .
#
from copy import deepcopy as copy
+import numpy as np
from .generic_standard_approximant import GenericStandardApproximant
from rrompy.utilities.base import verbosityManager as vbMng
+from rrompy.utilities.poly_fitting.nearest_neighbor import (
+ NearestNeighborInterpolator as NNI)
from rrompy.utilities.numerical import dot
from rrompy.utilities.exception_manager import RROMPyAssert
__all__ = ['NearestNeighbor']
class NearestNeighbor(GenericStandardApproximant):
"""
ROM nearest neighbor approximant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'S': total number of samples current approximant relies upon;
- - 'sampler': sample point generator.
+ - 'sampler': sample point generator;
+ - 'nNeighbors': number of nearest neighbors; defaults to 1;
+ - 'radialDirectionalWeights': directional weights for computation
+ of parameter distance; defaults to 1.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults and must
be True.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- - 'scaleFactorDer': scaling factors for derivative computation.
+ - 'scaleFactorDer': scaling factors for derivative computation;
+ - 'nNeighbors': number of nearest neighbors;
+ - 'radialDirectionalWeights': directional weights for computation
+ of parameter distance.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
+ nNeighbors: Number of nearest neighbors.
+ radialDirectionalWeights: Directional weights for computation of
+ parameter distance.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
"""
+ def __init__(self, *args, **kwargs):
+ self._preInit()
+ self._addParametersToList(["nNeighbors", "radialDirectionalWeights"],
+ [1, [1.]])
+ super().__init__(*args, **kwargs)
+ self._postInit()
+
@property
def tModelType(self):
from .trained_model.trained_model_nearest_neighbor import (
TrainedModelNearestNeighbor)
return TrainedModelNearestNeighbor
+ @property
+ def nNeighbors(self):
+ """Value of nNeighbors."""
+ return self._nNeighbors
+ @nNeighbors.setter
+ def nNeighbors(self, nNeighbors):
+ self._nNeighbors = max(1, nNeighbors)
+ self._approxParameters["nNeighbors"] = self.nNeighbors
+
+ @property
+ def radialDirectionalWeights(self):
+ """Value of radialDirectionalWeights."""
+ return self._radialDirectionalWeights
+ @radialDirectionalWeights.setter
+ def radialDirectionalWeights(self, radialDirectionalWeights):
+ self._radialDirectionalWeights = radialDirectionalWeights
+ self._approxParameters["radialDirectionalWeights"] = (
+ self.radialDirectionalWeights)
+
def setupApprox(self) -> int:
"""Compute RB projection matrix."""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5)
self.computeSnapshots()
pMat = self.samplingEngine.samples.data
pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat
if self.trainedModel is None:
self.trainedModel = self.tModelType()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
- datadict = {"mu0": self.mu0, "projMat": pMatEff,
- "scaleFactor": self.scaleFactor,
+ datadict = {"mu0": self.mu0, "mus": copy(self.mus),
+ "projMat": pMatEff, "scaleFactor": self.scaleFactor,
"rescalingExp": self.HFEngine.rescalingExp}
self.trainedModel.data = self.initializeModelData(datadict)[0]
+ self.trainedModel.data.NN = NNI()
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(pMatEff)
+ self.trainedModel.data.mus = copy(self.mus)
if self.POD:
- self.trainedModel.data.matR = copy(self.samplingEngine.RPOD)
+ suppVals = self.samplingEngine.RPOD.T
else:
- self.trainedModel.data.matR = 1.
- self.trainedModel.data.mus = copy(self.mus)
+ suppVals = np.eye(len(self.mus))
+ self.trainedModel.data.NN.setupByInterpolation(self.mus, suppVals,
+ self.nNeighbors,
+ self.radialDirectionalWeights)
self.trainedModel.data.approxParameters = copy(self.approxParameters)
vbMng(self, "DEL", "Done setting up approximant.", 5)
return 0
diff --git a/rrompy/reduction_methods/standard/rational_interpolant.py b/rrompy/reduction_methods/standard/rational_interpolant.py
index 4f189ae..bccc6f7 100644
--- a/rrompy/reduction_methods/standard/rational_interpolant.py
+++ b/rrompy/reduction_methods/standard/rational_interpolant.py
@@ -1,694 +1,674 @@
# 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 .
#
from copy import deepcopy as copy
import numpy as np
from rrompy.reduction_methods.base import checkRobustTolerance
from .generic_standard_approximant import GenericStandardApproximant
from rrompy.utilities.poly_fitting.polynomial import (
polybases as ppb, polyfitname,
polyvander as pvP, polyvanderTotal as pvTP,
polyTimes, polyTimesTable, vanderInvTable,
PolynomialInterpolator as PI)
from rrompy.utilities.poly_fitting.heaviside import rational2heaviside
from rrompy.utilities.poly_fitting.radial_basis import (polybases as rbpb,
RadialBasisInterpolator as RBI)
-from rrompy.utilities.poly_fitting.moving_least_squares import (
- polybases as mlspb,
- MovingLeastSquaresInterpolator as MLSI)
from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, sampList
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical import customPInv, dot, potential
from rrompy.utilities.numerical.hash_derivative import nextDerivativeIndices
from rrompy.utilities.numerical.degree import (reduceDegreeN,
degreeTotalToFull, fullDegreeMaxMask,
totalDegreeMaxMask)
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPyWarning)
__all__ = ['RationalInterpolant']
class RationalInterpolant(GenericStandardApproximant):
"""
ROM rational interpolant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator;
- 'polybasis': type of polynomial basis for interpolation; defaults
to 'MONOMIAL';
- 'M': degree of rational interpolant numerator; defaults to
'AUTO', i.e. maximum allowed;
- 'N': degree of rational interpolant denominator; defaults to
'AUTO', i.e. maximum allowed;
- 'polydegreetype': type of polynomial degree; defaults to 'TOTAL';
- 'radialDirectionalWeights': radial basis weights for interpolant
numerator; defaults to 1;
- - 'nNearestNeighbor': mumber of nearest neighbors considered in
- numerator if polybasis allows; defaults to -1;
- 'interpRcond': tolerance for interpolation; defaults to None;
- 'robustTol': tolerance for robust rational denominator
management; defaults to 0;
- 'correctorForce': whether corrector should forcefully delete bad
poles; defaults to False;
- 'correctorTol': tolerance for corrector step; defaults to 0.,
i.e. no bad poles;
- 'correctorMaxIter': maximum number of corrector iterations;
defaults to 1.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults to
False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation;
- 'polybasis': type of polynomial basis for interpolation;
- 'M': degree of rational interpolant numerator;
- 'N': degree of rational interpolant denominator;
- 'polydegreetype': type of polynomial degree;
- 'radialDirectionalWeights': radial basis weights for interpolant
numerator;
- - 'nNearestNeighbor': mumber of nearest neighbors considered in
- numerator if polybasis allows;
- 'interpRcond': tolerance for interpolation via numpy.polyfit;
- 'robustTol': tolerance for robust rational denominator
management;
- 'correctorForce': whether corrector should forcefully delete bad
poles;
- 'correctorTol': tolerance for corrector step;
- 'correctorMaxIter': maximum number of corrector iterations.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
polybasis: type of polynomial basis for interpolation.
M: Numerator degree of approximant.
N: Denominator degree of approximant.
polydegreetype: Type of polynomial degree.
radialDirectionalWeights: Radial basis weights for interpolant
numerator.
- nNearestNeighbor: Number of nearest neighbors considered in numerator
- if polybasis allows.
interpRcond: Tolerance for interpolation via numpy.polyfit.
robustTol: Tolerance for robust rational denominator management.
correctorForce: Whether corrector should forcefully delete bad poles.
correctorTol: Tolerance for corrector step.
correctorMaxIter: Maximum number of corrector iterations.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
Q: Numpy 1D vector containing complex coefficients of approximant
denominator.
P: Numpy 2D vector whose columns are FE dofs of coefficients of
approximant numerator.
"""
def __init__(self, *args, **kwargs):
self._preInit()
self._addParametersToList(["polybasis", "M", "N", "polydegreetype",
- "radialDirectionalWeights",
- "nNearestNeighbor", "interpRcond",
+ "radialDirectionalWeights", "interpRcond",
"robustTol", "correctorForce",
"correctorTol", "correctorMaxIter"],
["MONOMIAL", "AUTO", "AUTO", "TOTAL", [1.],
- -1, -1, 0., False, 0., 1])
+ -1, 0., False, 0., 1])
super().__init__(*args, **kwargs)
self.catchInstability = 0
self._postInit()
@property
def tModelType(self):
from .trained_model.trained_model_rational import TrainedModelRational
return TrainedModelRational
@property
def polybasis(self):
"""Value of polybasis."""
return self._polybasis
@polybasis.setter
def polybasis(self, polybasis):
try:
polybasis = polybasis.upper().strip().replace(" ","")
- if polybasis not in ppb + rbpb + mlspb:
+ if polybasis not in ppb + rbpb:
raise RROMPyException("Prescribed polybasis not recognized.")
self._polybasis = polybasis
except:
RROMPyWarning(("Prescribed polybasis not recognized. Overriding "
"to 'MONOMIAL'."))
self._polybasis = "MONOMIAL"
self._approxParameters["polybasis"] = self.polybasis
@property
def polybasis0(self):
if "_" in self.polybasis:
return self.polybasis.split("_")[0]
return self.polybasis
@property
def interpRcond(self):
"""Value of interpRcond."""
return self._interpRcond
@interpRcond.setter
def interpRcond(self, interpRcond):
self._interpRcond = interpRcond
self._approxParameters["interpRcond"] = self.interpRcond
@property
def radialDirectionalWeights(self):
"""Value of radialDirectionalWeights."""
return self._radialDirectionalWeights
@radialDirectionalWeights.setter
def radialDirectionalWeights(self, radialDirectionalWeights):
if not hasattr(radialDirectionalWeights, "__len__"):
radialDirectionalWeights = [radialDirectionalWeights]
self._radialDirectionalWeights = radialDirectionalWeights
self._approxParameters["radialDirectionalWeights"] = (
self.radialDirectionalWeights)
- @property
- def nNearestNeighbor(self):
- """Value of nNearestNeighbor."""
- return self._nNearestNeighbor
- @nNearestNeighbor.setter
- def nNearestNeighbor(self, nNearestNeighbor):
- self._nNearestNeighbor = nNearestNeighbor
- self._approxParameters["nNearestNeighbor"] = self.nNearestNeighbor
-
@property
def M(self):
"""Value of M."""
return self._M
@M.setter
def M(self, M):
if isinstance(M, str):
M = M.strip().replace(" ","")
if "-" not in M: M = M + "-0"
self._M_isauto, self._M_shift = True, int(M.split("-")[-1])
M = 0
if M < 0: raise RROMPyException("M must be non-negative.")
self._M = M
self._approxParameters["M"] = self.M
def _setMAuto(self):
self.M = max(0, reduceDegreeN(self.S, self.S, self.npar,
self.polydegreetype) - self._M_shift)
vbMng(self, "MAIN", "Automatically setting M to {}.".format(self.M),
25)
@property
def N(self):
"""Value of N."""
return self._N
@N.setter
def N(self, N):
if isinstance(N, str):
N = N.strip().replace(" ","")
if "-" not in N: N = N + "-0"
self._N_isauto, self._N_shift = True, int(N.split("-")[-1])
N = 0
if N < 0: raise RROMPyException("N must be non-negative.")
self._N = N
self._approxParameters["N"] = self.N
def _setNAuto(self):
self.N = max(0, reduceDegreeN(self.S, self.S, self.npar,
self.polydegreetype) - self._N_shift)
vbMng(self, "MAIN", "Automatically setting N to {}.".format(self.N),
25)
@property
def polydegreetype(self):
"""Value of polydegreetype."""
return self._polydegreetype
@polydegreetype.setter
def polydegreetype(self, polydegreetype):
try:
polydegreetype = polydegreetype.upper().strip().replace(" ","")
if polydegreetype not in ["TOTAL", "FULL"]:
raise RROMPyException(("Prescribed polydegreetype not "
"recognized."))
self._polydegreetype = polydegreetype
except:
RROMPyWarning(("Prescribed polydegreetype not recognized. "
"Overriding to 'TOTAL'."))
self._polydegreetype = "TOTAL"
self._approxParameters["polydegreetype"] = self.polydegreetype
@property
def robustTol(self):
"""Value of tolerance for robust rational denominator management."""
return self._robustTol
@robustTol.setter
def robustTol(self, robustTol):
if robustTol < 0.:
RROMPyWarning(("Overriding prescribed negative robustness "
"tolerance to 0."))
robustTol = 0.
self._robustTol = robustTol
self._approxParameters["robustTol"] = self.robustTol
@property
def correctorForce(self):
"""Value of correctorForce."""
return self._correctorForce
@correctorForce.setter
def correctorForce(self, correctorForce):
self._correctorForce = correctorForce
self._approxParameters["correctorForce"] = self.correctorForce
@property
def correctorTol(self):
"""Value of correctorTol."""
return self._correctorTol
@correctorTol.setter
def correctorTol(self, correctorTol):
if correctorTol < 0. or (correctorTol > 0. and self.npar > 1):
RROMPyWarning(("Overriding prescribed corrector tolerance "
"to 0."))
correctorTol = 0.
self._correctorTol = correctorTol
self._approxParameters["correctorTol"] = self.correctorTol
@property
def correctorMaxIter(self):
"""Value of correctorMaxIter."""
return self._correctorMaxIter
@correctorMaxIter.setter
def correctorMaxIter(self, correctorMaxIter):
if correctorMaxIter < 1 or (correctorMaxIter > 1 and self.npar > 1):
RROMPyWarning(("Overriding prescribed max number of corrector "
"iterations to 1."))
correctorMaxIter = 1
self._correctorMaxIter = correctorMaxIter
self._approxParameters["correctorMaxIter"] = self.correctorMaxIter
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
self._musUniqueCN = None
self._derIdxs = None
self._reorder = None
def _setupInterpolationIndices(self):
"""Setup parameters for polyvander."""
if self._musUniqueCN is None or len(self._reorder) != len(self.mus):
self._musUniqueCN, musIdxsTo, musIdxs, musCount = (
self.trainedModel.centerNormalize(self.mus).unique(
return_index = True, return_inverse = True,
return_counts = True))
self._musUnique = self.mus[musIdxsTo]
self._derIdxs = [None] * len(self._musUniqueCN)
self._reorder = np.empty(len(musIdxs), dtype = int)
filled = 0
for j, cnt in enumerate(musCount):
self._derIdxs[j] = nextDerivativeIndices([], self.mus.shape[1],
cnt)
jIdx = np.nonzero(musIdxs == j)[0]
self._reorder[jIdx] = np.arange(filled, filled + cnt)
filled += cnt
def _setupDenominator(self):
"""Compute rational denominator."""
RROMPyAssert(self._mode, message = "Cannot setup denominator.")
vbMng(self, "INIT", "Starting computation of denominator.", 7)
if hasattr(self, "_N_isauto"):
self._setNAuto()
else:
N = reduceDegreeN(self.N, self.S, self.npar, self.polydegreetype)
if N < self.N:
RROMPyWarning(("N too large compared to S. Reducing N by "
"{}").format(self.N - N))
self.N = N
while self.N > 0:
invD, fitinv = self._computeInterpolantInverseBlocks()
idxSamplesEff = list(range(self.S))
if self.POD:
ev, eV = self.findeveVGQR(
self.samplingEngine.RPOD[:, idxSamplesEff], invD)
else:
ev, eV = self.findeveVGExplicit(
self.samplingEngine.samples(idxSamplesEff), invD)
nevBad = checkRobustTolerance(ev, self.robustTol)
if nevBad <= 1: break
if self.catchInstability > 0:
raise RROMPyException(("Instability in denominator "
"computation: eigenproblem is poorly "
"conditioned."),
self.catchInstability == 1)
vbMng(self, "MAIN", ("Smallest {} eigenvalues below tolerance. "
"Reducing N by 1.").format(nevBad), 10)
self.N = self.N - 1
if self.N <= 0:
self.N = 0
eV = np.ones((1, 1))
q = PI()
q.npar = self.npar
q.polybasis = self.polybasis0
if self.polydegreetype == "TOTAL":
q.coeffs = degreeTotalToFull(tuple([self.N + 1] * self.npar),
self.npar, eV[:, 0])
else:
q.coeffs = eV[:, 0].reshape([self.N + 1] * self.npar)
vbMng(self, "DEL", "Done computing denominator.", 7)
return q, fitinv
def _setupNumerator(self):
"""Compute rational numerator."""
RROMPyAssert(self._mode, message = "Cannot setup numerator.")
vbMng(self, "INIT", "Starting computation of numerator.", 7)
self._setupInterpolationIndices()
Qevaldiag = polyTimesTable(self.trainedModel.data.Q, self._musUniqueCN,
self._reorder, self._derIdxs,
self.scaleFactorRel)
if self.POD:
Qevaldiag = Qevaldiag.dot(self.samplingEngine.RPOD.T)
if hasattr(self, "_M_isauto"):
self._setMAuto()
M = self.M
else:
M = reduceDegreeN(self.M, self.S, self.npar, self.polydegreetype)
if M < self.M:
RROMPyWarning(("M too large compared to S. Reducing M by "
"{}").format(self.M - M))
self.M = M
while self.M >= 0:
pParRest = [self.verbosity >= 5, self.polydegreetype == "TOTAL",
{"derIdxs": self._derIdxs, "reorder": self._reorder,
"scl": self.scaleFactorRel}]
if self.polybasis in ppb:
p = PI()
else:
pParRest = [self.radialDirectionalWeights] + pParRest
- pParRest[-1]["nNearestNeighbor"] = self.nNearestNeighbor
- p = RBI() if self.polybasis in rbpb else MLSI()
+ p = RBI()
if self.polybasis in ppb + rbpb:
pParRest += [{"rcond": self.interpRcond}]
wellCond, msg = p.setupByInterpolation(self._musUniqueCN,
Qevaldiag, self.M,
self.polybasis, *pParRest)
vbMng(self, "MAIN", msg, 5)
if wellCond: break
if self.catchInstability > 0:
raise RROMPyException(("Instability in numerator computation: "
"polyfit is poorly conditioned."),
self.catchInstability == 1)
vbMng(self, "MAIN", ("Polyfit is poorly conditioned. Reducing M "
"by 1."), 10)
self.M = self.M - 1
if self.M < 0:
raise RROMPyException(("Instability in computation of numerator. "
"Aborting."))
self.M = M
vbMng(self, "DEL", "Done computing numerator.", 7)
return p
def setupApprox(self) -> int:
"""Compute rational interpolant."""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5)
self.computeSnapshots()
pMat = self.samplingEngine.samples.data
pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat
if self.trainedModel is None:
self.trainedModel = self.tModelType()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
- datadict = {"mu0": self.mu0, "projMat": pMatEff,
- "scaleFactor": self.scaleFactor,
+ datadict = {"mu0": self.mu0, "mus": copy(self.mus),
+ "projMat": pMatEff, "scaleFactor": self.scaleFactor,
"rescalingExp": self.HFEngine.rescalingExp}
self.trainedModel.data = self.initializeModelData(datadict)[0]
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(pMatEff)
- self.trainedModel.data.mus = copy(self.mus)
+ self.trainedModel.data.mus = copy(self.mus)
self._iterCorrector()
self.trainedModel.data.approxParameters = copy(self.approxParameters)
vbMng(self, "DEL", "Done setting up approximant.", 5)
return 0
def _iterCorrector(self):
if self.correctorTol > 0. and (self.correctorMaxIter > 1
or self.correctorForce):
vbMng(self, "INIT", "Starting correction iterations.", 5)
self._Qhat = PI()
self._Qhat.npar = self.npar
self._Qhat.polybasis = "MONOMIAL"
self._Qhat.coeffs = np.ones(1, dtype = np.complex)
if self.POD:
self._RPODOld = copy(self.samplingEngine.RPOD)
else:
self._samplesOld = copy(self.samplingEngine.samples)
else: vbMng(self, "INIT", "Starting approximant finalization.", 5)
for j in range(self.correctorMaxIter):
if self.N > 0 or (hasattr(self, "_N_isauto")
and self.S > self.npar):
Q = self._setupDenominator()[0]
else:
Q = PI()
Q.coeffs = np.ones((1,) * self.npar, dtype = np.complex)
Q.npar = self.npar
Q.polybasis = self.polybasis
self.N = 0
if j == 0: _N0 = self.N
self.trainedModel.data.Q = Q
self.trainedModel.data.P = self._setupNumerator()
self._applyCorrector(j)
if self.N <= 0: break
self.N = _N0
if self.correctorTol <= 0. or (self.correctorMaxIter <= 1
and not self.correctorForce):
vbMng(self, "DEL", "Terminated approximant finalization.", 5)
return
if self.POD:
self.samplingEngine.RPOD = self._RPODOld
del self._RPODOld
else:
self.samplingEngine.samples = self._samplesOld
del self._samplesOld
if self.correctorForce:
QOld, QOldBasis = [1.], "MONOMIAL"
else:
QOld, QOldBasis = Q.coeffs, self.polybasis
Q = polyTimes(self._Qhat.coeffs, QOld, Pbasis = self._Qhat.polybasis,
Qbasis = QOldBasis, Rbasis = self.polybasis)
del self._Qhat
gamma = np.linalg.norm(Q)
self.trainedModel.data.Q.coeffs = np.pad(Q, (0, self.N - len(Q) + 1),
"constant") / gamma
if self.correctorForce:
self.trainedModel.data.P = self._setupNumerator()
else:
self.trainedModel.data.P.coeffs /= gamma
vbMng(self, "DEL", "Terminated correction iterations.", 5)
def _applyCorrector(self, j:int):
if self.correctorTol <= 0. or (j >= self.correctorMaxIter - 1
and not self.correctorForce):
self.N = 0
return
cfs, pls, _ = rational2heaviside(self.trainedModel.data.P,
self.trainedModel.data.Q)
cfs = cfs[: self.N]
if self.POD:
resEff = np.linalg.norm(cfs, axis = 1)
else:
resEff = self.HFEngine.norm(self.samplingEngine.samples.dot(cfs.T),
is_state = self.approx_state)
goodPole = np.logical_not(np.isinf(pls))
potentialGood = (potential(pls[goodPole], self.sampler.normalFoci())
/ self.sampler.groundPotential())
potentialGood[potentialGood < 1.] = 1.
resEff[goodPole] /= potentialGood
resEff /= np.max(resEff)
idxKeep = np.where(resEff >= self.correctorTol)[0]
vbMng(self, "MAIN",
("Correction iteration no. {}: {} out of {} residuals satisfy "
"tolerance.").format(j + 1, len(idxKeep), self.N), 10)
self.N -= len(idxKeep)
if self.N <= 0 and not self.correctorForce: return
for i in idxKeep:
self._Qhat.coeffs = polyTimes(self._Qhat.coeffs, [- pls[i], 1.],
Pbasis = self._Qhat.polybasis,
Rbasis = self._Qhat.polybasis)
self._Qhat.coeffs /= np.linalg.norm(self._Qhat.coeffs)
if self.N <= 0: return
vbMng(self, "MAIN",
("Removing poles at (normalized positions): "
"{}.").format(pls[resEff < self.correctorTol]), 65)
That = polyTimesTable(self._Qhat, self._musUniqueCN,
self._reorder, self._derIdxs,
self.scaleFactorRel).T
if self.POD:
self.samplingEngine.RPOD = self._RPODOld.dot(That)
else:
self.samplingEngine.samples = self._samplesOld.dot(That)
def _computeInterpolantInverseBlocks(self) -> Tuple[List[Np2D], Np2D]:
"""
Compute inverse factors for minimal interpolant target functional.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
self._setupInterpolationIndices()
TEGen = pvTP if self.polydegreetype == "TOTAL" else pvP
TEGenPar = [self.polybasis0, self._derIdxs, self._reorder,
self.scaleFactorRel]
if hasattr(self, "_M_isauto"): self._setMAuto()
E = max(self.M, self.N)
while E >= 0:
if self.polydegreetype == "TOTAL":
Eeff = E
idxsB = totalDegreeMaxMask(E, self.npar)
else: #if self.polydegreetype == "FULL":
Eeff = [E] * self.npar
idxsB = fullDegreeMaxMask(E, self.npar)
TE = TEGen(self._musUniqueCN, Eeff, *TEGenPar)
fitOut = customPInv(TE, rcond = self.interpRcond, full = True)
vbMng(self, "MAIN",
("Fitting {} samples with degree {} through {}... "
"Conditioning of pseudoinverse system: {:.4e}.").format(
TE.shape[0], E,
polyfitname(self.polybasis0),
fitOut[1][1][0] / fitOut[1][1][-1]),
5)
if fitOut[1][0] == TE.shape[1]:
fitinv = fitOut[0][idxsB, :]
break
if self.catchInstability > 0:
raise RROMPyException(("Instability in denominator "
"computation: polyfit is poorly "
"conditioned."),
self.catchInstability == 1)
EeqN = E == self.N
vbMng(self, "MAIN", ("Polyfit is poorly conditioned. Reducing E {}"
"by 1.").format("and N " * EeqN), 10)
if EeqN: self.N = self.N - 1
E -= 1
if self.N < 0:
raise RROMPyException(("Instability in computation of "
"denominator. Aborting."))
invD = vanderInvTable(fitinv, idxsB, self._reorder, self._derIdxs)
if self.N == E:
TN = TE
else:
if self.polydegreetype == "TOTAL":
Neff = self.N
idxsB = totalDegreeMaxMask(self.N, self.npar)
else: #if self.polydegreetype == "FULL":
Neff = [self.N] * self.npar
idxsB = fullDegreeMaxMask(self.N, self.npar)
TN = TEGen(self._musUniqueCN, Neff, *TEGenPar)
for k in range(len(invD)): invD[k] = dot(invD[k], TN)
return invD, fitinv
def findeveVGExplicit(self, sampleE:sampList,
invD:List[Np2D]) -> Tuple[Np1D, Np2D]:
"""
Compute explicitly eigenvalues and eigenvectors of rational denominator
matrix.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
nEnd = invD[0].shape[1]
eWidth = len(invD)
vbMng(self, "INIT", "Building gramian matrix.", 10)
gramian = self.HFEngine.innerProduct(sampleE, sampleE,
is_state = self.approx_state)
G = np.zeros((nEnd, nEnd), dtype = np.complex)
for k in range(eWidth):
G += dot(dot(gramian, invD[k]).T, invD[k].conj()).T
vbMng(self, "DEL", "Done building gramian.", 10)
vbMng(self, "INIT", "Solving eigenvalue problem for gramian matrix.",
7)
try:
ev, eV = np.linalg.eigh(G)
except np.linalg.LinAlgError as e:
raise RROMPyException(e)
vbMng(self, "MAIN",
("Solved eigenvalue problem of size {} with condition number "
"{:.4e}.").format(nEnd, ev[-1] / ev[0]), 5)
vbMng(self, "DEL", "Done solving eigenvalue problem.", 7)
return ev, eV
def findeveVGQR(self, RPODE:Np2D, invD:List[Np2D]) -> Tuple[Np1D, Np2D]:
"""
Compute eigenvalues and eigenvectors of rational denominator matrix
through SVD of R factor.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
nEnd = invD[0].shape[1]
S = RPODE.shape[0]
eWidth = len(invD)
vbMng(self, "INIT", "Building half-gramian matrix stack.", 10)
Rstack = np.zeros((S * eWidth, nEnd), dtype = np.complex)
for k in range(eWidth):
Rstack[k * S : (k + 1) * S, :] = dot(RPODE, invD[k])
vbMng(self, "DEL", "Done building half-gramian.", 10)
vbMng(self, "INIT", "Solving svd for square root of gramian matrix.",
7)
try:
_, s, eV = np.linalg.svd(Rstack, full_matrices = False)
except np.linalg.LinAlgError as e:
raise RROMPyException(e)
ev = s[::-1]
eV = eV[::-1, :].T.conj()
vbMng(self, "MAIN",
("Solved svd problem of size {} x {} with condition number "
"{:.4e}.").format(*Rstack.shape, s[0] / s[-1]), 5)
vbMng(self, "DEL", "Done solving svd.", 7)
return ev, eV
def getResidues(self, *args, **kwargs) -> Np1D:
"""
Obtain approximant residues.
Returns:
Matrix with residues as columns.
"""
return self.trainedModel.getResidues(*args, **kwargs)
diff --git a/rrompy/reduction_methods/standard/rational_moving_least_squares.py b/rrompy/reduction_methods/standard/rational_moving_least_squares.py
deleted file mode 100644
index d965c4a..0000000
--- a/rrompy/reduction_methods/standard/rational_moving_least_squares.py
+++ /dev/null
@@ -1,337 +0,0 @@
-# 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 .
-#
-
-from copy import deepcopy as copy
-import numpy as np
-from .rational_interpolant import RationalInterpolant
-from rrompy.utilities.poly_fitting.polynomial import (polybases as ppb,
- polyvander as pvP,
- polyvanderTotal as pvTP)
-from rrompy.utilities.base.types import Np2D
-from rrompy.utilities.base import verbosityManager as vbMng
-from rrompy.utilities.numerical import dot
-from rrompy.utilities.numerical.degree import (fullDegreeMaxMask,
- totalDegreeMaxMask)
-from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
- RROMPyWarning)
-
-__all__ = ['RationalMovingLeastSquares']
-
-class RationalMovingLeastSquares(RationalInterpolant):
- """
- ROM rational moving LS interpolant computation for parametric problems.
-
- Args:
- HFEngine: HF problem solver.
- mu0(optional): Default parameter. Defaults to 0.
- approxParameters(optional): Dictionary containing values for main
- parameters of approximant. Recognized keys are:
- - 'POD': whether to compute POD of snapshots; defaults to True;
- - 'scaleFactorDer': scaling factors for derivative computation;
- defaults to 'AUTO';
- - 'S': total number of samples current approximant relies upon;
- - 'sampler': sample point generator;
- - 'polybasis': type of polynomial basis for interpolation; defaults
- to 'MONOMIAL';
- - 'M': degree of rational interpolant numerator; defaults to
- 'AUTO', i.e. maximum allowed;
- - 'N': degree of rational interpolant denominator; defaults to
- 'AUTO', i.e. maximum allowed;
- - 'polydegreetype': type of polynomial degree; defaults to 'TOTAL';
- - 'radialBasis': numerator radial basis type; defaults to
- 'GAUSSIAN';
- - 'radialDirectionalWeights': radial basis weights for interpolant
- numerator; defaults to 1;
- - 'nNearestNeighbor': number of nearest neighbors considered in
- numerator if radialBasis allows; defaults to -1;
- - 'radialBasisDen': denominator radial basis type; defaults to
- 'GAUSSIAN';
- - 'radialDirectionalWeightsDen': radial basis weights for
- interpolant denominator; defaults to 1;
- - 'nNearestNeighborDen': number of nearest neighbors considered in
- denominator if radialBasisDen allows; defaults to -1;
- - 'interpRcond': tolerance for interpolation; defaults to None;
- - 'robustTol': tolerance for robust rational denominator
- management; defaults to 0.
- Defaults to empty dict.
- approx_state(optional): Whether to approximate state. Defaults to
- False.
- verbosity(optional): Verbosity level. Defaults to 10.
-
- Attributes:
- HFEngine: HF problem solver.
- mu0: Default parameter.
- mus: Array of snapshot parameters.
- approxParameters: Dictionary containing values for main parameters of
- approximant. Recognized keys are in parameterList.
- parameterListSoft: Recognized keys of soft approximant parameters:
- - 'POD': whether to compute POD of snapshots;
- - 'scaleFactorDer': scaling factors for derivative computation;
- - 'polybasis': type of polynomial basis for interpolation;
- - 'M': degree of rational interpolant numerator;
- - 'N': degree of rational interpolant denominator;
- - 'polydegreetype': type of polynomial degree;
- - 'radialBasis': numerator radial basis type;
- - 'radialDirectionalWeights': radial basis weights for interpolant
- numerator;
- - 'nNearestNeighbor': number of nearest neighbors considered in
- numerator if radialBasis allows;
- - 'radialBasisDen': denominator radial basis type;
- - 'radialDirectionalWeightsDen': radial basis weights for
- interpolant denominator;
- - 'nNearestNeighborDen': number of nearest neighbors considered in
- denominator if radialBasisDen allows;
- - 'interpRcond': tolerance for interpolation via numpy.polyfit;
- - 'robustTol': tolerance for robust rational denominator
- management.
- parameterListCritical: Recognized keys of critical approximant
- parameters:
- - 'S': total number of samples current approximant relies upon;
- - 'sampler': sample point generator.
- approx_state: Whether to approximate state.
- verbosity: Verbosity level.
- POD: Whether to compute POD of snapshots.
- scaleFactorDer: Scaling factors for derivative computation.
- S: Number of solution snapshots over which current approximant is
- based upon.
- sampler: Sample point generator.
- polybasis: type of polynomial basis for interpolation.
- M: Numerator degree of approximant.
- N: Denominator degree of approximant.
- polydegreetype: Type of polynomial degree.
- radialBasis: Numerator radial basis type.
- radialDirectionalWeights: Radial basis weights for interpolant
- numerator.
- nNearestNeighbor: Number of nearest neighbors considered in numerator
- if radialBasis allows.
- radialBasisDen: Denominator radial basis type.
- radialDirectionalWeightsDen: Radial basis weights for interpolant
- denominator.
- nNearestNeighborDen: Number of nearest neighbors considered in
- denominator if radialBasisDen allows.
- interpRcond: Tolerance for interpolation via numpy.polyfit.
- robustTol: Tolerance for robust rational denominator management.
- muBounds: list of bounds for parameter values.
- samplingEngine: Sampling engine.
- uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
- sampleList.
- lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
- solution(s) as parameterList.
- uApproxReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApprox as sampleList.
- lastSolvedApproxReduced: Parameter(s) corresponding to last computed
- reduced approximate solution(s) as parameterList.
- uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
- sampleList.
- lastSolvedApprox: Parameter(s) corresponding to last computed
- approximate solution(s) as parameterList.
- Q: Numpy 1D vector containing complex coefficients of approximant
- denominator.
- P: Numpy 2D vector whose columns are FE dofs of coefficients of
- approximant numerator.
- """
-
- def __init__(self, *args, **kwargs):
- self._preInit()
- self._addParametersToList(["radialBasis", "radialBasisDen",
- "radialDirectionalWeightsDen",
- "nNearestNeighborDen"],
- ["GAUSSIAN", "GAUSSIAN", 1., -1],
- toBeExcluded = ["correctorForce",
- "correctorTol",
- "correctorMaxIter"])
- super().__init__(*args, **kwargs)
- self.catchInstability = 0
- self._postInit()
-
- @property
- def correctorForce(self):
- """Value of correctorForce."""
- return False
- @correctorForce.setter
- def correctorForce(self, correctorForce):
- RROMPyWarning(("correctorForce is used just to simplify inheritance, "
- "and its value cannot be changed from False."))
-
- @property
- def correctorTol(self):
- """Value of correctorTol."""
- return 0.
- @correctorTol.setter
- def correctorTol(self, correctorTol):
- RROMPyWarning(("correctorTol is used just to simplify inheritance, "
- "and its value cannot be changed from 0."))
-
- @property
- def correctorMaxIter(self):
- """Value of correctorMaxIter."""
- return 1
- @correctorMaxIter.setter
- def correctorMaxIter(self, correctorMaxIter):
- RROMPyWarning(("correctorMaxIter is used just to simplify "
- "inheritance, and its value cannot be changed from 1."))
-
- @property
- def tModelType(self):
- from .trained_model.trained_model_rational_mls import (
- TrainedModelRationalMLS)
- return TrainedModelRationalMLS
-
- @property
- def polybasis(self):
- """Value of polybasis."""
- return self._polybasis
- @polybasis.setter
- def polybasis(self, polybasis):
- try:
- polybasis = polybasis.upper().strip().replace(" ","")
- if polybasis not in ppb:
- raise RROMPyException("Prescribed polybasis not recognized.")
- self._polybasis = polybasis
- except:
- RROMPyWarning(("Prescribed polybasis not recognized. Overriding "
- "to 'MONOMIAL'."))
- self._polybasis = "MONOMIAL"
- self._approxParameters["polybasis"] = self.polybasis
-
- @property
- def radialBasis(self):
- """Value of radialBasis."""
- return self._radialBasis
- @radialBasis.setter
- def radialBasis(self, radialBasis):
- self._radialBasis = radialBasis
- self._approxParameters["radialBasis"] = self.radialBasis
-
- @property
- def radialBasisDen(self):
- """Value of radialBasisDen."""
- return self._radialBasisDen
- @radialBasisDen.setter
- def radialBasisDen(self, radialBasisDen):
- self._radialBasisDen = radialBasisDen
- self._approxParameters["radialBasisDen"] = self.radialBasisDen
-
- @property
- def radialDirectionalWeightsDen(self):
- """Value of radialDirectionalWeightsDen."""
- return self._radialDirectionalWeightsDen
- @radialDirectionalWeightsDen.setter
- def radialDirectionalWeightsDen(self, radialDirectionalWeightsDen):
- self._radialDirectionalWeightsDen = radialDirectionalWeightsDen
- self._approxParameters["radialDirectionalWeightsDen"] = (
- self.radialDirectionalWeightsDen)
-
- @property
- def nNearestNeighborDen(self):
- """Value of nNearestNeighborDen."""
- return self._nNearestNeighborDen
- @nNearestNeighborDen.setter
- def nNearestNeighborDen(self, nNearestNeighborDen):
- self._nNearestNeighborDen = nNearestNeighborDen
- self._approxParameters["nNearestNeighborDen"] = (
- self.nNearestNeighborDen)
-
- def _setupDenominator(self) -> Np2D:
- """Compute rational denominator."""
- RROMPyAssert(self._mode, message = "Cannot setup denominator.")
- vbMng(self, "INIT",
- "Starting computation of denominator-related blocks.", 7)
- self._setupInterpolationIndices()
- pPar = [self._musUniqueCN, self.N, self.polybasis0, self._derIdxs,
- self._reorder, self.scaleFactorRel]
- if self.polydegreetype == "TOTAL":
- TN = pvTP(*pPar)
- else: #if self.polydegreetype == "FULL":
- pPar[1] = [pPar[1]] * self.npar
- TN = pvP(*pPar)
- TNTen = np.zeros((self.S, self.S, TN.shape[1]), dtype = TN.dtype)
- TNTen[np.arange(self.S), np.arange(self.S)] = TN
- if self.POD: TNTen = dot(self.samplingEngine.RPOD, TNTen)
- vbMng(self, "DEL", "Done computing denominator-related blocks.", 7)
- return TN, TNTen
-
- def _setupNumerator(self) -> Np2D:
- """Compute rational numerator."""
- RROMPyAssert(self._mode, message = "Cannot setup numerator.")
- vbMng(self, "INIT",
- "Starting computation of denominator-related blocks.", 7)
- self._setupInterpolationIndices()
- pPar = [self._musUniqueCN, self.M, self.polybasis0, self._derIdxs,
- self._reorder, self.scaleFactorRel]
- if self.polydegreetype == "TOTAL":
- TM = pvTP(*pPar)
- else: #if self.polydegreetype == "FULL":
- pPar[1] = [pPar[1]] * self.npar
- TM = pvP(*pPar)
- vbMng(self, "DEL", "Done computing denominator-related blocks.", 7)
- return TM
-
- def setupApprox(self) -> int:
- """Compute rational interpolant."""
- if self.checkComputedApprox(): return -1
- RROMPyAssert(self._mode, message = "Cannot setup approximant.")
- vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5)
- self.computeSnapshots()
- pMat = self.samplingEngine.samples.data
- pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat
- if self.trainedModel is None:
- self.trainedModel = self.tModelType()
- self.trainedModel.verbosity = self.verbosity
- self.trainedModel.timestamp = self.timestamp
- datadict = {"mu0": self.mu0, "projMat": pMatEff,
- "scaleFactor": self.scaleFactor,
- "rescalingExp": self.HFEngine.rescalingExp}
- data = self.initializeModelData(datadict)[0]
- data.POD = self.POD
- data.polybasis = self.polybasis
- data.polydegreetype = self.polydegreetype
- data.radialBasis = self.radialBasis
- data.radialWeights = self.radialDirectionalWeights
- data.nNearestNeighbor = self.nNearestNeighbor
- data.radialBasisDen = self.radialBasisDen
- data.radialWeightsDen = self.radialDirectionalWeightsDen
- data.nNearestNeighborDen = self.nNearestNeighborDen
- data.interpRcond = self.interpRcond
- self.trainedModel.data = data
- else:
- self.trainedModel = self.trainedModel
- self.trainedModel.data.projMat = copy(pMatEff)
- if not self.POD:
- self.trainedModel.data.gramian = self.HFEngine.innerProduct(
- self.samplingEngine.samples,
- self.samplingEngine.samples,
- is_state = self.approx_state)
- self.trainedModel.data.mus = copy(self.mus)
- self.trainedModel.data.M = self.M
- self.trainedModel.data.N = self.N
- QVan, self.trainedModel.data.QBlocks = self._setupDenominator()
- self.trainedModel.data.PVan = self._setupNumerator()
- if self.polydegreetype == "TOTAL":
- degreeMaxMask = totalDegreeMaxMask
- else: #if self.polydegreetype == "FULL":
- degreeMaxMask = fullDegreeMaxMask
- if self.N > self.M:
- self.trainedModel.data.QVan = QVan
- self.trainedModel.data.domQIdxs = degreeMaxMask(self.N, self.npar)
- else:
- self.trainedModel.data.domQIdxs = degreeMaxMask(self.M, self.npar)
- self.trainedModel.data.approxParameters = copy(self.approxParameters)
- vbMng(self, "DEL", "Done setting up approximant.", 5)
- return 0
-
diff --git a/rrompy/reduction_methods/standard/rational_pade.py b/rrompy/reduction_methods/standard/rational_pade.py
index 3260f7f..6b48d90 100644
--- a/rrompy/reduction_methods/standard/rational_pade.py
+++ b/rrompy/reduction_methods/standard/rational_pade.py
@@ -1,317 +1,307 @@
# 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 .
#
-from copy import deepcopy as copy
import numpy as np
from rrompy.reduction_methods.base import checkRobustTolerance
from .rational_interpolant import RationalInterpolant
from rrompy.utilities.poly_fitting.polynomial import (
polybases as ppb, polyfitname,
polyvander as pvP, polyvanderTotal as pvTP,
polyTimesTable, vanderInvTable,
PolynomialInterpolator as PI)
from rrompy.utilities.poly_fitting.radial_basis import (polybases as rbpb,
RadialBasisInterpolator as RBI)
-from rrompy.utilities.poly_fitting.moving_least_squares import (
- MovingLeastSquaresInterpolator as MLSI)
from rrompy.utilities.base.types import Np2D, Tuple, List
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical import customPInv, dot
from rrompy.utilities.numerical.degree import (fullDegreeN, totalDegreeN,
reduceDegreeN, degreeTotalToFull,
fullDegreeMaxMask, totalDegreeMaxMask)
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPyWarning)
__all__ = ['RationalPade']
class RationalPade(RationalInterpolant):
"""
ROM rational Pade' computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator;
- 'polybasis': type of polynomial basis for interpolation; defaults
to 'MONOMIAL';
- 'M': degree of rational interpolant numerator; defaults to
'AUTO', i.e. maximum allowed;
- 'N': degree of rational interpolant denominator; defaults to
'AUTO', i.e. maximum allowed;
- 'polydegreetype': type of polynomial degree; defaults to 'TOTAL';
- 'radialDirectionalWeights': radial basis weights for interpolant
numerator; defaults to 1;
- - 'nNearestNeighbor': mumber of nearest neighbors considered in
- numerator if polybasis allows; defaults to -1;
- 'interpRcond': tolerance for interpolation; defaults to None;
- 'robustTol': tolerance for robust rational denominator
management; defaults to 0;
- 'correctorForce': whether corrector should forcefully delete bad
poles; defaults to False;
- 'correctorTol': tolerance for corrector step; defaults to 0.,
i.e. no bad poles;
- 'correctorMaxIter': maximum number of corrector iterations;
defaults to 1.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults to
False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation;
- 'polybasis': type of polynomial basis for interpolation;
- 'M': degree of rational interpolant numerator;
- 'N': degree of rational interpolant denominator;
- 'polydegreetype': type of polynomial degree;
- 'radialDirectionalWeights': radial basis weights for interpolant
numerator;
- - 'nNearestNeighbor': mumber of nearest neighbors considered in
- numerator if polybasis allows;
- 'interpRcond': tolerance for interpolation via numpy.polyfit;
- 'robustTol': tolerance for robust rational denominator
management;
- 'correctorForce': whether corrector should forcefully delete bad
poles;
- 'correctorTol': tolerance for corrector step;
- 'correctorMaxIter': maximum number of corrector iterations.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
polybasis: type of polynomial basis for interpolation.
M: Numerator degree of approximant.
N: Denominator degree of approximant.
polydegreetype: Type of polynomial degree.
radialDirectionalWeights: Radial basis weights for interpolant
numerator.
- nNearestNeighbor: Number of nearest neighbors considered in numerator
- if polybasis allows.
interpRcond: Tolerance for interpolation via numpy.polyfit.
robustTol: Tolerance for robust rational denominator management.
correctorForce: Whether corrector should forcefully delete bad poles.
correctorTol: Tolerance for corrector step.
correctorMaxIter: Maximum number of corrector iterations.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
Q: Numpy 1D vector containing complex coefficients of approximant
denominator.
P: Numpy 2D vector whose columns are FE dofs of coefficients of
approximant numerator.
"""
def _setupInterpolationIndices(self):
"""Setup parameters for polyvander."""
super()._setupInterpolationIndices()
if len(self._musUniqueCN) > 1:
raise RROMPyException(("Cannot apply centered-like method with "
"more than one distinct sample point."))
def _setupDenominator(self):
"""Compute rational denominator."""
RROMPyAssert(self._mode, message = "Cannot setup denominator.")
vbMng(self, "INIT", "Starting computation of denominator.", 7)
cfun = totalDegreeN if self.polydegreetype == "TOTAL" else fullDegreeN
if hasattr(self, "_N_isauto"):
self._setNAuto()
else:
N = reduceDegreeN(self.N, self.S, self.npar, self.polydegreetype)
if N < self.N:
RROMPyWarning(("N too large compared to S. Reducing N by "
"{}").format(self.N - N))
self.N = N
while self.N > 0:
invD, fitinv = self._computeInterpolantInverseBlocks()
Seff = cfun(self.N, self.npar)
idxSamplesEff = list(range(self.S - Seff, self.S))
if self.POD:
ev, eV = self.findeveVGQR(
self.samplingEngine.RPOD[:, idxSamplesEff], invD)
else:
ev, eV = self.findeveVGExplicit(
self.samplingEngine.samples(idxSamplesEff), invD)
nevBad = checkRobustTolerance(ev, self.robustTol)
if nevBad <= 1: break
if self.catchInstability > 0:
raise RROMPyException(("Instability in denominator "
"computation: eigenproblem is poorly "
"conditioned."),
self.catchInstability == 1)
RROMPyWarning(("Smallest {} eigenvalues below tolerance. Reducing "
"N by 1.").format(nevBad))
self.N = self.N - 1
if self.N <= 0:
self.N = 0
eV = np.ones((1, 1))
q = PI()
q.npar = self.npar
q.polybasis = self.polybasis0
if self.polydegreetype == "TOTAL":
q.coeffs = degreeTotalToFull(tuple([self.N + 1] * self.npar),
self.npar, eV[:, 0])
else:
q.coeffs = eV[:, 0].reshape([self.N + 1] * self.npar)
vbMng(self, "DEL", "Done computing denominator.", 7)
return q, fitinv
def _setupNumerator(self):
"""Compute rational numerator."""
RROMPyAssert(self._mode, message = "Cannot setup numerator.")
vbMng(self, "INIT", "Starting computation of numerator.", 7)
self._setupInterpolationIndices()
Qevaldiag = polyTimesTable(self.trainedModel.data.Q, self._musUniqueCN,
self._reorder, self._derIdxs,
self.scaleFactorRel)
if self.POD:
Qevaldiag = Qevaldiag.dot(self.samplingEngine.RPOD.T)
cfun = totalDegreeN if self.polydegreetype == "TOTAL" else fullDegreeN
if hasattr(self, "_M_isauto"):
self._setMAuto()
M = self.M
else:
M = reduceDegreeN(self.M, self.S, self.npar, self.polydegreetype)
if M < self.M:
RROMPyWarning(("M too large compared to S. Reducing M by "
"{}").format(self.M - M))
self.M = M
while self.M >= 0:
Seff = cfun(self.M, self.npar)
pParRest = [self.verbosity >= 5, self.polydegreetype == "TOTAL",
{"derIdxs": [self._derIdxs[0][: Seff]],
"reorder": self._reorder[: Seff],
"scl": self.scaleFactorRel}]
if self.polybasis in ppb:
p = PI()
else:
pParRest = [self.radialDirectionalWeights] + pParRest
- pParRest[-1]["nNearestNeighbor"] = self.nNearestNeighbor
- p = RBI() if self.polybasis in rbpb else MLSI()
+ p = RBI()
if self.polybasis in ppb + rbpb:
pParRest += [{"rcond": self.interpRcond}]
wellCond, msg = p.setupByInterpolation(self._musUniqueCN,
Qevaldiag[: Seff, : Seff],
self.M, self.polybasis,
*pParRest)
vbMng(self, "MAIN", msg, 5)
if wellCond: break
if self.catchInstability > 0:
raise RROMPyException(("Instability in numerator computation: "
"polyfit is poorly conditioned."),
self.catchInstability == 1)
vbMng(self, "MAIN", ("Polyfit is poorly conditioned. Reducing M "
"by 1."), 10)
self.M = self.M - 1
if self.M < 0:
raise RROMPyException(("Instability in computation of numerator. "
"Aborting."))
self.M = M
vbMng(self, "DEL", "Done computing numerator.", 7)
return p
def _computeInterpolantInverseBlocks(self) -> Tuple[List[Np2D], Np2D]:
"""
Compute inverse factors for minimal interpolant target functional.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
self._setupInterpolationIndices()
if self.polydegreetype == "TOTAL":
cfun, TEGen = totalDegreeN, pvTP
else:
cfun, TEGen = fullDegreeN, pvP
E = max(self.M, self.N)
while E >= 0:
Seff = cfun(E, self.npar)
TEGenPar = [self.polybasis0, [self._derIdxs[0][: Seff]],
self._reorder[: Seff], self.scaleFactorRel]
if self.polydegreetype == "TOTAL":
Eeff = E
idxsB = totalDegreeMaxMask(E, self.npar)
else: #if self.polydegreetype == "FULL":
Eeff = [E] * self.npar
idxsB = fullDegreeMaxMask(E, self.npar)
TE = TEGen(self._musUniqueCN, Eeff, *TEGenPar)
fitOut = customPInv(TE, rcond = self.interpRcond, full = True)
vbMng(self, "MAIN",
("Fitting {} samples with degree {} through {}... "
"Conditioning of pseudoinverse system: {:.4e}.").format(
TE.shape[0], E,
polyfitname(self.polybasis0),
fitOut[1][1][0] / fitOut[1][1][-1]),
5)
if fitOut[1][0] == TE.shape[1]:
fitinv = fitOut[0][idxsB, :]
break
if self.catchInstability > 0:
raise RROMPyException(("Instability in denominator "
"computation: polyfit is poorly "
"conditioned."),
self.catchInstability == 1)
EeqN = E == self.N
vbMng(self, "MAIN", ("Polyfit is poorly conditioned. Reducing E {}"
"by 1.").format("and N " * EeqN), 10)
if EeqN: self.N = self.N - 1
E -= 1
if self.N < 0:
raise RROMPyException(("Instability in computation of "
"denominator. Aborting."))
invD = vanderInvTable(fitinv, idxsB, self._reorder[: Seff],
[self._derIdxs[0][: Seff]])
if self.N == E:
TN = TE
else:
if self.polydegreetype == "TOTAL":
Neff = self.N
idxsB = totalDegreeMaxMask(self.N, self.npar)
else: #if self.polydegreetype == "FULL":
Neff = [self.N] * self.npar
idxsB = fullDegreeMaxMask(self.N, self.npar)
TN = TEGen(self._musUniqueCN, Neff, *TEGenPar)
for k in range(len(invD)): invD[k] = dot(invD[k], TN)
return invD, fitinv
diff --git a/rrompy/reduction_methods/standard/reduced_basis.py b/rrompy/reduction_methods/standard/reduced_basis.py
index 5ef4b96..c9dfb80 100644
--- a/rrompy/reduction_methods/standard/reduced_basis.py
+++ b/rrompy/reduction_methods/standard/reduced_basis.py
@@ -1,218 +1,218 @@
# 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 .
#
from copy import deepcopy as copy
import numpy as np
from .generic_standard_approximant import GenericStandardApproximant
from rrompy.hfengines.base.linear_affine_engine import checkIfAffine
from rrompy.reduction_methods.base.reduced_basis_utils import \
projectAffineDecomposition
from rrompy.utilities.base.types import Np1D, Np2D, List, Tuple, sampList
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical import dot
from rrompy.utilities.exception_manager import (RROMPyWarning, RROMPyException,
RROMPyAssert)
__all__ = ['ReducedBasis']
class ReducedBasis(GenericStandardApproximant):
"""
ROM RB approximant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'scaleFactorDer': scaling factors for derivative computation;
defaults to 'AUTO';
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator;
- 'R': rank for Galerkin projection; defaults to 'AUTO', i.e.
maximum allowed;
- 'PODTolerance': tolerance for snapshots POD; defaults to -1.
Defaults to empty dict.
approx_state(optional): Whether to approximate state. Defaults and must
be True.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'scaleFactorDer': scaling factors for derivative computation;
- 'R': rank for Galerkin projection;
- 'PODTolerance': tolerance for snapshots POD.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
approx_state: Whether to approximate state.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
scaleFactorDer: Scaling factors for derivative computation.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
R: Rank for Galerkin projection.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
uApproxReduced: Reduced approximate solution(s) with parameter(s)
lastSolvedApprox as sampleList.
lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
lastSolvedApprox: Parameter(s) corresponding to last computed
approximate solution(s) as parameterList.
"""
def __init__(self, *args, **kwargs):
self._preInit()
self._addParametersToList(["R", "PODTolerance"], ["AUTO", -1])
super().__init__(*args, **kwargs)
checkIfAffine(self.HFEngine, "apply RB method")
if not self.approx_state:
raise RROMPyException("Must compute RB approximation of state.")
self._postInit()
@property
def tModelType(self):
from .trained_model.trained_model_reduced_basis import (
TrainedModelReducedBasis)
return TrainedModelReducedBasis
@property
def R(self):
"""Value of R. Its assignment may change S."""
return self._R
@R.setter
def R(self, R):
if isinstance(R, str):
R = R.strip().replace(" ","")
if "-" not in R: R = R + "-0"
self._R_isauto, self._R_shift = True, int(R.split("-")[-1])
R = 0
if R < 0: raise RROMPyException("R must be non-negative.")
self._R = R
self._approxParameters["R"] = self.R
def _setRAuto(self):
self.R = max(0, self.S - self._R_shift)
vbMng(self, "MAIN", "Automatically setting R to {}.".format(self.R),
25)
@property
def PODTolerance(self):
"""Value of PODTolerance."""
return self._PODTolerance
@PODTolerance.setter
def PODTolerance(self, PODTolerance):
self._PODTolerance = PODTolerance
self._approxParameters["PODTolerance"] = self.PODTolerance
def _setupProjectionMatrix(self):
"""Compute projection matrix."""
RROMPyAssert(self._mode, message = "Cannot setup numerator.")
vbMng(self, "INIT", "Starting computation of projection matrix.", 7)
if hasattr(self, "_R_isauto"):
self._setRAuto()
else:
if self.S < self.R:
RROMPyWarning(("R too large compared to S. Reducing R by "
"{}").format(self.R - self.S))
self.S = self.S
try:
if self.POD:
U, s, _ = np.linalg.svd(self.samplingEngine.RPOD)
s = s ** 2.
else:
Gramian = self.HFEngine.innerProduct(
self.samplingEngine.samples,
self.samplingEngine.samples, is_state = True)
U, s, _ = np.linalg.svd(Gramian)
except np.linalg.LinAlgError as e:
raise RROMPyException(e)
snorm = np.cumsum(s[::-1]) / np.sum(s)
nPODTrunc = min(self.S - np.argmax(snorm > self.PODTolerance),
self.R)
pMat = dot(self.samplingEngine.samples, U[:, : nPODTrunc])
vbMng(self, "MAIN",
("Assembling {}x{} projection matrix from {} "
"samples.").format(*(pMat.shape), self.S), 5)
vbMng(self, "DEL", "Done computing projection matrix.", 7)
return pMat
def setupApprox(self) -> int:
"""Compute RB projection matrix."""
if self.checkComputedApprox(): return -1
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5)
self.computeSnapshots()
pMat = self._setupProjectionMatrix().data
pMatEff = dot(self.HFEngine.C, pMat)
if self.trainedModel is None:
self.trainedModel = self.tModelType()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
- datadict = {"mu0": self.mu0, "projMat": pMatEff,
- "scaleFactor": self.scaleFactor,
+ datadict = {"mu0": self.mu0, "mus": copy(self.mus),
+ "projMat": pMatEff, "scaleFactor": self.scaleFactor,
"rescalingExp": self.HFEngine.rescalingExp}
data = self.initializeModelData(datadict)[0]
data.affinePoly = self.HFEngine.affinePoly
data.thAs, data.thbs = self.HFEngine.thAs, self.HFEngine.thbs
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(pMatEff)
- self.trainedModel.data.mus = copy(self.mus)
+ self.trainedModel.data.mus = copy(self.mus)
ARBs, bRBs = self.assembleReducedSystem(pMat)
self.trainedModel.data.ARBs = ARBs
self.trainedModel.data.bRBs = bRBs
self.trainedModel.data.approxParameters = copy(self.approxParameters)
vbMng(self, "DEL", "Done setting up approximant.", 5)
return 0
def assembleReducedSystem(self, pMat : sampList = None,
pMatOld : sampList = None)\
-> Tuple[List[Np2D], List[Np1D]]:
"""Build affine blocks of RB linear system through projections."""
if pMat is None:
self.setupApprox()
ARBs = self.trainedModel.data.ARBs
bRBs = self.trainedModel.data.bRBs
else:
self.HFEngine.buildA()
self.HFEngine.buildb()
vbMng(self, "INIT", "Projecting affine terms of HF model.", 10)
ARBsOld = None if pMatOld is None else self.trainedModel.data.ARBs
bRBsOld = None if pMatOld is None else self.trainedModel.data.bRBs
ARBs, bRBs = projectAffineDecomposition(self.HFEngine.As,
self.HFEngine.bs, pMat,
ARBsOld, bRBsOld, pMatOld)
vbMng(self, "DEL", "Done projecting affine terms.", 10)
return ARBs, bRBs
diff --git a/rrompy/reduction_methods/standard/trained_model/trained_model_nearest_neighbor.py b/rrompy/reduction_methods/standard/trained_model/trained_model_nearest_neighbor.py
index 9d763bd..e8a8c89 100644
--- a/rrompy/reduction_methods/standard/trained_model/trained_model_nearest_neighbor.py
+++ b/rrompy/reduction_methods/standard/trained_model/trained_model_nearest_neighbor.py
@@ -1,101 +1,74 @@
# 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 numbers import Number
from rrompy.reduction_methods.base.trained_model.trained_model import (
TrainedModel)
-from rrompy.utilities.base.types import Np1D, paramVal, paramList, sampList
+from rrompy.utilities.base.types import Np1D, paramList, sampList
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.parameter import checkParameterList
from rrompy.sampling import sampleList
__all__ = ['TrainedModelNearestNeighbor']
class TrainedModelNearestNeighbor(TrainedModel):
"""
ROM approximant evaluation for nearest neighbor approximant.
Attributes:
Data: dictionary with all that can be pickled.
"""
- def centerNormalize(self, mu : paramList = [],
- mu0 : paramVal = None) -> paramList:
- """
- Compute normalized parameter to be plugged into approximant.
-
- Args:
- mu: Parameter(s) 1.
- mu0: Parameter(s) 2. If None, set to self.data.mu0.
-
- Returns:
- Normalized parameter.
- """
- mu = checkParameterList(mu, self.data.npar)[0]
- if mu0 is None: mu0 = self.data.mu0
- rad = ((mu ** self.data.rescalingExp - mu0 ** self.data.rescalingExp)
- / self.data.scaleFactor)
- return rad
+ def collapse(self):
+ self.data.NN.postmultiplyTensorize(self.data.projMat.T)
+ super().collapse()
def getNearestNeighbor(self, mu : paramList = []) -> Np1D:
"""
Find nearest neighbor to arbitrary parameter.
Args:
mu: Target parameter.
"""
mu = checkParameterList(mu, self.data.npar)[0]
vbMng(self, "INIT", "Finding nearest neighbor to mu = {}.".format(mu),
22)
- if not hasattr(self.data, "musCentered"):
- self.data.musCentered = self.centerNormalize(self.data.mus)
- muTile = np.repeat(self.data.musCentered.data.reshape(
- self.data.musCentered.shape + (1,)),
- len(mu), axis = -1)
- muCN = self.centerNormalize(mu).data.T
- ids = np.argmin(np.linalg.norm(muTile - muCN, axis = 1), axis = 0)
+ nn = sampleList(self.data.NN(mu))
vbMng(self, "DEL", "Done finding nearest neighbor.", 22)
- return ids
+ return nn
def getApproxReduced(self, mu : paramList = []) -> sampList:
"""
Evaluate reduced representation of approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
mu = checkParameterList(mu, self.data.npar)[0]
if (not hasattr(self, "lastSolvedApproxReduced")
or self.lastSolvedApproxReduced != mu):
vbMng(self, "INIT",
"Evaluating approximant at mu = {}.".format(mu), 12)
- ids = self.getNearestNeighbor(mu)
- if isinstance(self.data.matR, Number):
- matRed = np.zeros((self.data.projMat.shape[1], len(ids)))
- matRed[ids, np.arange(len(ids))] = self.data.matR
- else:
- matRed = self.data.matR[:, ids]
- self.uApproxReduced = sampleList(matRed)
+ self.uApproxReduced = self.getNearestNeighbor(mu)
vbMng(self, "DEL", "Done evaluating approximant.", 12)
self.lastSolvedApproxReduced = mu
return self.uApproxReduced
def getPoles(self, *args, **kwargs) -> Np1D:
"""Obtain approximant poles."""
return np.empty(0)
diff --git a/rrompy/reduction_methods/standard/trained_model/trained_model_rational.py b/rrompy/reduction_methods/standard/trained_model/trained_model_rational.py
index 1965383..f052c79 100644
--- a/rrompy/reduction_methods/standard/trained_model/trained_model_rational.py
+++ b/rrompy/reduction_methods/standard/trained_model/trained_model_rational.py
@@ -1,178 +1,183 @@
# 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.reduction_methods.base.trained_model.trained_model import (
TrainedModel)
from rrompy.utilities.base.types import (Np1D, List, paramVal, paramList,
sampList)
from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp
from rrompy.utilities.exception_manager import RROMPyException, RROMPyWarning
from rrompy.parameter import (checkParameter, checkParameterList,
emptyParameterList)
from rrompy.sampling import sampleList
__all__ = ['TrainedModelRational']
class TrainedModelRational(TrainedModel):
"""
ROM approximant evaluation for rational approximant.
Attributes:
Data: dictionary with all that can be pickled.
"""
+ def collapse(self):
+ self.data.P.postmultiplyTensorize(self.data.projMat.T)
+ super().collapse()
+
def centerNormalize(self, mu : paramList = [],
mu0 : paramVal = None) -> paramList:
"""
Compute normalized parameter to be plugged into approximant.
Args:
mu: Parameter(s) 1.
mu0: Parameter(s) 2. If None, set to self.data.mu0.
Returns:
Normalized parameter.
"""
mu = checkParameterList(mu, self.data.npar)[0]
if mu0 is None: mu0 = self.data.mu0
rad = ((mu ** self.data.rescalingExp - mu0 ** self.data.rescalingExp)
/ self.data.scaleFactor)
return rad
def getPVal(self, mu : paramList = []) -> sampList:
"""
Evaluate rational numerator at arbitrary parameter.
Args:
mu: Target parameter.
"""
mu = checkParameterList(mu, self.data.npar)[0]
vbMng(self, "INIT", "Evaluating numerator at mu = {}.".format(mu), 17)
muCenter = self.centerNormalize(mu)
p = sampleList(self.data.P(muCenter))
vbMng(self, "DEL", "Done evaluating numerator.", 17)
return p
def getQVal(self, mu:Np1D, der : List[int] = None,
scl : Np1D = None) -> Np1D:
"""
Evaluate rational denominator at arbitrary parameter.
Args:
mu: Target parameter.
der(optional): Derivatives to take before evaluation.
"""
mu = checkParameterList(mu, self.data.npar)[0]
vbMng(self, "INIT", "Evaluating denominator at mu = {}.".format(mu),
17)
muCenter = self.centerNormalize(mu)
q = self.data.Q(muCenter, der, scl)
vbMng(self, "DEL", "Done evaluating denominator.", 17)
return q
def getApproxReduced(self, mu : paramList = []) -> sampList:
"""
Evaluate reduced representation of approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
mu = checkParameterList(mu, self.data.npar)[0]
if (not hasattr(self, "lastSolvedApproxReduced")
or self.lastSolvedApproxReduced != mu):
vbMng(self, "INIT",
"Evaluating approximant at mu = {}.".format(mu), 12)
QV = self.getQVal(mu)
QVzero = np.where(QV == 0.)[0]
if len(QVzero) > 0:
RROMPyWarning(("Adjusting approximation to avoid division by "
"numerically zero denominator."))
QV[QVzero] = np.finfo(np.complex).eps / (1.
+ self.data.Q.deg[0])
self.uApproxReduced = self.getPVal(mu) / QV
vbMng(self, "DEL", "Done evaluating approximant.", 12)
self.lastSolvedApproxReduced = mu
return self.uApproxReduced
def getPoles(self, *args, **kwargs) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
if len(args) + len(kwargs) > 1:
raise RROMPyException(("Wrong number of parameters passed. "
"Only 1 available."))
elif len(args) + len(kwargs) == 1:
if len(args) == 1:
mVals = args[0]
else:
mVals = kwargs["marginalVals"]
if not hasattr(mVals, "__len__"): mVals = [mVals]
mVals = list(mVals)
else:
mVals = [fp]
try:
rDim = mVals.index(fp)
if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]:
raise
except:
raise RROMPyException(("Exactly 1 'freepar' entry in "
"marginalVals must be provided."))
mVals[rDim] = self.data.mu0(rDim)
mVals = self.centerNormalize(checkParameter(mVals, len(mVals)))
mVals = list(mVals.data.flatten())
mVals[rDim] = fp
return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim]
+ self.data.scaleFactor[rDim] * self.data.Q.roots(mVals),
1. / self.data.rescalingExp[rDim])
def getResidues(self, *args, **kwargs) -> Np1D:
"""
Obtain approximant residues.
Returns:
Numpy matrix with residues as columns.
"""
pls = self.getPoles(*args, **kwargs)
if len(args) == 1:
mVals = args[0]
elif len(args) == 0:
mVals = [None]
else:
mVals = kwargs["marginalVals"]
if not hasattr(mVals, "__len__"): mVals = [mVals]
mVals = list(mVals)
rDim = mVals.index(fp)
poles = emptyParameterList()
poles.reset((len(pls), self.data.npar), dtype = pls.dtype)
for k, pl in enumerate(pls):
poles[k] = mVals
poles.data[k, rDim] = pl
QV = self.getQVal(poles, list(1 * (np.arange(self.data.npar) == rDim)))
QVzero = np.where(QV == 0.)[0]
if len(QVzero) > 0:
RROMPyWarning(("Adjusting residuals to avoid division by "
"numerically zero denominator."))
QV[QVzero] = np.finfo(np.complex).eps / (1. + self.data.Q.deg[0])
Res = self.getPVal(poles).data
- res = self.data.projMat[:, : Res.shape[0]].dot(Res) / QV
+ if not self.data._collapsed:
+ Res = self.data.projMat[:, : Res.shape[0]].dot(Res)
+ res = Res / QV
return pls, res
-
diff --git a/rrompy/reduction_methods/standard/trained_model/trained_model_rational_mls.py b/rrompy/reduction_methods/standard/trained_model/trained_model_rational_mls.py
deleted file mode 100644
index 329c5b5..0000000
--- a/rrompy/reduction_methods/standard/trained_model/trained_model_rational_mls.py
+++ /dev/null
@@ -1,191 +0,0 @@
-# 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 .trained_model_rational import TrainedModelRational
-from rrompy.utilities.base.types import Np1D, paramVal, paramList, sampList
-from rrompy.utilities.base import verbosityManager as vbMng
-from rrompy.utilities.poly_fitting.moving_least_squares import mlsweights
-from rrompy.utilities.poly_fitting.polynomial import (
- PolynomialInterpolator as PI)
-from rrompy.utilities.numerical import customPInv
-from rrompy.utilities.numerical.degree import degreeTotalToFull
-from rrompy.parameter import checkParameterList
-from rrompy.sampling import emptySampleList
-from rrompy.utilities.exception_manager import RROMPyException, RROMPyWarning
-
-__all__ = ['TrainedModelRationalMLS']
-
-class TrainedModelRationalMLS(TrainedModelRational):
- """
- ROM approximant evaluation for rational moving least squares approximant.
-
- Attributes:
- Data: dictionary with all that can be pickled.
- """
-
- def reset(self):
- super().reset()
- self.lastSetupMu = None
-
- def assembleReducedModel(self, mu:paramVal):
- if not (hasattr(self.data, "lastSetupMu")
- and self.data.lastSetupMu == mu):
- vbMng(self, "INIT", "Assembling reduced model for mu = {}."\
- .format(mu), 17)
- vbMng(self, "INIT", "Starting computation of denominator.", 35)
- muC = self.centerNormalize(mu)
- muSC = self.centerNormalize(self.data.mus)
- wQ = mlsweights(muC, muSC, self.data.radialBasisDen,
- directionalWeights = self.data.radialWeightsDen,
- nNearestNeighbor = self.data.nNearestNeighborDen)
- if self.data.N > self.data.M:
- PQVan = self.data.QVan
- else:
- PQVan = self.data.PVan
- VQAdjW = PQVan.conj().T * wQ
- VQAdjWVQ = VQAdjW.dot(PQVan)
- interpPseudoInverse, info = customPInv(VQAdjWVQ, full = True,
- rcond = self.data.interpRcond)
- interpPseudoInverse = interpPseudoInverse.dot(VQAdjW).dot(
- self.data.QBlocks)
- if info[0] < interpPseudoInverse.shape[-1]:
- q = np.zeros(interpPseudoInverse.shape[-1], dtype = np.complex)
- q[0] = 1.
- else:
- halfGram = interpPseudoInverse[self.data.domQIdxs]
- if self.data.POD:
- Rstack = halfGram.reshape(-1, halfGram.shape[-1])
- vbMng(self, "INIT",
- "Solving svd for square root of gramian matrix.", 67)
- try:
- _, s, eV = np.linalg.svd(Rstack, full_matrices = False)
- except np.linalg.LinAlgError as e:
- raise RROMPyException(e)
- condN = s[0] / s[-1]
- q = eV[-1, :].T.conj()
- vbMng(self, "MAIN",
- ("Solved svd problem of size {} x {} with condition "
- "number {:.4e}.").format(*Rstack.shape, condN), 55)
- vbMng(self, "DEL", "Done solving svd.", 67)
- else:
- RRstack = np.tensordot(self.trainedModel.gramian, halfGram,
- 1).reshape(-1, halfGram.shape[-1])
- RLstack = halfGram.reshape(-1, halfGram.shape[-1])
- gram = RLstack.T.conj().dot(RRstack)
- vbMng(self, "INIT",
- "Solving eigenvalue problem for gramian matrix.", 67)
- try:
- ev, eV = np.linalg.eigh(gram)
- except np.linalg.LinAlgError as e:
- raise RROMPyException(e)
- condN = ev[-1] / ev[0]
- q = eV[:, 0]
- vbMng(self, "MAIN",
- ("Solved eigenvalue problem of size {} with "
- "condition number {:.4e}.").format(gram.shape[0],
- condN), 55)
- vbMng(self, "DEL", "Done solving eigenvalue problem.", 67)
- self.data.Q = PI()
- self.data.Q.npar = self.npar
- self.data.Q.polybasis = self.data.polybasis
- if self.data.polydegreetype == "TOTAL":
- self.data.Q.coeffs = degreeTotalToFull(
- (self.data.N + 1,) * self.npar,
- self.npar, q)
- else:
- self.data.Q.coeffs = q.reshape((self.data.N + 1,) * self.npar)
- vbMng(self, "DEL", "Done computing denominator.", 35)
- vbMng(self, "INIT", "Starting computation of numerator.", 35)
- self.data.P = PI()
- self.data.P.npar = self.npar
- self.data.P.polybasis = self.data.polybasis
- wP = mlsweights(muC, muSC, self.data.radialBasis,
- directionalWeights = self.data.radialWeights,
- nNearestNeighbor = self.data.nNearestNeighbor)
- VAdjW = self.data.PVan.conj().T * wP
- VAdjWV = VAdjW.dot(self.data.PVan)
- interpPPseudoInverse = customPInv(VAdjWV, self.data.interpRcond)
- Pcoeffs = np.tensordot(interpPPseudoInverse.dot(VAdjW),
- self.data.QBlocks.dot(q), ([1], [1]))
- if self.data.polydegreetype == "TOTAL":
- self.data.P.coeffs = degreeTotalToFull(
- (self.data.M + 1,) * self.npar
- + (self.data.QBlocks.shape[0],),
- self.npar, Pcoeffs)
- else:
- self.data.P.coeffs = Pcoeffs.reshape(
- (self.data.M + 1,) * self.npar
- + (self.data.QBlocks.shape[0],))
- vbMng(self, "DEL", "Done computing numerator.", 35)
- vbMng(self, "DEL", "Done assembling reduced model.", 17)
- self.data.lastSetupMu = mu
-
- def getApproxReduced(self, mu : paramList = []) -> sampList:
- """
- Evaluate reduced representation of approximant at arbitrary parameter.
-
- Args:
- mu: Target parameter.
- """
- mu = checkParameterList(mu, self.data.npar)[0]
- if (not hasattr(self, "lastSolvedApproxReduced")
- or self.lastSolvedApproxReduced != mu):
- vbMng(self, "INIT",
- "Evaluating approximant at mu = {}.".format(mu), 12)
- self.uApproxReduced = emptySampleList()
- for i in range(len(mu)):
- self.assembleReducedModel(mu[i])
- vbMng(self, "INIT",
- "Solving reduced model for mu = {}.".format(mu[i]), 15)
- Qv = self.getQVal(mu[i])
- if Qv == 0.:
- RROMPyWarning(("Adjusting approximation to avoid division "
- "by numerically zero denominator."))
- Qv = np.finfo(np.complex).eps / (1. + self.data.Q.deg[0])
- uAppR = self.getPVal(mu[i]) / Qv
- if i == 0:
- self.uApproxReduced.reset((uAppR.shape[0], len(mu)),
- dtype = uAppR.dtype)
- self.uApproxReduced[i] = uAppR
- vbMng(self, "DEL", "Done solving reduced model.", 15)
- vbMng(self, "DEL", "Done evaluating approximant.", 12)
- self.lastSolvedApproxReduced = mu
- return self.uApproxReduced
-
- def getPoles(self, *args, mu : paramVal = None, **kwargs) -> Np1D:
- """
- Obtain approximant poles.
-
- Returns:
- Numpy complex vector of poles.
- """
- if mu is None: mu = self.data.mu0
- self.assembleReducedModel(mu)
- return super().getPoles(*args, **kwargs)
-
- def getResidues(self, *args, mu : paramVal = None, **kwargs) -> Np1D:
- """
- Obtain approximant residues.
-
- Returns:
- Numpy matrix with residues as columns.
- """
- if mu is None: mu = self.data.mu0
- self.assembleReducedModel(mu)
- return super().getResidues(*args, **kwargs)
diff --git a/rrompy/reduction_methods/standard/trained_model/trained_model_reduced_basis.py b/rrompy/reduction_methods/standard/trained_model/trained_model_reduced_basis.py
index 4b9d180..3e9e594 100644
--- a/rrompy/reduction_methods/standard/trained_model/trained_model_reduced_basis.py
+++ b/rrompy/reduction_methods/standard/trained_model/trained_model_reduced_basis.py
@@ -1,126 +1,129 @@
# 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.reduction_methods.base.trained_model.trained_model import (
TrainedModel)
from rrompy.utilities.base.types import (Np1D, ListAny, paramVal, paramList,
sampList)
from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp
from rrompy.utilities.numerical.marginalize_poly_list import marginalizePolyList
from rrompy.utilities.numerical.nonlinear_eigenproblem import (
eigvalsNonlinearDense)
from rrompy.utilities.expression import expressionEvaluator
from rrompy.utilities.exception_manager import RROMPyException, RROMPyWarning
from rrompy.parameter import checkParameter, checkParameterList
from rrompy.sampling import emptySampleList
__all__ = ['TrainedModelReducedBasis']
class TrainedModelReducedBasis(TrainedModel):
"""
ROM approximant evaluation for RB approximant.
Attributes:
Data: dictionary with all that can be pickled.
"""
def reset(self):
super().reset()
if hasattr(self, "data") and hasattr(self.data, "lastSetupMu"):
self.data.lastSetupMu = None
+ def collapse(self):
+ raise RROMPyException("Cannot collapse implicit surrogates.")
+
def assembleReducedModel(self, mu:paramVal):
mu = checkParameter(mu, self.data.npar)
if not (hasattr(self.data, "lastSetupMu")
and self.data.lastSetupMu == mu):
vbMng(self, "INIT", "Assembling reduced model for mu = {}."\
.format(mu), 17)
muEff = mu ** self.data.rescalingExp
self.data.ARBmu, self.data.bRBmu = 0., 0.
for thA, ARB in zip(self.data.thAs, self.data.ARBs):
self.data.ARBmu = (expressionEvaluator(thA[0], muEff) * ARB
+ self.data.ARBmu)
for thb, bRB in zip(self.data.thbs, self.data.bRBs):
self.data.bRBmu = (expressionEvaluator(thb[0], muEff) * bRB
+ self.data.bRBmu)
vbMng(self, "DEL", "Done assembling reduced model.", 17)
self.data.lastSetupMu = mu
def getApproxReduced(self, mu : paramList = []) -> sampList:
"""
Evaluate reduced representation of approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
mu = checkParameterList(mu, self.data.npar)[0]
if (not hasattr(self, "lastSolvedApproxReduced")
or self.lastSolvedApproxReduced != mu):
vbMng(self, "INIT",
"Computing RB solution at mu = {}.".format(mu), 12)
self.uApproxReduced = emptySampleList()
for i in range(len(mu)):
self.assembleReducedModel(mu[i])
vbMng(self, "INIT",
"Solving reduced model for mu = {}.".format(mu[i]), 15)
try:
uAppR = np.linalg.solve(self.data.ARBmu, self.data.bRBmu)
except np.linalg.LinAlgError as e:
raise RROMPyException(e)
if i == 0:
#self.data.ARBs[0].shape[-1], len(mu)
self.uApproxReduced.reset((len(uAppR), len(mu)),
dtype = uAppR.dtype)
self.uApproxReduced[i] = uAppR
vbMng(self, "DEL", "Done solving reduced model.", 15)
vbMng(self, "DEL", "Done computing RB solution.", 12)
self.lastSolvedApproxReduced = mu
return self.uApproxReduced
def getPoles(self, marginalVals : ListAny = [fp], jSupp : int = 1,
**kwargs) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
if not self.data.affinePoly:
RROMPyWarning(("Unable to compute approximate poles due "
"to parametric dependence (detected non-"
"polynomial). Change HFEngine.affinePoly to True "
"if necessary."))
return
if not hasattr(marginalVals, "__len__"): marginalVals = [marginalVals]
mVals = list(marginalVals)
try:
rDim = mVals.index(fp)
if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]:
raise
except:
raise RROMPyException(("Exactly 1 'freepar' entry in "
"marginalVals must be provided."))
ARBs = self.data.ARBs
if self.data.npar > 1:
mVals[rDim] = self.data.mu0(rDim)
mVals = checkParameter(mVals).data.flatten()
mVals[rDim] = fp
ARBs = marginalizePolyList(ARBs, mVals, "auto")
ev = eigvalsNonlinearDense(ARBs, jSupp = jSupp, **kwargs)
return np.power(ev, 1. / self.data.rescalingExp[rDim])
diff --git a/rrompy/sampling/pivoted/sampling_engine_pivoted.py b/rrompy/sampling/pivoted/sampling_engine_pivoted.py
index 534cfdd..b073f6d 100644
--- a/rrompy/sampling/pivoted/sampling_engine_pivoted.py
+++ b/rrompy/sampling/pivoted/sampling_engine_pivoted.py
@@ -1,125 +1,122 @@
# 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 .
#
from copy import deepcopy as copy
import numpy as np
from rrompy.sampling.base.sampling_engine_base_pivoted import (
SamplingEngineBasePivoted)
from rrompy.hfengines.base import MarginalProxyEngine
from rrompy.utilities.base.types import Np1D, paramVal, paramList, sampList
from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp
from rrompy.utilities.exception_manager import RROMPyException
from rrompy.utilities.numerical import dot
from rrompy.utilities.numerical.hash_derivative import nextDerivativeIndices
from rrompy.parameter import checkParameter, checkParameterList
__all__ = ['SamplingEnginePivoted']
class SamplingEnginePivoted(SamplingEngineBasePivoted):
def preprocesssamples(self, idxs:Np1D, j:int) -> sampList:
if self.samples[j] is None or len(self.samples[j]) == 0: return
return self.samples[j](idxs)
def postprocessu(self, u:sampList, j:int, overwrite : bool = False):
self.setsample(u, j, overwrite)
def postprocessuBulk(self, j:int):
pass
def _getSampleConcurrence(self, mu:paramVal, j:int,
previous:Np1D) -> sampList:
if not (self.sample_state or self.HFEngine.isCEye):
raise RROMPyException(("Derivatives of solution with non-scalar "
"C not computable."))
- if not self.HFEngine._isStateShiftZero:
- raise RROMPyException(("Derivatives of solution with non-zero "
- "solution shift not computable."))
if len(previous) >= len(self._derIdxs[j]):
self._derIdxs[j] += nextDerivativeIndices(
self._derIdxs[j], self.nPivot,
len(previous) + 1 - len(self._derIdxs[j]))
derIdx = self._derIdxs[j][len(previous)]
mu = checkParameter(mu, self.nPivot)
samplesOld = self.preprocesssamples(previous, j)
RHS = self.scaleDer(derIdx) * self.HFEngineMarginalized.b(mu, derIdx)
for j, derP in enumerate(self._derIdxs[j][: len(previous)]):
diffP = [x - y for (x, y) in zip(derIdx, derP)]
if np.all([x >= 0 for x in diffP]):
RHS -= self.scaleDer(diffP) * dot(
self.HFEngineMarginalized.A(mu, diffP),
samplesOld[j])
return self.solveLS(mu, RHS = RHS)
def nextSample(self, mu:paramVal, j:int, overwrite : bool = False,
postprocess : bool = True) -> Np1D:
mu = checkParameter(mu, self.nPivot)
muidxs = self.mus[j].findall(mu[0])
if len(muidxs) > 0:
u = self._getSampleConcurrence(mu, j, np.sort(muidxs))
else:
u = self.solveLS(mu)
if postprocess:
self.postprocessu(u, j, overwrite = overwrite)
else:
self.setsample(u, j, overwrite)
if overwrite:
self.mus[j][self.nsamples[j]] = mu[0]
else:
self.mus[j].append(mu)
self.nsamples[j] += 1
return self.samples[j][self.nsamples[j] - 1]
def iterSample(self, mus:paramList, musM:paramList) -> sampList:
mus = checkParameterList(mus, self.nPivot)[0]
musM = checkParameterList(musM, self.nMarginal)[0]
vbMng(self, "INIT", "Starting sampling iterations.", 5)
n = len(mus)
m = len(musM)
if n <= 0:
raise RROMPyException("Number of samples must be positive.")
if m <= 0:
raise RROMPyException(("Number of marginal samples must be "
"positive."))
repeatedSamples = len(mus.unique()) != n
for j in range(m):
muMEff = [fp] * self.HFEngine.npar
for k, x in enumerate(self.directionMarginal):
muMEff[x] = musM(j, k)
self.HFEngineMarginalized = MarginalProxyEngine(self.HFEngine,
list(muMEff))
if repeatedSamples:
for k in range(n):
vbMng(self, "MAIN",
"Computing sample {} / {} for marginal {} / {}."\
.format(k + 1, n, j, m), 10)
self.nextSample(mus[k], j, overwrite = (k > 0),
postprocess = False)
if n > 1 and k == 0:
self.preallocateSamples(self.samples[j][0], mus[0],
n, j)
else:
self.setsample(self.solveLS(mus), j, overwrite = False)
self.mus[j] = copy(mus)
self.nsamples[j] = n
if len(self.musMarginal) > j:
self.musMarginal[j] = copy(musM[j])
else:
self.musMarginal.append(musM[j])
self.postprocessuBulk(j)
vbMng(self, "DEL", "Finished sampling iterations.", 5)
return self.samples[j]
diff --git a/rrompy/sampling/standard/sampling_engine_standard.py b/rrompy/sampling/standard/sampling_engine_standard.py
index e3a3694..4c7fc79 100644
--- a/rrompy/sampling/standard/sampling_engine_standard.py
+++ b/rrompy/sampling/standard/sampling_engine_standard.py
@@ -1,105 +1,102 @@
# 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 .
#
from copy import deepcopy as copy
import numpy as np
from rrompy.sampling.base.sampling_engine_base import SamplingEngineBase
from rrompy.utilities.base.types import Np1D, paramVal, paramList, sampList
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.exception_manager import RROMPyException
from rrompy.utilities.numerical import dot
from rrompy.utilities.numerical.hash_derivative import nextDerivativeIndices
from rrompy.parameter import checkParameter, checkParameterList
__all__ = ['SamplingEngineStandard']
class SamplingEngineStandard(SamplingEngineBase):
def preprocesssamples(self, idxs:Np1D) -> sampList:
if self.samples is None or len(self.samples) == 0: return
return self.samples(idxs)
def postprocessu(self, u:sampList, overwrite : bool = False):
self.setsample(u, overwrite)
def postprocessuBulk(self):
pass
def _getSampleConcurrence(self, mu:paramVal, previous:Np1D) -> sampList:
if not (self.sample_state or self.HFEngine.isCEye):
raise RROMPyException(("Derivatives of solution with non-scalar "
"C not computable."))
- if not self.HFEngine._isStateShiftZero:
- raise RROMPyException(("Derivatives of solution with non-zero "
- "solution shift not computable."))
if len(previous) >= len(self._derIdxs):
self._derIdxs += nextDerivativeIndices(self._derIdxs,
self.HFEngine.npar,
len(previous) + 1 - len(self._derIdxs))
derIdx = self._derIdxs[len(previous)]
mu = checkParameter(mu, self.HFEngine.npar)
samplesOld = self.preprocesssamples(previous)
RHS = self.scaleDer(derIdx) * self.HFEngine.b(mu, derIdx)
for j, derP in enumerate(self._derIdxs[: len(previous)]):
diffP = [x - y for (x, y) in zip(derIdx, derP)]
if np.all([x >= 0 for x in diffP]):
RHS -= self.scaleDer(diffP) * dot(self.HFEngine.A(mu, diffP),
samplesOld[j])
return self.solveLS(mu, RHS = RHS)
def nextSample(self, mu:paramVal, overwrite : bool = False,
postprocess : bool = True) -> Np1D:
mu = checkParameter(mu, self.HFEngine.npar)
muidxs = self.mus.findall(mu[0])
if len(muidxs) > 0:
u = self._getSampleConcurrence(mu, np.sort(muidxs))
else:
u = self.solveLS(mu)
if postprocess:
self.postprocessu(u, overwrite = overwrite)
else:
self.setsample(u, overwrite)
if overwrite:
self.mus[self.nsamples] = mu[0]
else:
self.mus.append(mu)
self.nsamples += 1
return self.samples[self.nsamples - 1]
def iterSample(self, mus:paramList) -> sampList:
mus = checkParameterList(mus, self.HFEngine.npar)[0]
vbMng(self, "INIT", "Starting sampling iterations.", 5)
n = len(mus)
if n <= 0:
raise RROMPyException(("Number of samples must be positive."))
self.resetHistory()
if len(mus.unique()) != n:
for j in range(n):
vbMng(self, "MAIN",
"Computing sample {} / {}.".format(j + 1, n), 7)
self.nextSample(mus[j], overwrite = (j > 0),
postprocess = False)
if n > 1 and j == 0:
self.preallocateSamples(self.samples[0], mus[0], n)
else:
self.setsample(self.solveLS(mus), overwrite = False)
self.mus = copy(mus)
self.nsamples = n
self.postprocessuBulk()
vbMng(self, "DEL", "Finished sampling iterations.", 5)
return self.samples
diff --git a/rrompy/utilities/base/data_structures.py b/rrompy/utilities/base/data_structures.py
index 116b9ce..75eca6b 100644
--- a/rrompy/utilities/base/data_structures.py
+++ b/rrompy/utilities/base/data_structures.py
@@ -1,77 +1,77 @@
# 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 os
import time
from rrompy.utilities.base.types import Any, DictAny, ListAny
from rrompy.utilities.exception_manager import RROMPyWarning
__all__ = ['findDictStrKey', 'purgeDict', 'purgeList']
def findDictStrKey(key:Any, keyList:ListAny):
for akey in keyList:
if isinstance(key, str) and key.lower() == akey.lower():
return akey
return None
def purgeDict(dct:DictAny, allowedKeys : ListAny = [], silent : bool = False,
complement : bool = False, dictname : str = "",
baselevel : int = 0) -> DictAny:
if dictname != "":
dictname = " in " + dictname
dctcp = {}
for key in dct.keys():
akey = findDictStrKey(key, allowedKeys)
if (akey is None) != complement:
if not silent:
RROMPyWarning(("Ignoring key {0}{2} with value "
"{1}.").format(key, dct[key], dictname),
baselevel)
else:
if akey is None:
akey = key
dctcp[akey] = dct[key]
return dctcp
def purgeList(lst:ListAny, allowedEntries : ListAny = [],
silent : bool = False, complement : bool = False,
listname : str = "", baselevel : int = 0) -> ListAny:
if listname != "":
listname = " in " + listname
lstcp = []
for x in lst:
ax = findDictStrKey(x, allowedEntries)
if (ax is None) != complement:
if not silent:
RROMPyWarning("Ignoring entry {0}{1}.".format(x, listname),
baselevel)
else:
lstcp = lstcp + [ax]
return lstcp
def getNewFilename(prefix : str = "", extension : str = "dat",
timestamp : bool = True) -> str:
extra = ""
if timestamp: extra = time.strftime("_%y-%m-%d_%H:%M:%S", time.localtime())
- filenameBase = "{}{}.{}".format(prefix, extra, extension)
+ filenameBase = "{}{}".format(prefix, extra)
idx = 0
- filename = filenameBase
+ filename = filenameBase + ".{}".format(extension)
while os.path.exists(filename):
idx += 1
- filename = filenameBase + "{}".format(idx)
+ filename = filenameBase + "_{}.{}".format(idx, extension)
return filename
diff --git a/rrompy/utilities/poly_fitting/heaviside/val.py b/rrompy/utilities/poly_fitting/heaviside/val.py
index af590f6..39707c2 100644
--- a/rrompy/utilities/poly_fitting/heaviside/val.py
+++ b/rrompy/utilities/poly_fitting/heaviside/val.py
@@ -1,46 +1,46 @@
# 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.utilities.base.types import Np1D, Np2D, paramList
from rrompy.parameter import checkParameterList
from rrompy.utilities.poly_fitting.polynomial.val import polyval as pvP
__all__ = ['polyval']
def polyval(x:paramList, c:Np2D, poles:Np1D,
basis : str = "MONOMIAL_HEAVISIDE") -> Np2D:
x = checkParameterList(x, 1)[0]
poles = poles.flatten()
if len(c) > len(poles):
basisp = basis.split("_")[0]
c0 = pvP(x, c[len(poles) :], basisp)
else:
c0 = np.zeros(c.shape[1:] + (len(x),), dtype = c.dtype)
csh = copy(c0.shape)
if len(csh) == 1: c0 = c0.reshape(1, -1)
for j in range(len(x)):
muDiff = 1. / (x[j] - poles)
- val = muDiff.dot(c[: len(poles)])
+ val = np.tensordot(muDiff, c[: len(poles)], 1)
try:
c0[..., j] += val
except:
c0[..., j] += val.flatten()
if len(csh) == 1: c0 = c0.flatten()
return c0
diff --git a/rrompy/utilities/poly_fitting/moving_least_squares/__init__.py b/rrompy/utilities/poly_fitting/moving_least_squares/__init__.py
deleted file mode 100644
index e230f15..0000000
--- a/rrompy/utilities/poly_fitting/moving_least_squares/__init__.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# 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 .
-#
-
-from .kernel import (radialGaussian, thinPlateSpline, multiQuadric,
- localWendland, nearestNeighbor)
-from .base import mlsbases, polybases, polyfitname, polydomcoeff
-from .vander import mlsweights, polyvander, polyvanderTotal
-from .moving_least_squares_interpolator import MovingLeastSquaresInterpolator
-
-__all__ = [
- 'radialGaussian',
- 'thinPlateSpline',
- 'multiQuadric',
- 'localWendland',
- 'nearestNeighbor',
- 'mlsbases',
- 'polybases',
- 'polyfitname',
- 'polydomcoeff',
- 'mlsweights',
- 'polyvander',
- 'polyvanderTotal',
- 'MovingLeastSquaresInterpolator'
- ]
-
-
diff --git a/rrompy/utilities/poly_fitting/moving_least_squares/moving_least_squares_interpolator.py b/rrompy/utilities/poly_fitting/moving_least_squares/moving_least_squares_interpolator.py
deleted file mode 100644
index a333338..0000000
--- a/rrompy/utilities/poly_fitting/moving_least_squares/moving_least_squares_interpolator.py
+++ /dev/null
@@ -1,142 +0,0 @@
-# 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.utilities.base.types import (List, ListAny, DictAny, Np1D, Np2D,
- paramList)
-from rrompy.utilities.numerical import customPInv, dot
-from .vander import mlsweights
-from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator
-from rrompy.utilities.poly_fitting.polynomial.vander import (polyvander as pv,
- polyvanderTotal as pvT)
-from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
-from rrompy.parameter import checkParameterList
-
-__all__ = ['MovingLeastSquaresInterpolator']
-
-class MovingLeastSquaresInterpolator(GenericInterpolator):
- def __init__(self, other = None):
- if other is None: return
- self.support = other.support
- self.localProjector = other.localProjector
- self.localVanders = other.localVanders
- self.suppValues = other.suppValues
- self.directionalWeights = other.directionalWeights
- self.degree = other.degree
- self.npar = other.npar
- self.radialbasis = other.radialbasis
- self.polybasis = other.polybasis
- self.evalParams = other.evalParams
- self.totalDegree = other.totalDegree
-
- @property
- def shape(self):
- sh = self.suppValues.shape[1 :] if self.suppValues.ndim > 1 else 1
- return sh
-
- @property
- def deg(self):
- return self.degree
-
- def __call__(self, mu:paramList, der : List[int] = None,
- scl : Np1D = None):
- if der is not None and np.sum(der) > 0:
- raise RROMPyException(("Cannot take derivatives of moving least "
- "squares function."))
- mu = checkParameterList(mu, self.npar)[0]
- sh = self.shape
- if sh == 1: sh = tuple([])
- values = np.empty((len(mu),) + sh, dtype = np.complex)
- for i, m in enumerate(mu):
- weights = mlsweights(m, self.support, self.radialbasis,
- directionalWeights = self.directionalWeights,
- nNearestNeighbor = self.evalParams["nNearestNeighbor"])
- weights /= np.linalg.norm(weights)
- vanderLS = np.sum(self.localVanders * weights, axis = 2)
- RHSLS = dot(self.localProjector * weights, self.suppValues)
- if self.totalDegree:
- vanderEval = pvT(m, self.deg[0], self.polybasis,
- **self.evalParams)
- else:
- vanderEval = pv(m, self.deg, self.polybasis, **self.evalParams)
- vanderEval = vanderEval.flatten()
- values[i] = dot(vanderEval, dot(customPInv(vanderLS), RHSLS))
- return values
-
- def __copy__(self):
- return MovingLeastSquaresInterpolator(self)
-
- def __deepcopy__(self, memo):
- other = MovingLeastSquaresInterpolator()
- (other.support, other.localProjector, other.localVanders,
- other.suppValues, other.directionalWeights, other.degree, other.npar,
- other.radialbasis, other.polybasis, other.evalParams,
- other.totalDegree) = copy(
- (self.support, self.localProjector, self.localVanders,
- self.suppValues, self.directionalWeights, self.degree,
- self.npar, self.radialbasis, self.polybasis,
- self.evalParams, self.totalDegree), memo)
- return other
-
- def postmultiplyTensorize(self, A:Np2D):
- RROMPyAssert(A.shape[0], self.shape[-1], "Shape of output")
- self.suppValues = self.suppValues.dot(A)
-
- def pad(self, nleft : List[int] = None, nright : List[int] = None):
- if nleft is None: nleft = [0] * len(self.shape)
- if nright is None: nright = [0] * len(self.shape)
- if not hasattr(nleft, "__len__"): nleft = [nleft]
- if not hasattr(nright, "__len__"): nright = [nright]
- RROMPyAssert(len(self.shape), len(nleft), "Shape of output")
- RROMPyAssert(len(self.shape), len(nright), "Shape of output")
- padwidth = [(0, 0)] + [(l, r) for l, r in zip(nleft, nright)]
- self.suppValues = np.pad(self.suppValues, padwidth, "constant",
- constant_values = (0., 0.))
-
- def setupByInterpolation(self, support:paramList, values:ListAny,
- deg:int, polybasis : str = "MONOMIAL_GAUSSIAN",
- directionalWeights : Np1D = None,
- totalDegree : bool = True,
- vanderCoeffs : DictAny = {}):
- support = checkParameterList(support)[0]
- self.support = copy(support)
- if "reorder" in vanderCoeffs.keys():
- self.support = self.support[vanderCoeffs["reorder"]]
- if "nNearestNeighbor" not in vanderCoeffs.keys():
- vanderCoeffs["nNearestNeighbor"] = -1
- self.npar = support.shape[1]
- if directionalWeights is None:
- directionalWeights = np.ones(self.npar)
- self.directionalWeights = directionalWeights
- self.polybasis, self.radialbasis, _ = polybasis.split("_")
- self.totalDegree = totalDegree
- self.evalParams = vanderCoeffs
- if totalDegree:
- vander = pvT(support, deg, self.polybasis, **vanderCoeffs)
- if not hasattr(deg, "__len__"): deg = [deg] * self.npar
- else:
- if not hasattr(deg, "__len__"): deg = [deg] * self.npar
- vander = pv(support, deg, self.polybasis, **vanderCoeffs)
- self.degree = deg
- self.localProjector = vander.T.conj()
- self.localVanders = np.array([np.outer(van, van.conj()) \
- for van in vander])
- self.localVanders = np.swapaxes(self.localVanders, 0, 2)
- self.suppValues = np.array(values)
-
diff --git a/rrompy/utilities/poly_fitting/moving_least_squares/vander.py b/rrompy/utilities/poly_fitting/moving_least_squares/vander.py
deleted file mode 100644
index 141023b..0000000
--- a/rrompy/utilities/poly_fitting/moving_least_squares/vander.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# 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 .kernel import (radialGaussian, thinPlateSpline, multiQuadric,
- localWendland, nearestNeighbor)
-from rrompy.utilities.poly_fitting.polynomial.vander import (polyvander as pvP,
- polyvanderTotal as pvTP)
-from rrompy.utilities.base.types import (Np1D, Np2D, Tuple, List, paramVal,
- paramList)
-from rrompy.parameter import checkParameter, checkParameterList
-from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
-
-__all__ = ['mlsweights', 'polyvander', 'polyvanderTotal']
-
-def mlsweights(x:paramVal, xSupp:paramList, basis:str,
- reorder : List[int] = None, directionalWeights : Np1D = None,
- nNearestNeighbor : int = -1) -> Np2D:
- """Compute moving least squares weight vector."""
- x = checkParameter(x)
- xSupp = checkParameterList(xSupp)[0]
- x = x.data
- xSupp = xSupp.data
- if directionalWeights is None:
- directionalWeights = np.ones(x.shape[1])
- elif not hasattr(directionalWeights, "__len__"):
- directionalWeights = directionalWeights * np.ones(x.shape[1])
- RROMPyAssert(len(directionalWeights), x.shape[1],
- "Number of directional weights")
- try:
- radialkernel = {
- "GAUSSIAN" : radialGaussian, "THINPLATE" : thinPlateSpline,
- "MULTIQUADRIC" : multiQuadric, "WENDLAND" : localWendland,
- "NEARESTNEIGHBOR" : nearestNeighbor}[basis.upper()]
- except:
- raise RROMPyException("Radial basis not recognized.")
- if reorder is not None: xSupp = xSupp[reorder]
- muDiff = (xSupp.data - x) * directionalWeights
- r2 = np.sum(np.abs(muDiff) ** 2., axis = 1).reshape(1, -1)
- if basis.upper() == "NEARESTNEIGHBOR":
- if nNearestNeighbor > 0 and nNearestNeighbor < len(xSupp):
- cutoffValue = np.partition(r2, nNearestNeighbor - 1)[0,
- nNearestNeighbor - 1]
- r2 /= cutoffValue
- else:
- r2[0, :] = 1. * (nNearestNeighbor == 0)
- return radialkernel(r2)[0]
-
-def polyvander(x:paramVal, xSupp:paramList, degs:List[int], basis:str,
- derIdxs : List[List[List[int]]] = None,
- reorder : List[int] = None, directionalWeights : Np1D = None,
- scl : Np1D = None,
- nNearestNeighbor : int = -1) -> Tuple[Np2D, Np2D]:
- """
- Compute full Hermite-Vandermonde matrix with specified derivative
- directions.
- """
- basisp, basisr, _ = basis.split("_")
- Weights = mlsweights(x, xSupp, basisr, reorder, directionalWeights,
- nNearestNeighbor)
- VanP = pvP(xSupp, degs, basisp, derIdxs = derIdxs, reorder = reorder,
- scl = scl)
- RHP = VanP.T.conj() * Weights
- return RHP.dot(VanP), RHP
-
-def polyvanderTotal(x:paramList, xSupp:paramList, deg:int, basis:str,
- derIdxs : List[List[List[int]]] = None,
- reorder : List[int] = None,
- directionalWeights : Np1D = None, scl : Np1D = None,
- nNearestNeighbor : int = -1) -> Tuple[Np2D, Np2D]:
- """
- Compute full total degree Hermite-Vandermonde matrix with specified
- derivative directions.
- """
- basisp, basisr, _ = basis.split("_")
- Weights = mlsweights(x, xSupp, basisr, reorder, directionalWeights,
- nNearestNeighbor)
- VanP = pvTP(x, deg, basisp, derIdxs = derIdxs,
- reorder = reorder, scl = scl)
- RHP = VanP.T.conj() * Weights
- return RHP.dot(VanP), RHP
diff --git a/rrompy/utilities/poly_fitting/moving_least_squares/base.py b/rrompy/utilities/poly_fitting/nearest_neighbor/__init__.py
similarity index 70%
rename from rrompy/utilities/poly_fitting/moving_least_squares/base.py
rename to rrompy/utilities/poly_fitting/nearest_neighbor/__init__.py
index 155b780..8fe10f6 100644
--- a/rrompy/utilities/poly_fitting/moving_least_squares/base.py
+++ b/rrompy/utilities/poly_fitting/nearest_neighbor/__init__.py
@@ -1,27 +1,27 @@
# 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 .
#
-from rrompy.utilities.poly_fitting.radial_basis.base import (
- rbbases, polybases as rbpb, polyfitname, polydomcoeff)
+from .val import polyval
+from .nearest_neighbor_interpolator import NearestNeighborInterpolator
-__all__ = ['mlsbases', 'polybases', 'polyfitname', 'polydomcoeff']
+__all__ = [
+ 'polyval',
+ 'NearestNeighborInterpolator'
+ ]
-mlsbases = [rbb + "_MLS" for rbb in rbbases]
-
-polybases = [rbp + "_MLS" for rbp in rbpb]
diff --git a/rrompy/utilities/poly_fitting/nearest_neighbor/nearest_neighbor_interpolator.py b/rrompy/utilities/poly_fitting/nearest_neighbor/nearest_neighbor_interpolator.py
new file mode 100644
index 0000000..34a6f47
--- /dev/null
+++ b/rrompy/utilities/poly_fitting/nearest_neighbor/nearest_neighbor_interpolator.py
@@ -0,0 +1,91 @@
+# 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.utilities.base.types import List, ListAny, Np1D, Np2D, paramList
+from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator
+from .val import polyval
+from rrompy.utilities.numerical import dot
+from rrompy.utilities.exception_manager import RROMPyAssert
+from rrompy.parameter import checkParameterList
+
+__all__ = ['NearestNeighborInterpolator']
+
+class NearestNeighborInterpolator(GenericInterpolator):
+ def __init__(self, other = None):
+ if other is None: return
+ self.support = other.support
+ self.coeffsLocal = other.coeffsLocal
+ self.nNeighbors = other.nNeighbors
+ self.directionalWeights = other.directionalWeights
+ self.npar = other.npar
+
+ @property
+ def shape(self):
+ sh = self.coeffsLocal.shape[1 :] if self.coeffsLocal.ndim > 1 else 1
+ return sh
+
+ def __call__(self, mu:paramList, der : List[int] = None,
+ scl : Np1D = None):
+ if der is not None and np.sum(der) > 0:
+ return np.zeros(self.coeffsLocal.shape[1 :] + (len(mu),))
+ return polyval(mu, self.coeffsLocal, self.support,
+ self.nNeighbors, self.directionalWeights)
+
+ def __copy__(self):
+ return NearestNeighborInterpolator(self)
+
+ def __deepcopy__(self, memo):
+ other = NearestNeighborInterpolator()
+ (other.support, other.coeffsLocal, other.nNeighbors,
+ other.directionalWeights, other.npar) = copy((self.support,
+ self.coeffsLocal, self.nNeighbors,
+ self.directionalWeights,
+ self.npar), memo)
+ return other
+
+ def postmultiplyTensorize(self, A:Np2D):
+ RROMPyAssert(A.shape[0], self.shape[-1], "Shape of output")
+ self.coeffsLocal = dot(self.coeffsLocal, A)
+
+ def pad(self, nleft : List[int] = None, nright : List[int] = None):
+ if nleft is None: nleft = [0] * len(self.shape)
+ if nright is None: nright = [0] * len(self.shape)
+ if not hasattr(nleft, "__len__"): nleft = [nleft]
+ if not hasattr(nright, "__len__"): nright = [nright]
+ RROMPyAssert(len(self.shape), len(nleft), "Shape of output")
+ RROMPyAssert(len(self.shape), len(nright), "Shape of output")
+ padwidth = [(0, 0)] + [(l, r) for l, r in zip(nleft, nright)]
+ self.coeffsLocal = np.pad(self.coeffsLocal, padwidth, "constant",
+ constant_values = (0., 0.))
+
+ def setupByInterpolation(self, support:paramList, values:ListAny,
+ nNeighbors : int = 1,
+ directionalWeights : Np1D = None):
+ support = checkParameterList(support)[0]
+ RROMPyAssert(len(support), len(values), "Number of support points")
+ self.support = copy(support)
+ self.npar = support.shape[1]
+ self.coeffsLocal = values
+ self.nNeighbors = max(1, nNeighbors)
+ if directionalWeights is None:
+ directionalWeights = np.ones(self.npar)
+ self.directionalWeights = directionalWeights
+ RROMPyAssert(len(support), len(values), "Number of support points")
+ return True, None
diff --git a/rrompy/utilities/poly_fitting/nearest_neighbor/val.py b/rrompy/utilities/poly_fitting/nearest_neighbor/val.py
new file mode 100644
index 0000000..ccc02ad
--- /dev/null
+++ b/rrompy/utilities/poly_fitting/nearest_neighbor/val.py
@@ -0,0 +1,41 @@
+# 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, paramList
+from rrompy.parameter import checkParameterList
+
+__all__ = ['polyval']
+
+def polyval(x:paramList, cL:Np2D, supportPoints:paramList,
+ nNeighbors : int = 1, directionalWeights : Np1D = None) -> Np2D:
+ supportPoints = checkParameterList(supportPoints)[0]
+ if directionalWeights is None:
+ directionalWeights = np.ones(supportPoints.shape[1])
+ npar = supportPoints.shape[1]
+ x = checkParameterList(x, npar)[0]
+ muDiff = (np.repeat(supportPoints.data.reshape((len(supportPoints), 1,
+ npar)), len(x), axis = 1)
+ - x.data) * directionalWeights
+ dist = (np.sum(np.abs(muDiff) ** 2., axis = 2)
+ + np.finfo(float).eps ** 2.) ** -.5
+ if len(dist) > nNeighbors:
+ iOut = np.argpartition(dist, - nNeighbors, axis = 0)[: - nNeighbors]
+ np.put_along_axis(dist, iOut, 0., 0)
+ dist /= np.linalg.norm(dist, axis = 0, ord = 1)
+ return np.moveaxis(np.tensordot(dist.T, cL, 1), 0, -1)
diff --git a/rrompy/utilities/poly_fitting/radial_basis/__init__.py b/rrompy/utilities/poly_fitting/radial_basis/__init__.py
index 8830a91..98a445b 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/__init__.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/__init__.py
@@ -1,43 +1,42 @@
# 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 .
#
from .kernel import (radialGaussian, thinPlateSpline, multiQuadric,
- localWendland, nearestNeighbor)
+ localWendland)
from .base import rbbases, polybases, polyfitname, polydomcoeff
from .val import polyval
from .vander import rbvander, polyvander, polyvanderTotal
from .radial_basis_interpolator import RadialBasisInterpolator
__all__ = [
'radialGaussian',
'thinPlateSpline',
'multiQuadric',
'localWendland',
- 'nearestNeighbor',
'rbbases',
'polybases',
'polyfitname',
'polydomcoeff',
'polyval',
'rbvander',
'polyvander',
'polyvanderTotal',
'RadialBasisInterpolator'
]
diff --git a/rrompy/utilities/poly_fitting/radial_basis/base.py b/rrompy/utilities/poly_fitting/radial_basis/base.py
index 4fc5818..2423bf0 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/base.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/base.py
@@ -1,45 +1,43 @@
# 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 .
#
from itertools import product
from rrompy.utilities.exception_manager import RROMPyException
from rrompy.utilities.poly_fitting.polynomial.base import (polybases as pbP,
polyfitname as pfnP,
polydomcoeff as polydomcoeffB)
__all__ = ['rbbases', 'polybases', 'polyfitname', 'polydomcoeff']
-rbbases = ["GAUSSIAN", "THINPLATE", "MULTIQUADRIC", "WENDLAND",
- "NEARESTNEIGHBOR"]
+rbbases = ["GAUSSIAN", "THINPLATE", "MULTIQUADRIC", "WENDLAND"]
polybases = [x + "_" + y for x, y in product(pbP, rbbases)]
def polyfitname(basis:str) -> str:
fitrnames = {"GAUSSIAN" : "gaussian", "THINPLATE" : "thinplate",
- "MULTIQUADRIC" : "multiquadric", "WENDLAND" : "wendland",
- "NEARESTNEIGHBOR" : "nearestneighbor"}
+ "MULTIQUADRIC" : "multiquadric", "WENDLAND" : "wendland"}
basisp, basisr = basis.split("_")
try:
return pfnP(basisp) + "_" + fitrnames[basisr]
except:
raise RROMPyException("Polynomial-radial basis combination not "
"recognized.")
def polydomcoeff(n:int, basis:str) -> float:
return polydomcoeffB(n, basis.split("_")[0])
diff --git a/rrompy/utilities/poly_fitting/radial_basis/kernel.py b/rrompy/utilities/poly_fitting/radial_basis/kernel.py
index e2af2b6..e349e58 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/kernel.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/kernel.py
@@ -1,46 +1,42 @@
# 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
from rrompy.utilities.exception_manager import RROMPyAssert
__all__ = ['radialGaussian', 'thinPlateSpline', 'multiQuadric',
- 'localWendland', 'nearestNeighbor']
+ 'localWendland']
def radialGaussian(r2:Np1D, der : int = 0) -> Np1D:
RROMPyAssert(der, 0, "Radial basis derivative")
return np.exp(- .5 * r2)
def thinPlateSpline(r2:Np1D, der : int = 0) -> Np1D:
RROMPyAssert(der, 0, "Radial basis derivative")
return .5 * r2 * np.log(np.finfo(float).eps + r2)
def multiQuadric(r2:Np1D, der : int = 0) -> Np1D:
RROMPyAssert(der, 0, "Radial basis derivative")
return np.power(r2 + 1., -.5)
def localWendland(r2:Np1D, der : int = 0) -> Np1D:
RROMPyAssert(der, 0, "Radial basis derivative")
rm1 = 1. - r2 ** .5
rm1[rm1 <= 0.] = 0.
return rm1 ** 4. * (5. - 4. * rm1)
-
-def nearestNeighbor(r2:Np1D, der : int = 0) -> Np1D:
- RROMPyAssert(der, 0, "Radial basis derivative")
- return 1. * (r2 <= 1.)
diff --git a/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py b/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py
index 9b2ba80..bd84a36 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py
@@ -1,144 +1,137 @@
# 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.utilities.base.types import (List, ListAny, DictAny, Np1D, Np2D,
paramList)
from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator
from rrompy.utilities.poly_fitting.custom_fit import customFit
from .base import polyfitname
from .val import polyval
from .vander import polyvander as pv, polyvanderTotal as pvT
from rrompy.utilities.numerical import dot
from rrompy.utilities.numerical.degree import degreeTotalToFull
from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
from rrompy.parameter import checkParameterList
__all__ = ['RadialBasisInterpolator']
class RadialBasisInterpolator(GenericInterpolator):
def __init__(self, other = None):
if other is None: return
self.support = other.support
self.coeffsGlobal = other.coeffsGlobal
self.coeffsLocal = other.coeffsLocal
self.directionalWeights = other.directionalWeights
self.npar = other.npar
self.polybasis = other.polybasis
- self.nNearestNeighbor = other.nNearestNeighbor
@property
def shape(self):
sh = self.coeffsLocal.shape[1 :] if self.coeffsLocal.ndim > 1 else 1
return sh
@property
def deg(self):
return [x - 1 for x in self.coeffsGlobal.shape[: self.npar]]
def __call__(self, mu:paramList, der : List[int] = None,
scl : Np1D = None):
if der is not None and np.sum(der) > 0:
raise RROMPyException(("Cannot take derivatives of radial basis "
"function."))
return polyval(mu, self.coeffsGlobal, self.coeffsLocal, self.support,
- self.directionalWeights, self.polybasis,
- self.nNearestNeighbor)
+ self.directionalWeights, self.polybasis)
def __copy__(self):
return RadialBasisInterpolator(self)
def __deepcopy__(self, memo):
other = RadialBasisInterpolator()
(other.support, other.coeffsGlobal, other.coeffsLocal,
- other.directionalWeights, other.npar, other.polybasis,
- other.nNearestNeighbor) = copy(
- (self.support, self.coeffsGlobal, self.coeffsLocal,
- self.directionalWeights, self.npar, self.polybasis,
- self.nNearestNeighbor), memo)
+ other.directionalWeights, other.npar, other.polybasis) = copy(
+ (self.support, self.coeffsGlobal,
+ self.coeffsLocal, self.directionalWeights,
+ self.npar, self.polybasis), memo)
return other
def postmultiplyTensorize(self, A:Np2D):
RROMPyAssert(A.shape[0], self.shape[-1], "Shape of output")
self.coeffsLocal = dot(self.coeffsLocal, A)
self.coeffsGlobal = dot(self.coeffsGlobal, A)
def pad(self, nleft : List[int] = None, nright : List[int] = None):
if nleft is None: nleft = [0] * len(self.shape)
if nright is None: nright = [0] * len(self.shape)
if not hasattr(nleft, "__len__"): nleft = [nleft]
if not hasattr(nright, "__len__"): nright = [nright]
RROMPyAssert(len(self.shape), len(nleft), "Shape of output")
RROMPyAssert(len(self.shape), len(nright), "Shape of output")
padwidth = [(0, 0)] + [(l, r) for l, r in zip(nleft, nright)]
self.coeffsLocal = np.pad(self.coeffsLocal, padwidth, "constant",
constant_values = (0., 0.))
padwidth = [(0, 0)] * (self.npar - 1) + padwidth
self.coeffsGlobal = np.pad(self.coeffsGlobal, padwidth, "constant",
constant_values = (0., 0.))
def setupByInterpolation(self, support:paramList, values:ListAny,
deg:int, polybasis : str = "MONOMIAL_GAUSSIAN",
directionalWeights : Np1D = None,
verbose : bool = True, totalDegree : bool = True,
vanderCoeffs : DictAny = {},
fitCoeffs : DictAny = {}):
support = checkParameterList(support)[0]
self.support = copy(support)
if "reorder" in vanderCoeffs.keys():
self.support = self.support[vanderCoeffs["reorder"]]
- if "nNearestNeighbor" in vanderCoeffs.keys():
- self.nNearestNeighbor = vanderCoeffs["nNearestNeighbor"]
- else:
- self.nNearestNeighbor = -1
self.npar = support.shape[1]
if directionalWeights is None:
directionalWeights = np.ones(self.npar)
self.directionalWeights = directionalWeights
self.polybasis = polybasis
if totalDegree:
vander = pvT(support, deg, basis = polybasis,
directionalWeights = self.directionalWeights,
**vanderCoeffs)
else:
if not hasattr(deg, "__len__"): deg = [deg] * self.npar
vander = pv(support, deg, basis = polybasis,
directionalWeights = self.directionalWeights,
**vanderCoeffs)
outDim = values.shape[1:]
values = values.reshape(values.shape[0], -1)
values = np.pad(values, ((0, len(vander) - len(values)), (0, 0)),
"constant")
fitOut = customFit(vander, values, full = True, **fitCoeffs)
P = fitOut[0][len(support) :]
if verbose:
msg = ("Fitting {}+{} samples with degree {} through {}... "
"Conditioning of LS system: {:.4e}.").format(
len(support), len(vander) - len(support),
deg, polyfitname(self.polybasis),
fitOut[1][2][0] / fitOut[1][2][-1])
else: msg = None
self.coeffsLocal = fitOut[0][: len(support)]
if totalDegree:
self.coeffsGlobal = degreeTotalToFull(tuple([deg + 1] * self.npar)
+ outDim, self.npar, P)
else:
self.coeffsGlobal = P.reshape(tuple([d + 1 for d in deg]) + outDim)
return fitOut[1][1] == vander.shape[1], msg
diff --git a/rrompy/utilities/poly_fitting/radial_basis/val.py b/rrompy/utilities/poly_fitting/radial_basis/val.py
index 25d9ac5..f5c8405 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/val.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/val.py
@@ -1,63 +1,54 @@
# 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 .
#
from copy import deepcopy as copy
import numpy as np
from .kernel import (radialGaussian, thinPlateSpline, multiQuadric,
- localWendland, nearestNeighbor)
+ localWendland)
from rrompy.utilities.poly_fitting.polynomial.val import polyval as pvP
from rrompy.utilities.base.types import Np1D, Np2D, paramList
from rrompy.parameter import checkParameterList
from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['polyval']
def polyval(x:paramList, cG:Np2D, cL:Np2D, supportPoints:paramList,
- directionalWeights:Np1D, basis:str,
- nNearestNeighbor : int = -1) -> Np2D:
+ directionalWeights:Np1D, basis:str) -> Np2D:
x = checkParameterList(x)[0]
basisp, basisr = basis.split("_")
c = pvP(x, cG, basisp)
try:
- radialvalbase = {
- "GAUSSIAN" : radialGaussian, "THINPLATE" : thinPlateSpline,
- "MULTIQUADRIC" : multiQuadric, "WENDLAND" : localWendland,
- "NEARESTNEIGHBOR" : nearestNeighbor}[basisr.upper()]
+ radialvalbase = {"GAUSSIAN" : radialGaussian,
+ "THINPLATE" : thinPlateSpline,
+ "MULTIQUADRIC" : multiQuadric,
+ "WENDLAND" : localWendland}[basisr.upper()]
except:
raise RROMPyException("Radial basis not recognized.")
supportPoints = checkParameterList(supportPoints)[0]
- isnearestneighbor = basisr.upper() == "NEARESTNEIGHBOR"
csh = copy(c.shape)
if len(csh) == 1: c = c.reshape(1, -1)
for j in range(len(x)):
muDiff = (supportPoints.data - x[j]) * directionalWeights
r2j = np.sum(np.abs(muDiff) ** 2., axis = 1).reshape(1, -1)
- if isnearestneighbor:
- if nNearestNeighbor > 0 and nNearestNeighbor < len(supportPoints):
- cutoffValue = np.partition(r2j, nNearestNeighbor - 1)[0,
- nNearestNeighbor - 1]
- r2j /= cutoffValue
- else:
- r2j[0, :] = 1. * (nNearestNeighbor == 0)
- val = radialvalbase(r2j).dot(cL)
+ val = np.tensordot(radialvalbase(r2j), cL, 1)
try:
c[..., j] += val
except:
c[..., j] += val.flatten()
if len(csh) == 1: c = c.flatten()
return c
diff --git a/rrompy/utilities/poly_fitting/radial_basis/vander.py b/rrompy/utilities/poly_fitting/radial_basis/vander.py
index ba246de..773a5b3 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/vander.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/vander.py
@@ -1,108 +1,97 @@
# 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 .kernel import (radialGaussian, thinPlateSpline, multiQuadric,
- localWendland, nearestNeighbor)
+ localWendland)
from rrompy.utilities.poly_fitting.polynomial.vander import (polyvander as pvP,
polyvanderTotal as pvTP)
from rrompy.utilities.base.types import Np1D, Np2D, List, paramList
from rrompy.parameter import checkParameterList
from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
__all__ = ['rbvander', 'polyvander', 'polyvanderTotal']
def rbvander(x:paramList, basis:str, reorder : List[int] = None,
- directionalWeights : Np1D = None,
- nNearestNeighbor : int = -1) -> Np2D:
+ directionalWeights : Np1D = None) -> Np2D:
"""Compute radial-basis-Vandermonde matrix."""
x = checkParameterList(x)[0]
x_un = x.unique()
nx = len(x)
if len(x_un) < nx:
raise RROMPyException("Sample points must be distinct.")
del x_un
x = x.data
if directionalWeights is None:
directionalWeights = np.ones(x.shape[1])
elif not hasattr(directionalWeights, "__len__"):
directionalWeights = directionalWeights * np.ones(x.shape[1])
RROMPyAssert(len(directionalWeights), x.shape[1],
"Number of directional weights")
try:
- radialkernel = {
- "GAUSSIAN" : radialGaussian, "THINPLATE" : thinPlateSpline,
- "MULTIQUADRIC" : multiQuadric, "WENDLAND" : localWendland,
- "NEARESTNEIGHBOR" : nearestNeighbor}[basis.upper()]
+ radialkernel = {"GAUSSIAN" : radialGaussian,
+ "THINPLATE" : thinPlateSpline,
+ "MULTIQUADRIC" : multiQuadric,
+ "WENDLAND" : localWendland}[basis.upper()]
except:
raise RROMPyException("Radial basis not recognized.")
- isnearestneighbor = basis.upper() == "NEARESTNEIGHBOR"
Van = np.zeros((nx, nx))
if reorder is not None: x = x[reorder]
for j in range(nx):
muDiff = (x - x[j]) * directionalWeights
r2j = np.sum(np.abs(muDiff) ** 2., axis = 1).reshape(1, -1)
- if isnearestneighbor:
- if nNearestNeighbor > 0 and nNearestNeighbor < len(x):
- cutoffValue = np.partition(r2j, nNearestNeighbor - 1)[0,
- nNearestNeighbor - 1]
- r2j /= cutoffValue
- else:
- r2j[0, :] = 1. * (nNearestNeighbor == 0)
Van[j] = radialkernel(r2j)
return Van
def polyvander(x:paramList, degs:List[int], basis:str,
derIdxs : List[List[List[int]]] = None,
reorder : List[int] = None, directionalWeights : Np1D = None,
- scl : Np1D = None, nNearestNeighbor : int = -1) -> Np2D:
+ 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 radial basis "
"function."))
basisp, basisr = basis.split("_")
VanR = rbvander(x, basisr, reorder = reorder,
- directionalWeights = directionalWeights,
- nNearestNeighbor = nNearestNeighbor)
+ directionalWeights = directionalWeights)
VanP = pvP(x, degs, basisp, derIdxs = derIdxs, reorder = reorder,
scl = scl)
return np.block([[VanR, VanP],
[VanP.T.conj(), np.zeros(tuple([VanP.shape[1]] * 2))]])
def polyvanderTotal(x:paramList, deg:int, basis:str,
derIdxs : List[List[List[int]]] = None,
reorder : List[int] = None,
- directionalWeights : Np1D = None, scl : Np1D = None,
- nNearestNeighbor : int = -1) -> Np2D:
+ directionalWeights : Np1D = 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, basisr = basis.split("_")
VanR = rbvander(x, basisr, reorder = reorder,
- directionalWeights = directionalWeights,
- nNearestNeighbor = nNearestNeighbor)
+ directionalWeights = directionalWeights)
VanP = pvTP(x, deg, basisp, derIdxs = derIdxs, reorder = reorder, scl = scl)
return np.block([[VanR, VanP],
[VanP.T.conj(), np.zeros(tuple([VanP.shape[1]] * 2))]])
diff --git a/setup.py b/setup.py
index efda160..9f721f5 100644
--- a/setup.py
+++ b/setup.py
@@ -1,52 +1,52 @@
# 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 os
from setuptools import find_packages, setup
rrompy_directory = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
#rrompy_directory = os.path.join(rrompy_directory, 'rrompy')
setup(name="RROMPy",
description="Rational reduced order modelling in Python",
long_description="Rational reduced order modelling in Python",
author="Davide Pradovera",
author_email="davide.pradovera@epfl.ch",
- version="2.3",
+ version="2.4",
license="GNU Library or Lesser General Public License (LGPL)",
classifiers=[
"Development Status :: 1 - Planning"
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Topic :: Scientific/Engineering :: Mathematics",
"Topic :: Software Development :: Libraries :: Python Modules",
],
packages=find_packages(rrompy_directory),
setup_requires=[
"pytest-runner"
],
tests_require=[
"pytest"
],
zip_safe=False
)
diff --git a/tests/reduction_methods_multiD/greedy_pivoted_rational_2d.py b/tests/reduction_methods_multiD/greedy_pivoted_rational_2d.py
index e827904..efc737f 100644
--- a/tests/reduction_methods_multiD/greedy_pivoted_rational_2d.py
+++ b/tests/reduction_methods_multiD/greedy_pivoted_rational_2d.py
@@ -1,83 +1,83 @@
# 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 matrix_random import matrixRandom
from rrompy.reduction_methods import (
RationalInterpolantPivotedGreedy as RIPG,
RationalInterpolantGreedyPivotedGreedy as RIGPG)
-from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
-from rrompy.parameter import localSparseGrid as LSG
+from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
+ SparseGridSampler as SGS)
-def test_greedy_pivoted():
+def test_pivoted_greedy():
mu = [5.05, 7.1]
mu0 = [5., 7.]
solver = matrixRandom()
uh = solver.solve(mu)[0]
params = {"POD": True, "S": 5, "polybasis": "CHEBYSHEV",
"samplerPivot": QS([4.75, 5.25], "CHEBYSHEV"),
"MMarginal": 1, "SMarginal": 3, "greedyTolMarginal": 1e-2,
"radialDirectionalWeightsMarginal": 2.,
"polybasisMarginal": "MONOMIAL_GAUSSIAN",
- "matchingWeight": 1., "samplerMarginalGrid":LSG([6.75, 7.25])}
+ "matchingWeight": 1., "samplerMarginalGrid":SGS([6.75, 7.25])}
approx = RIPG([0], solver, mu0, approx_state = True,
approxParameters = params, verbosity = 0)
approx.setupApprox()
uhP1 = approx.getApprox(mu)[0]
errP = approx.getErr(mu)[0]
errNP = approx.normErr(mu)[0]
myerrP = uhP1 - uh
assert np.allclose(np.abs(errP - myerrP), 0., rtol = 1e-3)
assert np.isclose(solver.norm(errP), errNP, rtol = 1e-3)
resP = approx.getRes(mu)[0]
resNP = approx.normRes(mu)
assert np.isclose(solver.norm(resP), resNP, rtol = 1e-3)
assert np.allclose(np.abs(resP - (solver.b(mu) - solver.A(mu).dot(uhP1))),
0., rtol = 1e-3)
assert np.isclose(errNP / solver.norm(uh), 6.0631706e-04, rtol = 1)
def test_greedy_pivoted_greedy():
mu = [5.05, 7.1]
mu0 = [5., 7.]
solver = matrixRandom()
uh = solver.solve(mu)[0]
params = {"POD": True, "nTestPoints": 100, "greedyTol": 1e-3, "S": 2,
"polybasis": "CHEBYSHEV",
"samplerPivot": QS([4.75, 5.25], "CHEBYSHEV"),
"trainSetGenerator": QS([4.75, 5.25], "CHEBYSHEV"),
"MMarginal": 1, "SMarginal": 3, "greedyTolMarginal": 1e-2,
"radialDirectionalWeightsMarginal": 2.,
"polybasisMarginal": "MONOMIAL_GAUSSIAN",
- "matchingWeight": 1., "samplerMarginalGrid":LSG([6.75, 7.25])}
+ "matchingWeight": 1., "samplerMarginalGrid":SGS([6.75, 7.25])}
approx = RIGPG([0], solver, mu0, approx_state = True,
approxParameters = params, verbosity = 0)
approx.setupApprox()
uhP1 = approx.getApprox(mu)[0]
errP = approx.getErr(mu)[0]
errNP = approx.normErr(mu)[0]
myerrP = uhP1 - uh
assert np.allclose(np.abs(errP - myerrP), 0., rtol = 1e-3)
assert np.isclose(solver.norm(errP), errNP, rtol = 1e-3)
resP = approx.getRes(mu)[0]
resNP = approx.normRes(mu)
assert np.isclose(solver.norm(resP), resNP, rtol = 1e-3)
assert np.allclose(np.abs(resP - (solver.b(mu) - solver.A(mu).dot(uhP1))),
0., rtol = 1e-3)
assert np.isclose(errNP / solver.norm(uh), 6.0631706e-04, rtol = 1)
diff --git a/tests/utilities/nearest_neighbor_fitting.py b/tests/utilities/nearest_neighbor_fitting.py
new file mode 100644
index 0000000..695e953
--- /dev/null
+++ b/tests/utilities/nearest_neighbor_fitting.py
@@ -0,0 +1,63 @@
+# 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.nearest_neighbor import polyval
+from rrompy.parameter import checkParameterList
+
+def test_nearest_neighbor_1d():
+ directionalWeights = [1.]
+ nNeighbors = 1
+ xSupp = checkParameterList(np.arange(-1, 3), 1)[0]
+ ySupp = np.array([-1., 3., -3., 1.])
+ xx = np.linspace(-2., 3., 100)
+ yy = polyval(checkParameterList(xx, 1)[0], ySupp, xSupp, nNeighbors,
+ directionalWeights)
+
+ nn = [np.where(xx < -.5), np.where(np.logical_and(xx > -.5, xx < .5)),
+ np.where(np.logical_and(xx > .5, xx < 1.5)), np.where(xx > 1.5)]
+ for j in range(4): assert np.allclose(yy[nn[j]], ySupp[j], atol = 1e-5)
+
+def test_nearest_neighbor_exact_1d():
+ directionalWeights = [1.]
+ nNeighbors = 2
+ xSupp = checkParameterList(np.arange(3), 1)[0]
+ ySupp = np.array([-1., 3., -3.])
+ xx = np.linspace(0., 1., 100)
+ yy = polyval(checkParameterList(xx, 1)[0], ySupp, xSupp, nNeighbors,
+ directionalWeights)
+ assert np.allclose(yy, -1. + 4 * xx, atol = 1e-5)
+ xxL = np.linspace(-10., -.5, 100)
+ yyL = polyval(checkParameterList(xxL, 1)[0], ySupp, xSupp, nNeighbors,
+ directionalWeights)
+ xxR = np.linspace(1.5, 9., 100)
+ yyR = polyval(checkParameterList(xxR, 1)[0], ySupp, xSupp, nNeighbors,
+ directionalWeights)
+ assert np.all(yyL > -1. + 4 * xxL) and np.all(yyR < -1. + 4 * xxR)
+
+def test_nearest_neighbors_bounded_2d():
+ directionalWeights = [5., 1.]
+ nNeighbors = 3
+ xSupp = checkParameterList([[0., 0.], [1., 0.], [0., 1.], [0., 10.]], 2)[0]
+ ySupp = np.array([1., 2., -1., 1e5])
+ x1 = np.tile(np.linspace(0., 1., 100), 100)
+ x2 = np.repeat(np.linspace(0., 1., 100), 100)
+ xx = np.hstack((x1.reshape(-1, 1), x2.reshape(-1, 1)))
+ yy = polyval(checkParameterList(xx, 2)[0], ySupp, xSupp, nNeighbors,
+ directionalWeights)
+ assert np.all(yy <= 2.) and np.all(yy >= -1.)