Simone Cancellara
Simone Cancellara

Reputation: 45

Matlab Function Behaves Unexpectedly on Array of Inputs

I made a function (bisection) that calculates the zero of a function (like fzero) using the bisection method, however when I run the function I get the following warning: "Warning: Function behaves unexpectedly on array inputs. To improve performance, properly vectorize your function to return an output with the same size and shape as the input arguments."

No idea how to solve this.

bisection function

function[x, output, graf] = bisection(f, x0, TOL, NMAX)
graf = "no";
if(nargin > 1)
    if (~exist('TOL','var') || TOL == '~')
        TOL = eps;
    end
    if (~exist('NMAX','var') || NMAX == '~')
        NMAX = 500;
     end    
else
    error('Too few input arguments');
end
    if isa(f, 'function_handle')
        % f is a function
    else
        error('f is not a function!');
    end

    if isvector(x0)
        disp(size(x0));
        if (size(x0) > 2)
            error('x0 has too many elements!');
        else 
            if(size(x0) < 2)
                error('x0 has too few elements!');
            end
        end
        if ~isnumeric(x0(1)) || ~isnumeric(x0(2))
            error('Elements of x0 must be numeric!');
        end 
        if x0(1)>x0(2)
            error('x0(1) is > than x0(2)');
        end
    else
        error('x0 is not a vector!');
    end

    if f(x0(1))*f(x0(2)) > 0
        error('There are no zeros!');
    end

    if ~isnumeric(NMAX)
       error('NMAX must be a number'); 
    else 
        if floor(NMAX) ~= NMAX
            error('NMAX must be integer');
        end
    end

    if ~isnumeric(TOL)
       error('TOL must be numeric!');
    end 


    x0 = [x0(1), x0(2)];    
    [x, output] = do_bisection(f,x0,TOL,NMAX);

        output = [ output(1), NMAX - output(2) ];

        if("yes" == "yes")
            fplot(f,[x0(1)*2, x0(2)*2]);
            title(strcat('Grafico f = ',func2str(f)));
            xlabel('x');
            ylabel('y = f(x)');
            grid on;
            grid minor;
            set(gca,'xaxislocation','origin');
            set(gca,'yaxislocation','origin');
            hold on;
            fplot(@(x) 0,[x0(1), x0(2)],'r -');
            plot(x,f(x),'k s');
            text(x0(1), 0.5, strcat('a=',num2str(x0(1))));
            text(x0(2), 0.5, strcat('b=',num2str(x0(2))));
            text(x, -0.5, strcat('x=',num2str(x)));
            graf = "yes";
        end  
end

do_bisection

function[x, output] = do_bisection(f, x0, TOL, NMAX)
    a = x0(1);
    b = x0(2);
    if f(a)*f(b)>0 
        error('non ci sono zeri');
    end
    iter = 0;
    c = (a+b)/2;
    while(iter<NMAX && (((abs(x0(2)-x0(1))/max(abs(x0(1)),abs(x0(2))))>TOL) && (abs(f(c)) > eps)))
        if(f(c)*f(a) < 0)
            b = c;
        else
            a = c;
        end
        c = (a +b)/2;
        iter = iter +1;
    end
    x = c;
    output = [ f(c), iter ];
end

I call the function

f = @(x) x^2 - 5;
x0 = [1, 3];
[x, out, graf] = bisection(f,x0,10.^(-5),500);

Upvotes: 1

Views: 6654

Answers (2)

Thales
Thales

Reputation: 1316

Your code runs fine and your bisection algorithm seems to be correctly implemented. The warning message that appears is because of the call to fplot function.

fplot(f,[x0(1)*2, x0(2)*2]);

This function automatically plots the function f defined as a function handle. It warns you, however, that your function f is not properly vectorized. To get rid of the warning, just change your function definition

f = @(x) x^2 - 5;

to

f = @(x) x.^2 - 5;

Notice that the .^ indicates an element-wise exponentiation. If you simply write x^2, MATLAB will try to do a matrix multiplication instead.

This warning will also appear on the other fplot call. Change it to:

fplot(@(x) 0.*x,[x0(1), x0(2)],'r -');

And ou should be fine. This time, I just multiply the 0 for the array x. Essentially, the results are the same. However, multiplying 0 by x automatically takes into consideration the array size.

With only this changes, the function runs fine without any warnigs, and produce the desired output:

enter image description here

Upvotes: 1

Wolfie
Wolfie

Reputation: 30047

You can debug this...

The full warning looks like this:

Warning: Function behaves unexpectedly on array inputs. To improve performance, properly vectorize your function to return an output with the same size and shape as the input arguments.
[...]
In fplot (line 161)
In bisection (line 70)

Line 70 of bisection is this one:

fplot(@(x) 0,[x0(1), x0(2)],'r -');

The function you're supplying is @(x)0, which returns a scalar 0 for a vector input (which fplot creates). Therefore the error makes sense, you're inputting an array to @(x)0, which doesn't properly handle arrays!

Two solutions:

  1. Properly define your anonymous function to handle array inputs x

    fplot( @(x) zeros(size(x)), [x0(1), x0(2)], 'r-' );
    
  2. Use a quicker function than fplot to simply draw a line at y=0 between your two x0 points.

    plot( x0(1:2), [0 0], 'r-' );
    

I'm suggesting plot because all you're trying to do is plot a straight line between two points. fplot is creating an array under the hood to do this for you, but it's overkill here, and plot is a simpler function! You could also use line which is even simpler but has slightly different syntax to specify styles.

Similarly, you need to change the definition of f to handle arrays properly by using the element-wise power operator:

f = @(x) x.^2 - 5;

Upvotes: 1

Related Questions