# Version 3.9.9 Release Notes

by on May 19, 2016

New version of toolbox includes a lot of updates released over the course of six months. We focused on speed, better support of sparse matrices, special functions and stability. Upgrade to the newest version is highly recommended as it contains critical updates for all platforms.

Short summary of changes:

1. The Core
• All elementary mathematical functions have been heavily optimized for quadruple precision and now enabled with sparse matrices support. This results in serious performance boost:
% < 3.9.9:
>> A = 100*mp(rand(1000,1000)-0.5);
>> B = 100*mp(rand(1000,1000)-0.5);

>> tic; atan2(A,B); toc;
Elapsed time is 18.056319 seconds.

>> tic; log(A); toc;
Elapsed time is 31.506463 seconds.
% 3.9.9
>> A = 100*mp(rand(1000,1000)-0.5);
>> B = 100*mp(rand(1000,1000)-0.5);

>> tic; atan2(A,B); toc;
Elapsed time is 0.262123 seconds.   % ~70 times faster

>> tic; C = log(A); toc;
Elapsed time is 0.843535 seconds.   % ~37 times faster

This improvement covers all elementary and majority of special functions. Please note, all appropriate functions handle branch cuts correctly and support signed zeros to the contrary of MATLAB.

• Now toolbox provides overloads for global built-in functions for array creation: zeros, ones, eye, nan, inf, rand, randn and randi.

The main goal is to embed support for â€˜mpâ€™ arrays in MATLAB out-of-the-box, without the need for mp(...) wrapper:

% Before:
>> A = zeros(3,3,'mp'); % didn't work since built-in zeros doesn't know what 'mp' is.
>> A = mp(zeros(3,3));  % hence manual modification was needed.
>> A = mp(pascal(3));

% 3.9.9
>> A = zeros(3,3,'mp'); % creates matrix of 'mp' type automatically
>> A = pascal(3,'mp');
>> whos A
Name      Size            Bytes  Class    Attributes
A         3x3               376  mp

This simplifies development of precision-independent scripts, e.g.:

function [c,r] = inverthilb(n, classname)
% INVERTHILB Class-independent inversion of Hilbert matrix

A = hilb(n,classname);
c = cond(A);
r = norm(eye(n,classname)-A*invhilb(n,classname),1)/norm(A,1); % relative accuracy
end

Now we can use the function with double or extended precision without changing its code:

% Double precision:
>> [c,r] = inverthilb(20,'double')
c =
1.5316e+18
r =
2.1436e+11

% Extended:
>> mp.Digits(50);
>> [c,r] = inverthilb(20,'mp')
c =
2.4522e+28
r =
9.4122e-25

>> mp.Digits(100);
>> [c,r] = inverthilb(20,'mp')
c =
2.4522e+28
r =
2.2689e-74

Basic test matrices are enabled with classname='mp' support as well: hadamard, hilb, invhilb, magic, pascal, rosser and wilkinson.

• (Sparse matrices) Indexing engine now supports all basic operations with sparse matrices: indexing (subsref), indexed assignment (subsasgn), concatenation (cat, horzcat, vertcat) and similar operations.
• (Sparse matrices) Basic matrix manipulation, relational, logical and analysis functions have been optimized to work with sparse matrices more efficiently (diag, triu, tril, norm, max, min, abs, <, <=, >, >=, ==, ~=, not, and, or, xor, isinf, isnan, isfinite, isequal, isequaln, etc.):
>> A = mp(sprand(10000,10000,0.01));
>> nnz(A)
ans =
994960

>> tic; norm(A,1); toc;
Elapsed time is 0.0531895 seconds.

>> tic; max(sum(abs(A))); toc;
Elapsed time is 0.103413 seconds.
2. Linear algebra
• Added LDLT decomposition for Hermitian indefinite matrices (dense). Real and complex cases are supported and optimized for multi-core CPUs.
>> A = mp(rand(1000)-0.5);
>> A = A+A';

>> tic; [L,D,p] = ldl(A,'vector'); toc;
Elapsed time is 7.412486 seconds.

