Opened 13 months ago

Last modified 4 weeks ago

#31169 new defect

Metaticket: improve quality of one-variable function plots

Reported by: gh-DaveWitteMorris Owned by:
Priority: major Milestone: sage-9.6
Component: graphics Keywords: plotting
Cc: Merged in:
Authors: Reviewers:
Report Upstream: N/A Work issues:
Branch: Commit:
Dependencies: Stopgaps:

Status badges

Description (last modified by slelievre)

This metaticket addresses defects in Sage's algorithm for plotting one-variable functions.

  • #31089: detect_poles puts a gap in function plots (even if there are no poles).
  • #29954: plots sometimes have a gap in intervals where the function is nearly linear.
  • #6895: plots should not have a vertical line segment at places where the function has a vertical asymptote or jump discontinuity (see also ticket:29954#comment:12 and ticket:29954#comment:13). The behaviour of detect_poles=True is a small step in this direction.
  • "adaptive_refinement fails quite a lot ... because it takes the absolute error instead of a relative one" (from ticket:29954#comment:13). On intervals where the graph has large slope, even a relative error is not correct -- an angle (or the horizontal relative distance) should be used instead.
  • plots do not accurately locate the endpoints of an interval where the function is not defined (see ticket:13246#comment:44 and plot(sqrt(cos(27*x)), 0, 1)): adaptive plotting should not be terminated when an exclusion point of the function is detected.
  • plots do not accurately locate the tip of a cusp (such as plot(abs(x - sqrt(2))^(1/3), 1, 2), which should touch the x-axis)
  • #8341: detect_poles="show" does not detect a vertical asymptote unless the function changes sign.

Related tickets that could also be discussed:

  • #31870: Fix using matplotlib stylesheets in plots
  • #13368: plot(x, xmin=1, xmax=-1) comes up empty
  • #12676: plot exclude sometimes just connects instead of excluding
  • #6893: plotting code improvements
  • Clarify the documentation in sage/plot/plot.py:
    • The description of sage.plot.line.line in the class docstring says "a line determined by a sequence of points (this need not be straight!)". This should be changed to something like "a piecewise-linear curve determined by a sequence of points".
    • A point is an ordered pair (x,y), but, confusingly, this word is sometimes also used to refer to a real number that represents a possible value of x. (Regrettably, this is institutionalized in the naming of the argument initial_points of the function generate_plot_points.)

Change History (8)

comment:1 Changed 13 months ago by gh-DaveWitteMorris

  • Summary changed from Metaticket: improve accuracy of one-variable function plots to Metaticket: improve quality of one-variable function plots

Perhaps we can avoid reinventing the wheel, and also compare with the quality of plots provided by completing platforms. Here are some references that may be relevant:

  • Section 4.1.3 ("Sampling") of "The Mathematica Graphics Guidebook" by Cameron Smith and Nancy Blachman
Last edited 13 months ago by slelievre (previous) (diff)

comment:2 Changed 13 months ago by gh-kliem

What I don't like about the current behaviour, is that things are connected even if adaptive refinment fails.

Examples plot(sin(1/x)), plot(floor(x)) and similar.

This would also automatically take care of poles, if done right (of course the absolute, relative error etc are even more an issue then). at

Additionally one should also take care of functions, that need many refinments in only few locations, e.g. plot(real_nth_root(x, 3)). You don't see the error currently, because we just connect everything, but 0 actually needs about 50 refinements, which isn't a big deal because it is only one value.

Currently, the keyword adaptive_recursion does not really take care of this, because it does not distinguish between functions where refinement needs to be done an exponential number of times and those where this needs not to be done. Of course you can always leave this up to the user as well to discover that plot(real_nth_root(x, 3), adaptive_recursion=50) does indeed do many recursions but only at one location so it is not a runtime problem.

comment:3 Changed 12 months ago by gh-DaveWitteMorris

I think the default behaviour should probably be to connect the graph even if adaptive refinement fails (because it is usually the right thing to do for continuous functions), but I also strongly believe that there should be an option to disable this, and it is certainly reasonable to consider changing the default (or, at least, make detect_poles=True the default). (I think Maple has keywords discont and fdiscont to disable the default behaviour.) Let's move the discussion of this issue to #6895.

I think that being smart about adaptively increasing the number of refinements is a difficult problem, but certainly worth thinking about. Let's open a new ticket when we start discussing it in detail. However, I don't think that plot(real_nth_root(x, 3)) should require a lot of refinements, because the graph is nearly linear near 0. (The horizontal distance from the graph to the linear approximation is very small, even if the vertical distance is relatively large.)

comment:4 Changed 12 months ago by gh-DaveWitteMorris

  • Description modified (diff)

comment:5 Changed 8 months ago by mkoeppe

  • Milestone changed from sage-9.3 to sage-9.4

Moving to 9.4, as 9.3 has been released.

comment:6 Changed 8 months ago by slelievre

  • Description modified (diff)

Adding #31870 (Fix using matplotlib stylesheets in plots).

comment:7 Changed 6 months ago by mkoeppe

  • Milestone changed from sage-9.4 to sage-9.5

comment:8 Changed 4 weeks ago by mkoeppe

  • Milestone changed from sage-9.5 to sage-9.6
Note: See TracTickets for help on using tickets.