The best approach to evaluate the performance of some method is to write a benchmark. Four cases are considered:
- loop fzero: uses a loop to solve the equations separately using
fzero
- loop fsolve: uses a loop to solve the equations separately using
fsolve
- default fsolve: solves the equations together as one system of equations
- independent fsolve: same as default fsolve, but specifies that the equations are independent
f = @(x) x.^2-1; % the set of non-linear equations
ns = 1:1:100; % the sizes for which the benchmark is performed
options=optimset('Display','off'); % disable displaying
figure
hold on
plot(ns, loopFSolve(f, ns, options), 'DisplayName', 'loop fsolve')
plot(ns, loopFZero(f, ns, options), 'DisplayName', 'loop fzero')
plot(ns, defaultFSsolve(f, ns, options), 'DisplayName', 'default fsolve')
plot(ns, independentFSolve(f, ns, options), 'DisplayName', 'independent fsolve')
legend ('Location', 'northwest')
function t = loopFZero(f, ns, options)
t1 = timeit(@() fzero(f, rand(1), options));
t = ns * t1;
end
function t = loopFSolve(f, ns, options)
t1 = timeit(@() fsolve(f, rand(1), options));
t = ns * t1;
end
function t = defaultFSsolve(f, ns, options)
t = zeros(size(ns));
for i=1:length(ns)
n = ns(i);
un = rand(n, 1);
t(i) = timeit(@() fsolve(f, un, options));
end
end
function t = independentFSolve(f, ns, options)
t = zeros(size(ns));
for i=1:length(ns)
n = ns(i);
un = rand(n, 1);
options.Algorithm = 'trust-region-reflective';
options.JacobPattern = speye(n);
options.PrecondBandWidth = 0;
t(i) = timeit(@() fsolve(f, un, options));
end
end
Results
All the figures show the computation time for the complete system in function of n
, the number of equations.
The first two figures plot n
up to 1000, with an interval of 100. The last two figures plot n
up to 100 with an interval of 1. For each, the second plot is the same as the first one but without loop fzero as it is much slower than the others.
Conclusion
- loop fsolve: do not use it, startup time is too high
- loop fzero: you can use it for small
n
(fastest method for n < ~20
)
- default fsolve: you can use it for relative small
n
(fastest method for ~20 < n < ~50
, but difference with 2 and 3 is relative small).
- independent fsolve: you should use it for large
n
(fastest method for ~50 < n
)
In general, you should use independent fsolve, only for small n
loop fzero may be used instead, i.e. use fsolve
with the following options:
options.Algorithm = 'trust-region-reflective';
options.JacobPattern = speye(n);
options.PrecondBandWidth = 0;
Lazy people may just use the default fsolve as it has a reasonable performance for a moderate number of equations (n < ~200
)
Important remark
Bob pointed out that a vectorised version of fzero
may be multiple orders of magnitude faster. According to Bob's test results, his method is the fastest method for every size of the problem, i.e. large and small n
.
Remarks
- Note that the time complexity of default fsolve is O(n^2) while the others are O(n).
- Note that
fzero
and fsolve
may behave differently in some boundary cases, f.e fzero
will not found a solution for x^2
as it searches the location of a sign change.