sagemath/sage

Metaticket: improve quality of one-variable function plots

Opened this issue · 7 comments

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 #29954 comment:12
    and #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 #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 #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.)

Component: graphics

Keywords: plotting

Issue created by migration from https://trac.sagemath.org/ticket/31169

comment:1

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:

kliem commented
comment:2

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

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.)

Description changed:

--- 
+++ 
@@ -12,7 +12,7 @@
 
 * 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)
 
-* `detect_poles="show"` does not detect a vertical asymptote unless the function changes sign.
+* #8341: `detect_poles="show"` does not detect a vertical asymptote unless the function changes sign.
 
 
 Related tickets that could also be discussed:
comment:5

Moving to 9.4, as 9.3 has been released.

slel commented

Description changed:

--- 
+++ 
@@ -1,28 +1,47 @@
-This metaticket addresses defects in sage's algorithm for plotting one-variable functions.
+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 [#29954 comment:12](https://github.com/sagemath/sage/issues/29954#comment:12) and [#29954 comment:13](https://github.com/sagemath/sage/issues/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 [#29954 comment:13](https://github.com/sagemath/sage/issues/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 [#13246 comment:44](https://github.com/sagemath/sage/issues/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.
-
+* #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 [#29954 comment:12](https://github.com/sagemath/sage/issues/29954#comment:12)
+  and [#29954 comment:13](https://github.com/sagemath/sage/issues/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 [#29954 comment:13](https://github.com/sagemath/sage/issues/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 [#13246 comment:44](https://github.com/sagemath/sage/issues/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`.)
 
-* #12676: plot exclude sometimes just connects instead of excluding
-
-* #6893: plotting code improvements
-
-* (no ticket yet) 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`.)
slel commented
comment:6

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