diff --git a/examples/2d/pod/fracture_pod.py b/examples/2d/pod/fracture_pod.py
index 36c04e2..08e798a 100644
--- a/examples/2d/pod/fracture_pod.py
+++ b/examples/2d/pod/fracture_pod.py
@@ -1,263 +1,263 @@
import numpy as np
from rrompy.hfengines.linear_problem.bidimensional import \
MembraneFractureEngine as MFE
from rrompy.reduction_methods.standard import RationalInterpolant as RI
from rrompy.reduction_methods.standard import ReducedBasis as RB
from rrompy.reduction_methods.pivoted import RationalInterpolantPivoted as RIP
from rrompy.reduction_methods.pivoted import ReducedBasisPivoted as RBP
from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
QuadratureSamplerTotal as QST,
ManualSampler as MS,
RandomSampler as RS)
verb = 70
size = 2
show_sample = True
show_norm = True
Delta = 0
MN = 5
R = (MN + 2) * (MN + 1) // 2
S = [int(np.ceil(R ** .5))] * 2
PODTol = 1e-6
SPivot = [MN + 1, 4]
MMarginal = SPivot[1] - 1
matchingWeight = 1.
cutOffTolerance = 1. * np.inf
cutOffType = "MAGNITUDE"
samples = "centered"
samples = "standard"
samples = "pivoted"
algo = "rational"
-algo = "RB"
+#algo = "RB"
sampling = "quadrature"
sampling = "quadrature_total"
#sampling = "random"
samplingM = "quadrature"
#samplingM = "quadrature_total"
#samplingM = "random"
polydegreetype = "TOTAL"
polydegreetype = "FULL"
if samples in ["standard", "pivoted"]:
radial = ""
# radial = "_gaussian"
# radial = "_thinplate"
# radial = "_multiquadric"
rW0 = 5.
radialWeight = [rW0]
if samples == "pivoted":
radialM = ""
# radialM = "_gaussian"
# radialM = "_thinplate"
# radialM = "_multiquadric"
rMW0 = 2.
radialWeightM = [rMW0]
assert Delta <= 0
if size == 1: # below
mu0 = [40 ** .5, .4]
mutar = [45 ** .5, .4]
murange = [[30 ** .5, .3], [50 ** .5, .5]]
elif size == 2: # top
mu0 = [40 ** .5, .6]
mutar = [45 ** .5, .6]
murange = [[30 ** .5, .5], [50 ** .5, .7]]
elif size == 3: # interesting
mu0 = [40 ** .5, .5]
mutar = [45 ** .5, .5]
murange = [[30 ** .5, .3], [50 ** .5, .7]]
elif size == 4: # wide_low
mu0 = [40 ** .5, .2]
mutar = [45 ** .5, .2]
murange = [[10 ** .5, .1], [70 ** .5, .3]]
elif size == 5: # wide_hi
mu0 = [40 ** .5, .8]
mutar = [45 ** .5, .8]
murange = [[10 ** .5, .7], [70 ** .5, .9]]
elif size == 6: # top_zoom
mu0 = [50 ** .5, .8]
mutar = [55 ** .5, .8]
murange = [[40 ** .5, .7], [60 ** .5, .9]]
elif size == 7: # huge
mu0 = [50 ** .5, .5]
mutar = [55 ** .5, .5]
murange = [[10 ** .5, .2], [90 ** .5, .8]]
elif size == 11: #L below
mu0 = [110 ** .5, .4]
mutar = [115 ** .5, .4]
murange = [[90 ** .5, .3], [130 ** .5, .5]]
elif size == 12: #L top
mu0 = [110 ** .5, .6]
mutar = [115 ** .5, .6]
murange = [[90 ** .5, .5], [130 ** .5, .7]]
elif size == 13: #L interesting
mu0 = [110 ** .5, .5]
mutar = [115 ** .5, .5]
murange = [[90 ** .5, .3], [130 ** .5, .7]]
elif size == 14: #L belowbelow
mu0 = [110 ** .5, .2]
mutar = [115 ** .5, .2]
murange = [[90 ** .5, .1], [130 ** .5, .3]]
elif size == 15: #L toptop
mu0 = [110 ** .5, .8]
mutar = [115 ** .5, .8]
murange = [[90 ** .5, .7], [130 ** .5, .9]]
elif size == 16: #L interestinginteresting
mu0 = [110 ** .5, .5]
mutar = [115 ** .5, .6]
murange = [[90 ** .5, .1], [130 ** .5, .9]]
elif size == 17: #L interestingtop
mu0 = [110 ** .5, .7]
mutar = [115 ** .5, .6]
murange = [[90 ** .5, .5], [130 ** .5, .9]]
elif size == 18: #L interestingbelow
mu0 = [110 ** .5, .3]
mutar = [115 ** .5, .4]
murange = [[90 ** .5, .1], [130 ** .5, .5]]
elif size == 100: # tiny
mu0 = [32.5 ** .5, .5]
mutar = [34 ** .5, .5]
murange = [[30 ** .5, .3], [35 ** .5, .7]]
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[1][0]**2. + bEff*murange[0][0]**2.) ** .5,
aEff*murange[1][1] + bEff*murange[0][1]]]
H = 1.
L = .75
delta = .05
n = 20
solver = MFE(mu0 = mu0, H = H, L = L, delta = delta, n = n, verbosity = verb)
rescalingExp = [2., 1.]
if algo == "rational":
params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True,
'polydegreetype':polydegreetype}
if samples == "standard":
params['polybasis'] = "CHEBYSHEV" + radial
# params['polybasis'] = "LEGENDRE" + radial
# params['polybasis'] = "MONOMIAL" + radial
params['radialDirectionalWeights'] = radialWeight
method = RI
elif samples == "centered":
params['polybasis'] = "MONOMIAL"
params['S'] = R
method = RI
elif samples == "pivoted":
params['S'] = [SPivot[0]]
params['SMarginal'] = [SPivot[1]]
params['MMarginal'] = MMarginal
params['polybasisPivot'] = "CHEBYSHEV" + radial
params['polybasisMarginal'] = "MONOMIAL" + radialM
params['radialDirectionalWeightsPivot'] = radialWeight
params['radialDirectionalWeightsMarginal'] = radialWeightM
params['matchingWeight'] = matchingWeight
params['cutOffTolerance'] = cutOffTolerance
params["cutOffType"] = cutOffType
method = RIP
else: #if algo == "RB":
params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':S,
'POD':True, 'PODTolerance':PODTol}
if samples == "standard":
method = RB
elif samples == "centered":
params['S'] = R
method = RB
elif samples == "pivoted":
params['S'] = [SPivot[0]]
params['R'] = SPivot[0]
params['SMarginal'] = [SPivot[1]]
params['MMarginal'] = MMarginal
params['polybasisMarginal'] = "MONOMIAL" + radialM
params['radialDirectionalWeightsMarginal'] = radialWeightM
params['matchingWeight'] = matchingWeight
params['cutOffTolerance'] = cutOffTolerance
params["cutOffType"] = cutOffType
method = RBP
if samples == "standard":
if sampling == "quadrature":
params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp)
# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp)
# params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp)
params['S'] = [max(j, MN + 1) for j in params['S']]
elif sampling == "quadrature_total":
params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp)
params['S'] = R
else: # if sampling == "random":
params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp)
params['S'] = R
elif samples == "centered":
params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp)
elif samples == "pivoted":
if sampling == "quadrature":
params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "CHEBYSHEV")
# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "GAUSSLEGENDRE")
# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "UNIFORM")
elif sampling == "quadrature_total":
params['samplerPivot'] = QST([murange[0][0], murange[1][0]], "CHEBYSHEV")
else: # if sampling == "random":
params['samplerPivot'] = RS([murange[0][0], murange[1][0]], "HALTON")
if samplingM == "quadrature":
params['samplerMarginal'] = QS([murange[0][1], murange[1][1]], "UNIFORM")
elif samplingM == "quadrature_total":
params['samplerMarginal'] = QST([murange[0][1], murange[1][1]], "CHEBYSHEV")
else: # if samplingM == "random":
params['samplerMarginal'] = RS([murange[0][1], murange[1][1]], "HALTON")
if samples != "pivoted":
approx = method(solver, mu0 = mu0, approxParameters = params,
verbosity = verb)
else:
approx = method(solver, mu0 = mu0, directionPivot = [0],
approxParameters = params, verbosity = verb)
if samples != "centered": approx.samplingEngine.allowRepeatedSamples = False
approx.setupApprox()
if show_sample:
import ufl
import fenics as fen
from rrompy.solver.fenics import affine_warping
L = mutar[1]
y = fen.SpatialCoordinate(solver.V.mesh())[1]
warp1, warpI1 = affine_warping(solver.V.mesh(),
np.array([[1, 0], [0, 2. * L]]))
warp2, warpI2 = affine_warping(solver.V.mesh(),
np.array([[1, 0], [0, 2. - 2. * L]]))
warp = ufl.conditional(ufl.ge(y, 0.), warp1, warp2)
warpI = ufl.conditional(ufl.ge(y, 0.), warpI1, warpI2)
approx.plotApprox(mutar, [warp, warpI], name = 'u_app', what = "REAL")
approx.plotHF(mutar, [warp, warpI], name = 'u_HF', what = "REAL")
approx.plotErr(mutar, [warp, warpI], name = 'err', what = "REAL")
# approx.plotRes(mutar, [warp, warpI], name = 'res', what = "REAL")
appErr = approx.normErr(mutar)
solNorm = approx.normHF(mutar)
resNorm = approx.normRes(mutar)
RHSNorm = approx.normRHS(mutar)
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)))
approx.verbosity = 5
from plot_zero_set import plotZeroSet2
muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0,
200, [2., 1.])
if show_norm:
solver._solveBatchSize = 100
from plot_inf_set import plotInfSet2
muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2(
murange, murangeEff, approx, mu0, 50,
[2., 1.], relative = True,
nobeta = True)
print(np.sort(approx.getPoles([None, .5]) ** 2.))
diff --git a/examples/airfoil/greedy.py b/examples/airfoil/greedy.py
index bb8aca3..ada4d7e 100644
--- a/examples/airfoil/greedy.py
+++ b/examples/airfoil/greedy.py
@@ -1,107 +1,107 @@
import numpy as np
from airfoil_engine import AirfoilScatteringEngine
from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI
from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB
from rrompy.solver.fenics import L2NormMatrix
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
verb = 5
timed = False
algo = "RI"
#algo = "RB"
polyBasis = "LEGENDRE"
#polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
homog = True
homog = False
k0s = np.linspace(5, 10, 100)
k0 = np.mean(k0s)
kl, kr = min(k0s), max(k0s)
params = {'S':5, 'sampler':QS([kl, kr], "UNIFORM"), 'nTestPoints':500,
'greedyTol':1e-2, 'polybasis':polyBasis,
- 'errorEstimatorKind':'BASIC'}
+ 'errorEstimatorKind':'INTERPOLATORY'}
#########
kappa = 10
theta = np.pi * - 45 / 180.
solver = AirfoilScatteringEngine(kappa, theta, verbosity = verb,
degree_threshold = 8, homogeneized = homog)
#########
if algo == "RI":
approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb)
else:
params.pop("polybasis")
params.pop("errorEstimatorKind")
approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb)
approx.initEstimatorNormEngine(L2NormMatrix(solver.V))
if timed:
from time import clock
start_time = clock()
approx.greedy()
print("Time: ", clock() - start_time)
else:
approx.greedy(True)
approx.samplingEngine.verbosity = 0
approx.verbosity = 0
kl, kr = np.real(kl), np.real(kr)
from matplotlib import pyplot as plt
normApp = np.zeros(len(k0s))
norm = np.zeros_like(normApp)
res = np.zeros_like(normApp)
err = np.zeros_like(normApp)
for j in range(len(k0s)):
normApp[j] = approx.normApprox(k0s[j])
norm[j] = approx.normHF(k0s[j])
res[j] = (approx.estimatorNormEngine.norm(
approx.getRes(k0s[j], duality = False))
/ approx.estimatorNormEngine.norm(
approx.getRHS(k0s[j], duality = False)))
err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j])
resApp = approx.errorEstimator(k0s)
plt.figure()
plt.plot(k0s, norm)
plt.plot(k0s, normApp, '--')
plt.plot(np.real(approx.mus.data),
1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, res)
plt.semilogy(k0s, resApp, '--')
plt.semilogy(np.real(approx.mus.data),
4.*np.max(resApp)*np.ones_like(approx.mus.data, dtype = float),
'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, err)
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
polesApp = approx.getPoles()
mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr)
print("Outliers:", polesApp[mask])
polesApp = polesApp[~mask]
plt.figure()
plt.plot(np.real(polesApp), np.imag(polesApp), 'kx')
plt.axis('equal')
plt.grid()
plt.show()
plt.close()
diff --git a/examples/all_forcing/greedy.py b/examples/all_forcing/greedy.py
index b32a4cc..3351a66 100644
--- a/examples/all_forcing/greedy.py
+++ b/examples/all_forcing/greedy.py
@@ -1,96 +1,96 @@
import numpy as np
from all_forcing_engine import AllForcingEngine
from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI
from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB
from rrompy.solver.fenics import L2NormMatrix
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
verb = 5
timed = False
algo = "RI"
#algo = "RB"
polyBasis = "LEGENDRE"
#polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
if timed: verb = 0
z0s = np.linspace(-3., 3., 100)
z0 = np.mean(z0s)
zl, zr = min(z0s), max(z0s)
n = 30
solver = AllForcingEngine(mu0 = z0, n = n, degree_threshold = 8, verbosity = 0)
params = {'sampler':QS([zl, zr], "UNIFORM"), 'nTestPoints':500,
'greedyTol':1e-2, 'S':2, 'polybasis':polyBasis,
-# 'errorEstimatorKind':'BARE'}
-# 'errorEstimatorKind':'BASIC'}
- 'errorEstimatorKind':'EXACT'}
+# 'errorEstimatorKind':'DIAGONAL'}
+# 'errorEstimatorKind':'INTERPOLATORY'}
+ 'errorEstimatorKind':'AFFINE'}
if algo == "RI":
approx = RI(solver, mu0 = solver.mu0, approxParameters = params,
verbosity = verb)
else:
params.pop("polybasis")
params.pop("errorEstimatorKind")
approx = RB(solver, mu0 = solver.mu0, approxParameters = params,
verbosity = verb)
approx.initEstimatorNormEngine(L2NormMatrix(solver.V))
if timed:
from time import clock
start_time = clock()
approx.greedy()
print("Time: ", clock() - start_time)
else:
approx.greedy(True)
polesApp = approx.getPoles()
print("Poles:\n", polesApp)
approx.samplingEngine.verbosity = 0
approx.trainedModel.verbosity = 0
approx.verbosity = 0
zl, zr = np.real(zl), np.real(zr)
from matplotlib import pyplot as plt
normApp = np.zeros(len(z0s))
norm = np.zeros_like(normApp)
res = np.zeros_like(normApp)
err = np.zeros_like(normApp)
for j in range(len(z0s)):
normApp[j] = approx.normApprox(z0s[j])
norm[j] = approx.normHF(z0s[j])
res[j] = (approx.estimatorNormEngine.norm(
approx.getRes(z0s[j], duality = False))
/ approx.estimatorNormEngine.norm(
approx.getRHS(z0s[j], duality = False)))
err[j] = approx.normErr(z0s[j]) / norm[j]
resApp = approx.errorEstimator(z0s)
plt.figure()
plt.semilogy(z0s, norm)
plt.semilogy(z0s, normApp, '--')
plt.semilogy(np.real(approx.mus.data),
1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float),
'rx')
plt.xlim([zl, zr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(z0s, res)
plt.semilogy(z0s, resApp, '--')
plt.semilogy(np.real(approx.mus.data),
approx.greedyTol*np.ones_like(approx.mus.data, dtype = float),
'rx')
plt.xlim([zl, zr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(z0s, err)
plt.xlim([zl, zr])
plt.grid()
plt.show()
plt.close()
diff --git a/examples/diapason/greedy.py b/examples/diapason/greedy.py
index 3121583..7debb24 100644
--- a/examples/diapason/greedy.py
+++ b/examples/diapason/greedy.py
@@ -1,129 +1,129 @@
import numpy as np
from diapason_engine import DiapasonEngine, DiapasonEngineDamped
from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI
from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB
from rrompy.solver.fenics import L2NormMatrix
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
verb = 5
timed = False
algo = "RI"
#algo = "RB"
polyBasis = "LEGENDRE"
#polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
if timed: verb = 0
dampingEta = 0 * 1e4 / 2. / np.pi
k0s = np.linspace(2.5e2, 7.5e3, 100)
#k0s = np.linspace(2.5e3, 1.5e4, 100)
#k0s = np.linspace(5.0e4, 1.0e5, 100)
k0s = np.linspace(2.0e5, 2.5e5, 100)
kl, kr = min(k0s), max(k0s)
theta = 20. * np.pi / 180.
phi = 10. * np.pi / 180.
c = 3.e2
rho = 8e3 * (2. * np.pi) ** 2.
E = 1.93e11
nu = .3
T = 1e6
###
if np.isclose(dampingEta, 0.):
solver = DiapasonEngine(kappa = np.mean(np.power(k0s, 2.)) ** .5, c = c,
rho = rho, E = E, nu = nu, T = T, theta = theta,
phi = phi, meshNo = 1, degree_threshold = 8,
verbosity = 0)
else:
solver = DiapasonEngineDamped(kappa = np.mean(k0s), c = c, rho = rho,
E = E, nu = nu, T = T, theta = theta,
phi = phi, dampingEta = dampingEta,
meshNo = 1, degree_threshold = 8,
verbosity = 0)
params = {'sampler':QS([kl, kr], "UNIFORM"),#, solver.rescalingExp),
'nTestPoints':500, 'greedyTol':1e-2, 'S':5, 'polybasis':polyBasis,
-# 'errorEstimatorKind':'BARE'}
-# 'errorEstimatorKind':'BASIC'}
- 'errorEstimatorKind':'EXACT'}
+# 'errorEstimatorKind':'DIAGONAL'}
+# 'errorEstimatorKind':'INTERPOLATORY'}
+ 'errorEstimatorKind':'AFFINE'}
if algo == "RI":
approx = RI(solver, mu0 = solver.mu0, approxParameters = params,
verbosity = verb)
else:
params.pop("polybasis")
params.pop("errorEstimatorKind")
approx = RB(solver, mu0 = solver.mu0, approxParameters = params,
verbosity = verb)
approx.initEstimatorNormEngine(L2NormMatrix(solver.V))
if timed:
from time import clock
start_time = clock()
approx.greedy()
print("Time: ", clock() - start_time)
else:
approx.greedy(True)
polesApp = approx.getPoles()
print("Poles:\n", polesApp)
approx.samplingEngine.verbosity = 0
approx.trainedModel.verbosity = 0
approx.verbosity = 0
kl, kr = np.real(kl), np.real(kr)
from matplotlib import pyplot as plt
normApp = np.zeros(len(k0s))
norm = np.zeros_like(normApp)
res = np.zeros_like(normApp)
err = np.zeros_like(normApp)
for j in range(len(k0s)):
normApp[j] = approx.normApprox(k0s[j])
norm[j] = approx.normHF(k0s[j])
res[j] = (approx.estimatorNormEngine.norm(
approx.getRes(k0s[j], duality = False))
/ approx.estimatorNormEngine.norm(
approx.getRHS(k0s[j], duality = False)))
err[j] = approx.normErr(k0s[j]) / norm[j]
resApp = approx.errorEstimator(k0s)
plt.figure()
plt.semilogy(k0s, norm)
plt.semilogy(k0s, normApp, '--')
plt.semilogy(np.real(approx.mus.data),
1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float),
'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, res)
plt.semilogy(k0s, resApp, '--')
plt.semilogy(np.real(approx.mus.data),
approx.greedyTol*np.ones_like(approx.mus.data, dtype = float),
'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, err)
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr)
print("Outliers:", polesApp[mask])
polesAppEff = polesApp[~mask]
plt.figure()
plt.plot(np.real(polesAppEff), np.imag(polesAppEff), 'kx')
plt.axis('equal')
plt.grid()
plt.show()
plt.close()
diff --git a/examples/from_papers/greedy_internalBox.py b/examples/from_papers/greedy_internalBox.py
index caa2d7f..2e92707 100644
--- a/examples/from_papers/greedy_internalBox.py
+++ b/examples/from_papers/greedy_internalBox.py
@@ -1,114 +1,116 @@
import numpy as np
import fenics as fen
from rrompy.hfengines.linear_problem import HelmholtzProblemEngine as HPE
from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI
from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB
from rrompy.solver.fenics import L2NormMatrix
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
dim = 3
verb = 5
timed = False
algo = "RI"
#algo = "RB"
polyBasis = "LEGENDRE"
#polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
k0s = np.power(np.linspace(500 ** 2., 2250 ** 2., 200), .5)
k0 = np.mean(np.power(k0s, 2.)) ** .5
kl, kr = min(k0s), max(k0s)
params = {'sampler':QS([kl, kr], "UNIFORM", 2.), 'nTestPoints':500,
'greedyTol':1e-2, 'S':2, 'polybasis':polyBasis,
- 'errorEstimatorKind':'BASIC'}
+# 'errorEstimatorKind':'AFFINE'}
+# 'errorEstimatorKind':'INTERPOLATORY'}
+ 'errorEstimatorKind':'LOCALL2'}
if dim == 2:
mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(.1, .15), 10, 15)
x, y = fen.SpatialCoordinate(mesh)[:]
f = fen.exp(- 1e2 * (x + y))
else:#if dim == 3:
mesh = fen.BoxMesh(fen.Point(0., 0., 0.), fen.Point(.1, .15, .25), 4, 6,10)
x, y, z = fen.SpatialCoordinate(mesh)[:]
f = fen.exp(- 1e2 * (x + y + z))
solver = HPE(np.real(k0), verbosity = verb)
solver.V = fen.FunctionSpace(mesh, "P", 3)
solver.refractionIndex = fen.Constant(1. / 54.6)
solver.forcingTerm = f
solver.NeumannBoundary = "ALL"
#########
if algo == "RI":
approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb)
else:
params.pop("polybasis")
params.pop("errorEstimatorKind")
approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb)
approx.initEstimatorNormEngine(L2NormMatrix(solver.V))
if timed:
from time import clock
start_time = clock()
approx.greedy()
print("Time: ", clock() - start_time)
else:
approx.greedy(True)
approx.samplingEngine.verbosity = 0
approx.verbosity = 0
kl, kr = np.real(kl), np.real(kr)
from matplotlib import pyplot as plt
normApp = np.zeros(len(k0s))
norm = np.zeros_like(normApp)
res = np.zeros_like(normApp)
err = np.zeros_like(normApp)
for j in range(len(k0s)):
normApp[j] = approx.normApprox(k0s[j])
norm[j] = approx.normHF(k0s[j])
res[j] = (approx.estimatorNormEngine.norm(
approx.getRes(k0s[j], duality = False))
/ approx.estimatorNormEngine.norm(
approx.getRHS(k0s[j], duality = False)))
err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j])
resApp = approx.errorEstimator(k0s)
plt.figure()
plt.plot(k0s, norm)
plt.plot(k0s, normApp, '--')
plt.plot(np.real(approx.mus(0)),
1.05*np.max(norm)*np.ones(approx.mus.shape, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, res)
plt.semilogy(k0s, resApp, '--')
plt.semilogy(np.real(approx.mus(0)),
4.*np.max(resApp)*np.ones(approx.mus.shape, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, err)
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
polesApp = approx.getPoles()
mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr)
print("Outliers:", polesApp[mask])
polesApp = polesApp[~mask]
plt.figure()
plt.plot(np.real(polesApp), np.imag(polesApp), 'kx')
plt.axis('equal')
plt.grid()
plt.show()
plt.close()
diff --git a/examples/from_papers/greedy_scatteringAirfoil.py b/examples/from_papers/greedy_scatteringAirfoil.py
index ed94f56..46c8187 100644
--- a/examples/from_papers/greedy_scatteringAirfoil.py
+++ b/examples/from_papers/greedy_scatteringAirfoil.py
@@ -1,151 +1,151 @@
import numpy as np
import fenics as fen
import ufl
from rrompy.hfengines.linear_problem import \
HelmholtzBoxScatteringProblemEngine as HSP
from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI
from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB
from rrompy.solver.fenics import fenONE, L2NormMatrix
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
verb = 2
timed = False
algo = "RI"
#algo = "RB"
polyBasis = "LEGENDRE"
#polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
homog = True
homog = False
k0s = np.linspace(5, 20, 25)
k0 = np.mean(k0s)
kl, kr = min(k0s), max(k0s)
params = {'sampler':QS([kl, kr], "UNIFORM"), 'nTestPoints':500,
'greedyTol':1e-2, 'S':10, 'polybasis':polyBasis,
- 'errorEstimatorKind':'BARE'}
-# 'errorEstimatorKind':'BASIC'}
-# 'errorEstimatorKind':'EXACT'}
+ 'errorEstimatorKind':'DIAGONAL'}
+# 'errorEstimatorKind':'INTERPOLATORY'}
+# 'errorEstimatorKind':'AFFINE'}
#########
PI = np.pi
R = 2
def Dboundary(x, on_boundary):
return on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R
kappa = 10
theta = PI * - 45 / 180.
mu = 1.1
epsilon = .1
mesh = fen.Mesh('../data/mesh/airfoil.xml')
c, s = np.cos(theta), np.sin(theta)
x, y = fen.SpatialCoordinate(mesh)[:]
u0R = - fen.cos(kappa * (c * x + s * y))
u0I = - fen.sin(kappa * (c * x + s * y))
checkReal = x**2-x+y**2
rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25
phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2
phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2
kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/
((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.)
)**.5 - mu
kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/
((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.)
)**.5 - mu
Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1
Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1
cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE)
c_F = fen.Constant(.1)
cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE)
c_F = fen.Constant(.1)
cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F)
cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F)
a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF)
solver = HSP(R, np.real(k0), theta, n = 1, verbosity = verb,
degree_threshold = 8, homogeneized = homog)
solver.omega = np.real(k0)
solver.V = fen.FunctionSpace(mesh, "P", 2)
solver.diffusivity = a
solver.DirichletBoundary = Dboundary
solver.RobinBoundary = "REST"
solver.DirichletDatum = [u0R, u0I]
#########
if algo == "RI":
approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb)
else:
params.pop("polybasis")
params.pop("errorEstimatorKind")
approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb)
approx.initEstimatorNormEngine(L2NormMatrix(solver.V))
if timed:
from time import clock
start_time = clock()
approx.greedy()
print("Time: ", clock() - start_time)
else:
approx.greedy(True)
approx.samplingEngine.verbosity = 0
approx.verbosity = 0
kl, kr = np.real(kl), np.real(kr)
from matplotlib import pyplot as plt
normApp = np.zeros(len(k0s))
norm = np.zeros_like(normApp)
res = np.zeros_like(normApp)
err = np.zeros_like(normApp)
for j in range(len(k0s)):
normApp[j] = approx.normApprox(k0s[j])
norm[j] = approx.normHF(k0s[j])
res[j] = (approx.estimatorNormEngine.norm(
approx.getRes(k0s[j], duality = False))
/ approx.estimatorNormEngine.norm(
approx.getRHS(k0s[j], duality = False)))
err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j])
resApp = approx.errorEstimator(k0s)
plt.figure()
plt.plot(k0s, norm)
plt.plot(k0s, normApp, '--')
plt.plot(np.real(approx.mus(0)),
1.05*np.max(norm)*np.ones(approx.mus.shape, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, res)
plt.semilogy(k0s, resApp, '--')
plt.semilogy(np.real(approx.mus(0)),
4.*np.max(resApp)*np.ones(approx.mus.shape, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, err)
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
polesApp = approx.getPoles()
mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr)
print("Outliers:", polesApp[mask])
polesApp = polesApp[~mask]
plt.figure()
plt.plot(np.real(polesApp), np.imag(polesApp), 'kx')
plt.axis('equal')
plt.grid()
plt.show()
plt.close()
diff --git a/rrompy/parameter/parameter_sampling/quadrature_sampler.py b/rrompy/parameter/parameter_sampling/quadrature_sampler.py
index 0efb5f6..53e2135 100644
--- a/rrompy/parameter/parameter_sampling/quadrature_sampler.py
+++ b/rrompy/parameter/parameter_sampling/quadrature_sampler.py
@@ -1,82 +1,87 @@
# 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 .generic_sampler import GenericSampler
from rrompy.utilities.base.types import List, paramList
from rrompy.utilities.exception_manager import RROMPyException
from rrompy.utilities.numerical import lowDiscrepancy, kroneckerer
from rrompy.parameter import checkParameterList
__all__ = ['QuadratureSampler']
class QuadratureSampler(GenericSampler):
"""Generator of quadrature sample points."""
- allowedKinds = ["UNIFORM", "CHEBYSHEV", "GAUSSLEGENDRE"]
+ _allowedKinds = ["UNIFORM", "CHEBYSHEV", "EXTENDEDCHEBYSHEV",
+ "GAUSSLEGENDRE", "EXTENDEDGAUSSLEGENDRE"]
def __init__(self, lims:paramList, kind : str = "UNIFORM",
scalingExp : List[float] = None):
super().__init__(lims = lims, scalingExp = scalingExp)
self.kind = kind
def __str__(self) -> str:
return "{}_{}".format(super().__str__(), self.kind)
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
@property
def kind(self):
"""Value of kind."""
return self._kind
@kind.setter
def kind(self, kind):
- if kind.upper() not in self.allowedKinds:
+ if kind.upper() not in self._allowedKinds:
raise RROMPyException("Generator kind not recognized.")
self._kind = kind.upper()
def generatePoints(self, n:List[int], reorder : bool = True) -> paramList:
"""Array of sample points."""
if not hasattr(n, "__len__"): n = [n]
super().generatePoints(n)
nleft, nright = 1, np.prod(n)
xmat = np.empty((nright, self.npar), dtype = self.lims.dtype)
for d in range(self.npar):
nright //= n[d]
a = self.lims(0, d) ** self.scalingExp[d]
b = self.lims(1, d) ** self.scalingExp[d]
c, r = (a + b) / 2., (a - b) / 2.
if self.kind == "UNIFORM":
xd = np.linspace(a, b, n[d])
- elif self.kind == "CHEBYSHEV":
- nodes, _ = np.polynomial.chebyshev.chebgauss(n[d])
+ elif self.kind in ["CHEBYSHEV", "EXTENDEDCHEBYSHEV"]:
+ nodes = np.polynomial.chebyshev.chebgauss(n[d])[0]
+ if n[d] > 1 and self.kind == "EXTENDEDCHEBYSHEV":
+ nodes /= nodes[0]
+ xd = c + r * nodes
+ elif self.kind in ["GAUSSLEGENDRE", "EXTENDEDGAUSSLEGENDRE"]:
+ nodes = np.polynomial.legendre.leggauss(n[d])[0][::-1]
+ if n[d] > 1 and self.kind == "EXTENDEDCHEBYSHEV":
+ nodes /= nodes[0]
xd = c + r * nodes
- elif self.kind == "GAUSSLEGENDRE":
- nodes, _ = np.polynomial.legendre.leggauss(n[d])
- xd = c + r * nodes[::-1]
xd **= 1. / self.scalingExp[d]
xmat[:, d] = kroneckerer(xd, nleft, nright)
nleft *= n[d]
x = checkParameterList(xmat, self.npar)[0]
nright = np.prod(n)
if nright > 1 and reorder:
fejerOrdering = [nright - 1] + lowDiscrepancy(nright - 1)
xmat = xmat[fejerOrdering, :]
return x
diff --git a/rrompy/reduction_methods/greedy/generic_greedy_approximant.py b/rrompy/reduction_methods/greedy/generic_greedy_approximant.py
index 2154c96..bb81d6f 100644
--- a/rrompy/reduction_methods/greedy/generic_greedy_approximant.py
+++ b/rrompy/reduction_methods/greedy/generic_greedy_approximant.py
@@ -1,667 +1,701 @@
# 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.standard.generic_standard_approximant \
import GenericStandardApproximant
from rrompy.utilities.base.types import (Np1D, Np2D, DictAny, HFEng, Tuple,
List, normEng, paramVal, paramList,
sampList)
+from rrompy.utilities.poly_fitting.polynomial import (polyvanderTotal as pvT,
+ hashIdxToDerivative as hashI,
+ totalDegreeN)
from rrompy.utilities.base import verbosityManager as vbMng
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) -> paramList:
+ tol : float = 1e-8) -> Np1D:
"""Remove from mus all the elements which are too close to badmus."""
if len(badmus) == 0: return mus
- musNp = np.array(mus(0))
- badmus = np.array(badmus(0))
- proximity = np.min(np.abs(musNp.reshape(-1, 1)
- - np.tile(badmus.reshape(1, -1), [len(mus), 1])),
- axis = 1).flatten()
- idxPop = np.arange(len(mus))[proximity <= tol]
- for i, j in enumerate(idxPop):
- mus.pop(j - i)
- return mus
+ musA = mus.data
+ badmusA = badmus.data
+ proximity = np.min(localL2Distance(musA, badmusA), 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;
- '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.;
- 'interactive': whether to interactively terminate greedy
algorithm; defaults to False;
- 'maxIter': maximum number of greedy steps; defaults to 1e2;
- 'refinementRatio': ratio of test points to be exhausted before
test set refinement; defaults to 0.2;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator; defaults
to sampler.
Defaults to empty dict.
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.
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'collinearityTol': collinearity 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.
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.
greedyTol: Uniform error tolerance for greedy algorithm.
collinearityTol: Collinearity 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.
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.
"""
TOL_INSTABILITY = 1e-6
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, verbosity : int = 10,
timestamp : bool = True):
self._preInit()
self._addParametersToList(["greedyTol", "collinearityTol",
"interactive", "maxIter", "refinementRatio",
"nTestPoints"],
[1e-2, 0., False, 1e2, .2, 5e2],
["trainSetGenerator"], ["AUTO"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
verbosity = verbosity, timestamp = timestamp)
- RROMPyAssert(self.HFEngine.npar, 1, "Parameter dimension")
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 interactive(self):
"""Value of interactive."""
return self._interactive
@interactive.setter
def interactive(self, interactive):
self._interactive = interactive
@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 refinementRatio(self):
"""Value of refinementRatio."""
return self._refinementRatio
@refinementRatio.setter
def refinementRatio(self, refinementRatio):
if refinementRatio <= 0. or refinementRatio > 1.:
raise RROMPyException(("refinementRatio must be between 0 "
"(excluded) and 1."))
if (hasattr(self, "_refinementRatio")
and self.refinementRatio is not None):
refinementRatioold = self.refinementRatio
else:
refinementRatioold = -1
self._refinementRatio = refinementRatio
self._approxParameters["refinementRatio"] = self.refinementRatio
if refinementRatioold != self.refinementRatio:
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 not hasattr(self.HFEngine, "energyNormPartialDualMatrix"):
self.HFEngine.buildEnergyNormPartialDualForm()
estimatorEnergyMatrix = (
self.HFEngine.energyNormPartialDualMatrix)
else:
if hasattr(normEngn, "buildEnergyNormPartialDualForm"):
if not hasattr(normEngn, "energyNormPartialDualMatrix"):
normEngn.buildEnergyNormPartialDualForm()
estimatorEnergyMatrix = (
normEngn.energyNormPartialDualMatrix)
else:
estimatorEnergyMatrix = normEngn
self.estimatorNormEngine = normEngine(estimatorEnergyMatrix)
def errorEstimator(self, mus:Np1D) -> Np1D:
"""Standard residual-based error estimator."""
self.setupApprox()
self.assembleReducedResidualBlocks(full = True)
nmus = len(mus)
nAs = self.trainedModel.data.resAA.shape[1]
nbs = self.trainedModel.data.resbb.shape[0]
- radiusA = np.empty((len(self.mus), nAs, nmus), dtype = np.complex)
- radiusb = np.empty((nbs, nmus), dtype = np.complex)
mustr = mus
if nmus > 2:
mustr = "[{} ..({}).. {}]".format(mus[0], nmus - 2, mus[-1])
vbMng(self.trainedModel, "INIT",
"Computing reduced solution at mu = {}.".format(mustr), 5)
verb = self.trainedModel.verbosity
self.trainedModel.verbosity = 0
mus = checkParameterList(mus, self.npar)[0]
uApproxRs = self.getApproxReduced(mus)
muRTest = ((mus ** self.HFEngine.rescalingExp
- self.mu0 ** self.HFEngine.rescalingExp)
- / self.scaleFactor)(0)
- radiusb = np.power(muRTest.reshape(1, -1), np.arange(nbs))
- for j, muPL in enumerate(muRTest):
- uApproxR = uApproxRs[j]
- for i in range(nAs):
- radiusA[:, i, j] = muPL ** i * uApproxR
+ / self.scaleFactor)
+ vanderBase, _, vanderBaseIdxs = pvT(muRTest,
+ np.sum(hashI(max(nAs, nbs) - 1, self.npar)),
+ "MONOMIAL")
+ vanderBase = vanderBase[:, vanderBaseIdxs].T
+ radiusA = np.expand_dims(uApproxRs.data, 1) * vanderBase[: nAs, :]
+ radiusb = vanderBase[: nbs, :]
self.trainedModel.verbosity = verb
vbMng(self.trainedModel, "DEL", "Done computing reduced solution.", 5)
# 'ij,jk,ik->k', resbb, radiusb, radiusb.conj()
ff = np.sum(self.trainedModel.data.resbb.dot(radiusb) * radiusb.conj(),
axis = 0)
# 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj()
Lf = np.sum(np.tensordot(self.trainedModel.data.resAb, radiusA, 2)
* radiusb.conj(), axis = 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))
return np.abs((LL - 2. * np.real(Lf) + ff) / ff) ** .5
- def getMaxErrorEstimator(self, mus:paramList,
+ def getMaxErrorEstimator(self, mus:paramList, n : int = 1,
plot : bool = False) -> Tuple[Np1D, int, float]:
"""
Compute maximum of (and index of maximum of) error estimator over given
parameters.
"""
errorEstTest = self.errorEstimator(mus)
- idxMaxEst = np.argmax(errorEstTest)
+ errCP = copy(errorEstTest)
+ idxMaxEst = np.empty(n, dtype = int)
+ for j in range(n):
+ k = np.argmax(errCP)
+ idxMaxEst[j] = k
+ if j + 1 < n:
+ errCP *= np.linalg.norm((mus.data ** self.HFEngine.rescalingExp
+ - mus[k] ** self.HFEngine.rescalingExp)
+ / self.scaleFactor, axis = 1)
maxEst = errorEstTest[idxMaxEst]
- if plot and not np.all(np.isinf(errorEstTest)):
- musre = mus.re(0)
+ if plot and not np.any(np.isinf(errorEstTest)):
+ musre = copy(mus.re.data)
from matplotlib import pyplot as plt
plt.figure()
- plt.semilogy(musre, errorEstTest, 'k')
- plt.semilogy([musre[0], musre[-1]], [self.greedyTol] * 2, 'r--')
+ plt.semilogy([musre[0, 0], musre[-1, 0]], [self.greedyTol] * 2,
+ 'r--')
plt.semilogy(self.mus.re(0),
2. * self.greedyTol * np.ones(len(self.mus)), '*m')
- plt.semilogy(musre[idxMaxEst], maxEst, 'xr')
+ plt.semilogy(musre[idxMaxEst, 0], maxEst, 'xr')
+ while len(musre) > 0:
+ if self.npar == 1:
+ currIdx = np.arange(len(musre))
+ else:
+ currIdx = np.where(np.isclose(np.sum(
+ np.abs(musre[:, 1 :] - musre[0, 1 :]), 1), 0.))[0]
+ plt.semilogy(musre[currIdx, 0], errorEstTest[currIdx], 'k')
+ musre = np.delete(musre, currIdx, 0)
plt.grid()
plt.show()
plt.close()
return errorEstTest, idxMaxEst, maxEst
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)[:, -1]
return np.abs(reff[-1]) < self.collinearityTol * np.linalg.norm(reff)
- def greedyNextSample(self, muidx:int, plotEst : bool = False)\
+ def greedyNextSample(self, muidx:int, n : int = 1, plotEst : bool = False)\
-> Tuple[Np1D, int, float, paramVal]:
"""Compute next greedy snapshot of solution map."""
RROMPyAssert(self._mode, message = "Cannot add greedy sample.")
- mu = copy(self.muTest[muidx])
+ mus = copy(self.muTest[muidx])
self.muTest.pop(muidx)
- vbMng(self, "MAIN",
- ("Adding {}-th sample point at {} to training "
- "set.").format(self.samplingEngine.nsamples + 1, mu), 2)
- self.mus.append(mu)
- self.samplingEngine.nextSample(mu)
- if self._isLastSampleCollinear():
- RROMPyWarning("Collinearity above tolerance detected.")
- errorEstTest = np.empty(len(self.muTest))
- errorEstTest[:] = np.nan
- return errorEstTest, -1, np.nan, np.nan
+ for mu in mus:
+ vbMng(self, "MAIN",
+ ("Adding {}-th sample point at {} to training "
+ "set.").format(self.samplingEngine.nsamples + 1, mu), 2)
+ self.mus.append(mu)
+ self.samplingEngine.nextSample(mu)
+ if self._isLastSampleCollinear():
+ RROMPyWarning("Collinearity above tolerance detected.")
+ errorEstTest = np.empty(len(self.muTest))
+ errorEstTest[:] = np.nan
+ return errorEstTest, [-1], np.nan, np.nan
errorEstTest, muidx, maxErrorEst = self.getMaxErrorEstimator(
- self.muTest, plotEst)
+ self.muTest, n, plotEst)
+ self.derivativeShell += 1
+ self.derivativeShellSize = totalDegreeN(self.npar - 1,
+ self.derivativeShell)
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.mus = self.trainSetGenerator.generatePoints(self.S)
+ self.derivativeShell, self.derivativeShellSize, shellTot = -1, 0, 0
+ while shellTot <= len(self.mus):
+ self.derivativeShellSize = totalDegreeN(self.npar - 1,
+ self.derivativeShell + 1)
+ shellTot += self.derivativeShellSize
+ self.derivativeShell += 1
+ self._S = shellTot - self.derivativeShellSize
+ self.mus = self.mus[list(range(self.S))]
muTestBase = self.sampler.generatePoints(self.nTestPoints)
- muTestBase = pruneSamples(muTestBase, self.mus,
- 1e-10 * self.scaleFactor[0]).sort()
+ idxPop = pruneSamples(muTestBase ** self.HFEngine.rescalingExp,
+ self.mus ** self.HFEngine.rescalingExp,
+ 1e-10 * self.scaleFactor[0])
+ muTestBase.pop(idxPop)
+ muTestBase = muTestBase.sort()
muLast = copy(self.mus[-1])
self.mus.pop()
if len(self.mus) > 0:
- nSamples = np.prod(self.S) - 1
vbMng(self, "MAIN",
("Adding first {} sample point{} at {} to training "
- "set.").format(nSamples, "" + "s" * (nSamples > 1),
+ "set.").format(self.S - 1, "" + "s" * (self.S > 2),
self.mus), 2)
self.samplingEngine.iterSample(self.mus)
self.muTest = emptyParameterList()
self.muTest.reset((len(muTestBase) + 1, self.mus.shape[1]))
self.muTest[: -1] = muTestBase
self.muTest[-1] = muLast
def _enrichTestSet(self, nTest:int):
"""Add extra elements to test set."""
RROMPyAssert(self._mode, message = "Cannot enrich test set.")
muTestExtra = self.sampler.generatePoints(2 * nTest)
muTotal = copy(self.mus)
muTotal.append(self.muTest)
- muTestExtra = pruneSamples(muTestExtra, muTotal,
- 1e-10 * self.scaleFactor[0])
+ idxPop = pruneSamples(muTestExtra ** self.HFEngine.rescalingExp,
+ muTotal ** self.HFEngine.rescalingExp,
+ 1e-10 * self.scaleFactor[0])
+ muTestExtra.pop(idxPop)
muTestNew = np.empty(len(self.muTest) + len(muTestExtra),
dtype = np.complex)
muTestNew[: len(self.muTest)] = self.muTest(0)
muTestNew[len(self.muTest) :] = muTestExtra(0)
self.muTest = checkParameterList(np.sort(muTestNew), self.npar)[0]
vbMng(self, "MAIN",
"Enriching test set by {} elements.".format(len(muTestExtra)), 5)
def greedy(self, plotEst : bool = False):
"""Compute greedy snapshots of solution map."""
RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
if self.samplingEngine.nsamples > 0:
return
vbMng(self, "INIT", "Starting computation of snapshots.", 2)
self._preliminaryTraining()
nTest = self.nTestPoints
muT0 = copy(self.muTest[-1])
- errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample(-1,
- plotEst)
- if np.isnan(maxErrorEst):
+ errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample(
+ [len(self.muTest) - 1], self.derivativeShellSize, plotEst)
+ if np.any(np.isnan(maxErrorEst)):
RROMPyWarning(("Instability in a posteriori estimator. "
"Starting preemptive greedy loop termination."))
self.muTest.append(muT0)
self.mus.pop(-1)
self.samplingEngine.popSample()
self.setupApprox()
else:
vbMng(self, "MAIN", ("Uniform testing error estimate "
- "{:.4e}.").format(maxErrorEst), 2)
+ "{:.4e}.").format(np.max(maxErrorEst)), 2)
trainedModelOld = copy(self.trainedModel)
while (self.samplingEngine.nsamples < self.maxIter
- and maxErrorEst > self.greedyTol):
+ and np.max(maxErrorEst) > self.greedyTol):
if (1. - self.refinementRatio) * nTest > len(self.muTest):
self._enrichTestSet(nTest)
nTest = len(self.muTest)
- muTestOld, maxErrorEstOld = self.muTest, maxErrorEst
+ muTestOld, maxErrorEstOld = self.muTest, np.max(maxErrorEst)
errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample(
- muidx, plotEst)
+ muidx, self.derivativeShellSize, plotEst)
vbMng(self, "MAIN", ("Uniform testing error estimate "
- "{:.4e}.").format(maxErrorEst), 2)
- if (np.isnan(maxErrorEst) or np.isinf(maxErrorEst)
- or maxErrorEstOld < maxErrorEst * self.TOL_INSTABILITY):
+ "{:.4e}.").format(np.max(maxErrorEst)), 2)
+ if (np.any(np.isnan(maxErrorEst))
+ or np.any(np.isinf(maxErrorEst))
+ or maxErrorEstOld < (np.max(maxErrorEst)
+ * self.TOL_INSTABILITY)):
RROMPyWarning(("Instability in a posteriori estimator. "
"Starting preemptive greedy loop "
"termination."))
self.muTest = muTestOld
self.mus.pop(-1)
self.samplingEngine.popSample()
self.trainedModel.data = copy(trainedModelOld.data)
break
trainedModelOld.data = copy(self.trainedModel.data)
- if (self.interactive and maxErrorEst <= self.greedyTol):
+ if (self.interactive and np.max(maxErrorEst) <= self.greedyTol):
vbMng(self, "MAIN",
("Required tolerance {} achieved. Want to decrease "
"greedyTol and continue? "
"Y/N").format(self.greedyTol), 0, end = "")
increasemaxIter = input()
if increasemaxIter.upper() == "Y":
vbMng(self, "MAIN", "Reducing value of greedyTol...",
0)
- while maxErrorEst <= self._greedyTol:
+ while np.max(maxErrorEst) <= self._greedyTol:
self._greedyTol *= .5
if (self.interactive
and self.samplingEngine.nsamples >= self.maxIter):
vbMng(self, "MAIN",
("Maximum number of iterations {} reached. Want to "
"increase maxIter and continue? "
"Y/N").format(self.maxIter), 0, end = "")
increasemaxIter = input()
if increasemaxIter.upper() == "Y":
vbMng(self, "MAIN", "Doubling value of maxIter...", 0)
self._maxIter *= 2
vbMng(self, "DEL",
("Done computing snapshots (final snapshot count: "
"{}).").format(self.samplingEngine.nsamples), 2)
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])
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 = As[j].dot(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 = As[j].dot(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 = As[i].dot(pMat)
resAA[:, i, :, i] = (
self.estimatorNormEngine.innerProduct(MAi, MAi))
for j in range(i):
MAj = As[j].dot(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 = As[i].dot(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 = As[j].dot(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 _setupAffineBlocks(self, full : bool = True):
bmsg = "" if full else " of RHS"
if (full and not hasattr(self, "As")) or not hasattr(self, "bs"):
vbMng(self, "INIT",
"Computing affine blocks{} of system.".format(bmsg), 10)
if full:
self.As = self.HFEngine.affineLinearSystemA(self.mu0,
self.scaleFactor)
self.bs = self.HFEngine.affineLinearSystemb(self.mu0,
self.scaleFactor)
vbMng(self, "DEL",
"Done computing affine blocks{}.".format(bmsg), 10)
def assembleReducedResidualBlocks(self, full : bool = False):
"""Build affine blocks of affine decomposition of residual."""
self._setupAffineBlocks(full)
self.assembleReducedResidualBlocksbb(self.bs)
if full:
pMat = self.trainedModel.data.projMat
self.assembleReducedResidualBlocksAb(self.As, self.bs, pMat)
self.assembleReducedResidualBlocksAA(self.As, pMat)
diff --git a/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py b/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py
index e738c71..c5ec641 100644
--- a/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py
+++ b/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py
@@ -1,273 +1,309 @@
# 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_greedy_approximant import GenericGreedyApproximant
+from .generic_greedy_approximant import (GenericGreedyApproximant,
+ localL2Distance)
from rrompy.utilities.poly_fitting.polynomial import (polybases,
- PolynomialInterpolator as PI)
+ PolynomialInterpolator as PI,
+ polyvanderTotal as pvT,
+ hashIdxToDerivative as hashI)
from rrompy.reduction_methods.standard import RationalInterpolant
from rrompy.reduction_methods.trained_model import (
TrainedModelRational as tModel)
from rrompy.reduction_methods.trained_model import TrainedModelData
-from rrompy.utilities.base.types import Np1D, DictAny, HFEng, paramVal
+from rrompy.utilities.base.types import (Np1D, Tuple, DictAny, HFEng, paramVal,
+ paramList)
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(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;
- 'S': number of starting training points;
- 'sampler': sample point generator;
- 'radialDirectionalWeights': 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 sampler;
- 'polybasis': type of basis for interpolation; defaults to
'MONOMIAL';
- 'errorEstimatorKind': kind of error estimator; available values
- include 'EXACT', 'BASIC', and 'BARE'; defaults to 'EXACT';
+ include 'AFFINE', 'INTERPOLATORY', 'LOCALL2', and 'DIAGONAL';
+ defaults to 'AFFINE';
- 'interpRcond': tolerance for interpolation; defaults to None;
- 'robustTol': tolerance for robust rational denominator
management; defaults to 0.
Defaults to empty dict.
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.
- 'radialDirectionalWeights': 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;
- 'errorEstimatorKind': kind of error estimator;
- 'interpRcond': tolerance for interpolation;
- '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.
POD: whether to compute POD of snapshots.
S: number of test points.
sampler: Sample point generator.
radialDirectionalWeights: 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 rational denominator management.
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 = ["EXACT", "BASIC", "BARE"]
+ _allowedEstimatorKinds = ["AFFINE", "INTERPOLATORY", "LOCALL2", "DIAGONAL"]
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, verbosity : int = 10,
timestamp : bool = True):
self._preInit()
self._addParametersToList(["polybasis", "errorEstimatorKind",
"interpRcond", "robustTol"],
- ["MONOMIAL", "EXACT", -1, 0])
+ ["MONOMIAL", "AFFINE", -1, 0])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
verbosity = verbosity, timestamp = timestamp)
self._postInit()
@property
def E(self):
"""Value of E."""
- self._E = np.prod(self._S) - 1
+ self._E = self._S - 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 prod(S) - 1."))
+ "cannot be changed from that of S - 1."))
@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 'EXACT'."))
- errorEstimatorKind = "EXACT"
+ "to 'AFFINE'."))
+ errorEstimatorKind = "AFFINE"
self._errorEstimatorKind = errorEstimatorKind
self._approxParameters["errorEstimatorKind"] = self.errorEstimatorKind
- def _errorSamplingRatio(self, mus:Np1D, muRTest:Np1D) -> Np1D:
+ def _errorSamplingRatio(self, mus:Np1D,
+ muRTest:paramList) -> Tuple[Np1D, Np1D]:
"""Scalar ratio in explicit error estimator."""
self.setupApprox()
- muRTrain = self.trainedModel.centerNormalize(self.mus)(0)
- testTile = np.tile(np.reshape(muRTest, (-1, 1)), [1, len(self.mus)])
- nodalVals = np.prod(testTile - np.reshape(muRTrain, (1, -1)), axis = 1)
+ muRTrain = self.trainedModel.centerNormalize(self.mus)
+ nodalVals = np.prod(localL2Distance(muRTest.data, muRTrain.data),
+ axis = 1)
denVals = self.trainedModel.getQVal(mus)
- return np.abs(nodalVals / denVals)
-
- def _RHSNorms(self, muRT:Np1D) -> Np1D:
- """High fidelity system RHS norms."""
- self.assembleReducedResidualBlocks(full = False)
- radiusb = np.power(muRT.reshape(1, -1), np.arange(self.HFEngine.nbs))
- bresb = np.sum(self.trainedModel.data.resbb.dot(radiusb)
- * radiusb.conj(), axis = 0)
- return np.power(np.abs(bresb), .5)
+ return np.abs(nodalVals), np.abs(denVals)
def errorEstimator(self, mus:Np1D) -> Np1D:
"""Standard residual-based error estimator."""
- if self.errorEstimatorKind == "EXACT":
+ if self.errorEstimatorKind == "AFFINE":
return super().errorEstimator(mus)
+ RROMPyAssert(self.HFEngine.npar, 1, "Parameter dimension")
self.setupApprox()
- muRTest = self.trainedModel.centerNormalize(mus)(0)
- samplingRatio = self._errorSamplingRatio(mus, muRTest)
- RHSnorms = self._RHSNorms(muRTest)
- if self.errorEstimatorKind == "BARE":
- self.setupApprox()
+ muRTest = self.trainedModel.centerNormalize(mus)
+ samplingNodal, samplingQ = self._errorSamplingRatio(mus, muRTest)
+ samplingRatio = samplingNodal / samplingQ
+ if self.errorEstimatorKind == "DIAGONAL":
+ self.assembleReducedResidualBlocks(full = False)
+ nbs = self.HFEngine.nbs
+ vanderBase, _, vanderBaseIdxs = pvT(muRTest,
+ np.sum(hashI(nbs - 1, self.npar)),
+ "MONOMIAL")
+ radiusb = vanderBase[:, vanderBaseIdxs][:, : nbs].T
+ bresb = np.sum(self.trainedModel.data.resbb.dot(radiusb)
+ * radiusb.conj(), axis = 0)
self.assembleReducedResidualGramian(self.trainedModel.data.projMat)
pDom = self.trainedModel.data.P.domCoeff
LL = pDom.conj().dot(self.trainedModel.data.gramian.dot(pDom))
if not hasattr(self, "Anorm2Approx"):
if self.HFEngine.nAs > 1:
Adiag = self.scaleFactor[0] * np.diagonal(
self.HFEngine.A(self.mu0, [1]))
self.Anorm2Approx = np.mean(np.abs(Adiag) ** 2.)
if np.isclose(self.Anorm2Approx, 0.) or self.HFEngine.nAs <= 1:
self.Anorm2Approx = 1
- jOpt = np.abs(self.Anorm2Approx * LL) ** .5
- else: #if self.errorEstimatorKind == "BASIC":
+ jOpt = np.abs(self.Anorm2Approx * LL / bresb) ** .5
+ err = jOpt * samplingRatio
+ elif self.errorEstimatorKind == "INTERPOLATORY":
+ sampRCP = copy(samplingRatio)
+ idx_muTestSample = np.empty(self.derivativeShellSize, dtype = int)
+ for j in range(self.derivativeShellSize):
+ k = np.argmax(sampRCP)
+ idx_muTestSample[j] = k
+ if j + 1 < self.derivativeShellSize:
+ sampRCP *= np.linalg.norm(
+ (mus.data ** self.HFEngine.rescalingExp
+ - mus[k] ** self.HFEngine.rescalingExp)
+ / self.scaleFactor, axis = 1)
+ mu_muTestSample = mus[idx_muTestSample]
+ app_muTestSample = self.getApprox(mu_muTestSample)
+ resmus = self.HFEngine.residual(app_muTestSample, mu_muTestSample,
+ duality = False)
+ RHSmus = self.HFEngine.residual(None, mu_muTestSample,
+ duality = False)
+ ressamples = (self.estimatorNormEngine.norm(resmus)
+ / self.estimatorNormEngine.norm(RHSmus))
+ musT = copy(self.mus)
+ musT.append(mu_muTestSample)
+ musT = self.trainedModel.centerNormalize(musT)
+ resT = np.zeros(len(musT), dtype = np.complex)
+ resT[len(self.mus) :] = ressamples * samplingQ[idx_muTestSample]
+ p = PI()
+ wellCond, msg = p.setupByInterpolation(musT, resT, self.M + 1,
+ self.polybasis, self.verbosity >= 25,
+ True, {}, {"rcond": self.interpRcond})
+ err = np.abs(p(self.trainedModel.centerNormalize(mus))) / samplingQ
+ else: #if self.errorEstimatorKind == "LOCALL2":
idx_muTestSample = np.argmax(samplingRatio)
mu_muTestSample = mus[idx_muTestSample]
app_muTestSample = self.getApprox(mu_muTestSample)
resmu = self.HFEngine.residual(app_muTestSample, mu_muTestSample,
- duality = False)[0]
- jOpt = np.abs(self.estimatorNormEngine.norm(resmu)
- / samplingRatio[idx_muTestSample])
- return jOpt * samplingRatio / RHSnorms
+ duality = False)
+ RHSmu = self.HFEngine.residual(None, mu_muTestSample,
+ duality = False)
+ jOpt = np.abs(self.estimatorNormEngine.norm(resmu)[0]
+ / samplingRatio[idx_muTestSample]
+ / self.estimatorNormEngine.norm(RHSmu)[0])
+ err = jOpt * samplingRatio
+ return err
def setupApprox(self, plotEst : bool = False):
"""
Compute rational 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._S - 1] * 2
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.scaleFactor,
self.HFEngine.rescalingExp)
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)
self.catchInstability = True
if self.N > 0:
- Qf = self._setupDenominator()
- Q = Qf[0]
- if self.errorEstimatorKind == "EXACT":
- self._fitinv = Qf[1].flatten()
+ Q = self._setupDenominator()[0]
else:
Q = PI()
Q.coeffs = np.ones(1, dtype = np.complex)
Q.npar = 1
Q.polybasis = self.polybasis
- if self.errorEstimatorKind == "EXACT": self._fitinv = np.ones(1)
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)
diff --git a/rrompy/reduction_methods/greedy/reduced_basis_greedy.py b/rrompy/reduction_methods/greedy/reduced_basis_greedy.py
index 1c8803d..91fe185 100644
--- a/rrompy/reduction_methods/greedy/reduced_basis_greedy.py
+++ b/rrompy/reduction_methods/greedy/reduced_basis_greedy.py
@@ -1,173 +1,172 @@
# 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 .generic_greedy_approximant import GenericGreedyApproximant
from rrompy.reduction_methods.standard import ReducedBasis
from rrompy.reduction_methods.trained_model import \
TrainedModelReducedBasis as tModel
from rrompy.reduction_methods.trained_model import TrainedModelData
from rrompy.utilities.base.types import DictAny, HFEng, paramVal
from rrompy.utilities.base import verbosityManager as vbMng
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;
- 'S': number of starting training points;
- 'sampler': sample point generator;
- '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 test points to be exhausted before
test set refinement; defaults to 0.2;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator; defaults
to sampler.
Defaults to empty dict.
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.
- '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.
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.
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.
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, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, verbosity : int = 10,
timestamp : bool = True):
self._preInit()
self._addParametersToList([], [], toBeExcluded = ["R", "PODTolerance"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
verbosity = verbosity, timestamp = timestamp)
self._postInit()
@property
def R(self):
"""Value of R."""
- self._R = np.prod(self._S)
+ self._R = self._S
return self._R
@R.setter
def R(self, R):
RROMPyWarning(("R is used just to simplify inheritance, and its value "
- "cannot be changed from that of prod(S)."))
+ "cannot be changed from that of 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 setupApprox(self, plotEst : bool = False):
"""Compute RB projection matrix."""
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)
vbMng(self, "INIT", "Computing projection matrix.", 7)
pMat = self.samplingEngine.samples
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,
pMat, self.scaleFactor,
self.HFEngine.rescalingExp)
ARBs, bRBs = self.assembleReducedSystem(pMat)
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
pMatOld = self.trainedModel.data.projMat
Sold = pMatOld.shape[1]
idxNew = list(range(Sold, pMat.shape[1]))
ARBs, bRBs = self.assembleReducedSystem(pMat(idxNew), pMatOld)
self.trainedModel.data.projMat = copy(pMat)
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 approximant.", 5)
diff --git a/rrompy/reduction_methods/standard/rational_interpolant.py b/rrompy/reduction_methods/standard/rational_interpolant.py
index 78682b3..86a0ba3 100644
--- a/rrompy/reduction_methods/standard/rational_interpolant.py
+++ b/rrompy/reduction_methods/standard/rational_interpolant.py
@@ -1,536 +1,536 @@
# 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, nextDerivativeIndices,
hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI,
polyvander as pvP, polyvanderTotal as pvTP,
degreeTotalToFull, fullDegreeMaxMask, totalDegreeMaxMask,
PolynomialInterpolator as PI, fullDegreeN, totalDegreeN)
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.reduction_methods.trained_model import (
TrainedModelRational 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)
+ List, paramVal, sampList)
from rrompy.utilities.base import verbosityManager as vbMng
from rrompy.utilities.numerical import multifactorial, customPInv
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;
- '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 0;
- 'N': degree of rational interpolant denominator; defaults to 0;
- 'polydegreetype': type of polynomial degree; defaults to 'TOTAL';
- 'radialDirectionalWeights': radial basis weights for interpolant
numerator; defaults to 0, i.e. identity;
- 'interpRcond': tolerance for interpolation; defaults to None;
- 'robustTol': tolerance for robust rational denominator
management; defaults to 0.
Defaults to empty dict.
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;
- '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;
- '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.
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.
polydegreetype: Type of polynomial degree.
radialDirectionalWeights: Radial basis weights for interpolant
numerator.
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, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, verbosity : int = 10,
timestamp : bool = True):
self._preInit()
self._addParametersToList(["polybasis", "M", "N", "polydegreetype",
"radialDirectionalWeights", "interpRcond",
"robustTol"],
["MONOMIAL", 0, 0, "TOTAL", 1, -1, 0])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
verbosity = verbosity, timestamp = timestamp)
self.catchInstability = False
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 ppb + rbpb + mlspb:
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):
self._radialDirectionalWeights = radialDirectionalWeights
self._approxParameters["radialDirectionalWeights"] = (
self.radialDirectionalWeights)
@property
def M(self):
"""Value of M."""
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
@property
def N(self):
"""Value of N."""
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
@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
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.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)
while self.N > 0:
invD, fitinv = self._computeInterpolantInverseBlocks()
if self.POD:
ev, eV = self.findeveVGQR(self.samplingEngine.RPOD, invD)
else:
ev, eV = self.findeveVGExplicit(self.samplingEngine.samples,
invD)
nevBad = checkRobustTolerance(ev, self.robustTol)
if nevBad <= 1: break
if self.catchInstability:
raise RROMPyException(("Instability in denominator "
"computation: eigenproblem is poorly "
"conditioned."))
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)
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
cfun = totalDegreeN if self.polydegreetype == "TOTAL" else fullDegreeN
M = copy(self.M)
while len(self.mus) < cfun(M, self.npar): M -= 1
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:
if self.polybasis in ppb:
p = PI()
wellCond, msg = p.setupByInterpolation(
self._musUniqueCN, Qevaldiag, self.M,
self.polybasis, self.verbosity >= 5,
self.polydegreetype == "TOTAL",
{"derIdxs": self._derIdxs,
"reorder": self._reorder,
"scl": np.power(self.scaleFactor, -1.)},
{"rcond": self.interpRcond})
elif self.polybasis in rbpb:
p = RBI()
wellCond, msg = p.setupByInterpolation(
self._musUniqueCN, Qevaldiag, self.M, self.polybasis,
self.radialDirectionalWeights, self.verbosity >= 5,
self.polydegreetype == "TOTAL",
{"derIdxs": self._derIdxs, "reorder": self._reorder,
"scl": np.power(self.scaleFactor, -1.)},
{"rcond": self.interpRcond})
else:# if self.polybasis in mlspb:
p = MLSI()
wellCond, msg = p.setupByInterpolation(
self._musUniqueCN, Qevaldiag, self.M, self.polybasis,
self.radialDirectionalWeights, self.verbosity >= 5,
self.polydegreetype == "TOTAL",
{"derIdxs": self._derIdxs, "reorder": self._reorder,
"scl": np.power(self.scaleFactor, -1.)})
vbMng(self, "MAIN", msg, 5)
if wellCond: break
if self.catchInstability:
raise RROMPyException(("Instability in numerator computation: "
"polyfit is poorly conditioned."))
RROMPyWarning("Polyfit is poorly conditioned. Reducing M by 1.")
self.M = self.M - 1
if self.M < 0:
raise RROMPyException(("Instability in computation of numerator. "
"Aborting."))
vbMng(self, "DEL", "Done computing numerator.", 7)
return p
def setupApprox(self):
"""
Compute rational 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.scaleFactor,
self.HFEngine.rescalingExp)
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()[0]
else:
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) -> Tuple[List[Np2D], Np2D]:
"""
Compute inverse factors for minimal interpolant target functional.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
self._setupInterpolationIndices()
cfun = totalDegreeN if self.polydegreetype == "TOTAL" else fullDegreeN
N = copy(self.N)
while len(self.mus) < cfun(N, self.npar): N -= 1
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:
if self.polydegreetype == "TOTAL":
TE, _, argIdxs = pvTP(self._musUniqueCN, self.N,
self.polybasis0, self._derIdxs,
self._reorder,
scl = np.power(self.scaleFactor, -1.))
TE = TE[:, argIdxs]
idxsB = totalDegreeMaxMask(self.N, self.npar)
else: #if self.polydegreetype == "FULL":
TE = pvP(self._musUniqueCN, [self.N] * self.npar,
self.polybasis0, self._derIdxs, self._reorder,
scl = np.power(self.scaleFactor, -1.))
idxsB = fullDegreeMaxMask(self.N, self.npar)
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], self.N,
polyfitname(self.polybasis0),
fitOut[1][1][0] / fitOut[1][1][-1]),
5)
if fitOut[1][0] == TE.shape[1]:
fitinv = fitOut[0][idxsB, :]
break
RROMPyWarning("Polyfit is poorly conditioned. Reducing N by 1.")
self.N = self.N - 1
if self.polydegreetype == "TOTAL":
TN, _, argIdxs = pvTP(self._musUniqueCN, self.N, self.polybasis0,
self._derIdxs, self._reorder,
scl = np.power(self.scaleFactor, -1.))
TN = TN[:, argIdxs]
else: #if self.polydegreetype == "FULL":
TN = pvP(self._musUniqueCN, [self.N] * self.npar,
self.polybasis0, self._derIdxs, self._reorder,
scl = np.power(self.scaleFactor, -1.))
invD = [None] * (len(idxsB))
for k in range(len(idxsB)):
pseudoInv = np.diag(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 = 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, 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)
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 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, :] = 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 getResidues(self, *args, **kwargs) -> Np1D:
"""
Obtain approximant residues.
Returns:
Matrix with residues as columns.
"""
return self.trainedModel.getResidues(*args, **kwargs)
diff --git a/tests/reduction_methods_1D/rational_interpolant_greedy_1d.py b/tests/reduction_methods_1D/rational_interpolant_greedy_1d.py
index d57d9e2..31338b2 100644
--- a/tests/reduction_methods_1D/rational_interpolant_greedy_1d.py
+++ b/tests/reduction_methods_1D/rational_interpolant_greedy_1d.py
@@ -1,93 +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 matrix_fft import matrixFFT
from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RIG
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
def test_lax_tolerance(capsys):
mu = 2.25
solver = matrixFFT()
params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"), "S": 4,
"polybasis": "CHEBYSHEV", "greedyTol": 1e-2,
- "errorEstimatorKind": "bare",
+ "errorEstimatorKind": "DIAGONAL",
"trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")}
approx = RIG(solver, 4., params, verbosity = 100)
approx.greedy()
out, err = capsys.readouterr()
assert "Done computing snapshots (final snapshot count: 10)." in out
assert len(err) == 0
assert np.isclose(approx.normErr(mu)[0], 2.169678e-4, rtol = 1e-1)
def test_samples_at_poles():
solver = matrixFFT()
params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"), "S": 4,
"nTestPoints": 100, "polybasis": "CHEBYSHEV", "greedyTol": 1e-5,
- "errorEstimatorKind": "exact",
+ "errorEstimatorKind": "AFFINE",
"trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")}
approx = RIG(solver, 4., params, verbosity = 0)
approx.greedy()
for mu in approx.mus:
assert np.isclose(approx.normErr(mu)[0] / (1e-15+approx.normHF(mu)[0]),
0., atol = 1e-4)
poles = approx.getPoles()
for lambda_ in range(2, 7):
assert np.isclose(np.min(np.abs(poles - lambda_)), 0., atol = 1e-3)
assert np.isclose(np.min(np.abs(np.array(approx.mus(0)) - lambda_)),
0., atol = 1e-1)
def test_maxIter():
solver = matrixFFT()
params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"),
"S": 5, "nTestPoints": 500, "polybasis": "CHEBYSHEV",
- "greedyTol": 1e-6, "maxIter": 10, "errorEstimatorKind": "basic",
+ "greedyTol": 1e-6, "maxIter": 10,
+ "errorEstimatorKind": "INTERPOLATORY",
"trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")}
approx = RIG(solver, 4., params, verbosity = 0)
approx.input = lambda: "N"
approx.greedy()
assert len(approx.mus) == 10
_, _, maxEst = approx.getMaxErrorEstimator(approx.muTest)
assert maxEst > 1e-6
def test_load_copy(capsys):
mu = 3.
solver = matrixFFT()
params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"), "S": 4,
"nTestPoints": 100, "polybasis": "CHEBYSHEV",
- "greedyTol": 1e-5, "errorEstimatorKind": "exact",
+ "greedyTol": 1e-5, "errorEstimatorKind": "AFFINE",
"trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")}
approx1 = RIG(solver, 4., params, verbosity = 100)
approx1.greedy()
err1 = approx1.normErr(mu)[0]
out, err = capsys.readouterr()
assert "Solving HF model for mu =" in out
assert len(err) == 0
approx2 = RIG(solver, 4., params, verbosity = 100)
approx2.setTrainedModel(approx1)
approx2.setHF(mu, approx1.uHF)
err2 = approx2.normErr(mu)[0]
out, err = capsys.readouterr()
assert "Solving HF model for mu =" not in out
assert len(err) == 0
assert np.isclose(err1, err2, rtol = 1e-10)