Search code examples
pythonpandasnumpyclampsmoothstep

How to implement a smooth clamp function in python?


The clamp function is clamp(x, min, max) = min if x < min, max if x > max, else x

I need a function that behaves like the clamp function, but is smooth (i.e. has a continuous derivative).


Solution

  • Normal clamp:

    np.clip(x, mi, mx)
    

    Smoothclamp (guaranteed to agree with normal clamp for x < min and x > max):

    def smoothclamp(x, mi, mx): return mi + (mx-mi)*(lambda t: np.where(t < 0 , 0, np.where( t <= 1 , 3*t**2-2*t**3, 1 ) ) )( (x-mi)/(mx-mi) )
    

    Sigmoid (Approximates clamp, never smaller than min, never larger than max)

    def sigmoid(x,mi, mx): return mi + (mx-mi)*(lambda t: (1+200**(-t+0.5))**(-1) )( (x-mi)/(mx-mi) )
    

    For some purposes Sigmoid will be better than Smoothclamp because Sigmoid is an invertible function - no information is lost.

    For other purposes, you may need to be certain that f(x) = xmax for all x > xmax - in that case Smoothclamp is better. Also, as mentioned in another answer, there is a whole family of Smoothclamp functions, though the one given here is adequate for my purposes (no special properties other than a smooth derivative needed)

    Plot them:

    import numpy as np
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots(1, 1)
    x = np.linspace(-4,7,1000)
    ax.plot(x, np.clip(x, -1, 4),'k-', lw=2, alpha=0.8, label='clamp')
    ax.plot(x, smoothclamp(x, -1, 4),'g-', lw=3, alpha=0.5, label='smoothclamp')
    ax.plot(x, sigmoid(x, -1, 4),'b-', lw=3, alpha=0.5, label='sigmoid')
    plt.legend(loc='upper left')
    plt.show()
    

    graph

    Also of potential use is the arithmetic mean of these two:

    def clampoid(x, mi, mx): return mi + (mx-mi)*(lambda t: 0.5*(1+200**(-t+0.5))**(-1) + 0.5*np.where(t < 0 , 0, np.where( t <= 1 , 3*t**2-2*t**3, 1 ) ) )( (x-mi)/(mx-mi) )