Octave fplot abs looks very strange

f = @(x)(abs(x))
fplot(f, [-1, 1]

Freshly installed octave, with no configuration edited. It results in the following image, where it looks as if it is constant for a while around 0, looking more like a \_/ than a \/:

plotted function

Why does it look so different from a usual plot of the absolute value near 0? How can this be fixed?

Upvotes: 0

Views: 149

Answers (3)

Howard Rudd
Howard Rudd

Reputation: 926

Since fplot is written in Octave it is relatively easy to read. Its location can be found using the which command. On my system this gives:

octave:1> which fplot
'fplot' is a function from the file /usr/share/octave/5.2.0/m/plot/draw/fplot.m

Examining fplot.m reveals that the function to be plotted, f(x), is evaluated at n equally spaced points between the given limits. The algorithm for determining n starts at line 192 and can be summarised as follows:

  1. n is initially chosen to be 8 (unless specified differently by the user)
  2. Construct a vector of arguments using a coarser grid of n/2 + 1 points: x0 = linspace (limits(1), limits(2), n/2 + 1)' (The linspace function will accept a non-integer value for the number of points, which it rounds down)
  3. Calculate the corresponding values: y0 = f(x0)
  4. Construct a vector of arguments using a grid of n points: x = linspace (limits(1), limits(2), n)'
  5. Calculate the corresponding values: y = f(x0)
  6. Construct a vector of values corresponding to the members of x but calculated from x0 and y0 by linear interpolation using the function interp1(): yi = interp1 (x0, y0, x, "linear")
  7. Calculate an error metric using the following formula: err = 0.5 * max (abs ((yi - y) ./ (yi + y + eps))(:)) That is, err is proportional to the maximum difference between the calculated and linearly interpolated values.
  8. If err is greater than tol (2e-3 unless specified by the user) then put n = 2*(n-1) and repeat. Otherwise plot(x,y).

Because abs(x) is essentially a pair of straight lines, if x0 contains zero then the linearly interpolated values will always exactly match their corresponding calculated values and err will be exactly zero, so the above algorithm will terminate at the end of the first iteration. If x doesn't contain zero then plot(x,y) will be called on a set of points that doesn't include the 'cusp' of the function and the strange behaviour will occur.

This will happen if the limits are equally spaced either side of zero and floor(n/2 + 1) is odd, which is the case for the default values (limits = [-5, 5], n = 8).

The behaviour can be avoided by choosing a combination of n and limits so that either of the following is the case: a) the set of m = floor(n/2 + 1) equally spaced points doesn't include zero or b) the set of n equally spaced points does include zero.

For example, limits equally spaced either side of zero and odd n will plot correctly . This will not work for n=5, though, because, strangely, if the user inputs n=5, fplot.m substitutes 8 for it (I'm not sure why it does this, I think it may be a mistake). So fplot(@abs, [-1, 1], 3) and fplot(@abs, [-1, 1], 7) will plot correctly but fplot(@abs, [-1, 1], 5) won't.

(n/2 + 1) is odd, and therefore x0 contains zero for symmetrical limits, only for every 2nd even n. This is why it plots correctly with n=6 because for that value n/2 + 1 = 4, so x0 doesn't contain zero. This is also the case for n=10, 14, 18 and so on.

Choosing slightly asymmetrical limits will also do the trick, try: fplot(@abs, [-1.1, 1.2])

Upvotes: 2

Eldrad
Eldrad

Reputation: 743

The weird shape comes from the sampling rate, i.e. at how many points the function is evaluated. This is controlled by the parameter N of fplot The default call seems to accidentally skip x=0, and with fplot(@abs, [-1, 1], N=5) I get the same funny shape like you: enter image description here

However, trying out different values of N can yield the correct shape, try e.g. fplot(@abs, [-1, 1], N=6):

enter image description here

Although in general I would suggest to use way higher numbers, like N=100.

Upvotes: 0

Howard Rudd
Howard Rudd

Reputation: 926

The documentation says: "fplot works best with continuous functions. Functions with discontinuities are unlikely to plot well. This restriction may be removed in the future." so it is probably a bug/feature of the function itself that can't be fixed except by the developers. The ordinary plot() function works fine:

x = [-1 0 1];
y = abs(x);
plot(x, y);

Upvotes: 1

Related Questions