Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions qmat/lagrange.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,3 +530,51 @@ def getDerivationMatrix(self, order=1, duplicates=True):
return D2
else:
return D1, D2


def getSparseInterpolationMatrix(inPoints, outPoints, order):
"""
Get a sparse interpolation matrix from `inPoints` to `outPoints` of order
`order` using barycentric Lagrange interpolation.

The matrix will have `order` entries per line, and tends to be banded when
both `inPoints` and `outPoints` are equispaced and cover the same interval.

Parameters
----------
inPoints : np.1darray
The points you want to interpolate from
outPoints : np.1darray
The points you want to interpolate to
order : int
Order of the interpolation

Returns
-------
A : scipy.sparse.csc_matrix(len(outPoints), len(inPoints))
Sparse interpolation matrix
"""
import scipy.sparse as sp

assert order <= len(inPoints), f'Cannot interpolate {len(inPoints)} to order {order}! Please reduce order'

A = sp.lil_matrix((len(outPoints), len(inPoints)))
lastInterpolationLine = None
lastClosestPoints = None

for i in range(len(outPoints)):
closestPointsIdx = np.sort(np.argsort(np.abs(inPoints - outPoints[i]))[:order])
closestPoints = inPoints[closestPointsIdx] - outPoints[i]

if lastClosestPoints is not None and np.allclose(closestPoints, lastClosestPoints):
interpolationLine = lastInterpolationLine
else:
interpolator = LagrangeApproximation(points = closestPoints)
interpolationLine = interpolator.getInterpolationMatrix([0])[0]

lastInterpolationLine = interpolationLine
lastClosestPoints = closestPoints

A[i, closestPointsIdx] = interpolationLine

return A.tocsc()
32 changes: 31 additions & 1 deletion tests/test_2_lagrange.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,34 @@ def testDuplicates(nPoints, nCopy, duplicates):

assert np.allclose(D2[:, approx._zerIdx], 0), "[D2] zero indices have non-zero values"
assert np.allclose(D2[:, approx._nnzIdx], D2_ref), "[D2] nonzero values different from reference"
assert np.allclose(D2_noDuplicates, D2_ref), "[D2] no duplicates values different from reference"
assert np.allclose(D2_noDuplicates, D2_ref), "[D2] no duplicates values different from reference"


@pytest.mark.parametrize('inPoints', [np.linspace(0, 1, 15), np.linspace(0.3, 2.7, 13), np.linspace(0, 10, 4)])
@pytest.mark.parametrize('outPoints', [np.linspace(0, 1, 9), np.linspace(0.5, 2.2, 4), np.linspace(0.3, 2.7, 13)])
@pytest.mark.parametrize('order', [1, 2, 3, 4])
def testSparseInterpolation(inPoints, outPoints, order):
from qmat.lagrange import getSparseInterpolationMatrix
import scipy.sparse as sp

np.random.seed(47)

interpolationMatrix = getSparseInterpolationMatrix(inPoints, outPoints, order)
assert isinstance(interpolationMatrix, sp.csc_matrix)

polyCoeffs = np.random.randn(order)
inPolynomial = np.polyval(polyCoeffs,inPoints)
interpolated = interpolationMatrix @ inPolynomial
error = np.linalg.norm(np.polyval(polyCoeffs, outPoints)- interpolated)
assert error < 1e-11, f'Interpolation of order {order} polynomial is not exact with error {error:.2e}'

testInexactInterpolation = True
if len(inPoints) == len(outPoints):
if np.allclose(inPoints, outPoints):
testInexactInterpolation = False

if testInexactInterpolation:
polyCoeffs = np.random.randn(order+1)
inPolynomial = np.polyval(polyCoeffs,inPoints)
interpolated = interpolationMatrix @ inPolynomial
assert not np.allclose(np.polyval(polyCoeffs, outPoints), interpolated), f'Interpolation of order {order+1} polynomial is unexpectedly exact'