Padé approximant is a rationale function approximation expanded from a function value and the derivative function values similar to Taylor Series. Often, Padé approximant’s can converge when Taylor Series does not even though both use the same information at a point. The key is Taylor Series is a polynomial while Padé approximant is a rationale function. Rationale functions can have singularities/vertical-asymptotes and horizontal asymptotes. Polynomials do not have asymptotes.
High-order Padé Approximants can be hard to compute because of how fast the coefficients grow. Total orders (denominator plus numerator order) of less than 20 fail at times for MATLAB’s symbolic pade
function in the code below. padeapprox
from chebfun also struggles. It takes a lot of effort to compute a high-order Padé approximate.
The most useful way to understand Padé approximant is to watch the animations below. For details on the construction, see the resources section below.
Resources
Wikipedia Padé approximate – A good overview of Pade’s approximate.
Matlab Symbolic Pade Approximant Page
Chebfun has a function called padeapprox
that calculates a Padé approximant numerically. padeapprox
is based on this paper, Robust Padé Approximation via SVD.
Animations
exp(x) about 0 and 1
tanh(x) about 0 and 0.5
arctan(x) about 0 and 0.5
1/x about 0.5
1/(x-1) about 0.75
sin(x) about 0 and 0.5
x/(x^2 + 0.01^2)^(1/4) about 0.5
Runge function, 1/(1+25x^2) about 0 and 0.5
Code to Create Animations
MATLAB 2020a was used to write and execute the code below.
clc; clear; close all; %% exp(x) about 0 titleName = "exp(x) about 0"; func = @(x) exp(x); expansionPoint = 0; domain = [-5 5]; maxOrder = 20; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% exp(x) about 1 titleName = "exp(x) about 1"; func = @(x) exp(x); expansionPoint = 1; domain = [-5 5]; maxOrder = 20; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% tanh(x) about 0 titleName = "tanh(x) about 0"; func = @(x) tanh(x); expansionPoint = 0; domain = [-5 5]; maxOrder = 25; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% tanh(x) about 0.5 titleName = "tanh(x) about 0.5"; func = @(x) tanh(x); expansionPoint = 0.5; domain = [-5 5]; maxOrder = 30; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% arctan(x) about 0 titleName = "arctan(x) about 0"; func = @(x) atan(x); expansionPoint = 0; domain = [-5 5]; maxOrder = 30; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% arctan(x) about 0.5 titleName = "arctan(x) about 0.5"; func = @(x) atan(x); expansionPoint = 0.5; domain = [-5 5]; maxOrder = 30; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% 1/x about 0.5 titleName = "1/x about 0.5"; func = @(x) 1./x; expansionPoint = 0.5; domain = [-5 5]; maxOrder = 30; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% 1/(x-1) about 0.75 titleName = "1/(x-1) about 0.75"; func = @(x) 1./(x-1); expansionPoint = 0.75; domain = [-5 5]; maxOrder = 30; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% sin(x) about 0 titleName = "sin(x) about 0"; func = @(x) sin(x); expansionPoint = 0; domain = [-5 5]; maxOrder = 30; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% sin(x) about 0.5 titleName = "sin(x) about 0.5"; func = @(x) sin(x); expansionPoint = 0.5; domain = [-5 5]; maxOrder = 30; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% x/(x^2 + 0.01^2)^(1/4) titleName = "x/(x^2 + 0.01^2)^(1/4) about 0.5"; func = @(x) x./(x.^2 + 0.01^2).^(1/4); expansionPoint = 0.5; domain = [-5 5]; maxOrder = 30; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% Runge function titleName = "Runge function, 1/(1+25*x^2) about 0"; func = @(x) 1./(1+25*x.^2); expansionPoint = 0; domain = [-5 5]; maxOrder = 20; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% Runge function titleName = "Runge function, 1/(1+25*x^2) about 0.5"; func = @(x) 1./(1+25*x.^2); expansionPoint = 0.5; domain = [-5 5]; maxOrder = 20; createVideo(titleName, func, expansionPoint, domain, maxOrder); %% function createVideo(titleName,func,expansionPoint,domain,maxOrder,approach) arguments titleName (1,1) string; func (1,1) function_handle; expansionPoint (1,1) double; domain (1,2) double; maxOrder (1,1); approach (1,1) string {mustBeMember(approach,["chebfun","sym"])} = "sym"; end % setup figure width = 1920; height = 1080; figureObject = figure("Position",[10 10 width height]); % setup video file videoName = regexprep(titleName,["/","\*"],"_") + ".mp4"; video = VideoWriter(videoName,"MPEG-4"); video.FrameRate = 2; try video.open(); % setup function to approximate fplot(func,domain,"linewidth",3,"DisplayName",func2str(func)) hold all; xlabel("x"); ylabel("y") title(titleName) ylim(ylim); xlim(xlim); % find the permutations of the numerator and denominator [numeratorOrder, denominatorOrder] = ndgrid(0:maxOrder,0:maxOrder); totalOrder = numeratorOrder + denominatorOrder; isOrderToHigh = totalOrder > maxOrder; numeratorOrder(isOrderToHigh) = []; denominatorOrder(isOrderToHigh) = []; [~,sortedIndex] = sortrows([denominatorOrder(:)+numeratorOrder(:), numeratorOrder(:), denominatorOrder(:)]); numeratorOrder = numeratorOrder(sortedIndex); denominatorOrder = denominatorOrder(sortedIndex); % loop for i=1:length(numeratorOrder) % calculate next pade approximant order = [numeratorOrder(i), denominatorOrder(i)]; switch approach case "chebfun" % padeapprox is used from the Chebfun toolbox by Trefethen and others. The advatnage is computation is % faster then symbolic computation. The disadvatage is has numerical issues at total order % (numeratorOrder + denominatorOrder) of 16 or so. % % http://www.chebfun.org/ % P. Gonnet, S. Guettel, and L. N. Trefethen, "Robust Pade approximation via SVD", SIAM Rev., 55:101-117, 2013. % % Commentary: numerical issues are more of a statement of ill-conditioning of monomial basis combined % with constructing a pade approximate. Finding a pade approximate may be possible via an orthogonal % polynomial basis but I am not aware of any software that does this. % padeApproximant = padeapprox(@(x) func(x+expansionPoint), order(1),order(2)); padeApproximant = @(x) padeApproximant(x-expansionPoint); case "sym" % The symbolic approach is accurate but slow. For high enough total order (numeratorOrder + % denominatorOrder), large integer errors occur stopping further computation. To avoid this as much as % possible, we try to tell the symbolic engine that we want variable precision arithmetic, vpa(). if i==1 syms x real digits(20); func = vpa(func(vpa(1.0)*x)); end padeApproximant = pade(func, x, expansionPoint, 'order', order); end label = sprintf("Padé approximant, Pade Order = [%3d,%3d],Total Order = %3d",order,sum(order)); if i ==1 % setup plot fplotObject = fplot(padeApproximant,"--","LineWidth",3,"DisplayName",label); legend("location","best"); else % plot fplotObject.Function = padeApproximant; fplotObject.DisplayName = label; end % write frame video.writeVideo(getframe(figureObject)); end video.close(); catch e %#ok<NASGU> video.close(); fprintf("%s around %g failed at pade order = [%d, %d]\n",string(func),expansionPoint,order(1),order(2)); % if exist(videoName,"file") % delete(videoName); % end rethrow(e); end end