Search code examples
pythonmathinterpolation

How to interpolate points while taking their weights into account?


I'm trying to write a Python function which does the following:

  1. Takes in an array of points where each point has:
    • Position from 0 to 1 (x)
    • Weight (w)
  2. Smoothly interpolates between them in such a way that point weights are taken into account.

To make it more clear, here's an illustration:


1


Notice how points with weights are "pushing" other points around.


Solution

  • Here is a physics model that slowly moves the points around as if they were sliding magnets.

    At each iteration, the force exerted on each magnet is calculated, and the magnet moves accordingly.

    The repulsion exerted by a magnet over another magnet is proportional to the inverse of the square of the distance between the two, because that's how it works for actual magnets.

    import numpy as np
    #import matplotlib.pyplot as plt  ## uncomment those lines for graphics output
    
    def move_points(weights, points=None, left=0, right=1, n_iter = 1000, learning_rate = 0.0001):
        n = weights.shape[0]
        if not points:
            points = np.linspace(0, 1, n)
        assert(points.shape == weights.shape == (n,))
        #plt.scatter(points, [0]*n)
        for t in range(1, n_iter+1):
            dists = (points - points.reshape((n,1)))
            coeffs = np.sign(dists) / dists**2
            forces = np.nansum(weights*coeffs, axis=1)
            points = points - learning_rate * forces
            points[0] = max(left, points[0])
            points[-1] = min(points[-1], right)
            #plt.scatter(points, [t]*n)
        #plt.show()
        return points
    

    Testing:

    for weights in map(np.array, ([0,0,0,0,0], [1,0,0,0,2], [1,0,10,0,2])):
        print('weights: ', weights)
        print('points:  ', move_points(weights, n_iter=100))
        print()
    
    # weights:  [0 0 0 0 0]
    # points:   [0.   0.25 0.5  0.75 1.  ]
    
    # weights:  [1 0 0 0 2]
    # points:   [0.         0.32732937 0.46804381 0.59336546 1.        ]
    
    # weights:  [ 1  0 10  0  2]
    # points:   [0.         0.11141358 0.46804381 0.83729092 1.        ]
    

    Graphics output of trajectories:

    weights = [0 0 0 0 0] Weights = [0 0 0 0 0]

    weights = [1 0 0 0 2] Weights = [1 0 0 0 2]

    weights = [1 0 10 0 2] Weights = [1 0 10 0 2]