4
votes

I have two distinct problems, but they're posted together because I believe the solutions are related. I'm testing Newton's and secant methods (each of which is implemented with a loop) and plotting the results versus computing time on the same axes to compare them. I want the (discrete) Newton's method results to be connected by a blue line and the secant method results by a red line. These lines, in turn, are annotated by a corresponding legend. This is not happening because each and every point on the plot seems to be considered at individual object because they were individually created. And the legend command brings up two blue asterisks instead a blue one and a red one (I wish I could post my plot here, but I don't have the image privilege yet.)

Here's my abbreviated code:

f = (x) % define function
figure
hold on

%% Newton
tic
while % terminating condition
    % [Newtons method]

    t = toc;
    plot(t,log(abs(f(z)),'b*-')
end

%% Secant
tic
while % terminating condition
    % [secant method]

    t = toc;
    plot(t,log(abs(f(z)),'r*-')
end

legend('Newton''s','Secant')

Needless to day, the '-' in the linespec doesn't do anything because only a point is being plotted, not a line. I know I could make a line plot with each iteration with something like plot([t_old t],[log(abs(f(z_old) log(abs(f(z)]), but that isn't ideal, not least because log(abs(f(z_old))) would have to be reevaluated each time. Besides, that would not solve the problem with the legend.

I think both problems will be solved if I can get MATLAB to understand that I'm trying to create just two objects on the axes, one blue line and one red line. Thank you.

3
@thewaywewalk Not a duplicate, although Bas Swinckels's solution, broadly defined, is similar to the answers in the other thread. grantnz's solution, on the other hand, is different, and it is what I was looking for.bongbang

3 Answers

1
votes

If you don't want to store the x/y data in a vector and then replot the entire vector you could just add to the plotting line using code like this:

hNewton = [];
while % terminating condition
    % [Newtons method]
    t = toc;    
    if isempty(hNewton)
        hNewton = plot(t,log(abs(f(z))),'b*-');  % First time through plot and save the line handle
    else
        % On all subsequent passes, just add to the lines X/Y data
        set(hNewton,'XData',[get(hNewton,'XData')  t]);
        set(hNewton,'YData',[get(hNewton,'YData')  log(abs(f(z)))]);
    end
end

Since there are now only 2 lines, the legend works as expected.

Alternatively, you could put the code to add data to an existing line in a function

function hLineHandle = AddToLine( hLineHandle, xData, yData, lineStyle )
% AddToLine - Add data to a plotted line
    if isempty(hLineHandle)
        hLineHandle = plot(xData,yData, lineStyle);
    else
        set(hLineHandle,'XData',[get(hLineHandle,'XData')  xData]);
        set(hLineHandle,'YData',[get(hLineHandle,'YData')  yData]);
    end
end

Which makes the code in the main script/function a lot cleaner.

hNewton = [];
while % terminating condition
    % [Newtons method]
    t = toc;    
    hNewton = AddToLine(hNewton,t, log(abs(f(z))),'b*-' );
end
1
votes

You can use line object, for example:

f = (x) % define function
figure
hold on
lHandle1 = line(nan, nan); %# Generate a blank line and return the line handle
lHandle2 = line(nan, nan); %# Generate a blank line and return the line handle

%% Newton
tic
while % terminating condition
    % [Newtons method]
    t = get(lHandle1, 'XData');
    Y1 = get(lHandle1, 'YData');

    t = toc;
    Y1 = [Y1 log(abs(f(z)];
    set(lHandle1, 'XData', t, 'YData', Y1, 'LineWidth', 2 ,'Color' , [0 1 0]);

end

%% Secant
tic
while % terminating condition
    % [secant method]
    t = get(lHandle2, 'XData');
    Y2 = get(lHandle2, 'YData');

    t = toc;
    Y2 = [Y2 log(abs(f(z)];
    set(lHandle2, 'XData', t, 'YData', Y2, 'LineWidth', 2 ,'Color' , [1 0 0]);

end

legend('Newton''s','Secant')
1
votes

Good example of only showing the relevant parts of your code to ask a question. The others have explained tricks to have the legend behave as you want. I would go for a different solution, by saving your measurements in a vector and doing the plots after the loops. This has 2 advantages: you do not have to do the tricks with the legend, but more importantly, you are not doing a plot inside your loop, which potentially takes a lot of time. I would guess that your timing is dominated by the plotting, so the influence of your algorithm will hardly show up in the results. So change your code to something like this (untested):

f = (x) % define function
% preallocate plenty of space
[t_newton, t_secant, f_newton, f_secant] = deal(nan(1, 1000));

%% Newton
tic;
i = 1;
while % terminating condition
    % [Newtons method]
    f_newton(i) = current_result;
    t_newton(i) = toc;
    i = i + 1;
end

%% Secant
tic;
i = 1;
while % terminating condition
    % [secant method]
    f_secant(i) = current_result;    
    t_secant(i) = toc;
    i = i + 1;
end

% trim NaNs (not really needed, not plotted anyhow)
t_newton = t_newton(isfinite(t_newton));
f_newton = f_newton(isfinite(f_newton));
t_secant = t_secant(isfinite(t_secant));
f_secant = f_secant(isfinite(f_secant));

% do the plot
semilogy(t_newton, abs(f_newton), t_secant, abs(f_secant))
legend('Newton''s','Secant')