>> norm(A(p,p) - L*D*L',1)/norm(A,1)
ans =
9.900420499644941245191320505579399e-33
• The svd, null and norm have been updated to support more special cases. The rcond has been improved for better handling of singular matrices.
>> A = mp('[1 2 3; 1 2 3; 1 2 3]');
>> Z = null(A)
Z =
-0.8728715609439695250643899416624781         0.4082482904638630163662140124509814
-0.2182178902359923812660974854156192        -0.8164965809277260327324280249019639
0.4364357804719847625321949708312385         0.4082482904638630163662140124509821

>> norm(A*Z,1)
ans =
1.733336949948512267750380148326435e-33

>> null(A,'r')
ans =
-2        -3
1         0
0         1
• Whole family of iterative solvers has been added: bicg, bicgstab[l], cgs, gmres, minres and pcg. All special cases are supported including the function handle inputs and preconditioners, e.g.:

BiCGSTAB iterative solver for large/sparse matrices:
x = bicgstab(A,b)
bicgstab(A,b,tol)
bicgstab(A,b,tol,maxit)
bicgstab(A,b,tol,maxit,M)
bicgstab(A,b,tol,maxit,M1,M2)
bicgstab(A,b,tol,maxit,M1,M2,x0)
[x,flag] = bicgstab(A,b,...)
[x,flag,relres] = bicgstab(A,b,...)
[x,flag,relres,iter] = bicgstab(A,b,...)
[x,flag,relres,iter,resvec] = bicgstab(A,b,...)

Usage examples can be found in updated posts of Krylov Subspace Methods in Arbitrary Precision and Arbitrary Precision Iterative Solver â€“ GMRES.

• Improved generalized eigen-solver (unsymetric case). Now it tries to recover converged eigenvalues even if QZ has failed.
• Added new functions – orth, rref and linsolve.
3. Matrix functions
• The expm and sqrtm have been re-written for better stability and accuracy.
• Function for computing square root of upper triangular matrices – sqrtm_tri has been greatly optimized and now implemented in toolbox core due to its importance to other matrix functions.
• Added X = sylvester_tri(A,B,C) function for solving Sylvester equation A*X + X*B = C. The most important case is when A and B are upper quasi-triangular (Schur canonical form) – needed for computing matrix functions (sqrtm, logm, etc.).
4. Nonlinear system solver. We have added full-featured fsolve (finally):

x = fsolve(fun,x0)
x = fsolve(fun,x0,options)
x = fsolve(problem)
[x,fval] = fsolve(fun,x0)
[x,fval,exitflag] = fsolve(...)
[x,fval,exitflag,output] = fsolve(...)
[x,fval,exitflag,output,jacobian] = fsolve(...)

All modes are supported, including sparse formulation, Jacobian and three algorithms of optimization: trust-region-dogleg, trust-region-reflective and Levenberg-Marquardt.

function test_fsolve
x0 = mp([-5; -5]);
options = optimset('Display','iter', 'TolFun', mp.eps,...
'TolX', mp.eps,...
'Algorithm','levenberg-marquardt');

[x,fval,exitflag,output] = fsolve(@myfun,x0,options)

function F = myfun(x)
F = [2*x(1) - x(2) - exp(-x(1));
-x(1) + 2*x(2) - exp(-x(2))];
end
end

Output:

>> mp.Digits(34);
>> test_fsolve

First-Order                    Norm of
Iteration  Func-count    Residual       optimality      Lambda           step
0           3         47071.2        2.29e+04         0.01
1           6         6527.47        3.09e+03        0.001        1.45207
2           9         918.374             418       0.0001        1.49186
3          12         127.741            57.3        1e-05        1.55326
4          15         14.9153            8.26        1e-06        1.57591
5          18        0.779056            1.14        1e-07        1.27662
6          21      0.00372457          0.0683        1e-08       0.484659
7          24      9.2164e-08        0.000336        1e-09      0.0385554
8          27     5.66104e-17        8.34e-09        1e-10    0.000193709
9          30     2.35895e-34         1.7e-17        1e-11    4.80109e-09
10          33     1.70984e-51        4.58e-26        1e-12    9.80056e-18
11          36      1.8546e-68        1.51e-34        1e-13    2.63857e-26

Equation solved.

fsolve completed because the vector of function values is near zero
as measured by the selected value of the function tolerance, and
the problem appears regular as measured by the gradient.

x =
0.56714
0.56714
fval =
9.6296e-35
9.6296e-35
exitflag =
1
output =
iterations: 11
funcCount: 36
stepsize: [1x1 mp]
cgiterations: []
firstorderopt: [1x1 mp]
algorithm: 'levenberg-marquardt'
message: 'Equation solved.

Equation solved. The sum of squared function values, r = 1.854603e-68, is less
than sqrt(options.TolFun) = 1.387779e-17. The relative norm of the gradient of
r, 6.583665e-39, is less than 1e-4*options.TolFun = 1.925930e-38.

Optimization Metric                                        Options
relative norm(grad r) =   6.58e-39           1e-4*TolFun =   2e-38 (selected)
r =   1.85e-68                             sqrt(TolFun) =  1.4e-17 (selected)
5. Special functions.
• Added beta, betaln, ellipj, ellipke, legendre and airy.
• The bessely, besselj, gamma and erf have been optimized for higher speed.
6. Miscellaneous.
• Added routines for 1-D interpolation: interp1, spline, pchip, ppval, mkpp and unmkpp.
• Added solvers for algebraic Riccati equations: care, gcare, dare and gdare.
• Added various functions: sortrows, text, histogram, cast, cplxpair, unwrap, ode15s, etc.

We continue accuracy checks of various functions in MATLAB. Our recent finding is that in case of Bessel functions of the first and second kind Yn(x) and Jn(x)MATLAB suffers from catastrophic accuracy loss near zeros of the functions. Red lines show the expected limits of relative error for double precision:

MATLAB’s functions bessely(n,x) and besselj(n,x) are unable to approximate Yn(x) and Jn(x) correctly in neighborhood of its zeros. No single correct digit is produced!

## Acknowledgements

As always, this release would not be possible without feedback, support and requests from our dear users. We would like to express sincere gratitude to all of you – thank you all for your care and support!

Special thanks to Brian Gladman for enlightening discussion and for sharing his outstanding work on Chebyshev expansions for special functions. Also to GNU GSL developers for inviting and allowing me to contribute improved approximations for K0(x) and K1(x).