tw-S
tw-S

Reputation: 1217

Serious bug in matlab's solve - what can I do?

This post updates my question in Solve finds wrong solution?

Given the "simple" function in x:

f = (x + 3/10)^(1/2) - (9*(x + 3/10)^5)/5 - (x + 3/10)^6 + (x - 1)/(2*(x + 3/10)^(1/2));

Find the zeros by the call

solve(f,x)

This yields 3 zeros:

 ans =

 0.42846617518653978966562924618638
 0.15249587894102346284238111155954
 0.12068186494007759990714181154349

A simple look at the plot shows that the third root is nonsense:

Function with 2 zeros

I have a serious problem because I need to retrieve the smallest zero from the above vector. Calling min(ans) returns a wrong zero. What can I do to workaround?

Upvotes: 2

Views: 335

Answers (2)

Amro
Amro

Reputation: 124543

This is a non-polynomial equation, and it will probably fallback to a numeric solver (non-symbolic). So there might be numerical errors, or the numeric algorithm might get stuck and report false solutions, I'm not sure...

What you can do is substitute the solutions back into the equation, and reject ones that are above some specified threshold:

% define function
syms x real
syms f(x)
xx = x+3/10;
f(x) = sqrt(xx) - 9/5*xx^5 - xx^6 + (x - 1)/(2*sqrt(xx));
pretty(f)

% find roots
sol = solve(f==0, x, 'Real',true)

% filter bad solutions
err = subs(f, x, sol)
sol = sol(abs(err) < 1e-9);  % this test removes the 2nd solution

% plot
h = ezplot(f, [0.1 0.5]);
line(xlim(), [0 0], 'Color','r', 'LineStyle',':')
xlabel('x'), ylabel('f(x)')

% programmatically insert data tooltips
xd = get(h, 'XData'); yd = get(h ,'YData');
[~,idx] = min(abs(bsxfun(@minus, xd, double(sol))), [], 2);
dcm = datacursormode(gcf);
pos = [xd(idx) ; yd(idx)].';
for i=1:numel(idx)
    dtip = createDatatip(dcm, h);
    set(get(dtip,'DataCursor'), 'DataIndex',idx(i), 'TargetPoint',pos(i,:))
    set(dtip, 'Position',pos(i,:))
end

We are left only with the two desired solutions (one is rejected by our test):

                   /      3 \5
                 9 | x + -- |
    /      3 \     \     10 /    /      3 \6         x - 1
sqrt| x + -- | - ------------- - | x + -- |  + ----------------
    \     10 /         5         \     10 /          /      3 \
                                               2 sqrt| x + -- |
                                                     \     10 /
sol =
 0.42846617518653978966562924618638
 0.12068186494007759990714181154349       % <== this one is dropped
 0.15249587894102346284238111155954

err(x) =
 -9.1835496157991211560057541970488e-41
   -0.058517436737550288309001512815475   % <==
  1.8367099231598242312011508394098e-40

function_roots


I also tried using MATLAB's numeric solvers, which were able to find the two solutions given reasonable starting points:

(See this related question)

fcn = matlabFunction(f);  % convert symbolic f to a regular function handle
opts = optimset('Display','off', 'TolFun',1e-9, 'TolX',1e-6);

% FZERO
sol2(1) = fzero(fcn, 0.1, opts);
sol2(2) = fzero(fcn, 0.5, opts);
disp(sol2)    % [0.1525, 0.4285]

% FSOLVE
sol3(1) = fsolve(fcn, 0.0, opts);
sol3(2) = fsolve(fcn, 1.0, opts);
disp(sol3)    % [0.1525, 0.4285]

For comparison, I tried solving the equation directly into MuPAD, as well as in Mathematica.

Mathematica 9.0

mma

MuPAD (R2014a)

mupad

Of course we can always directly call MuPAD from inside MATLAB:

% f is the same symbolic function we've built above

>> sol = evalin(symengine, ['numeric::solve(' char(f) ' = 0, x, AllRealRoots)'])
sol =
[ 0.15249587894102346284238111155954, 0.42846617518653978966562924618638]

The above call is equivalent to searching the entire range x = -infinity .. infinity (which can be slow!). We should assist numeric::solve by providing a specific search ranges when possible:

>> sol = feval(symengine, 'numeric::solve', f==0, 'x = 0 .. 1', 'AllRealRoots')
sol =
[ 0.15249587894102346284238111155954, 0.42846617518653978966562924618638]

Upvotes: 8

Daniel Crane
Daniel Crane

Reputation: 257

One workaround could be taking min(ans), and then checking that f(min(ans))==0. If not, then use the next-smallest value.

Upvotes: 0

Related Questions