# Wolfram Function Repository

Instant-use add-on functions for the Wolfram Language

Function Repository Resource:

Make a monotonic InterpolatingFunction with continuous second derivative for monotonic data

Contributed by:
Ted Ersek

ResourceFunction["MonotonicInterpolation"][ returns an InterpolatingFunction that has a continuous second derivative and is monotonic wherever the |

The data should have the form {{*x*_{1},*y*_{1}},{*x*_{2},*y*_{2}},…} where the *x*_{i} and *y*_{i} are real numbers.

The data does not need to be sorted.

The data can have any Precision, but the InterpolatingFunction returned can only give MachinePrecision results.

The InterpolatingFunction returned approximates the smoothest possible function that is monotonic except for extremum near data samples that are local minima or maxima among the data.

The following options may be given:

"KnotsBetweenSamples" | 2 | number of knots inserted between each pair of data samples. |

MaxIterations | Automatic | maximum number of iterations performed in QuadraticOptimization |

Tolerance | Automatic | the tolerance to use for internal comparisons in QuadraticOptimization |

When the data is monotonic, there exists a monotonic InterpolatingFunction with continuous second derivative provided at least two appropriately-spaced knots are inserted between each pair of data samples (section 8.3 of this paper).

The data below is monotonic, and we get a monotonic interpolation:

In[1]:= |

Out[2]= |

Plot the function:

In[3]:= |

Out[3]= |

Next we see the function *fun* perfectly interpolates the data:

In[4]:= |

Out[4]= |

In[5]:= |

Out[5]= |

The following suggests the derivative of *fun* is positive over the interval {0,7}:

In[6]:= |

Out[6]= |

The derivative of *fun* is well behaved:

In[7]:= |

Out[7]= |

The second derivative of *fun* is continuous:

In[8]:= |

Out[8]= |

MonotonicInterpolation can handle exact data provided all values are real numbers:

In[9]:= |

Out[11]= |

The InterpolatingFunction above only gives MachinePrecision results:

In[12]:= |

Out[12]= |

In[13]:= |

Out[13]= |

*f*[1] evaluates to a MachinePrecision approximation of 4-ⅇ:

In[14]:= |

Out[14]= |

The next data has a local maximum at {2.4,9} and the InterpolatingFunction will have a local max someplace between *x* = 2 and *x* = 4. MonotonicInterpolation tries to find the smoothest InterpolatingFunction with no local min and no other local max; more precisely, MonotonicInterpolation tries to minimize with only one extremum:

In[15]:= |

Out[17]= |

The following suggests *g*' is positive for all values in the interval {0,2.84} and never positive for values in the interval {2.841,7}:

In[18]:= |

Out[18]= |

In[19]:= |

Out[19]= |

In[20]:= |

Out[20]= |

Two adjacent data samples above are {5,0.3}, {5.5,0.3}. Since these samples have equal *y*-values, *g*' is zero at all *x* in the interval {5,5.5}:

In[21]:= |

Out[21]= |

Using the same data, but unsorted, results in the same InterpolatingFunction:

In[22]:= |

Out[24]= |

More knots between each pair of samples will give smoother interpolations:

In[25]:= |

Out[26]= |

The derivative of the InterpolatingFunction changes more slowly as the setting of the KnotsBetweenSamples option is increased:

In[27]:= |

Out[28]= |

The integral of the second derivative squared decreases as the setting of "KnotsBetweenSamples" is increased:

In[29]:= |

Out[29]= |

In[30]:= |

Out[30]= |

In[31]:= |

Out[31]= |

In[32]:= |

Out[32]= |

MonotonicInterpolation passes the setting of MaxIterations directly to QuadraticOptimization:

In[33]:= |

Out[37]= |

Below a low setting for MaxIterations allows a solution to be found in less time than the default setting, but the quality of the solution may be sacrificed:

In[38]:= |

Out[38]= |

In[39]:= |

Out[39]= |

Integrating of the second derivative squared shows that default is smoother than *f10*. This is explained by the message above:

In[40]:= |

Out[40]= |

In[41]:= |

Out[41]= |

There is a slight difference between the InterpolatingFunction objects found above:

In[42]:= |

Out[42]= |

The default setting has the Tolerance determined automatically, but it is not able to find an InterpolatingFunction in this next example:

In[43]:= |

Out[44]= |

Two monotonic interpolating functions of the data above are found using specific settings for Tolerance:

In[45]:= |

Out[45]= |

In[46]:= |

Out[46]= |

We see there are significant differences between the above interpolating functions:

In[47]:= |

Out[47]= |

Integrating of the second derivative squared shows that *g* is smoother than *h*:

In[48]:= |

Out[48]= |

In[49]:= |

Out[49]= |

Given data representing the fraction of a population of dogs that died at or before a certain age, we use Interpolation to create a continuous function describing the fraction of dogs that that would have died by any age less than 22:

In[50]:= |

Out[52]= |

The next cell indicates that 13.25% of dogs would die at or before 10.25 years age, while the data provided says 14.0% of dogs died at or before 8 years of age. These are not compatible, so the above is not a very good InterpolatingFunction for this application:

In[53]:= |

Out[53]= |

The previous example requires a monotonically increasing InterpolatingFunction. Using the data above, we could have met that requirement if we used Interpolation[*data*,Method→"Hermite"], but that is not guaranteed to give a monotonic InterpolatingFunction for every monotonic set of data. Instead we can use linear interpolation as in the next cell, and that is guaranteed to give a monotonic InterpolatingFunction for every monotonic set of data:

In[54]:= |

Out[55]= |

However, this linear interpolation doesn’t have a continuous first derivative. It is reasonable to expect that the actual curve is smooth, and MonotonicInterpolation below tries to find the smoothest possible monotonically-increasing InterpolatingFunction that interpolates the monotonic data. The interpolation shown below is monotonic over the whole range plotted and it’s first two derivatives are continuous:

In[56]:= |

Out[57]= |

Below, MonotonicInterpolation returns an expression with the built-in head InterpolatingFunction:

In[58]:= |

Out[59]= |

We can use any of the following methods on an InterpolatingFunction:

In[60]:= |

Out[60]= |

Next we get a lists of the *x*-values and *y*-values used to define the InterpolatingFunction *fun*:

In[61]:= |

Out[61]= |

In[62]:= |

Out[62]= |

The InterpolatingFunction *fun* also has specified derivatives at each of the *x*-values. Those derivatives can be retrieved from the internal representation of the InterpolatingFunction shown below:

In[63]:= |

Out[63]= |

Get the derivative of the *fun*[*x*] at corresponding *x*-values:

In[64]:= |

Out[64]= |

Get an expression used in the definition of the InterpolatingFunction *fun*:

In[65]:= |

Out[65]= |

Next we see how the above expression was used to define the InterpolatingFunction *fun*:

In[66]:= |

Out[66]= |

The InterpolatingFunction *fun* is effectively a different cubic polynomial over several intervals. If we evaluate *fun*[*x*] using an *x* outside the domain of *fun*, it uses the cubic polynomial form the nearest end of the domain. The next cell performs surgery on the above expression to make an new InterpolatingFunction that extends the domain of the original function using linear extrapolation:

In[67]:= |

Out[71]= |

MonotonicInterpolation will often fail to find an InterpolatingFunction when some samples are very close together as in this example:

In[72]:= |

Out[73]= |

In[74]:= |

Out[74]= |

The number of unknowns that QuadraticOptimization needs to determine is Length[*data*]+2(Length[*data*]-1)*"KnotsBetweenSamples", and the time required for QuadraticOptimization to find a solution is proportional to (number of unknowns)^{3}. As a result, the time MonotonicInterpolation requires increases dramatically as the setting for "KnotsBetweenSamples" increases. Also, when the setting for "KnotsBetweenSamples" is too large, the limitations of MachinePrecision arithmetic often prevent QuadraticOptimization from finding a solution. An example of that is given in the next cell that takes a few minutes to evaluate:

In[75]:= |

Out[76]= |

MonotonicInterpolation is efficient enough to use in Manipulate when the List of data is not too long:

In[77]:= |

Out[77]= |

- 1.0.0 – 10 August 2020

This work is licensed under a Creative Commons Attribution 4.0 International License