diff --git a/examples/3d/pod/fracture3_pod.py b/examples/3d/pod/fracture3_pod.py
index d1eb22f..b1b9a60 100644
--- a/examples/3d/pod/fracture3_pod.py
+++ b/examples/3d/pod/fracture3_pod.py
@@ -1,195 +1,195 @@
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
from rrompy.hfengines.linear_problem.tridimensional import \
MembraneFractureEngine3 as MFE
from rrompy.reduction_methods.distributed import RationalInterpolant as RI
from rrompy.reduction_methods.distributed import RBDistributed as RBD
from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
QuadratureSamplerTotal as QST,
ManualSampler as MS,
RandomSampler as RS)
verb = 50
size = 4
show_sample = False
show_norm = False
clip = -1
#clip = .4
#clip = .6
homogeneize = False
#homogeneize = True
Delta = 0
-MN = 10
+MN = 5
R = (MN + 3) * (MN + 2) * (MN + 1) // 6
S = [int(np.ceil(R ** (1. / 3.)))] * 3
PODTol = 1e-8
samples = "centered"
samples = "distributed"
algo = "rational"
#algo = "RB"
sampling = "quadrature"
sampling = "quadrature_total"
#sampling = "random"
if samples == "distributed":
radial = 0
-# radial = "gaussian"
+ radial = "gaussian"
# radial = "thinplate"
# radial = "multiquadric"
rW0 = 5.
- radialWeight = [rW0] * 2
+ radialWeight = [rW0] * 3
assert Delta <= 0
if size == 1:
mu0 = [47.5 ** .5, .4, .05]
mutar = [50 ** .5, .45, .07]
murange = [[40 ** .5, .3, .025], [55 ** .5, .5, .075]]
if size == 2:
mu0 = [50 ** .5, .3, .02]
mutar = [55 ** .5, .4, .03]
murange = [[40 ** .5, .1, .005], [60 ** .5, .5, .035]]
if size == 3:
mu0 = [45 ** .5, .5, .05]
mutar = [47 ** .5, .4, .045]
murange = [[40 ** .5, .3, .04], [50 ** .5, .7, .06]]
if size == 4:
mu0 = [45 ** .5, .4, .05]
mutar = [47 ** .5, .45, .045]
murange = [[40 ** .5, .3, .04], [50 ** .5, .5, .06]]
aEff = 1.#25
bEff = 1. - aEff
murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5,
aEff*murange[0][1] + bEff*murange[1][1],
aEff*murange[0][2] + bEff*murange[1][2]],
[(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5,
aEff*murange[1][1] + bEff*murange[0][1],
aEff*murange[1][2] + bEff*murange[0][2]]]
H = 1.
L = .75
delta = .05
n = 50
solver = MFE(mu0 = mu0, H = H, L = L, delta = delta, n = n, verbosity = verb)
rescaling = [lambda x: np.power(x, 2.), lambda x: x, lambda x: x]
rescalingInv = [lambda x: np.power(x, .5), lambda x: x, lambda x: x]
if algo == "rational":
params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True}
if samples == "distributed":
params['polybasis'] = "CHEBYSHEV"
# params['polybasis'] = "LEGENDRE"
# params['polybasis'] = "MONOMIAL"
params['E'] = MN
params['radialBasis'] = radial
params['radialBasisWeights'] = radialWeight
method = RI
elif samples == "centered":
params['polybasis'] = "MONOMIAL"
params['S'] = R
method = RI
else: #if algo == "RB":
params = {'R':(MN + 3 + Delta) * (MN + 2 + Delta) * (MN + 1 + Delta) // 6,
'S':S, 'POD':True, 'PODTolerance':PODTol}
if samples == "distributed":
method = RBD
elif samples == "centered":
params['S'] = R
method = RBD
if samples == "distributed":
if sampling == "quadrature":
params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling,
# scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling,
# scalingInv = rescalingInv)
params['S'] = [max(j, MN + 1) for j in params['S']]
elif sampling == "quadrature_total":
params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
else: # if sampling == "random":
params['sampler'] = RS(murange, "HALTON", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
elif samples == "centered":
params['sampler'] = MS(murange, points = [mu0], scaling = rescaling,
scalingInv = rescalingInv)
approx = method(solver, mu0 = mu0, approxParameters = params,
verbosity = verb, homogeneized = homogeneize)
if samples == "distributed": approx.samplingEngine.allowRepeatedSamples = False
approx.setupApprox()
if show_sample:
from fracture3_warping import fracture3_warping
warps = fracture3_warping(solver.V.mesh(), L, mutar[1], delta, mutar[2])
approx.plotApprox(mutar, warps, name = 'u_app',
homogeneized = False, what = "REAL")
approx.plotHF(mutar, warps, name = 'u_HF',
homogeneized = False, what = "REAL")
approx.plotErr(mutar, warps, name = 'err',
homogeneized = False, what = "REAL")
# approx.plotRes(mutar, warps, name = 'res',
# homogeneized = False, what = "REAL")
appErr = approx.normErr(mutar, homogeneized = homogeneize)
solNorm = approx.normHF(mutar, homogeneized = homogeneize)
resNorm = approx.normRes(mutar, homogeneized = homogeneize)
RHSNorm = approx.normRHS(mutar, homogeneized = homogeneize)
print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
np.divide(appErr, solNorm)))
print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
np.divide(resNorm, RHSNorm)))
fig = plt.figure(figsize = (8, 6))
ax = Axes3D(fig)
ax.scatter(approx.trainedModel.data.mus(0) ** 2.,
approx.trainedModel.data.mus(1),
approx.trainedModel.data.mus(2), '.')
plt.show()
plt.close()
approx.verbosity = 0
approx.trainedModel.verbosity = 0
if algo == "rational" and approx.N > 0:
from plot_zero_set_3 import plotZeroSet3
# muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200,
# [None, mu0[1], mu0[2]], [2., 1., 1.],
# clip = clip)
# muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200,
# [None, None, mu0[2]], [2., 1., 1.],
# clip = clip)
# muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200,
# [None, mu0[1], None], [2., 1., 1.],
# clip = clip)
muZeroScatter = plotZeroSet3(murange, murangeEff, approx, mu0, 50,
[None, None, None], [2., 1., 1.], clip = clip)
if show_norm:
solver._solveBatchSize = 25
from plot_inf_set_3 import plotInfSet3
muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3(
murange, murangeEff, approx, mu0, 25,
[None, mu0[1], mu0[2]], [2., 1., 1.],
clip = clip, relative = False,
normalizeDen = True)
muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3(
murange, murangeEff, approx, mu0, 25,
[None, None, mu0[2]], [2., 1., 1.],
clip = clip, relative = False,
normalizeDen = True)
muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3(
murange, murangeEff, approx, mu0, 25,
[None, mu0[1], None], [2., 1., 1.],
clip = clip, relative = False,
normalizeDen = True)
print(approx.getPoles([None, .5, .05]) ** 2.)
diff --git a/rrompy/reduction_methods/distributed/rational_interpolant.py b/rrompy/reduction_methods/distributed/rational_interpolant.py
index 3845d8e..d8ce41e 100644
--- a/rrompy/reduction_methods/distributed/rational_interpolant.py
+++ b/rrompy/reduction_methods/distributed/rational_interpolant.py
@@ -1,571 +1,548 @@
# 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_distributed_approximant import GenericDistributedApproximant
-from rrompy.utilities.poly_fitting import customFit, customPInv
+from rrompy.utilities.poly_fitting import customPInv
from rrompy.utilities.poly_fitting.polynomial import (polybases, polyfitname,
nextDerivativeIndices,
hashDerivativeToIdx as hashD,
hashIdxToDerivative as hashI,
homogeneizedpolyvander as hpvP,
- homogeneizedToFull)
+ homogeneizedToFull,
+ PolynomialInterpolator as PI)
from rrompy.utilities.poly_fitting.radial_basis import (rbbases,
- radialFunction,
- polyfitname as polyfitnameR,
- homogeneizedpolyvander as hpvR)
+ RadialBasisInterpolator as RBI)
from rrompy.reduction_methods.trained_model import TrainedModelPade as tModel
from rrompy.reduction_methods.trained_model import TrainedModelData
from rrompy.utilities.base.types import (Np1D, Np2D, HFEng, DictAny, Tuple,
List, paramVal, paramList, sampList)
from rrompy.utilities.base import verbosityManager as vbMng, multifactorial
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPyWarning)
__all__ = ['RationalInterpolant']
class RationalInterpolant(GenericDistributedApproximant):
"""
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;
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator;
- 'polybasis': type of polynomial basis for interpolation; allowed
values include 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults
to 'MONOMIAL';
- 'E': number of derivatives used to compute interpolant; defaults
to 0;
- 'M': degree of Pade' interpolant numerator; defaults to 0;
- 'N': degree of Pade' interpolant denominator; defaults to 0;
- 'radialBasis': radial basis family for interpolant numerator;
defaults to 0, i.e. no radial basis;
- 'radialBasisWeights': radial basis weights for interpolant
numerator; defaults to 0, i.e. identity;
- 'interpRcond': tolerance for interpolation; defaults to None;
- 'robustTol': tolerance for robust Pade' denominator management;
defaults to 0.
Defaults to empty dict.
homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults
to False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
homogeneized: Whether to homogeneize Dirichlet BCs.
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;
- 'polybasis': type of polynomial basis for interpolation;
- 'E': number of derivatives used to compute interpolant;
- 'M': degree of Pade' interpolant numerator;
- 'N': degree of Pade' interpolant denominator;
- 'radialBasis': radial basis family for interpolant numerator;
- 'radialBasisWeights': radial basis weights for interpolant
numerator;
- 'interpRcond': tolerance for interpolation via numpy.polyfit;
- 'robustTol': tolerance for robust Pade' denominator management.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
POD: Whether to compute POD of snapshots.
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.
radialBasis: Radial basis family for interpolant numerator.
radialBasisWeights: Radial basis weights for interpolant numerator.
interpRcond: Tolerance for interpolation via numpy.polyfit.
robustTol: Tolerance for robust Pade' denominator management.
E: Complete derivative depth level of S.
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, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
self._addParametersToList(["polybasis", "E", "M", "N", "radialBasis",
"radialBasisWeights", "interpRcond",
"robustTol"],
["MONOMIAL", -1, 0, 0, 0, 1, -1, 0])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity, timestamp = timestamp)
self._postInit()
@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("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):
try:
if radialBasis != 0:
radialBasis = radialBasis.upper().strip().replace(" ","")
if radialBasis not in rbbases:
raise RROMPyException(("Prescribed radialBasis not "
"recognized."))
self._radialBasis = radialBasis
except:
RROMPyWarning(("Prescribed radialBasis not recognized. Overriding "
"to 0."))
self._radialBasis = 0
self._approxParameters["radialBasis"] = self.radialBasis
@property
def polybasisP(self):
if self.radialBasis == 0:
return self._polybasis
return self._polybasis + "_" + self.radialBasis
@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 radialBasisWeights(self):
"""Value of radialBasisWeights."""
return self._radialBasisWeights
@radialBasisWeights.setter
def radialBasisWeights(self, radialBasisWeights):
self._radialBasisWeights = radialBasisWeights
self._approxParameters["radialBasisWeights"] = self.radialBasisWeights
@property
def M(self):
"""Value of M. Its assignment may change S."""
return self._M
@M.setter
def M(self, M):
if M < 0: raise RROMPyException("M must be non-negative.")
self._M = M
self._approxParameters["M"] = self.M
if hasattr(self, "_E") and self.E >= 0 and self.E < self.M:
RROMPyWarning("Prescribed S is too small. Decreasing M.")
self.M = self.E
@property
def N(self):
"""Value of N. Its assignment may change S."""
return self._N
@N.setter
def N(self, N):
if N < 0: raise RROMPyException("N must be non-negative.")
self._N = N
self._approxParameters["N"] = self.N
if hasattr(self, "_E") and self.E >= 0 and self.E < self.N:
RROMPyWarning("Prescribed S is too small. Decreasing N.")
self.N = self.E
@property
def E(self):
"""Value of E."""
return self._E
@E.setter
def E(self, E):
if E < 0:
if not hasattr(self, "_S"):
raise RROMPyException(("Value of E must be positive if S is "
"not yet assigned."))
E = np.sum(hashI(np.prod(self.S), self.npar)) - 1
self._E = E
self._approxParameters["E"] = self.E
if (hasattr(self, "_S")
and self.E >= np.sum(hashI(np.prod(self.S), self.npar))):
RROMPyWarning("Prescribed S is too small. Decreasing E.")
self.E = -1
if hasattr(self, "_M"): self.M = self.M
if hasattr(self, "_N"): self.N = self.N
@property
def robustTol(self):
"""Value of tolerance for robust Pade' 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 S(self):
"""Value of S."""
return self._S
@S.setter
def S(self, S):
GenericDistributedApproximant.S.fset(self, S)
if hasattr(self, "_M"): self.M = self.M
if hasattr(self, "_N"): self.N = self.N
if hasattr(self, "_E"): self.E = self.E
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
self._musUniqueCN = None
self._derIdxs = None
self._reorder = None
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.mus):
self._musUniqueCN, musIdxsTo, musIdxs, musCount = (
self.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 Pade' denominator."""
RROMPyAssert(self._mode, message = "Cannot setup denominator.")
vbMng(self, "INIT", "Starting computation of denominator.", 7)
while self.N > 0:
invD = self._computeInterpolantInverseBlocks()
if self.POD:
ev, eV = self.findeveVGQR(self.samplingEngine.RPOD, invD)
else:
ev, eV = self.findeveVGExplicit(self.samplingEngine.samples,
invD)
newParams = checkRobustTolerance(ev, self.N, self.robustTol)
if not newParams:
break
self.approxParameters = newParams
if self.N <= 0:
self._N = 0
eV = np.ones((1, 1))
- q = homogeneizedToFull(tuple([self.N + 1] * self.npar), self.npar,
- eV[:, 0])
+ q = PI()
+ q.npar = self.npar
+ q.polybasis = self.polybasis
+ q.coeffs = homogeneizedToFull(tuple([self.N + 1] * self.npar),
+ self.npar, eV[:, 0])
vbMng(self, "DEL", "Done computing denominator.", 7)
return q
def _setupNumerator(self):
"""Compute Pade' numerator."""
RROMPyAssert(self._mode, message = "Cannot setup numerator.")
vbMng(self, "INIT", "Starting computation of numerator.", 7)
Qevaldiag = np.zeros((len(self.mus), len(self.mus)),
dtype = np.complex)
verb = self.trainedModel.verbosity
self.trainedModel.verbosity = 0
self._setupInterpolationIndices()
idxGlob = 0
for j, derIdxs in enumerate(self._derIdxs):
nder = len(derIdxs)
idxLoc = np.arange(len(self.mus))[(self._reorder >= idxGlob)
* (self._reorder < idxGlob + nder)]
idxGlob += nder
Qval = [0] * nder
for der in range(nder):
derIdx = hashI(der, self.npar)
Qval[der] = (self.trainedModel.getQVal(
self._musUnique[j], derIdx,
scl = np.power(self.scaleFactor, -1.))
/ multifactorial(derIdx))
for derU, derUIdx in enumerate(derIdxs):
for derQ, derQIdx in enumerate(derIdxs):
diffIdx = [x - y for (x, y) in zip(derUIdx, derQIdx)]
if all([x >= 0 for x in diffIdx]):
diffj = hashD(diffIdx)
Qevaldiag[idxLoc[derU], idxLoc[derQ]] = Qval[diffj]
if self.POD:
Qevaldiag = Qevaldiag.dot(self.samplingEngine.RPOD.T)
self.trainedModel.verbosity = verb
while self.M >= 0:
if self.radialBasis == 0:
- fitVander, _, argIdxs = hpvP(self._musUniqueCN, self.M,
- self.polybasisP, self._derIdxs,
- self._reorder,
- scl = np.power(self.scaleFactor, -1.))
- fitnameEff = polyfitname(self.polybasisP)
- nsamplesPrint = "{}".format(len(fitVander))
+ p = PI()
+ wellCond, msg = p.setupByInterpolation(
+ self._musUniqueCN, Qevaldiag, self.M,
+ self.polybasisP, self.verbosity >= 5, True,
+ {"derIdxs": self._derIdxs, "reorder": self._reorder,
+ "scl": np.power(self.scaleFactor, -1.)},
+ {"rcond": self.interpRcond})
else:
- fitVander, _, argIdxs = hpvR(self._musUniqueCN, self.M,
- self.polybasisP, self._derIdxs,
- self._reorder, self.radialBasisWeights,
- scl = np.power(self.scaleFactor, -1.))
- fitnameEff = polyfitnameR(self.polybasisP)
- nConstraints = len(fitVander) - len(Qevaldiag)
- if nConstraints > 0:
- Qevaldiag = np.pad(Qevaldiag, ((0, nConstraints), (0, 0)),
- "constant")
- elif nConstraints < 0:
- Qevaldiag = Qevaldiag[: len(fitVander)]
- fitVander = fitVander[argIdxs]
- nsamplesPrint = "{}+{}".format(len(self.mus),
- len(fitVander) - len(self.mus))
- fitVander = fitVander[:, argIdxs]
- fitOut = customFit(fitVander, Qevaldiag, full = True,
- rcond = self.interpRcond)
- vbMng(self, "MAIN",
- ("Fitting {} samples with degree {} through {}... "
- "Conditioning of LS system: {:.4e}.").format(
- nsamplesPrint, self.M, fitnameEff,
- fitOut[1][2][0] / fitOut[1][2][-1]),
- 5)
- if fitOut[1][1] == fitVander.shape[1]:
- P = fitOut[0]
- break
+ p = RBI()
+ wellCond, msg = p.setupByInterpolation(
+ self._musUniqueCN, Qevaldiag, self.M, self.polybasisP,
+ self.radialBasisWeights, self.verbosity >= 5, True,
+ {"derIdxs": self._derIdxs, "reorder": self._reorder,
+ "scl": np.power(self.scaleFactor, -1.)},
+ {"rcond": self.interpRcond})
+ vbMng(self, "MAIN", msg, 5)
+ if wellCond: break
RROMPyWarning("Polyfit is poorly conditioned. Reducing M by 1.")
self.M -= 1
if self.M < 0:
raise RROMPyException(("Instability in computation of numerator. "
"Aborting."))
- if self.radialBasis == 0:
- p = homogeneizedToFull(tuple([self.M + 1] * self.npar)
- + (P.shape[1],), self.npar, P)
- else:
- pGlob = homogeneizedToFull(
- tuple([self.M + 1] * self.npar) + (P.shape[1],),
- self.npar, P[len(self.mus) :])
- p = radialFunction(self._musUniqueCN[self._reorder],
- self.radialBasisWeights,
- P[: len(self.mus)], pGlob)
vbMng(self, "DEL", "Done computing numerator.", 7)
return p
def setupApprox(self):
"""
Compute Pade' interpolant.
SVD-based robust eigenvalue management.
"""
if self.checkComputedApprox():
return
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5)
self.computeScaleFactor()
self.computeSnapshots()
if self.trainedModel is None:
self.trainedModel = tModel()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
data = TrainedModelData(self.trainedModel.name(), self.mu0,
self.samplingEngine.samples,
self.HFEngine.rescalingExp)
- data.polytype = self.polybasis
- data.polytypeP = self.polybasisP
data.scaleFactor = self.scaleFactor
data.mus = copy(self.mus)
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(self.samplingEngine.samples)
if self.N > 0:
Q = self._setupDenominator()
else:
- Q = np.ones(tuple([1] * self.npar), dtype = np.complex)
- self.trainedModel.data.Q = copy(Q)
- self.trainedModel.data.P = copy(self._setupNumerator())
+ Q = PI()
+ Q.coeffs = np.ones(tuple([1] * self.npar), dtype = np.complex)
+ Q.npar = self.npar
+ Q.polybasis = self.polybasis
+ self.trainedModel.data.Q = Q
+ self.trainedModel.data.P = self._setupNumerator()
self.trainedModel.data.approxParameters = copy(self.approxParameters)
vbMng(self, "DEL", "Done setting up approximant.", 5)
def _computeInterpolantInverseBlocks(self) -> List[Np2D]:
"""
Compute inverse factors for minimal interpolant target functional.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
self._setupInterpolationIndices()
while self.E >= 0:
eWidth = (hashD([self.E + 1] + [0] * (self.npar - 1))
- hashD([self.E] + [0] * (self.npar - 1)))
TE, _, argIdxs = hpvP(self._musUniqueCN, self.E, self.polybasis,
self._derIdxs, self._reorder,
scl = np.power(self.scaleFactor, -1.))
fitOut = customPInv(TE[:, argIdxs], rcond = self.interpRcond,
full = True)
vbMng(self, "MAIN",
("Fitting {} samples with degree {} through {}... "
"Conditioning of pseudoinverse system: {:.4e}.").format(
TE.shape[0], self.E,
polyfitname(self.polybasis),
fitOut[1][1][0] / fitOut[1][1][-1]),
5)
if fitOut[1][0] == len(argIdxs):
self._fitinv = fitOut[0][- eWidth : , :]
break
RROMPyWarning("Polyfit is poorly conditioned. Reducing E by 1.")
self.E -= 1
if self.E < 0:
raise RROMPyException(("Instability in computation of "
"denominator. Aborting."))
TN, _, argIdxs = hpvP(self._musUniqueCN, self.N, self.polybasis,
self._derIdxs, self._reorder,
scl = np.power(self.scaleFactor, -1.))
TN = TN[:, argIdxs]
invD = [None] * (eWidth)
for k in range(eWidth):
pseudoInv = np.diag(self._fitinv[k, :])
idxGlob = 0
for j, derIdxs in enumerate(self._derIdxs):
nder = len(derIdxs)
idxGlob += nder
if nder > 1:
idxLoc = np.arange(len(self.mus))[
(self._reorder >= idxGlob - nder)
* (self._reorder < idxGlob)]
invLoc = self._fitinv[k, idxLoc]
pseudoInv[np.ix_(idxLoc, idxLoc)] = 0.
for diffj, diffjIdx in enumerate(derIdxs):
for derQ, derQIdx in enumerate(derIdxs):
derUIdx = [x - y for (x, y) in
zip(diffjIdx, derQIdx)]
if all([x >= 0 for x in derUIdx]):
derU = hashD(derUIdx)
pseudoInv[idxLoc[derU], idxLoc[derQ]] = (
invLoc[diffj])
invD[k] = pseudoInv.dot(TN)
return invD
def findeveVGExplicit(self, sampleE:sampList,
invD:List[Np2D]) -> Tuple[Np1D, Np2D]:
"""
Compute explicitly eigenvalues and eigenvectors of Pade' 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)
G = np.zeros((nEnd, nEnd), dtype = np.complex)
for k in range(eWidth):
G += invD[k].T.conj().dot(gramian.dot(invD[k]))
vbMng(self, "DEL", "Done building gramian.", 10)
vbMng(self, "INIT", "Solving eigenvalue problem for gramian matrix.",
7)
ev, eV = np.linalg.eigh(G)
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 Pade' 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, :] = RPODE.dot(invD[k])
vbMng(self, "DEL", "Done building half-gramian.", 10)
vbMng(self, "INIT", "Solving svd for square root of gramian matrix.",
7)
_, s, eV = np.linalg.svd(Rstack, full_matrices = False)
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 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.mu0.
Returns:
Normalized parameter.
"""
return self.trainedModel.centerNormalize(mu, mu0)
def getResidues(self) -> Np1D:
"""
Obtain approximant residues.
Returns:
Matrix with residues as columns.
"""
return self.trainedModel.getResidues()
diff --git a/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py b/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py
index b777bb0..0b2a6f2 100644
--- a/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py
+++ b/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py
@@ -1,414 +1,410 @@
# 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_distributed_greedy_approximant import \
GenericDistributedGreedyApproximant
-from rrompy.utilities.poly_fitting.polynomial import polybases, polydomcoeff
+from rrompy.utilities.poly_fitting.polynomial import polybases
from rrompy.reduction_methods.distributed import RationalInterpolant
from rrompy.reduction_methods.trained_model import TrainedModelPade as tModel
from rrompy.reduction_methods.trained_model import TrainedModelData
from rrompy.utilities.base.types import Np1D, Np2D, DictAny, HFEng, paramVal
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.exception_manager import RROMPyWarning
from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
__all__ = ['RationalInterpolantGreedy']
class RationalInterpolantGreedy(GenericDistributedGreedyApproximant,
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;
- 'S': number of starting training points;
- 'sampler': sample point generator;
- 'radialBasis': radial basis family for interpolant numerator;
defaults to 0, i.e. no radial basis;
- 'radialBasisWeights': radial basis weights for interpolant
numerator; defaults to 0, i.e. identity;
- 'greedyTol': uniform error tolerance for greedy algorithm;
defaults to 1e-2;
- 'interactive': whether to interactively terminate greedy
algorithm; defaults to False;
- 'maxIter': maximum number of greedy steps; defaults to 1e2;
- 'refinementRatio': ratio of training points to be exhausted
before training set refinement; defaults to 0.2;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator; defaults
to Chebyshev sampler within muBounds;
- 'polybasis': type of basis for interpolation; allowed values
include 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to
'MONOMIAL';
- 'Delta': difference between M and N in rational approximant;
defaults to 0;
- 'errorEstimatorKind': kind of error estimator; available values
include 'EXACT', 'BASIC', and 'BARE'; defaults to 'EXACT';
- 'interpRcond': tolerance for interpolation; defaults to None;
- 'robustTol': tolerance for robust Pade' denominator management;
defaults to 0.
Defaults to empty dict.
homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults
to False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
homogeneized: Whether to homogeneize Dirichlet BCs.
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.
- 'radialBasis': radial basis family for interpolant numerator;
defaults to 0, i.e. no radial basis;
- 'radialBasisWeights': radial basis weights for interpolant
numerator; defaults to 0, i.e. identity;
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'interactive': whether to interactively terminate greedy
algorithm;
- 'maxIter': maximum number of greedy steps;
- 'refinementRatio': ratio of test points to be exhausted before
test set refinement;
- 'nTestPoints': number of test points;
- 'trainSetGenerator': training sample points generator;
- 'Delta': difference between M and N in rational approximant;
- 'errorEstimatorKind': kind of error estimator;
- 'interpRcond': tolerance for interpolation;
- 'robustTol': tolerance for robust Pade' denominator management.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
POD: whether to compute POD of snapshots.
S: number of test points.
sampler: Sample point generator.
radialBasis: Radial basis family for interpolant numerator.
radialBasisWeights: Radial basis weights for interpolant numerator.
greedyTol: uniform error tolerance for greedy algorithm.
interactive: whether to interactively terminate greedy algorithm.
maxIter: maximum number of greedy steps.
refinementRatio: ratio of training points to be exhausted before
training set refinement.
nTestPoints: number of starting training points.
trainSetGenerator: training sample points generator.
robustTol: tolerance for robust Pade' denominator management.
Delta: difference between M and N in rational approximant.
errorEstimatorKind: kind of error estimator.
interpRcond: tolerance for interpolation.
robustTol: tolerance for robust Pade' 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 = ["EXACT", "BASIC", "BARE"]
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
self._addParametersToList(["Delta", "polybasis", "errorEstimatorKind",
"interpRcond", "robustTol"],
[0, "MONOMIAL", "EXACT", -1, 0],
toBeExcluded = ["E"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity, timestamp = timestamp)
vbMng(self, "INIT", "Computing Taylor blocks of system.", 7)
nAs = self.HFEngine.nAs - 1
nbs = max(self.HFEngine.nbs, (nAs + 1) * self.homogeneized)
self.As = [self.HFEngine.A(self.mu0, j + 1) for j in range(nAs)]
self.bs = [self.HFEngine.b(self.mu0, j, self.homogeneized)
for j in range(nbs)]
vbMng(self, "DEL", "Done computing Taylor blocks.", 7)
self._postInit()
@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 Delta(self):
"""Value of Delta."""
return self._Delta
@Delta.setter
def Delta(self, Delta):
if not np.isclose(Delta, np.floor(Delta)):
raise RROMPyException("Delta must be an integer.")
if Delta < 0:
RROMPyWarning(("Error estimator unreliable for Delta < 0. "
"Overloading of errorEstimator is suggested."))
else:
Deltamin = (max(self.HFEngine.nbs,
self.HFEngine.nAs * self.homogeneized)
- 1 - 1 * (self.HFEngine.nAs > 1))
if Delta < Deltamin:
RROMPyWarning(("Method may be unreliable for selected Delta. "
"Suggested minimal value of Delta: {}.").format(
Deltamin))
self._Delta = Delta
self._approxParameters["Delta"] = self.Delta
@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 'EXACT'."))
errorEstimatorKind = "EXACT"
self._errorEstimatorKind = errorEstimatorKind
self._approxParameters["errorEstimatorKind"] = self.errorEstimatorKind
@property
def nTestPoints(self):
"""Value of nTestPoints."""
return self._nTestPoints
@nTestPoints.setter
def nTestPoints(self, nTestPoints):
if nTestPoints <= np.abs(self.Delta):
RROMPyWarning(("nTestPoints must be at least abs(Delta) + 1. "
"Increasing value to abs(Delta) + 1."))
nTestPoints = np.abs(self.Delta) + 1
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()
def _errorSamplingRatio(self, mus:Np1D, muRTest:Np1D,
muRTrain:Np1D) -> Np1D:
"""Scalar ratio in explicit error estimator."""
self.setupApprox()
testTile = np.tile(np.reshape(muRTest, (-1, 1)), [1, len(muRTrain)])
nodalVals = np.prod(testTile - np.reshape(muRTrain, (1, -1)), axis = 1)
denVals = self.trainedModel.getQVal(mus)
return np.abs(nodalVals / denVals)
def _RHSNorms(self, radiusb0:Np2D) -> Np1D:
"""High fidelity system RHS norms."""
self.assembleReducedResidualBlocks(full = False)
# 'ij,jk,ik->k', resbb, radiusb0, radiusb0.conj()
b0resb0 = np.sum(self.trainedModel.data.resbb.dot(radiusb0)
* radiusb0.conj(), axis = 0)
RHSnorms = np.power(np.abs(b0resb0), .5)
return RHSnorms
def _errorEstimatorBare(self) -> Np1D:
"""Bare residual-based error estimator."""
self.setupApprox()
self.assembleReducedResidualGramian(self.trainedModel.data.projMat)
- pDom = self.trainedModel.data.P[-1, :]
+ pDom = self.trainedModel.data.P.domCoeff
LL = pDom.conj().dot(self.trainedModel.data.gramian.dot(pDom))
Adiag = self.As[0].diagonal()
LL = ((self.scaleFactor[0] * np.linalg.norm(Adiag)) ** 2. * LL
/ np.size(Adiag))
- scalingDom = polydomcoeff(len(self.mus) - 1, self.polybasis)
- return scalingDom * np.power(np.abs(LL), .5)
+ return np.power(np.abs(LL), .5)
def _errorEstimatorBasic(self, muTest:paramVal, ratioTest:complex) -> Np1D:
"""Basic residual-based error estimator."""
resmu = self.HFEngine.residual(self.trainedModel.getApprox(muTest),
muTest, self.homogeneized,
duality = False)[0]
return np.abs(self.estimatorNormEngine.norm(resmu) / ratioTest)
def _errorEstimatorExact(self, muRTrain:Np1D, vanderBase:Np2D) -> Np1D:
"""Exact residual-based error estimator."""
self.setupApprox()
self.assembleReducedResidualBlocks(full = True)
nAs = self.HFEngine.nAs - 1
nbs = max(self.HFEngine.nbs - 1, nAs * self.homogeneized)
- delta = len(self.mus) - len(self.trainedModel.data.Q)
+ delta = len(self.mus) - self.trainedModel.data.Q.deg[0]
nbsEff = max(0, nbs - delta)
momentQ = np.zeros(nbsEff, dtype = np.complex)
momentQu = np.zeros((len(self.mus), nAs), dtype = np.complex)
radiusbTen = np.zeros((nbsEff, nbsEff, vanderBase.shape[1]),
dtype = np.complex)
radiusATen = np.zeros((nAs, nAs, vanderBase.shape[1]),
dtype = np.complex)
if nbsEff > 0:
- momentQ[0] = self.trainedModel.data.Q[-1]
+ momentQ[0] = self.trainedModel.data.Q.domCoeff
radiusbTen[0, :, :] = vanderBase[: nbsEff, :]
- momentQu[:, 0] = self.trainedModel.data.P[-1, :]
+ momentQu[:, 0] = self.trainedModel.data.P.domCoeff
radiusATen[0, :, :] = vanderBase[: nAs, :]
Qvals = self.trainedModel.getQVal(self.mus)
for k in range(1, max(nAs, nbs * (nbsEff > 0))):
Qvals = Qvals * muRTrain
if k > delta and k < nbs:
momentQ[k - delta] = self._fitinv.dot(Qvals)
radiusbTen[k - delta, k :, :] = (
radiusbTen[0, : delta - k, :])
if k < nAs:
momentQu[:, k] = Qvals * self._fitinv
radiusATen[k, k :, :] = radiusATen[0, : - k, :]
if self.POD and nAs > 1:
momentQu[:, 1 :] = self.samplingEngine.RPOD.dot(
momentQu[:, 1 :])
radiusA = np.tensordot(momentQu, radiusATen, 1)
if nbsEff > 0:
radiusb = np.tensordot(momentQ, radiusbTen, 1)
# 'ij,jk,ik->k', resbb, radiusb, radiusb.conj()
ff = np.sum(self.trainedModel.data.resbb[delta + 1 :, delta + 1 :]\
.dot(radiusb) * radiusb.conj(), axis = 0)
# 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj()
Lf = np.sum(np.tensordot(
self.trainedModel.data.resAb[delta :, :, :], radiusA, 2)
* radiusb.conj(), axis = 0)
else:
ff, Lf = 0., 0.
# 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj()
LL = np.sum(np.tensordot(self.trainedModel.data.resAA, radiusA, 2)
* radiusA.conj(), axis = (0, 1))
- scalingDom = polydomcoeff(len(self.mus) - 1, self.polybasis)
- return scalingDom * np.power(np.abs(ff - 2. * np.real(Lf) + LL), .5)
+ return np.power(np.abs(ff - 2. * np.real(Lf) + LL), .5)
def errorEstimator(self, mus:Np1D) -> Np1D:
"""Standard residual-based error estimator."""
self.setupApprox()
- if (np.any(np.isnan(self.trainedModel.data.P[-1, :]))
- or np.any(np.isinf(self.trainedModel.data.P[-1, :]))):
+ if (np.any(np.isnan(self.trainedModel.data.P.domCoeff))
+ or np.any(np.isinf(self.trainedModel.data.P.domCoeff))):
err = np.empty(len(mus))
err[:] = np.inf
return err
nAs = self.HFEngine.nAs - 1
nbs = max(self.HFEngine.nbs - 1, nAs * self.homogeneized)
muRTest = self.centerNormalize(mus)(0)
muRTrain = self.centerNormalize(self.mus)(0)
samplingRatio = self._errorSamplingRatio(mus, muRTest, muRTrain)
vanderBase = np.polynomial.polynomial.polyvander(muRTest,
max(nAs, nbs)).T
RHSnorms = self._RHSNorms(vanderBase[: nbs + 1, :])
if self.errorEstimatorKind == "BARE":
jOpt = self._errorEstimatorBare()
elif self.errorEstimatorKind == "BASIC":
idx_muTestSample = np.argmax(samplingRatio)
jOpt = self._errorEstimatorBasic(mus[idx_muTestSample],
samplingRatio[idx_muTestSample])
else: #if self.errorEstimatorKind == "EXACT":
jOpt = self._errorEstimatorExact(muRTrain, vanderBase[: -1, :])
return jOpt * samplingRatio / RHSnorms
def setupApprox(self, plotEst : bool = False):
"""
Compute Pade' interpolant.
SVD-based robust eigenvalue management.
"""
if self.checkComputedApprox():
return
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5)
self.computeScaleFactor()
self.greedy(plotEst)
self._S = len(self.mus)
self._N, self._M, self._E = [self._S - 1] * 3
if self.Delta < 0:
self._M += self.Delta
else:
self._N -= self.Delta
if self.trainedModel is None:
self.trainedModel = tModel()
self.trainedModel.verbosity = self.verbosity
self.trainedModel.timestamp = self.timestamp
data = TrainedModelData(self.trainedModel.name(), self.mu0,
self.samplingEngine.samples,
self.HFEngine.rescalingExp)
- data.polytype = self.polybasis
- data.polytypeP = self.polybasisP
data.scaleFactor = self.scaleFactor
data.mus = copy(self.mus)
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(self.samplingEngine.samples)
self.trainedModel.data.mus = copy(self.mus)
if min(self.M, self.N) < 0:
vbMng(self, "MAIN", "Minimal sample size not achieved.", 5)
Q = np.empty(tuple([max(self.N, 0) + 1] * self.npar),
dtype = np.complex)
P = np.empty(tuple([max(self.M, 0) + 1] * self.npar)
+ (len(self.mus),), dtype = np.complex)
Q[:] = np.nan
P[:] = np.nan
self.trainedModel.data.Q = copy(Q)
self.trainedModel.data.P = copy(P)
self.trainedModel.data.approxParameters = copy(
self.approxParameters)
vbMng(self, "DEL", "Aborting computation of approximant.", 5)
return
if self.N > 0:
Q = self._setupDenominator()
else:
Q = np.ones(tuple([1] * self.npar), dtype = np.complex)
self.trainedModel.data.Q = copy(Q)
self.trainedModel.data.P = copy(self._setupNumerator())
self.trainedModel.data.approxParameters = copy(self.approxParameters)
vbMng(self, "DEL", "Done setting up approximant.", 5)
def assembleReducedResidualBlocks(self, full : bool = False):
"""Build affine blocks of reduced linear system through projections."""
scaling = self.trainedModel.data.scaleFactor[0]
self.assembleReducedResidualBlocksbb(self.bs, scaling)
if full:
pMat = self.trainedModel.data.projMat
self.assembleReducedResidualBlocksAb(self.As, self.bs[1 :],
pMat, scaling)
self.assembleReducedResidualBlocksAA(self.As, pMat, scaling)
diff --git a/rrompy/reduction_methods/trained_model/trained_model_pade.py b/rrompy/reduction_methods/trained_model/trained_model_pade.py
index 6e4b392..cbda031 100644
--- a/rrompy/reduction_methods/trained_model/trained_model_pade.py
+++ b/rrompy/reduction_methods/trained_model/trained_model_pade.py
@@ -1,159 +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 .
#
import numpy as np
from . import TrainedModel
from rrompy.utilities.base.types import (Np1D, List, paramVal, paramList,
sampList)
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.poly_fitting.polynomial import polyval, polyroots
from rrompy.utilities.poly_fitting.radial_basis import polyval as polyvalR
-from rrompy.utilities.exception_manager import RROMPyAssert, RROMPyException
+from rrompy.utilities.exception_manager import RROMPyException
from rrompy.parameter import checkParameter, checkParameterList
from rrompy.sampling import sampleList
__all__ = ['TrainedModelPade']
class TrainedModelPade(TrainedModel):
"""
ROM approximant evaluation for Pade' 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)
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 = [], der : List[int] = None) -> sampList:
+ def getPVal(self, mu : paramList = [], der : List[int] = None,
+ scl : Np1D = None) -> sampList:
"""
Evaluate Pade' numerator at arbitrary parameter.
Args:
mu: Target parameter.
der(optional): Derivatives to take before evaluation.
"""
mu, _ = checkParameterList(mu, self.data.npar)
vbMng(self, "INIT", "Evaluating numerator at mu = {}.".format(mu), 17)
muCenter = self.centerNormalize(mu)
- if "_" in self.data.polytypeP:
- p = sampleList(polyvalR(muCenter, self.data.P,
- self.data.polytypeP, der))
- else:
- p = sampleList(polyval(muCenter, self.data.P,
- self.data.polytypeP, der))
+ p = sampleList(self.data.P(muCenter, der, scl))
vbMng(self, "DEL", "Done evaluating numerator.", 17)
return p
def getQVal(self, mu:Np1D, der : List[int] = None,
scl : Np1D = None) -> Np1D:
"""
Evaluate Pade' denominator at arbitrary parameter.
Args:
mu: Target parameter.
der(optional): Derivatives to take before evaluation.
"""
mu, _ = checkParameterList(mu, self.data.npar)
vbMng(self, "INIT", "Evaluating denominator at mu = {}.".format(mu),
17)
muCenter = self.centerNormalize(mu)
- q = polyval(muCenter, self.data.Q, self.data.polytype, der, scl)
+ 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)
if (not hasattr(self, "lastSolvedApproxReduced")
or self.lastSolvedApproxReduced != mu):
vbMng(self, "INIT",
"Evaluating approximant at mu = {}.".format(mu), 12)
self.uApproxReduced = self.getPVal(mu) / self.getQVal(mu)
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 = [None]
try:
rDim = mVals.index(None)
if rDim < len(mVals) - 1 and None in mVals[rDim + 1 :]:
raise
except:
raise RROMPyException(("Exactly 1 'None' 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] = None
return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim]
+ self.data.scaleFactor[rDim]
- * polyroots(self.data.Q, self.data.polytype, mVals),
+ * polyroots(self.data.Q.coeffs,
+ self.data.Q.polybasis, mVals),
1. / self.data.rescalingExp[rDim])
def getResidues(self) -> Np1D:
"""
Obtain approximant residues.
Returns:
Numpy matrix with residues as columns.
"""
pls = self.getPoles()
poles, _ = checkParameterList(pls, 1)
res = (self.data.projMat.dot(self.getPVal(poles).data)
/ self.getQVal(poles, 1))
return pls, res
diff --git a/rrompy/utilities/base/types.py b/rrompy/utilities/base/types.py
index b84e189..06f9835 100644
--- a/rrompy/utilities/base/types.py
+++ b/rrompy/utilities/base/types.py
@@ -1,61 +1,60 @@
# 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 typing import TypeVar, List, Tuple, Dict, Any
__all__ = ['TupleAny','ListAny','DictAny','ScOp','Np1D','Np2D','Np1DLst',
'N2FSExpr','FenExpr','FenFunc','FenFuncSpace','FenBC','HFEng',
'ROMEng','sampleEng','normEng','paramVal','paramList', 'sampList',
'GenExpr','strLst', 'BfSExpr']
# ANY
TupleAny = Tuple[Any]
ListAny = List[Any]
DictAny = Dict[Any, Any]
# SCIPY
ScOp = TypeVar("Scipy sparse matrix for space operator")
# NUMPY
Np1D = TypeVar("NumPy 1D array")
Np2D = TypeVar("NumPy 2D array-like")
Np1DLst = TypeVar("NumPy 1D array or list of NumPy 1D array")
N2FSExpr = TypeVar("NumPy 2D array, float or str")
# FENICS
FenExpr = TypeVar("FEniCS expression")
FenFunc = TypeVar("FEniCS function")
FenFuncSpace = TypeVar("FEniCS function space")
FenBC = TypeVar("FEniCS boundary condition")
# ENGINES
HFEng = TypeVar("High fidelity engine")
ROMEng = TypeVar("ROM engine")
sampleEng = TypeVar("Sampling engine")
normEng = TypeVar("Norm engine")
# CUSTOM TYPES
paramVal = TypeVar("Parameter value tuple")
paramList = TypeVar("Parameter value tuple list")
sampList = TypeVar("Sample list")
-radialFun = TypeVar("Radial basis function")
# OTHERS
GenExpr = TypeVar("Generic expression")
strLst = TypeVar("str or list of str")
BfSExpr = TypeVar("Boolean function or string")
diff --git a/rrompy/utilities/poly_fitting/__init__.py b/rrompy/utilities/poly_fitting/__init__.py
index c9d4457..0e297b8 100644
--- a/rrompy/utilities/poly_fitting/__init__.py
+++ b/rrompy/utilities/poly_fitting/__init__.py
@@ -1,27 +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 .custom_fit import customFit
from .custom_pinv import customPInv
+from .interpolator import GenericInterpolator
__all__ = [
'customFit',
- 'customPInv'
+ 'customPInv',
+ 'GenericInterpolator'
]
diff --git a/rrompy/utilities/poly_fitting/radial_basis/der.py b/rrompy/utilities/poly_fitting/interpolator.py
similarity index 61%
rename from rrompy/utilities/poly_fitting/radial_basis/der.py
rename to rrompy/utilities/poly_fitting/interpolator.py
index 89f2aed..fd08cd3 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/der.py
+++ b/rrompy/utilities/poly_fitting/interpolator.py
@@ -1,30 +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, List, radialFun
-from rrompy.utilities.exception_manager import RROMPyException
+from abc import abstractmethod
+from rrompy.utilities.base.types import List, paramList
-__all__ = ['polyder']
+__all__ = ['GenericInterpolator']
-def polyder(c:radialFun, basis:str, m : List[int] = None,
- scl : Np1D = None) -> radialFun:
- if m is not None and np.sum(m) > 0:
- raise RROMPyException(("Cannot take derivatives of radial basis "
- "function."))
- return c
+class GenericInterpolator:
+ """HERE"""
+
+ @abstractmethod
+ def __init__(self, other = None):
+ pass
+
+ @abstractmethod
+ def __call__(self, mu:paramList, der : List[int] = None):
+ pass
+
+ @abstractmethod
+ def __copy__(self):
+ pass
+
+ @abstractmethod
+ def __deepcopy__(self, memo):
+ pass
diff --git a/rrompy/utilities/poly_fitting/polynomial/__init__.py b/rrompy/utilities/poly_fitting/polynomial/__init__.py
index 7f8faec..747c8b5 100644
--- a/rrompy/utilities/poly_fitting/polynomial/__init__.py
+++ b/rrompy/utilities/poly_fitting/polynomial/__init__.py
@@ -1,47 +1,49 @@
# 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 .base import (polybases, polyfitname, polydomcoeff)
from .der import polyder
from .val import polyval
from .marginalize import polymarginalize
from .vander import polyvander
from .roots import polyroots
from .derivative import nextDerivativeIndices
from .hash_derivative import hashDerivativeToIdx, hashIdxToDerivative
from .homogeneization import (homogeneizationMask, homogeneizedpolyvander,
homogeneizedToFull)
+from .polynomial_interpolator import PolynomialInterpolator
__all__ = [
'polybases',
'polyfitname',
'polydomcoeff',
'polyder',
'polyval',
'polymarginalize',
'polyvander',
'polyroots',
'nextDerivativeIndices',
'hashDerivativeToIdx',
'hashIdxToDerivative',
'homogeneizationMask',
'homogeneizedpolyvander',
- 'homogeneizedToFull'
+ 'homogeneizedToFull',
+ 'PolynomialInterpolator'
]
diff --git a/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py b/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py
new file mode 100644
index 0000000..6a864be
--- /dev/null
+++ b/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py
@@ -0,0 +1,124 @@
+# 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 List, ListAny, DictAny, Np1D, paramList
+from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator
+from rrompy.utilities.poly_fitting.custom_fit import customFit
+from rrompy.utilities.poly_fitting.polynomial.base import (polyfitname,
+ polydomcoeff)
+from rrompy.utilities.poly_fitting.polynomial.val import polyval
+from rrompy.utilities.poly_fitting.polynomial.roots import polyroots
+from rrompy.utilities.poly_fitting.polynomial.homogeneization import (
+ homogeneizedpolyvander as hpv,
+ homogeneizedToFull)
+from rrompy.utilities.poly_fitting.polynomial.vander import polyvander as pv
+from rrompy.utilities.exception_manager import RROMPyAssert, RROMPyException
+from rrompy.parameter import checkParameterList
+
+__all__ = ['PolynomialInterpolator']
+
+class PolynomialInterpolator(GenericInterpolator):
+ """HERE"""
+
+ def __init__(self, other = None):
+ if other is None: return
+ self.coeffs = other.coeffs
+ self.npar = other.npar
+ self.polybasis = other.polybasis
+
+ @property
+ def shape(self):
+ if self.coeffs.ndim > self.npar:
+ sh = self.coeffs.shape[self.npar :]
+ else: sh = 1
+ return sh
+
+ @property
+ def deg(self):
+ return [x - 1 for x in self.coeffs.shape[: self.npar]]
+
+ def __getitem__(self, key):
+ return self.coeffs[key]
+
+ def __call__(self, mu:paramList, der : List[int] = None,
+ scl : Np1D = None):
+ return polyval(mu, self.coeffs, self.polybasis, der, scl)
+
+ def __copy__(self):
+ return PolynomialInterpolator(self)
+
+ def __deepcopy__(self, memo):
+ other = PolynomialInterpolator()
+ other.coeffs, other.npar, other.polybasis = copy(
+ (self.coeffs, self.npar, self.polybasis), memo)
+ return other
+
+ @property
+ def domCoeff(self):
+ RROMPyAssert(self.npar, 1, "Number of parameters")
+ return polydomcoeff(self.deg, self.polybasis) * self[-1]
+
+ def setupByInterpolation(self, support:paramList, values:ListAny,
+ deg:int, polybasis : str = "MONOMIAL",
+ verbose : bool = True, homogeneized : bool = True,
+ vanderCoeffs : DictAny = {},
+ fitCoeffs : DictAny = {}):
+ support, _ = checkParameterList(support)
+ self.npar = support.shape[1]
+ self.polybasis = polybasis
+ if homogeneized:
+ vander, _, reorder = hpv(support, deg = deg, basis = polybasis,
+ **vanderCoeffs)
+ vander = vander[:, reorder]
+ else:
+ if not hasattr(deg, "__len__"): deg = [deg] * self.npar
+ vander = pv(support, deg = deg, basis = polybasis,
+ **vanderCoeffs)
+ outDim = values.shape[1:]
+ values = values.reshape(values.shape[0], -1)
+ fitOut = customFit(vander, values, full = True, **fitCoeffs)
+ P = fitOut[0]
+ if verbose:
+ msg = ("Fitting {} samples with degree {} through {}... "
+ "Conditioning of LS system: {:.4e}.").format(
+ len(vander), deg,
+ polyfitname(self.polybasis),
+ fitOut[1][2][0] / fitOut[1][2][-1])
+ else: msg = None
+ if homogeneized:
+ self.coeffs = homogeneizedToFull(
+ tuple([deg + 1] * self.npar) + outDim,
+ self.npar, P)
+ else:
+ self.coeffs = P.reshape(tuple([d + 1 for d in deg]) + outDim)
+ return fitOut[1][1] == vander.shape[1], msg
+
+ def roots(self, marginalVals : ListAny = [None]):
+ RROMPyAssert(self.shape, 1, "Shape of output")
+ RROMPyAssert(len(ListAny), self.npar, "Number of parameters")
+ try:
+ rDim = marginalVals.index(None)
+ if (rDim < len(marginalVals) - 1
+ and None in marginalVals[rDim + 1 :]):
+ raise
+ except:
+ raise RROMPyException(("Exactly 1 'None' entry in "
+ "marginalVals must be provided."))
+ return polyroots(self.coeffs, self.polybasis, marginalVals)
+
diff --git a/rrompy/utilities/poly_fitting/radial_basis/__init__.py b/rrompy/utilities/poly_fitting/radial_basis/__init__.py
index e4d799d..108132b 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/__init__.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/__init__.py
@@ -1,42 +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 .
#
from .kernel import radialGaussian, thinPlateSpline, multiQuadric
-from .base import rbbases, polybases, polyfitname, polydomcoeff, radialFunction
-from .der import polyder
+from .base import rbbases, polybases, polyfitname, polydomcoeff
from .val import polyval
from .vander import rbvander, polyvander
from .homogeneization import homogeneizedpolyvander
+from .radial_basis_interpolator import RadialBasisInterpolator
__all__ = [
'radialGaussian',
'thinPlateSpline',
'multiQuadric',
'rbbases',
'polybases',
'polyfitname',
'polydomcoeff',
- 'radialFunction',
- 'polyder',
'polyval',
'rbvander',
'polyvander',
- 'homogeneizedpolyvander'
+ 'homogeneizedpolyvander',
+ 'RadialBasisInterpolator'
]
diff --git a/rrompy/utilities/poly_fitting/radial_basis/base.py b/rrompy/utilities/poly_fitting/radial_basis/base.py
index d5ace7d..130c979 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/base.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/base.py
@@ -1,55 +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 .
#
from itertools import product
from rrompy.utilities.base.types import Np1D, Np2D, paramList
from rrompy.utilities.exception_manager import RROMPyException
from rrompy.utilities.poly_fitting.polynomial.base import polydomcoeff as \
polydomcoeffB
-__all__ = ['rbbases', 'polybases', 'polyfitname', 'polydomcoeff',
- 'radialFunction']
+__all__ = ['rbbases', 'polybases', 'polyfitname', 'polydomcoeff']
rbbases = ["GAUSSIAN", "THINPLATE", "MULTIQUADRIC"]
polybases = [x + "_" + y for x, y in product(
["CHEBYSHEV", "LEGENDRE", "MONOMIAL"], rbbases)]
def polyfitname(basis:str) -> str:
fitpnames = {"CHEBYSHEV" : "chebfit", "LEGENDRE" : "legfit",
"MONOMIAL" : "polyfit"}
fitrnames = {"GAUSSIAN" : "gaussian", "THINPLATE" : "thinplate",
"MULTIQUADRIC" : "multiquadric"}
basisp, basisr = basis.split("_")
try:
return fitpnames[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])
-class radialFunction:
- def __init__(self, supportPoints : paramList = None,
- directionalWeights : Np1D = None, localCoeffs : Np1D = None,
- globalCoeffs : Np2D = None):
- self.supportPoints = supportPoints
- self.directionalWeights = directionalWeights
- self.localCoeffs = localCoeffs
- self.globalCoeffs = globalCoeffs
diff --git a/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py b/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py
new file mode 100644
index 0000000..98f9b62
--- /dev/null
+++ b/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py
@@ -0,0 +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 copy import deepcopy as copy
+from rrompy.utilities.base.types import List, ListAny, DictAny, Np1D, paramList
+from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator
+from rrompy.utilities.poly_fitting.custom_fit import customFit
+from rrompy.utilities.poly_fitting.radial_basis.base import polyfitname
+from rrompy.utilities.poly_fitting.radial_basis.val import polyval
+from rrompy.utilities.poly_fitting.radial_basis.homogeneization import (
+ homogeneizedpolyvander as hpv)
+from rrompy.utilities.poly_fitting.radial_basis.vander import polyvander as pv
+from rrompy.utilities.poly_fitting.polynomial.homogeneization import (
+ homogeneizedToFull)
+from rrompy.utilities.exception_manager import RROMPyException
+from rrompy.parameter import checkParameterList
+
+__all__ = ['RadialBasisInterpolator']
+
+class RadialBasisInterpolator(GenericInterpolator):
+ """HERE"""
+
+ 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
+
+ @property
+ def shape(self):
+ if self.coeffsLocal.ndim > 1:
+ sh = self.coeffsLocal.shape[1 :]
+ else: sh = 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)
+
+ def __copy__(self):
+ return RadialBasisInterpolator(self)
+
+ def __deepcopy__(self, memo):
+ other = RadialBasisInterpolator()
+ (other.support, other.coeffsGlobal, other.coeffsLocal,
+ other.directionalWeights, other.npar, other.polybasis) = copy(
+ (self.support, self.coeffsGlobal, self.coeffsLocal,
+ self.directionalWeights, self.npar, self.polybasis), memo)
+ return other
+
+ def setupByInterpolation(self, support:paramList, values:ListAny,
+ deg:int, polybasis : str = "MONOMIAL_GAUSSIAN",
+ directionalWeights : Np1D = None,
+ verbose : bool = True, homogeneized : bool = True,
+ vanderCoeffs : DictAny = {},
+ fitCoeffs : DictAny = {}):
+ support, _ = checkParameterList(support)
+ self.support = copy(support)
+ self.npar = support.shape[1]
+ if directionalWeights is None:
+ directionalWeights = np.ones(self.npar)
+ self.directionalWeights = directionalWeights
+ self.polybasis = polybasis
+ if homogeneized:
+ vander, _, reorder = hpv(support, deg = deg, basis = polybasis,
+ directionalWeights = self.directionalWeights,
+ **vanderCoeffs)
+ vander = vander[reorder]
+ vander = vander[:, reorder]
+ else:
+ if not hasattr(deg, "__len__"): deg = [deg] * self.npar
+ vander = pv(support, deg = deg, basis = polybasis,
+ **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 homogeneized:
+ self.coeffsGlobal = homogeneizedToFull(
+ 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 51f4c5b..81d07f9 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/val.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/val.py
@@ -1,54 +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 .
#
from copy import deepcopy as copy
import numpy as np
-from rrompy.utilities.base.types import Np1D, Np2D, List, paramList, radialFun
+from rrompy.utilities.base.types import Np1D, Np2D, paramList
from rrompy.parameter import checkParameterList
from rrompy.utilities.exception_manager import RROMPyException
from rrompy.utilities.poly_fitting.polynomial import polyval as pvP
-from .der import polyder
from .kernel import radialGaussian, thinPlateSpline, multiQuadric
__all__ = ['polyval']
-def polyval(x:paramList, c:radialFun, basis:str, m : List[int] = None,
- scl : Np1D = None) -> Np2D:
- cFun = polyder(c, basis, m = m, scl = scl)
+def polyval(x:paramList, cG:Np2D, cL:Np2D, supportPoints:paramList,
+ directionalWeights:Np1D, basis:str) -> Np2D:
x, _ = checkParameterList(x)
basisp, basisr = basis.split("_")
- c = pvP(x, cFun.globalCoeffs, basisp, m, scl)
+ c = pvP(x, cG, basisp)
try:
radialvalbase = {"GAUSSIAN" : radialGaussian,
"THINPLATE" : thinPlateSpline,
"MULTIQUADRIC" : multiQuadric
}[basisr.upper()]
except:
raise RROMPyException("Radial basis not recognized.")
csh = copy(c.shape)
if len(csh) == 1: c = c.reshape(1, -1)
for j in range(len(x)):
- muDiff = (cFun.supportPoints.data - x[j]) * cFun.directionalWeights
+ muDiff = (supportPoints.data - x[j]) * directionalWeights
r2j = np.sum(np.abs(muDiff) ** 2., axis = 1).reshape(1, -1)
- val = radialvalbase(r2j).dot(cFun.localCoeffs)
+ val = radialvalbase(r2j).dot(cL)
try:
c[..., j] += val
except:
c[..., j] += val.flatten()
if len(csh) == 1: c = c.flatten()
return c
diff --git a/tests/utilities/radial_fitting.py b/tests/utilities/radial_fitting.py
index 766e0a9..d10448b 100644
--- a/tests/utilities/radial_fitting.py
+++ b/tests/utilities/radial_fitting.py
@@ -1,161 +1,167 @@
# 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 import customFit
from rrompy.utilities.poly_fitting.radial_basis import (radialGaussian,
thinPlateSpline,
multiQuadric,
polybases, polyfitname,
- polydomcoeff,
- radialFunction,
+ polydomcoeff,
polyval, polyvander,
homogeneizedpolyvander)
from rrompy.utilities.poly_fitting.polynomial import homogeneizedToFull
from rrompy.parameter import checkParameterList
def test_monomial_gaussian():
polyrbname = "MONOMIAL_GAUSSIAN"
assert polyrbname in polybases
fitname = polyfitname(polyrbname)
domcoeff = polydomcoeff(5, polyrbname)
assert fitname == "polyfit_gaussian"
assert np.isclose(domcoeff, 1., rtol = 1e-5)
directionalWeights = np.array([5.])
xSupp = checkParameterList(np.arange(-1, 3), 1)[0]
cRBCoeffs = np.array([-1., 3., -3., 1., 1., 2., -.5])
- cRB = radialFunction(directionalWeights = directionalWeights,
- supportPoints = xSupp, localCoeffs = cRBCoeffs[: 4],
- globalCoeffs = cRBCoeffs[4 :])
+
+ globalCoeffs = cRBCoeffs[4 :]
+ localCoeffs = cRBCoeffs[: 4]
+
ySupp = 1 + 2. * xSupp.data - .5 * xSupp.data ** 2.
xx = np.linspace(-2., 3., 100)
- yy = polyval(checkParameterList(xx, 1)[0], cRB, polyrbname)
+ yy = polyval(checkParameterList(xx, 1)[0], globalCoeffs, localCoeffs,
+ xSupp, directionalWeights, polyrbname)
yyman = 1. + 2. * xx - .5 * xx ** 2.
for j, xc in enumerate(np.arange(-1, 3)):
r2j = (5. * (xx - xc)) ** 2.
rbj = radialGaussian(r2j)
assert np.allclose(rbj, np.exp(-.5 * r2j))
- yyman += cRB.localCoeffs[j] * rbj
- ySupp += cRB.localCoeffs[j] * radialGaussian((directionalWeights[0]
- * (xSupp.data - xc)) ** 2.)
+ yyman += localCoeffs[j] * rbj
+ ySupp += localCoeffs[j] * radialGaussian((directionalWeights[0]
+ * (xSupp.data - xc)) ** 2.)
assert np.allclose(yy, yyman, atol = 1e-5)
VanT = polyvander(xSupp, [2], polyrbname,
directionalWeights = directionalWeights)
ySupp = np.pad(ySupp.flatten(), (0, len(VanT) - len(xSupp)), "constant")
out = customFit(VanT, ySupp)
assert np.allclose(out, cRBCoeffs, atol = 1e-8)
def test_legendre_thinplate():
polyrbname = "LEGENDRE_THINPLATE"
assert polyrbname in polybases
fitname = polyfitname(polyrbname)
domcoeff = polydomcoeff(5, polyrbname)
assert fitname == "legfit_thinplate"
assert np.isclose(domcoeff, 63. / 8, rtol = 1e-5)
directionalWeights = np.array([.5])
xSupp = checkParameterList(np.arange(-1, 3), 1)[0]
cRBCoeffs = np.array([-1., 3., -3., 1., 1., 2., -.5])
- cRB = radialFunction(directionalWeights = directionalWeights,
- supportPoints = xSupp, localCoeffs = cRBCoeffs[: 4],
- globalCoeffs = cRBCoeffs[4 :])
+
+ localCoeffs = cRBCoeffs[: 4]
+ globalCoeffs = cRBCoeffs[4 :]
+
ySupp = 1 + 2. * xSupp.data - .5 * (.5 * (3. * xSupp.data ** 2. - 1.))
xx = np.linspace(-2., 3., 100)
- yy = polyval(checkParameterList(xx, 1)[0], cRB, polyrbname)
+ yy = polyval(checkParameterList(xx, 1)[0], globalCoeffs, localCoeffs,
+ xSupp, directionalWeights, polyrbname)
yyman = 1. + 2. * xx - .5 * (.5 * (3. * xx ** 2. - 1.))
for j, xc in enumerate(np.arange(-1, 3)):
r2j = (directionalWeights[0] * (xx - xc)) ** 2.
rbj = thinPlateSpline(r2j)
assert np.allclose(rbj, .5 * r2j * np.log(np.finfo(float).eps + r2j))
- yyman += cRB.localCoeffs[j] * rbj
- ySupp += cRB.localCoeffs[j] * thinPlateSpline((directionalWeights[0]
+ yyman += localCoeffs[j] * rbj
+ ySupp += localCoeffs[j] * thinPlateSpline((directionalWeights[0]
* (xSupp.data - xc)) ** 2.)
assert np.allclose(yy, yyman, atol = 1e-5)
VanT = polyvander(xSupp, [2], polyrbname,
directionalWeights = directionalWeights)
ySupp = np.pad(ySupp.flatten(), (0, len(VanT) - len(xSupp)), "constant")
out = customFit(VanT, ySupp)
assert np.allclose(out, cRBCoeffs, atol = 1e-8)
def test_chebyshev_multiquadric():
polyrbname = "CHEBYSHEV_MULTIQUADRIC"
assert polyrbname in polybases
fitname = polyfitname(polyrbname)
domcoeff = polydomcoeff(5, polyrbname)
assert fitname == "chebfit_multiquadric"
assert np.isclose(domcoeff, 16, rtol = 1e-5)
directionalWeights = np.array([1.])
xSupp = checkParameterList(np.arange(-1, 3), 1)[0]
cRBCoeffs = np.array([-1., 3., -3., 1., 1., 2., -.5])
- cRB = radialFunction(directionalWeights = directionalWeights,
- supportPoints = xSupp, localCoeffs = cRBCoeffs[: 4],
- globalCoeffs = cRBCoeffs[4 :])
+
+ localCoeffs = cRBCoeffs[: 4]
+ globalCoeffs = cRBCoeffs[4 :]
+
ySupp = 1 + 2. * xSupp.data - .5 * (2. * xSupp.data ** 2. - 1.)
xx = np.linspace(-2., 3., 100)
- yy = polyval(checkParameterList(xx, 1)[0], cRB, polyrbname)
+ yy = polyval(checkParameterList(xx, 1)[0], globalCoeffs, localCoeffs,
+ xSupp, directionalWeights, polyrbname)
yyman = 1. + 2. * xx - .5 * (2. * xx ** 2. - 1.)
for j, xc in enumerate(np.arange(-1, 3)):
r2j = (directionalWeights[0] * (xx - xc)) ** 2.
rbj = multiQuadric(r2j)
assert np.allclose(rbj, np.power(r2j + 1, -.5))
- yyman += cRB.localCoeffs[j] * rbj
- ySupp += cRB.localCoeffs[j] * multiQuadric((directionalWeights[0]
- * (xSupp.data - xc)) ** 2.)
+ yyman += localCoeffs[j] * rbj
+ ySupp += localCoeffs[j] * multiQuadric((directionalWeights[0]
+ * (xSupp.data - xc)) ** 2.)
assert np.allclose(yy, yyman, atol = 1e-5)
VanT = polyvander(xSupp, [2], polyrbname,
directionalWeights = directionalWeights)
ySupp = np.pad(ySupp.flatten(), (0, len(VanT) - len(xSupp)), "constant")
out = customFit(VanT, ySupp)
assert np.allclose(out, cRBCoeffs, atol = 1e-8)
def test_total_degree_2d():
values = lambda x, y: (x - 3.) ** 2. * y - (x + 1.) * y ** 2.
polyrbname = "CHEBYSHEV_GAUSSIAN"
xs, ys = np.meshgrid(np.linspace(0., 4., 5), np.linspace(0., 4., 4))
xySupp = np.concatenate((xs.reshape(-1, 1), ys.reshape(-1, 1)), axis = 1)
zs = values(xs, ys)
zSupp = zs.flatten()
deg = 3
directionalWeights = [2., 1.]
VanT, _, reidxs = homogeneizedpolyvander(xySupp, deg, polyrbname,
directionalWeights = directionalWeights)
VanT = VanT[reidxs]
VanT = VanT[:, reidxs]
cFit = np.linalg.solve(VanT, np.pad(zSupp, (0, len(VanT) - len(zSupp)),
'constant'))
globCoeff = homogeneizedToFull([deg + 1] * 2, 2, cFit[len(zSupp) :])
- cRB = radialFunction(directionalWeights = directionalWeights,
- supportPoints = xySupp,
- localCoeffs = cFit[: len(zSupp)],
- globalCoeffs = globCoeff)
+
+ localCoeffs = cFit[: len(zSupp)]
+ globalCoeffs = globCoeff
+
xx, yy = np.meshgrid(np.linspace(0., 4., 100), np.linspace(0., 4., 100))
xxyy = np.concatenate((xx.reshape(-1, 1), yy.reshape(-1, 1)), axis = 1)
- zz = polyval(xxyy, cRB, polyrbname).reshape(xx.shape)
+ zz = polyval(xxyy, globalCoeffs, localCoeffs, xySupp, directionalWeights,
+ polyrbname).reshape(xx.shape)
zzex = values(xx, yy)
error = np.abs(zz - zzex)
print(np.max(error))
assert np.max(error) < 1e-10