Skip to content

Knot placement under knot multiplicity #128

@dential

Description

@dential

I am trying to build splines with discontinuities by increasing the multiplicity of the knot at the discontinuity. My understanding is that this can be achieved through splinter's support for custom knots; namely, using the two-step procedure described in #76 and #72.

However, as soon as I move away from using the bspline_interpolator() builder to building the spline from parameters using BSpline.from_param(), I get oscillations (as illustrated below).

Oscillations occur, even when I don't introduce knot multiplicity in the interior of the knot vector, i.e. when just trying to replicate bspline_interpolator() using BSpline.from_param():

import numpy as np
import matplotlib.pyplot as plt
import sys, os
import splinterpy
# splinterpy.load( PATH_HERE )

def g(x):
    if x <= 0.5:
        return 1
    else:
        return np.exp(-10*(x - 0.5))

gridsize = 51
grid = np.linspace(0, 1, gridsize)

# Define knots
knots = np.sort(np.append(grid, [0, 0, 0, 1, 1, 1]))

# Sample from function
y_data = np.zeros(gridsize)
for x_index, x in enumerate(grid):
    y_data[x_index] = g(x)

# Build splines
# Using bspline_interpolator
builder_spline = splinterpy.bspline_interpolator(grid, y_data, degree=3)
# Using BSpline.from_param
degrees = 3
knots = knots.tolist()
dim_y = 1
spline = splinterpy.BSpline.from_param(degrees, knots, dim_y)
fitted_spline = spline.fit(grid, y_data)

# Evaluate splines at fine grid
fine_grid = np.linspace(0, 1, 1001)
builder_spline_eval = builder_spline.eval(fine_grid)
fitted_spline_eval = fitted_spline.eval(fine_grid)

fig_builder_vs_param

Examining the knots of the two splines shows that they differ in that bspline_interpolator() has "removed" the second and second-to-last elements (0.02 and 0.98, respectively):

print(builder_spline.get_knot_vectors())
# [[0.0, 0.0, 0.0, 0.0, 0.04, 0.06000000000000001, 0.08, 0.1, 0.12, 0.13999999999999999, 0.16, 0.18, 0.2, 0.22000000000000003, 0.24000000000000005, 0.26, 0.28, 0.30000000000000004, 0.32, 0.33999999999999997, 0.36, 0.38, 0.4, 0.42000000000000004, 0.44000000000000006, 0.45999999999999996, 0.48, 0.5, 0.52, 0.54, 0.56, 0.5800000000000001, 0.6000000000000001, 0.62, 0.64, 0.66, 0.68, 0.7, 0.72, 0.74, 0.76, 0.78, 0.8, 0.8200000000000001, 0.8400000000000001, 0.86, 0.8800000000000001, 0.9, 0.9199999999999999, 0.9400000000000001, 0.9600000000000002, 1.0, 1.0, 1.0, 1.0]]
print(fitted_spline.get_knot_vectors())
# [[0.0, 0.0, 0.0, 0.0, 0.02, 0.04, 0.06, 0.08, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.22, 0.24, 0.26, 0.28, 0.3, 0.32, 0.34, 0.36, 0.38, 0.4, 0.42, 0.44, 0.46, 0.48, 0.5, 0.52, 0.54, 0.56, 0.58, 0.6, 0.62, 0.64, 0.66, 0.68, 0.7000000000000001, 0.72, 0.74, 0.76, 0.78, 0.8, 0.8200000000000001, 0.84, 0.86, 0.88, 0.9, 0.92, 0.9400000000000001, 0.96, 0.98, 1.0, 1.0, 1.0, 1.0]]

Removing these two elements from the knot vector fed into BSpline.from_param() makes the two splines identical:

knots = np.sort(np.append(grid, [0, 0, 0, 1, 1, 1]))
knots = knots[(knots != 0.02) & (knots != 0.98)]

fig_builder_vs_param

Introducing multiple knots at the discontinuity (0.5) leads to oscillations around the discontinuity...

knots = np.sort(np.append(grid, [0, 0, 0, 0.5, 0.5, 1, 1, 1]))
knots = knots[(knots != 0.02) & (knots != 0.98)]

fig_1

... unless the knots surrounding the discontinuity (0.48 and 0.52) are removed:

knots = np.sort(np.append(grid, [0, 0, 0, 0.5, 0.5, 1, 1, 1]))
knots = knots[(knots != 0.02) & (knots != 0.98)]
knots = knots[(knots != 0.48) & (knots != 0.52)]

fig_1

I think I am missing something about knot placement. Why do knots surrounding knot multiplicities need to be "removed" in order to avoid oscillations?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions