Here is a (mostly) general routine that (tries to) convert a one-dimensional InterpolatingFunction[]
into an equivalent Piecewise[]
function:
convertToPiecewise::umet = "Unknown interpolation method `1`.";
SetAttributes[convertToPiecewise, Listable];
convertToPiecewise[iF_InterpolatingFunction, x_,
OptionsPattern[{"Extrapolation" -> False,
InterpolationOrder -> Automatic}]] :=
Module[{bf, extQ, imet, kp, makePP, met, nodes, perQ, pieces, pts, vand, xt},
Switch[met = iF["InterpolationMethod"],
"Hermite" | "Chebyshev",
pts = Transpose[{Flatten[iF["Grid"]], iF["ValuesOnGrid"]}],
"BSpline",
bf = First[Cases[iF, _BSplineFunction, ∞]];
pts = {#, bf[#]} & /@ Union[First[bf["Knots"]]],
_,
Message[convertToPiecewise::umet, met]; Return[$Failed, Module]];
kp = OptionValue[InterpolationOrder];
If[kp === Automatic,
(* repeated differentiation to determine maximal order *)
kp = If[met =!= "BSpline",
First[NestWhile[Derivative[1],
Derivative[First[iF["InterpolationOrder"]] +
1][iF],
(Norm[#["ValuesOnGrid"], ∞] > 0) &]
["DerivativeOrder"]] - 1,
kp = First[bf["Degree"]]]];
If[kp > 0, (* normal case *)
(* use equispaced nodes in the exact case, and Chebyshev otherwise *)
nodes = Range[kp - 1]/kp;
If[MatrixQ[pts, InexactNumberQ],
nodes = N[Haversine[π nodes], Precision[pts]]];
vand = LinearAlgebra`Private`VandermondeSolve[##, Transpose -> True] &;
makePP[{{x1_, y1_}, {x2_, y2_}}] := Module[{h = x2 - x1, ip},
ip = Transpose[Join[{{x1 - x1, y1}},
{#, iF[x1 + #]} & /@ (h nodes), {{h, y2}}]];
(* solve for interpolating polynomial coefficients *)
{Fold[(#1 (xt - x1) + #2) &, Reverse[vand @@ ip]], x1 <= xt <= x2}];
pieces = makePP /@ Partition[pts, 2, 1],
(* zero-order interpolation *)
pieces = Transpose[{Rest[pts[[All, -1]]],
#1 <= xt <= #2 & @@@
Partition[pts[[All, 1]], 2, 1]}]];
perQ = TrueQ[First[iF["Periodicity"]]];
If[! perQ, extQ = OptionValue["Extrapolation"];
If[! ListQ[extQ], extQ = {extQ, extQ}]; extQ = TrueQ /@ extQ;
If[extQ[[1]], pieces[[1, 2]] = Delete[pieces[[1, 2]], 1]];
If[extQ[[2]], pieces[[-1, 2]] = Delete[pieces[[-1, 2]], 3]]];
Piecewise[pieces /. xt ->
If[! perQ, x, Mod[x, #2 - #1, #1] & @@ First[iF["Domain"]]]]]
(N.B. replace LinearAlgebra`Private`VandermondeSolve[]
with LinearAlgebra`VandermondeSolve[]
when using the function in versions before 11.2.)
It should work for InterpolatingFunction[]
objects that come from Interpolation[]
, ListInterpolation[]
, or FunctionInterpolation[]
. It mostly works for InterpolatingFunction[]
objects from NDSolve[]
, but may fail in some cases. (If you find an example, please tell me!)
Some examples:
if1 = Interpolation[{1, 3, 5, 2, 1}, InterpolationOrder -> 1];
Here, we tell convertToPiecewise[]
to use the rightmost piece for extrapolation to the right:
convertToPiecewise[if1, x, "Extrapolation" -> {False, True}]
$$\begin{cases}
2 (x-1)+1 & 1\leq x\leq 2 \\
2 (x-2)+3 & 2\leq x\leq 3 \\
5-3 (x-3) & 3\leq x\leq 4 \\
6-x & 4\leq x \\
0 & \mathtt{True}
\end{cases}$$
Convert an InterpolatingFunction[]
with irregular spacing:
if2 = Interpolation[{{0, 0}, {0.1, .3}, {0.5, .6}, {1, -.2}, {2, 3}}, Method -> "Spline"];
pw2[x_] = convertToPiecewise[if2, x];
Plot[if4[x] - pw4[x], {x, 0, 2}, PlotRange -> All]
Convert the result of NDSolve[]
:
if3 = NDSolveValue[{g'[x] == Sin[2 x] - g[x], g[0] == 1}, g, {x, 0, 6}];
pw3[x_] = convertToPiecewise[if3, x];
Plot[if3[x] - pw3[x], {x, 0, 6}, PlotRange -> All]
Another NDSolve[]
example. The previous version of the routine was unable to handle this.
if4 = NDSolveValue[{y''[t] == 10 (1 - y[t]^2) y[t] - y[t], y[0] == 2, y'[0] == 0},
y, {t, 0, 6}, Method -> "StiffnessSwitching"];
pw4[x_] = convertToPiecewise[if4, x];
Plot[if4[x] - pw4[x], {x, 0, 6}, PlotRange -> All]
InterpolatingFunction
using "Methods" as I outlined here: (19042) however that only shows the data that is used and the kind of interpolation, not the actual function itself. You might find value inInterpolatingPolynomial
, though it is not the same asInterpolation
. $\endgroup$InterpolatingPolynomial
instead ofInterpolation
. $\endgroup$