I'm trying to write a Python function which does the following:
To make it more clear, here's an illustration:
Notice how points with weights are "pushing" other points around.
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: