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_polesputs 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
ofdetect_poles=Trueis a small step in this direction. - "
adaptive_refinementfails 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
andplot(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 asplot(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.linein 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
pointis an ordered pair(x,y), but, confusingly,
this word is sometimes also used to refer to a real number
that represents a possible value ofx. (Regrettably,
this is institutionalized in the naming of the argument
initial_pointsof the functiongenerate_plot_points.)
- The description of
Component: graphics
Keywords: plotting
Issue created by migration from https://trac.sagemath.org/ticket/31169
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:
-
""Adaptive function plotting" in "The Yacas Book of Algorithms""]
-
Section 4.1.3 ("Sampling") of "The Mathematica Graphics Guidebook" by Cameron Smith and Nancy Blachman
-
Algorithms for choosing the domain and range when plotting a function
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.
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:Moving to 9.4, as 9.3 has been released.
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`.)