passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-plot might be problematic. Click here for more details.
- passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
- passagemath_plot-10.6.31rc3.dist-info/RECORD +82 -0
- passagemath_plot-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_plot-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_plot.libs/libgfortran-83c28eba.so.5.0.0 +0 -0
- passagemath_plot.libs/libgsl-cda90e79.so.28.0.0 +0 -0
- passagemath_plot.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
- passagemath_plot.libs/libquadmath-2284e583.so.0.0.0 +0 -0
- sage/all__sagemath_plot.py +15 -0
- sage/ext_data/threejs/animation.css +195 -0
- sage/ext_data/threejs/animation.html +85 -0
- sage/ext_data/threejs/animation.js +273 -0
- sage/ext_data/threejs/fat_lines.js +48 -0
- sage/ext_data/threejs/threejs-version.txt +1 -0
- sage/ext_data/threejs/threejs_template.html +597 -0
- sage/interfaces/all__sagemath_plot.py +1 -0
- sage/interfaces/gnuplot.py +196 -0
- sage/interfaces/jmoldata.py +208 -0
- sage/interfaces/povray.py +56 -0
- sage/plot/all.py +42 -0
- sage/plot/animate.py +1796 -0
- sage/plot/arc.py +504 -0
- sage/plot/arrow.py +671 -0
- sage/plot/bar_chart.py +205 -0
- sage/plot/bezier_path.py +400 -0
- sage/plot/circle.py +435 -0
- sage/plot/colors.py +1606 -0
- sage/plot/complex_plot.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/plot/complex_plot.pyx +1446 -0
- sage/plot/contour_plot.py +1792 -0
- sage/plot/density_plot.py +318 -0
- sage/plot/disk.py +373 -0
- sage/plot/ellipse.py +375 -0
- sage/plot/graphics.py +3580 -0
- sage/plot/histogram.py +354 -0
- sage/plot/hyperbolic_arc.py +404 -0
- sage/plot/hyperbolic_polygon.py +416 -0
- sage/plot/hyperbolic_regular_polygon.py +296 -0
- sage/plot/line.py +626 -0
- sage/plot/matrix_plot.py +629 -0
- sage/plot/misc.py +509 -0
- sage/plot/multigraphics.py +1294 -0
- sage/plot/plot.py +4183 -0
- sage/plot/plot3d/all.py +23 -0
- sage/plot/plot3d/base.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/plot/plot3d/base.pxd +12 -0
- sage/plot/plot3d/base.pyx +3378 -0
- sage/plot/plot3d/implicit_plot3d.py +659 -0
- sage/plot/plot3d/implicit_surface.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/plot/plot3d/implicit_surface.pyx +1453 -0
- sage/plot/plot3d/index_face_set.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/plot/plot3d/index_face_set.pxd +32 -0
- sage/plot/plot3d/index_face_set.pyx +1873 -0
- sage/plot/plot3d/introduction.py +131 -0
- sage/plot/plot3d/list_plot3d.py +649 -0
- sage/plot/plot3d/parametric_plot3d.py +1130 -0
- sage/plot/plot3d/parametric_surface.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/plot/plot3d/parametric_surface.pxd +12 -0
- sage/plot/plot3d/parametric_surface.pyx +893 -0
- sage/plot/plot3d/platonic.py +601 -0
- sage/plot/plot3d/plot3d.py +1442 -0
- sage/plot/plot3d/plot_field3d.py +162 -0
- sage/plot/plot3d/point_c.pxi +148 -0
- sage/plot/plot3d/revolution_plot3d.py +309 -0
- sage/plot/plot3d/shapes.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/plot/plot3d/shapes.pxd +22 -0
- sage/plot/plot3d/shapes.pyx +1382 -0
- sage/plot/plot3d/shapes2.py +1512 -0
- sage/plot/plot3d/tachyon.py +1779 -0
- sage/plot/plot3d/texture.py +453 -0
- sage/plot/plot3d/transform.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/plot/plot3d/transform.pxd +21 -0
- sage/plot/plot3d/transform.pyx +268 -0
- sage/plot/plot3d/tri_plot.py +589 -0
- sage/plot/plot_field.py +362 -0
- sage/plot/point.py +624 -0
- sage/plot/polygon.py +562 -0
- sage/plot/primitive.py +249 -0
- sage/plot/scatter_plot.py +199 -0
- sage/plot/step.py +85 -0
- sage/plot/streamline_plot.py +328 -0
- sage/plot/text.py +432 -0
sage/plot/plot.py
ADDED
|
@@ -0,0 +1,4183 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-plot
|
|
2
|
+
# sage.doctest: needs sage.symbolic
|
|
3
|
+
r"""
|
|
4
|
+
2D plotting
|
|
5
|
+
|
|
6
|
+
Sage provides extensive 2D plotting functionality. The underlying
|
|
7
|
+
rendering is done using the matplotlib Python library.
|
|
8
|
+
|
|
9
|
+
The following graphics primitives are supported:
|
|
10
|
+
|
|
11
|
+
- :func:`~sage.plot.arrow.arrow` -- an arrow from a min point to a max point
|
|
12
|
+
|
|
13
|
+
- :func:`~sage.plot.circle.circle` -- a circle with given radius
|
|
14
|
+
|
|
15
|
+
- :func:`~sage.plot.ellipse.ellipse` -- an ellipse with given radii
|
|
16
|
+
and angle
|
|
17
|
+
|
|
18
|
+
- :func:`~sage.plot.arc.arc` -- an arc of a circle or an ellipse
|
|
19
|
+
|
|
20
|
+
- :func:`~sage.plot.disk.disk` -- a filled disk (i.e. a sector or wedge of a circle)
|
|
21
|
+
|
|
22
|
+
- :func:`~sage.plot.line.line` -- a line determined by a sequence of points (this need not
|
|
23
|
+
be straight!)
|
|
24
|
+
|
|
25
|
+
- :func:`~sage.plot.point.point` -- a point
|
|
26
|
+
|
|
27
|
+
- :func:`~sage.plot.text.text` -- some text
|
|
28
|
+
|
|
29
|
+
- :func:`~sage.plot.polygon.polygon` -- a filled polygon
|
|
30
|
+
|
|
31
|
+
The following plotting functions are supported:
|
|
32
|
+
|
|
33
|
+
- :func:`plot` -- plot of a function or other Sage object (e.g., elliptic
|
|
34
|
+
curve)
|
|
35
|
+
|
|
36
|
+
- :func:`parametric_plot`
|
|
37
|
+
|
|
38
|
+
- :func:`~sage.plot.contour_plot.implicit_plot`
|
|
39
|
+
|
|
40
|
+
- :func:`polar_plot`
|
|
41
|
+
|
|
42
|
+
- :func:`~sage.plot.contour_plot.region_plot`
|
|
43
|
+
|
|
44
|
+
- :func:`list_plot`
|
|
45
|
+
|
|
46
|
+
- :func:`~sage.plot.scatter_plot.scatter_plot`
|
|
47
|
+
|
|
48
|
+
- :func:`~sage.plot.bar_chart.bar_chart`
|
|
49
|
+
|
|
50
|
+
- :func:`~sage.plot.contour_plot.contour_plot`
|
|
51
|
+
|
|
52
|
+
- :func:`~sage.plot.density_plot.density_plot`
|
|
53
|
+
|
|
54
|
+
- :func:`~sage.plot.plot_field.plot_vector_field`
|
|
55
|
+
|
|
56
|
+
- :func:`~sage.plot.plot_field.plot_slope_field`
|
|
57
|
+
|
|
58
|
+
- :func:`~sage.plot.matrix_plot.matrix_plot`
|
|
59
|
+
|
|
60
|
+
- :func:`~sage.plot.complex_plot.complex_plot`
|
|
61
|
+
|
|
62
|
+
- :func:`graphics_array`
|
|
63
|
+
|
|
64
|
+
- :func:`multi_graphics`
|
|
65
|
+
|
|
66
|
+
- The following log plotting functions:
|
|
67
|
+
|
|
68
|
+
- :func:`plot_loglog`
|
|
69
|
+
|
|
70
|
+
- :func:`plot_semilogx` and :func:`plot_semilogy`
|
|
71
|
+
|
|
72
|
+
- :func:`list_plot_loglog`
|
|
73
|
+
|
|
74
|
+
- :func:`list_plot_semilogx` and :func:`list_plot_semilogy`
|
|
75
|
+
|
|
76
|
+
The following miscellaneous Graphics functions are included:
|
|
77
|
+
|
|
78
|
+
- :func:`~sage.plot.graphics.Graphics`
|
|
79
|
+
|
|
80
|
+
- :func:`~sage.plot.graphics.is_Graphics`
|
|
81
|
+
|
|
82
|
+
- :func:`~sage.plot.colors.hue`
|
|
83
|
+
|
|
84
|
+
Type ``?`` after each primitive in Sage for help and examples.
|
|
85
|
+
|
|
86
|
+
EXAMPLES:
|
|
87
|
+
|
|
88
|
+
We draw a curve::
|
|
89
|
+
|
|
90
|
+
sage: plot(x^2, (x,0,5))
|
|
91
|
+
Graphics object consisting of 1 graphics primitive
|
|
92
|
+
|
|
93
|
+
.. PLOT::
|
|
94
|
+
|
|
95
|
+
g = plot(x**2, (x,0,5))
|
|
96
|
+
sphinx_plot(g)
|
|
97
|
+
|
|
98
|
+
We draw a circle and a curve::
|
|
99
|
+
|
|
100
|
+
sage: circle((1,1), 1) + plot(x^2, (x,0,5))
|
|
101
|
+
Graphics object consisting of 2 graphics primitives
|
|
102
|
+
|
|
103
|
+
.. PLOT::
|
|
104
|
+
|
|
105
|
+
g = circle((1,1), 1) + plot(x**2, (x,0,5))
|
|
106
|
+
sphinx_plot(g)
|
|
107
|
+
|
|
108
|
+
Notice that the aspect ratio of the above plot makes the plot very tall
|
|
109
|
+
because the plot adopts the default aspect ratio of the circle (to make
|
|
110
|
+
the circle appear like a circle). We can change the aspect ratio to be
|
|
111
|
+
what we normally expect for a plot by explicitly asking for an
|
|
112
|
+
'automatic' aspect ratio::
|
|
113
|
+
|
|
114
|
+
sage: show(circle((1,1), 1) + plot(x^2, (x,0,5)), aspect_ratio='automatic')
|
|
115
|
+
|
|
116
|
+
The aspect ratio describes the apparently height/width ratio of a unit
|
|
117
|
+
square. If you want the vertical units to be twice as big as the
|
|
118
|
+
horizontal units, specify an aspect ratio of 2::
|
|
119
|
+
|
|
120
|
+
sage: show(circle((1,1), 1) + plot(x^2, (x,0,5)), aspect_ratio=2)
|
|
121
|
+
|
|
122
|
+
The ``figsize`` option adjusts the figure size. The default figsize is
|
|
123
|
+
4. To make a figure that is roughly twice as big, use ``figsize=8``::
|
|
124
|
+
|
|
125
|
+
sage: show(circle((1,1), 1) + plot(x^2, (x,0,5)), figsize=8)
|
|
126
|
+
|
|
127
|
+
You can also give separate horizontal and vertical dimensions. Both
|
|
128
|
+
will be measured in inches::
|
|
129
|
+
|
|
130
|
+
sage: show(circle((1,1), 1) + plot(x^2, (x,0,5)), figsize=[4,8])
|
|
131
|
+
|
|
132
|
+
However, do not make the figsize too big (e.g. one dimension greater
|
|
133
|
+
than 327 or both in the mid-200s) as this will lead to errors or crashes.
|
|
134
|
+
See :meth:`~sage.plot.graphics.Graphics.show` for full details.
|
|
135
|
+
|
|
136
|
+
Note that the axes will not cross if the data is not on both sides of
|
|
137
|
+
both axes, even if it is quite close::
|
|
138
|
+
|
|
139
|
+
sage: plot(x^3, (x,1,10))
|
|
140
|
+
Graphics object consisting of 1 graphics primitive
|
|
141
|
+
|
|
142
|
+
.. PLOT::
|
|
143
|
+
|
|
144
|
+
g = plot(x**3, (x,1,10))
|
|
145
|
+
sphinx_plot(g)
|
|
146
|
+
|
|
147
|
+
When the labels have quite different orders of magnitude or are very
|
|
148
|
+
large, scientific notation (the `e` notation for powers of ten) is used::
|
|
149
|
+
|
|
150
|
+
sage: plot(x^2, (x,480,500)) # no scientific notation
|
|
151
|
+
Graphics object consisting of 1 graphics primitive
|
|
152
|
+
|
|
153
|
+
.. PLOT::
|
|
154
|
+
|
|
155
|
+
g = plot(x**2, (x,480,500))
|
|
156
|
+
sphinx_plot(g)
|
|
157
|
+
|
|
158
|
+
::
|
|
159
|
+
|
|
160
|
+
sage: plot(x^2, (x,300,500)) # scientific notation on y-axis
|
|
161
|
+
Graphics object consisting of 1 graphics primitive
|
|
162
|
+
|
|
163
|
+
.. PLOT::
|
|
164
|
+
|
|
165
|
+
g = plot(x**2, (x,300,500))
|
|
166
|
+
sphinx_plot(g)
|
|
167
|
+
|
|
168
|
+
But you can fix your own tick labels, if you know what to expect and
|
|
169
|
+
have a preference::
|
|
170
|
+
|
|
171
|
+
sage: plot(x^2, (x,300,500), ticks=[100,50000])
|
|
172
|
+
Graphics object consisting of 1 graphics primitive
|
|
173
|
+
|
|
174
|
+
.. PLOT::
|
|
175
|
+
|
|
176
|
+
g = plot(x**2, (x,300,500), ticks=[100,50000])
|
|
177
|
+
sphinx_plot(g)
|
|
178
|
+
|
|
179
|
+
To change the ticks on one axis only, use the following notation::
|
|
180
|
+
|
|
181
|
+
sage: plot(x^2, (x,300,500), ticks=[None,50000])
|
|
182
|
+
Graphics object consisting of 1 graphics primitive
|
|
183
|
+
|
|
184
|
+
.. PLOT::
|
|
185
|
+
|
|
186
|
+
g = plot(x**2, (x,300,500), ticks=[None,50000])
|
|
187
|
+
sphinx_plot(g)
|
|
188
|
+
|
|
189
|
+
You can even have custom tick labels along with custom positioning. ::
|
|
190
|
+
|
|
191
|
+
sage: plot(x^2, (x,0,3), ticks=[[1,2.5],pi/2], tick_formatter=[["$x_1$","$x_2$"],pi]) # long time
|
|
192
|
+
Graphics object consisting of 1 graphics primitive
|
|
193
|
+
|
|
194
|
+
.. PLOT::
|
|
195
|
+
|
|
196
|
+
g = plot(x**2, (x,0,3), ticks=[[1,2.5],pi/2], tick_formatter=[["$x_1$","$x_2$"],pi])
|
|
197
|
+
sphinx_plot(g)
|
|
198
|
+
|
|
199
|
+
We construct a plot involving several graphics objects::
|
|
200
|
+
|
|
201
|
+
sage: G = plot(cos(x), (x, -5, 5), thickness=5, color='green', title='A plot')
|
|
202
|
+
sage: P = polygon([[1,2], [5,6], [5,0]], color='red')
|
|
203
|
+
sage: G + P
|
|
204
|
+
Graphics object consisting of 2 graphics primitives
|
|
205
|
+
|
|
206
|
+
.. PLOT::
|
|
207
|
+
|
|
208
|
+
G = plot(cos(x), (x, -5, 5), thickness=5, color='green', title='A plot')
|
|
209
|
+
P = polygon([[1,2], [5,6], [5,0]], color='red')
|
|
210
|
+
sphinx_plot(G + P)
|
|
211
|
+
|
|
212
|
+
Next we construct the reflection of the above polygon about the
|
|
213
|
+
`y`-axis by iterating over the list of first-coordinates of
|
|
214
|
+
the first graphic element of ``P`` (which is the actual
|
|
215
|
+
Polygon; note that ``P`` is a Graphics object, which consists
|
|
216
|
+
of a single polygon)::
|
|
217
|
+
|
|
218
|
+
sage: Q = polygon([(-x,y) for x,y in P[0]], color='blue')
|
|
219
|
+
sage: Q # show it
|
|
220
|
+
Graphics object consisting of 1 graphics primitive
|
|
221
|
+
|
|
222
|
+
.. PLOT::
|
|
223
|
+
|
|
224
|
+
P = polygon([[1,2], [5,6], [5,0]], color='red')
|
|
225
|
+
Q = polygon([(-x,y) for x,y in P[0]], color='blue')
|
|
226
|
+
sphinx_plot(Q)
|
|
227
|
+
|
|
228
|
+
We combine together different graphics objects using "+"::
|
|
229
|
+
|
|
230
|
+
sage: H = G + P + Q
|
|
231
|
+
sage: print(H)
|
|
232
|
+
Graphics object consisting of 3 graphics primitives
|
|
233
|
+
sage: type(H)
|
|
234
|
+
<class 'sage.plot.graphics.Graphics'>
|
|
235
|
+
sage: H[1]
|
|
236
|
+
Polygon defined by 3 points
|
|
237
|
+
sage: list(H[1])
|
|
238
|
+
[(1.0, 2.0), (5.0, 6.0), (5.0, 0.0)]
|
|
239
|
+
sage: H # show it
|
|
240
|
+
Graphics object consisting of 3 graphics primitives
|
|
241
|
+
|
|
242
|
+
.. PLOT::
|
|
243
|
+
|
|
244
|
+
G = plot(cos(x), (x, -5, 5), thickness=5, color='green', title='A plot')
|
|
245
|
+
P = polygon([[1,2], [5,6], [5,0]], color='red')
|
|
246
|
+
Q = polygon([(-x,y) for x,y in P[0]], color='blue')
|
|
247
|
+
H = G + P + Q
|
|
248
|
+
sphinx_plot(H)
|
|
249
|
+
|
|
250
|
+
We can put text in a graph::
|
|
251
|
+
|
|
252
|
+
sage: L = [[cos(pi*i/100)^3,sin(pi*i/100)] for i in range(200)]
|
|
253
|
+
sage: p = line(L, rgbcolor=(1/4,1/8,3/4))
|
|
254
|
+
sage: tt = text('A Bulb', (1.5, 0.25))
|
|
255
|
+
sage: tx = text('x axis', (1.5,-0.2))
|
|
256
|
+
sage: ty = text('y axis', (0.4,0.9))
|
|
257
|
+
sage: g = p + tt + tx + ty
|
|
258
|
+
sage: g.show(xmin=-1.5, xmax=2, ymin=-1, ymax=1)
|
|
259
|
+
|
|
260
|
+
.. PLOT::
|
|
261
|
+
|
|
262
|
+
L = [[cos(pi*i/100)**3,sin(pi*i/100)] for i in range(200)]
|
|
263
|
+
p = line(L, rgbcolor=(1.0/4.0,1.0/8.0,3.0/4.0))
|
|
264
|
+
t = text('A Bulb', (1.5, 0.25))
|
|
265
|
+
x = text('x axis', (1.5,-0.2))
|
|
266
|
+
y = text('y axis', (0.4,0.9))
|
|
267
|
+
g = p+t+x+y
|
|
268
|
+
g.xmin(-1.5)
|
|
269
|
+
g.xmax(2)
|
|
270
|
+
g.ymin(-1)
|
|
271
|
+
g.ymax(1)
|
|
272
|
+
sphinx_plot(g)
|
|
273
|
+
|
|
274
|
+
We can add a graphics object to another one as an inset::
|
|
275
|
+
|
|
276
|
+
sage: g1 = plot(x^2*sin(1/x), (x, -2, 2), axes_labels=['$x$', '$y$'])
|
|
277
|
+
sage: g2 = plot(x^2*sin(1/x), (x, -0.3, 0.3), axes_labels=['$x$', '$y$'],
|
|
278
|
+
....: frame=True)
|
|
279
|
+
sage: g1.inset(g2, pos=(0.15, 0.7, 0.25, 0.25))
|
|
280
|
+
Multigraphics with 2 elements
|
|
281
|
+
|
|
282
|
+
.. PLOT::
|
|
283
|
+
|
|
284
|
+
g1 = plot(x**2*sin(1/x), (x, -2, 2), axes_labels=['$x$', '$y$'])
|
|
285
|
+
g2 = plot(x**2*sin(1/x), (x, -0.3, 0.3), axes_labels=['$x$', '$y$'], \
|
|
286
|
+
frame=True)
|
|
287
|
+
sphinx_plot(g1.inset(g2, pos=(0.15, 0.7, 0.25, 0.25)))
|
|
288
|
+
|
|
289
|
+
We can add a title to a graph::
|
|
290
|
+
|
|
291
|
+
sage: plot(x^2, (x,-2,2), title='A plot of $x^2$')
|
|
292
|
+
Graphics object consisting of 1 graphics primitive
|
|
293
|
+
|
|
294
|
+
.. PLOT::
|
|
295
|
+
|
|
296
|
+
g=plot(x**2, (x,-2,2), title='A plot of $x^2$')
|
|
297
|
+
sphinx_plot(g)
|
|
298
|
+
|
|
299
|
+
We can set the position of the title::
|
|
300
|
+
|
|
301
|
+
sage: plot(x^2, (-2,2), title='Plot of $x^2$', title_pos=(0.5,-0.05))
|
|
302
|
+
Graphics object consisting of 1 graphics primitive
|
|
303
|
+
|
|
304
|
+
.. PLOT::
|
|
305
|
+
|
|
306
|
+
g=plot(x**2, (-2,2), title='Plot of $x^2$', title_pos=(0.5,-0.05))
|
|
307
|
+
sphinx_plot(g)
|
|
308
|
+
|
|
309
|
+
We plot the Riemann zeta function along the critical line and see
|
|
310
|
+
the first few zeros::
|
|
311
|
+
|
|
312
|
+
sage: i = CDF.0 # define i this way for maximum speed.
|
|
313
|
+
sage: p1 = plot(lambda t: arg(zeta(0.5+t*i)), 1, 27, rgbcolor=(0.8,0,0))
|
|
314
|
+
sage: p2 = plot(lambda t: abs(zeta(0.5+t*i)), 1, 27, color=hue(0.7))
|
|
315
|
+
sage: print(p1 + p2)
|
|
316
|
+
Graphics object consisting of 2 graphics primitives
|
|
317
|
+
sage: p1 + p2 # display it
|
|
318
|
+
Graphics object consisting of 2 graphics primitives
|
|
319
|
+
|
|
320
|
+
.. PLOT::
|
|
321
|
+
|
|
322
|
+
from sage.rings.complex_double import ComplexDoubleElement
|
|
323
|
+
i = ComplexDoubleElement(0,1) # define i this way for maximum speed.
|
|
324
|
+
p1 = plot(lambda t: arg(zeta(0.5+t*i)), 1, 27, rgbcolor=(0.8,0,0))
|
|
325
|
+
p2 = plot(lambda t: abs(zeta(0.5+t*i)), 1, 27, color=hue(0.7))
|
|
326
|
+
g = p1 + p2
|
|
327
|
+
sphinx_plot(g)
|
|
328
|
+
|
|
329
|
+
.. NOTE::
|
|
330
|
+
|
|
331
|
+
Not all functions in Sage are symbolic. When plotting non-symbolic functions
|
|
332
|
+
they should be wrapped in ``lambda``::
|
|
333
|
+
|
|
334
|
+
sage: plot(lambda x:fibonacci(round(x)), (x,1,10))
|
|
335
|
+
Graphics object consisting of 1 graphics primitive
|
|
336
|
+
|
|
337
|
+
.. PLOT::
|
|
338
|
+
|
|
339
|
+
g=plot(lambda x:fibonacci(round(x)), (x,1,10))
|
|
340
|
+
sphinx_plot(g)
|
|
341
|
+
|
|
342
|
+
Many concentric circles shrinking toward the origin::
|
|
343
|
+
|
|
344
|
+
sage: show(sum(circle((i,0), i, hue=sin(i/10)) for i in [10,9.9,..,0])) # long time
|
|
345
|
+
|
|
346
|
+
.. PLOT::
|
|
347
|
+
|
|
348
|
+
g = sum(circle((i,0), i, hue=sin(i/10)) for i in srange(0,10,0.1))
|
|
349
|
+
sphinx_plot(g)
|
|
350
|
+
|
|
351
|
+
Here is a pretty graph::
|
|
352
|
+
|
|
353
|
+
sage: g = Graphics()
|
|
354
|
+
sage: for i in range(60):
|
|
355
|
+
....: p = polygon([(i*cos(i),i*sin(i)), (0,i), (i,0)],\
|
|
356
|
+
....: color=hue(i/40+0.4), alpha=0.2)
|
|
357
|
+
....: g = g + p
|
|
358
|
+
sage: g.show(dpi=200, axes=False)
|
|
359
|
+
|
|
360
|
+
.. PLOT::
|
|
361
|
+
|
|
362
|
+
g=Graphics()
|
|
363
|
+
for i in range(60):
|
|
364
|
+
# i/40 doesn't convert to real number
|
|
365
|
+
p = polygon([(i*cos(i),i*sin(i)), (0,i), (i,0)],\
|
|
366
|
+
color=hue(0.025*i+0.4), alpha=0.2)
|
|
367
|
+
g = g + p
|
|
368
|
+
g.axes(False)
|
|
369
|
+
sphinx_plot(g)
|
|
370
|
+
|
|
371
|
+
Another graph::
|
|
372
|
+
|
|
373
|
+
sage: x = var('x')
|
|
374
|
+
sage: P = plot(sin(x)/x, -4, 4, color='blue') + \
|
|
375
|
+
....: plot(x*cos(x), -4, 4, color='red') + \
|
|
376
|
+
....: plot(tan(x), -4, 4, color='green')
|
|
377
|
+
sage: P.show(ymin=-pi, ymax=pi)
|
|
378
|
+
|
|
379
|
+
.. PLOT::
|
|
380
|
+
|
|
381
|
+
g = plot(sin(x)/x, -4, 4, color='blue') + \
|
|
382
|
+
plot(x*cos(x), -4, 4, color='red') + \
|
|
383
|
+
plot(tan(x), -4, 4, color='green')
|
|
384
|
+
g.ymin(-pi)
|
|
385
|
+
g.ymax(pi)
|
|
386
|
+
sphinx_plot(g)
|
|
387
|
+
|
|
388
|
+
PYX EXAMPLES: These are some examples of plots similar to some of
|
|
389
|
+
the plots in the PyX (http://pyx.sourceforge.net) documentation:
|
|
390
|
+
|
|
391
|
+
Symbolline::
|
|
392
|
+
|
|
393
|
+
sage: y(x) = x*sin(x^2)
|
|
394
|
+
sage: v = [(x, y(x)) for x in [-3,-2.95,..,3]]
|
|
395
|
+
sage: show(points(v, rgbcolor=(0.2,0.6, 0.1), pointsize=30) + plot(spline(v), -3.1, 3))
|
|
396
|
+
|
|
397
|
+
.. PLOT::
|
|
398
|
+
|
|
399
|
+
#y(x)=x*sin(x**2) gave SyntaxError: can't assign to function call
|
|
400
|
+
def y(x): return x*sin(x**2)
|
|
401
|
+
v=list()
|
|
402
|
+
for x in srange(-3,3,0.05):
|
|
403
|
+
v.append((x, y(x)))
|
|
404
|
+
g = points(v, rgbcolor=(0.2,0.6, 0.1), pointsize=30) + plot(spline(v), -3.1, 3)
|
|
405
|
+
sphinx_plot(g)
|
|
406
|
+
|
|
407
|
+
Cycliclink::
|
|
408
|
+
|
|
409
|
+
sage: g1 = plot(cos(20*x)*exp(-2*x), 0, 1)
|
|
410
|
+
sage: g2 = plot(2*exp(-30*x) - exp(-3*x), 0, 1)
|
|
411
|
+
sage: show(graphics_array([g1, g2], 2, 1))
|
|
412
|
+
|
|
413
|
+
.. PLOT::
|
|
414
|
+
|
|
415
|
+
g1 = plot(cos(20*x)*exp(-2*x), 0, 1)
|
|
416
|
+
g2 = plot(2*exp(-30*x) - exp(-3*x), 0, 1)
|
|
417
|
+
g = graphics_array([g1, g2], 2, 1)
|
|
418
|
+
sphinx_plot(g)
|
|
419
|
+
|
|
420
|
+
Pi Axis::
|
|
421
|
+
|
|
422
|
+
sage: g1 = plot(sin(x), 0, 2*pi)
|
|
423
|
+
sage: g2 = plot(cos(x), 0, 2*pi, linestyle='--')
|
|
424
|
+
sage: (g1 + g2).show(ticks=pi/6, # show their sum, nicely formatted # long time
|
|
425
|
+
....: tick_formatter=pi)
|
|
426
|
+
|
|
427
|
+
.. PLOT::
|
|
428
|
+
|
|
429
|
+
g1 = plot(sin(x), 0, 2*pi, ticks=pi/6, tick_formatter=pi)
|
|
430
|
+
g2 = plot(cos(x), 0, 2*pi, linestyle='--', ticks=pi/6, tick_formatter=pi)
|
|
431
|
+
sphinx_plot(g1+g2)
|
|
432
|
+
|
|
433
|
+
An illustration of integration::
|
|
434
|
+
|
|
435
|
+
sage: f(x) = (x-3)*(x-5)*(x-7)+40
|
|
436
|
+
sage: P = line([(2,0),(2,f(2))], color='black')
|
|
437
|
+
sage: P += line([(8,0),(8,f(8))], color='black')
|
|
438
|
+
sage: P += polygon([(2,0),(2,f(2))] + [(x, f(x)) for x in [2,2.1,..,8]] + [(8,0),(2,0)],
|
|
439
|
+
....: rgbcolor=(0.8,0.8,0.8), aspect_ratio='automatic')
|
|
440
|
+
sage: P += text("$\\int_{a}^b f(x) dx$", (5, 20), fontsize=16, color='black')
|
|
441
|
+
sage: P += plot(f, (1, 8.5), thickness=3)
|
|
442
|
+
sage: P # show the result
|
|
443
|
+
Graphics object consisting of 5 graphics primitives
|
|
444
|
+
|
|
445
|
+
.. PLOT::
|
|
446
|
+
|
|
447
|
+
#inline f substitution to avoid SyntaxError: can't assign to function call in sphinx_plot
|
|
448
|
+
def f(x): return (x-3)*(x-5)*(x-7)+40
|
|
449
|
+
P = line([(2,0),(2,f(2))], color='black')
|
|
450
|
+
P = P + line([(8,0),(8,f(8))], color='black')
|
|
451
|
+
L = list(((2,0), (2,f(2))))
|
|
452
|
+
for i in srange(2,8.1,0.1):
|
|
453
|
+
L.append((i,f(i)))
|
|
454
|
+
L.append((8,0))
|
|
455
|
+
L.append((2,0))
|
|
456
|
+
P = P + polygon(L, rgbcolor=(0.8,0.8,0.8), aspect_ratio='automatic')
|
|
457
|
+
P = P + text("$\\int_{a}^b f(x) dx$", (5, 20), fontsize=16, color='black')
|
|
458
|
+
P = P + plot(f, (1, 8.5), thickness=3)
|
|
459
|
+
sphinx_plot(P)
|
|
460
|
+
|
|
461
|
+
NUMERICAL PLOTTING:
|
|
462
|
+
|
|
463
|
+
Sage includes Matplotlib, which provides 2D plotting with an interface
|
|
464
|
+
that is a likely very familiar to people doing numerical
|
|
465
|
+
computation.
|
|
466
|
+
You can use ``plt.clf()`` to clear the current image frame
|
|
467
|
+
and ``plt.close()`` to close it.
|
|
468
|
+
For example,
|
|
469
|
+
|
|
470
|
+
::
|
|
471
|
+
|
|
472
|
+
sage: import pylab as plt
|
|
473
|
+
sage: t = plt.arange(0.0, 2.0, 0.01)
|
|
474
|
+
sage: s = sin(2*pi*t)
|
|
475
|
+
sage: P = plt.plot(t, s, linewidth=1.0)
|
|
476
|
+
sage: xl = plt.xlabel('time (s)')
|
|
477
|
+
sage: yl = plt.ylabel('voltage (mV)')
|
|
478
|
+
sage: t = plt.title('About as simple as it gets, folks')
|
|
479
|
+
sage: plt.grid(True)
|
|
480
|
+
sage: import tempfile
|
|
481
|
+
sage: with tempfile.NamedTemporaryFile(suffix='.png') as f1:
|
|
482
|
+
....: plt.savefig(f1.name)
|
|
483
|
+
sage: plt.clf()
|
|
484
|
+
sage: with tempfile.NamedTemporaryFile(suffix='.png') as f2:
|
|
485
|
+
....: plt.savefig(f2.name)
|
|
486
|
+
sage: plt.close()
|
|
487
|
+
sage: plt.imshow([[1,2],[0,1]])
|
|
488
|
+
<matplotlib.image.AxesImage object at ...>
|
|
489
|
+
|
|
490
|
+
We test that ``imshow`` works as well, verifying that
|
|
491
|
+
:issue:`2900` is fixed (in Matplotlib).
|
|
492
|
+
|
|
493
|
+
::
|
|
494
|
+
|
|
495
|
+
sage: plt.imshow([[(0.0,0.0,0.0)]])
|
|
496
|
+
<matplotlib.image.AxesImage object at ...>
|
|
497
|
+
sage: import tempfile
|
|
498
|
+
sage: with tempfile.NamedTemporaryFile(suffix='.png') as f:
|
|
499
|
+
....: plt.savefig(f.name)
|
|
500
|
+
|
|
501
|
+
Since the above overwrites many Sage plotting functions, we reset
|
|
502
|
+
the state of Sage, so that the examples below work!
|
|
503
|
+
|
|
504
|
+
::
|
|
505
|
+
|
|
506
|
+
sage: reset()
|
|
507
|
+
|
|
508
|
+
See https://matplotlib.org/stable/ for complete documentation
|
|
509
|
+
about how to use Matplotlib.
|
|
510
|
+
|
|
511
|
+
TESTS:
|
|
512
|
+
|
|
513
|
+
We test dumping and loading a plot.
|
|
514
|
+
|
|
515
|
+
::
|
|
516
|
+
|
|
517
|
+
sage: p = plot(sin(x), (x, 0,2*pi))
|
|
518
|
+
sage: Q = loads(dumps(p))
|
|
519
|
+
|
|
520
|
+
Verify that a clean sage startup does *not* import matplotlib::
|
|
521
|
+
|
|
522
|
+
sage: os.system("sage -c \"if 'matplotlib' in sys.modules: sys.exit(1)\"") # long time
|
|
523
|
+
0
|
|
524
|
+
|
|
525
|
+
Verify that :issue:`10980` is fixed::
|
|
526
|
+
|
|
527
|
+
sage: plot(x,0,2,gridlines=([sqrt(2)],[]))
|
|
528
|
+
Graphics object consisting of 1 graphics primitive
|
|
529
|
+
|
|
530
|
+
AUTHORS:
|
|
531
|
+
|
|
532
|
+
- Alex Clemesha and William Stein (2006-04-10): initial version
|
|
533
|
+
|
|
534
|
+
- David Joyner: examples
|
|
535
|
+
|
|
536
|
+
- Alex Clemesha (2006-05-04) major update
|
|
537
|
+
|
|
538
|
+
- William Stein (2006-05-29): fine tuning, bug fixes, better server
|
|
539
|
+
integration
|
|
540
|
+
|
|
541
|
+
- William Stein (2006-07-01): misc polish
|
|
542
|
+
|
|
543
|
+
- Alex Clemesha (2006-09-29): added contour_plot, frame axes, misc
|
|
544
|
+
polishing
|
|
545
|
+
|
|
546
|
+
- Robert Miller (2006-10-30): tuning, NetworkX primitive
|
|
547
|
+
|
|
548
|
+
- Alex Clemesha (2006-11-25): added plot_vector_field, matrix_plot,
|
|
549
|
+
arrow, bar_chart, Axes class usage (see axes.py)
|
|
550
|
+
|
|
551
|
+
- Bobby Moretti and William Stein (2008-01): Change plot to specify
|
|
552
|
+
ranges using the (varname, min, max) notation.
|
|
553
|
+
|
|
554
|
+
- William Stein (2008-01-19): raised the documentation coverage from a
|
|
555
|
+
miserable 12 percent to a 'wopping' 35 percent, and fixed and
|
|
556
|
+
clarified numerous small issues.
|
|
557
|
+
|
|
558
|
+
- Jason Grout (2009-09-05): shifted axes and grid functionality over
|
|
559
|
+
to matplotlib; fixed a number of smaller issues.
|
|
560
|
+
|
|
561
|
+
- Jason Grout (2010-10): rewrote aspect ratio portions of the code
|
|
562
|
+
|
|
563
|
+
- Jeroen Demeyer (2012-04-19): move parts of this file to graphics.py (:issue:`12857`)
|
|
564
|
+
|
|
565
|
+
- Aaron Lauve (2016-07-13): reworked handling of 'color' when passed
|
|
566
|
+
a list of functions; now more in-line with other CAS's. Added list functionality
|
|
567
|
+
to linestyle and legend_label options as well. (:issue:`12962`)
|
|
568
|
+
|
|
569
|
+
- Eric Gourgoulhon (2019-04-24): add :func:`multi_graphics` and insets
|
|
570
|
+
"""
|
|
571
|
+
# ****************************************************************************
|
|
572
|
+
# Copyright (C) 2006 Alex Clemesha <clemesha@gmail.com>
|
|
573
|
+
# Copyright (C) 2006-2008 William Stein <wstein@gmail.com>
|
|
574
|
+
# Copyright (C) 2010 Jason Grout
|
|
575
|
+
#
|
|
576
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
577
|
+
# as published by the Free Software Foundation; either version 2 of
|
|
578
|
+
# the License, or (at your option) any later version.
|
|
579
|
+
# https://www.gnu.org/licenses/
|
|
580
|
+
# ****************************************************************************
|
|
581
|
+
|
|
582
|
+
from functools import reduce
|
|
583
|
+
|
|
584
|
+
# IMPORTANT: Do *not* import matplotlib at module scope. It takes a
|
|
585
|
+
# surprisingly long time to initialize itself. It's better if it is
|
|
586
|
+
# imported in functions, so it only gets started if it is actually
|
|
587
|
+
# going to be used.
|
|
588
|
+
|
|
589
|
+
# DEFAULT_FIGSIZE=(6, 3.70820393249937)
|
|
590
|
+
import sage.misc.verbose
|
|
591
|
+
from sage.arith.srange import srange
|
|
592
|
+
|
|
593
|
+
from sage.misc.randstate import current_randstate # for plot adaptive refinement
|
|
594
|
+
from math import sin, cos, pi, log, exp # for polar_plot and log scaling
|
|
595
|
+
|
|
596
|
+
from sage.ext.fast_eval import fast_float, is_fast_float
|
|
597
|
+
from sage.structure.element import Expression
|
|
598
|
+
from sage.misc.decorators import options
|
|
599
|
+
|
|
600
|
+
from .graphics import Graphics
|
|
601
|
+
from .multigraphics import GraphicsArray, MultiGraphics
|
|
602
|
+
from sage.plot.polygon import polygon
|
|
603
|
+
|
|
604
|
+
# import of line2d below is only for redirection of imports
|
|
605
|
+
from sage.plot.line import line
|
|
606
|
+
from sage.misc.lazy_import import lazy_import
|
|
607
|
+
lazy_import('sage.plot.line', 'line2d', deprecation=28717)
|
|
608
|
+
|
|
609
|
+
# Currently not used - see comment immediately above about
|
|
610
|
+
# figure.canvas.mpl_connect('draw_event', pad_for_tick_labels)
|
|
611
|
+
# TODO - figure out how to use this, add documentation
|
|
612
|
+
# def pad_for_tick_labels(event):
|
|
613
|
+
# import matplotlib.transforms as mtransforms
|
|
614
|
+
# figure=event.canvas.figure
|
|
615
|
+
# bboxes = []
|
|
616
|
+
# for ax in figure.axes:
|
|
617
|
+
# bbox = ax.xaxis.get_label().get_window_extent()
|
|
618
|
+
# # the figure transform goes from relative coords->pixels and we
|
|
619
|
+
# # want the inverse of that
|
|
620
|
+
# bboxi = bbox.inverse_transformed(figure.transFigure)
|
|
621
|
+
# bboxes.append(bboxi)
|
|
622
|
+
#
|
|
623
|
+
# bbox = ax.yaxis.get_label().get_window_extent()
|
|
624
|
+
# bboxi = bbox.inverse_transformed(figure.transFigure)
|
|
625
|
+
# bboxes.append(bboxi)
|
|
626
|
+
# for label in (ax.get_xticklabels()+ax.get_yticklabels() \
|
|
627
|
+
# + ax.get_xticklabels(minor=True) \
|
|
628
|
+
# +ax.get_yticklabels(minor=True)):
|
|
629
|
+
# bbox = label.get_window_extent()
|
|
630
|
+
# bboxi = bbox.inverse_transformed(figure.transFigure)
|
|
631
|
+
# bboxes.append(bboxi)
|
|
632
|
+
#
|
|
633
|
+
# # this is the bbox that bounds all the bboxes, again in relative
|
|
634
|
+
# # figure coords
|
|
635
|
+
# bbox = mtransforms.Bbox.union(bboxes)
|
|
636
|
+
# adjusted=adjust_figure_to_contain_bbox(figure,bbox)
|
|
637
|
+
#
|
|
638
|
+
# if adjusted:
|
|
639
|
+
# figure.canvas.draw()
|
|
640
|
+
# return False
|
|
641
|
+
#
|
|
642
|
+
# Currently not used - see comment above about
|
|
643
|
+
# figure.canvas.mpl_connect('draw_event', pad_for_tick_labels)
|
|
644
|
+
# TODO - figure out how to use this, add documentation
|
|
645
|
+
# def adjust_figure_to_contain_bbox(fig, bbox, pad=1.1):
|
|
646
|
+
# """
|
|
647
|
+
# For each amount we are over (in axes coordinates), we adjust by over*pad
|
|
648
|
+
# to give ourselves a bit of padding.
|
|
649
|
+
# """
|
|
650
|
+
# left=fig.subplotpars.left
|
|
651
|
+
# bottom=fig.subplotpars.bottom
|
|
652
|
+
# right=fig.subplotpars.right
|
|
653
|
+
# top=fig.subplotpars.top
|
|
654
|
+
#
|
|
655
|
+
# adjusted=False
|
|
656
|
+
# if bbox.xmin<0:
|
|
657
|
+
# left-=bbox.xmin*pad
|
|
658
|
+
# adjusted=True
|
|
659
|
+
# if bbox.ymin<0:
|
|
660
|
+
# bottom-=bbox.ymin*pad
|
|
661
|
+
# adjusted=True
|
|
662
|
+
# if bbox.xmax>1:
|
|
663
|
+
# right-=(bbox.xmax-1)*pad
|
|
664
|
+
# adjusted=True
|
|
665
|
+
# if bbox.ymax>1:
|
|
666
|
+
# top-=(bbox.ymax-1)*pad
|
|
667
|
+
# adjusted=True
|
|
668
|
+
#
|
|
669
|
+
# if left<right and bottom<top:
|
|
670
|
+
# fig.subplots_adjust(left=left, bottom=bottom, right=right, top=top)
|
|
671
|
+
# return adjusted
|
|
672
|
+
# else:
|
|
673
|
+
# return False
|
|
674
|
+
|
|
675
|
+
_SelectiveFormatterClass = None
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
def SelectiveFormatter(formatter, skip_values):
|
|
679
|
+
"""
|
|
680
|
+
This matplotlib formatter selectively omits some tick values and
|
|
681
|
+
passes the rest on to a specified formatter.
|
|
682
|
+
|
|
683
|
+
EXAMPLES:
|
|
684
|
+
|
|
685
|
+
This example is almost straight from a matplotlib example.
|
|
686
|
+
|
|
687
|
+
::
|
|
688
|
+
|
|
689
|
+
sage: # needs numpy
|
|
690
|
+
sage: from sage.plot.plot import SelectiveFormatter
|
|
691
|
+
sage: import matplotlib.pyplot as plt
|
|
692
|
+
sage: import numpy
|
|
693
|
+
sage: fig = plt.figure()
|
|
694
|
+
sage: ax = fig.add_subplot(111)
|
|
695
|
+
sage: t = numpy.arange(0.0, 2.0, 0.01)
|
|
696
|
+
sage: s = numpy.sin(2*numpy.pi*t)
|
|
697
|
+
sage: p = ax.plot(t, s)
|
|
698
|
+
sage: formatter = SelectiveFormatter(ax.xaxis.get_major_formatter(),
|
|
699
|
+
....: skip_values=[0,1])
|
|
700
|
+
sage: ax.xaxis.set_major_formatter(formatter)
|
|
701
|
+
sage: import tempfile
|
|
702
|
+
sage: with tempfile.NamedTemporaryFile(suffix='.png') as f:
|
|
703
|
+
....: fig.savefig(f.name)
|
|
704
|
+
"""
|
|
705
|
+
global _SelectiveFormatterClass
|
|
706
|
+
if _SelectiveFormatterClass is None:
|
|
707
|
+
|
|
708
|
+
from matplotlib.ticker import Formatter
|
|
709
|
+
|
|
710
|
+
class _SelectiveFormatterClass(Formatter):
|
|
711
|
+
def __init__(self, formatter, skip_values):
|
|
712
|
+
"""
|
|
713
|
+
Initialize a SelectiveFormatter object.
|
|
714
|
+
|
|
715
|
+
INPUT:
|
|
716
|
+
|
|
717
|
+
- ``formatter`` -- the formatter object to which we should pass labels
|
|
718
|
+
|
|
719
|
+
- ``skip_values`` -- list of values that we should skip when
|
|
720
|
+
formatting the tick labels
|
|
721
|
+
|
|
722
|
+
EXAMPLES::
|
|
723
|
+
|
|
724
|
+
sage: # needs numpy
|
|
725
|
+
sage: from sage.plot.plot import SelectiveFormatter
|
|
726
|
+
sage: import matplotlib.pyplot as plt
|
|
727
|
+
sage: import numpy
|
|
728
|
+
sage: fig = plt.figure()
|
|
729
|
+
sage: ax = fig.add_subplot(111)
|
|
730
|
+
sage: t = numpy.arange(0.0, 2.0, 0.01)
|
|
731
|
+
sage: s = numpy.sin(2*numpy.pi*t)
|
|
732
|
+
sage: line = ax.plot(t, s)
|
|
733
|
+
sage: formatter = SelectiveFormatter(ax.xaxis.get_major_formatter(),
|
|
734
|
+
....: skip_values=[0,1])
|
|
735
|
+
sage: ax.xaxis.set_major_formatter(formatter)
|
|
736
|
+
sage: from tempfile import NamedTemporaryFile
|
|
737
|
+
sage: with NamedTemporaryFile(suffix='.png') as f:
|
|
738
|
+
....: fig.savefig(f.name)
|
|
739
|
+
"""
|
|
740
|
+
self.formatter = formatter
|
|
741
|
+
self.skip_values = skip_values
|
|
742
|
+
|
|
743
|
+
def set_locs(self, locs):
|
|
744
|
+
"""
|
|
745
|
+
Set the locations for the ticks that are not skipped.
|
|
746
|
+
|
|
747
|
+
EXAMPLES::
|
|
748
|
+
|
|
749
|
+
sage: from sage.plot.plot import SelectiveFormatter
|
|
750
|
+
sage: import matplotlib.ticker
|
|
751
|
+
sage: formatter = SelectiveFormatter(matplotlib.ticker.Formatter(),
|
|
752
|
+
....: skip_values=[0,200])
|
|
753
|
+
sage: formatter.set_locs([i*100 for i in range(10)])
|
|
754
|
+
"""
|
|
755
|
+
self.formatter.set_locs([l for l in locs if l not in self.skip_values])
|
|
756
|
+
|
|
757
|
+
def __call__(self, x, *args, **kwds):
|
|
758
|
+
"""
|
|
759
|
+
Return the format for tick val *x* at position *pos*.
|
|
760
|
+
|
|
761
|
+
EXAMPLES::
|
|
762
|
+
|
|
763
|
+
sage: from sage.plot.plot import SelectiveFormatter
|
|
764
|
+
sage: import matplotlib.ticker
|
|
765
|
+
sage: formatter = SelectiveFormatter(matplotlib.ticker.FixedFormatter(['a','b']),
|
|
766
|
+
....: skip_values=[0,2])
|
|
767
|
+
sage: [formatter(i,1) for i in range(10)]
|
|
768
|
+
['', 'b', '', 'b', 'b', 'b', 'b', 'b', 'b', 'b']
|
|
769
|
+
"""
|
|
770
|
+
if x in self.skip_values:
|
|
771
|
+
return ''
|
|
772
|
+
else:
|
|
773
|
+
return self.formatter(x, *args, **kwds)
|
|
774
|
+
|
|
775
|
+
return _SelectiveFormatterClass(formatter, skip_values)
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def xydata_from_point_list(points):
|
|
779
|
+
r"""
|
|
780
|
+
Return two lists (xdata, ydata), each coerced to a list of floats,
|
|
781
|
+
which correspond to the x-coordinates and the y-coordinates of the
|
|
782
|
+
points.
|
|
783
|
+
|
|
784
|
+
The points parameter can be a list of 2-tuples or some object that
|
|
785
|
+
yields a list of one or two numbers.
|
|
786
|
+
|
|
787
|
+
This function can potentially be very slow for large point sets.
|
|
788
|
+
|
|
789
|
+
TESTS::
|
|
790
|
+
|
|
791
|
+
sage: from sage.plot.plot import xydata_from_point_list
|
|
792
|
+
sage: xydata_from_point_list([CC(0), CC(1)]) # issue 8082
|
|
793
|
+
([0.0, 1.0], [0.0, 0.0])
|
|
794
|
+
|
|
795
|
+
This function should work for anything than can be turned into a
|
|
796
|
+
list, such as iterators and such (see :issue:`10478`)::
|
|
797
|
+
|
|
798
|
+
sage: xydata_from_point_list(iter([(0,0), (sqrt(3), 2)]))
|
|
799
|
+
([0.0, 1.7320508075688772], [0.0, 2.0])
|
|
800
|
+
sage: xydata_from_point_list((x, x^2) for x in range(5))
|
|
801
|
+
([0.0, 1.0, 2.0, 3.0, 4.0], [0.0, 1.0, 4.0, 9.0, 16.0])
|
|
802
|
+
sage: xydata_from_point_list(enumerate(prime_range(1, 15)))
|
|
803
|
+
([0.0, 1.0, 2.0, 3.0, 4.0, 5.0], [2.0, 3.0, 5.0, 7.0, 11.0, 13.0])
|
|
804
|
+
sage: from builtins import zip
|
|
805
|
+
sage: xydata_from_point_list(list(zip([2,3,5,7], [11, 13, 17, 19])))
|
|
806
|
+
([2.0, 3.0, 5.0, 7.0], [11.0, 13.0, 17.0, 19.0])
|
|
807
|
+
|
|
808
|
+
The code now accepts mixed lists of complex and real numbers::
|
|
809
|
+
|
|
810
|
+
sage: xydata_from_point_list(map(N,[0,1,1+I,I,I-1,-1,-1-I,-I,1-I]))
|
|
811
|
+
([0.0, 1.0, 1.0, 0.0, -1.0, -1.0, -1.0, 0.0, 1.0],
|
|
812
|
+
[0.0, 0.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0, -1.0])
|
|
813
|
+
sage: point2d([0, 1., CC(0,1)])
|
|
814
|
+
Graphics object consisting of 1 graphics primitive
|
|
815
|
+
sage: point2d((x^5-1).roots(multiplicities=False))
|
|
816
|
+
Graphics object consisting of 1 graphics primitive
|
|
817
|
+
"""
|
|
818
|
+
import numbers
|
|
819
|
+
zero = float(0)
|
|
820
|
+
|
|
821
|
+
xdata = []
|
|
822
|
+
ydata = []
|
|
823
|
+
for xy in points:
|
|
824
|
+
if isinstance(xy, Expression):
|
|
825
|
+
xy = xy.n()
|
|
826
|
+
if isinstance(xy, numbers.Real):
|
|
827
|
+
xdata.append(float(xy))
|
|
828
|
+
ydata.append(zero)
|
|
829
|
+
elif isinstance(xy, numbers.Complex):
|
|
830
|
+
xdata.append(float(xy.real()))
|
|
831
|
+
ydata.append(float(xy.imag()))
|
|
832
|
+
else:
|
|
833
|
+
try:
|
|
834
|
+
x, y = xy
|
|
835
|
+
except TypeError:
|
|
836
|
+
raise TypeError('invalid input for 2D point')
|
|
837
|
+
else:
|
|
838
|
+
xdata.append(float(x))
|
|
839
|
+
ydata.append(float(y))
|
|
840
|
+
return xdata, ydata
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
@options(alpha=1, thickness=1, fill=False, fillcolor='automatic',
|
|
844
|
+
fillalpha=0.5, plot_points=200, adaptive_tolerance=0.01,
|
|
845
|
+
adaptive_recursion=5, detect_poles=False, exclude=None,
|
|
846
|
+
legend_label=None, __original_opts=True,
|
|
847
|
+
aspect_ratio='automatic', imaginary_tolerance=1e-8)
|
|
848
|
+
def plot(funcs, *args, **kwds):
|
|
849
|
+
r"""
|
|
850
|
+
Use plot by writing.
|
|
851
|
+
|
|
852
|
+
``plot(X, ...)``
|
|
853
|
+
|
|
854
|
+
where `X` is a Sage object (or list of Sage objects) that
|
|
855
|
+
either is callable and returns numbers that can be coerced to
|
|
856
|
+
floats, or has a plot method that returns a
|
|
857
|
+
``GraphicPrimitive`` object.
|
|
858
|
+
|
|
859
|
+
There are many other specialized 2D plot commands available
|
|
860
|
+
in Sage, such as ``plot_slope_field``, as well as various
|
|
861
|
+
graphics primitives like :class:`~sage.plot.arrow.Arrow`;
|
|
862
|
+
type ``sage.plot.plot?`` for a current list.
|
|
863
|
+
|
|
864
|
+
Type ``plot.options`` for a dictionary of the default
|
|
865
|
+
options for plots. You can change this to change the defaults for
|
|
866
|
+
all future plots. Use ``plot.reset()`` to reset to the
|
|
867
|
+
default options.
|
|
868
|
+
|
|
869
|
+
PLOT OPTIONS:
|
|
870
|
+
|
|
871
|
+
- ``plot_points`` -- (default: 200) the minimal number of plot points
|
|
872
|
+
|
|
873
|
+
- ``adaptive_recursion`` -- (default: 5) how many levels of recursion to go
|
|
874
|
+
before giving up when doing adaptive refinement. Setting this to 0
|
|
875
|
+
disables adaptive refinement.
|
|
876
|
+
|
|
877
|
+
- ``adaptive_tolerance`` -- (default: 0.01) how large a difference should be
|
|
878
|
+
before the adaptive refinement code considers it significant. See the
|
|
879
|
+
documentation further below for more information, starting at "the
|
|
880
|
+
algorithm used to insert".
|
|
881
|
+
|
|
882
|
+
- ``imaginary_tolerance`` -- (default: ``1e-8``) if an imaginary
|
|
883
|
+
number arises (due, for example, to numerical issues), this
|
|
884
|
+
tolerance specifies how large it has to be in magnitude before
|
|
885
|
+
we raise an error. In other words, imaginary parts smaller than
|
|
886
|
+
this are ignored in your plot points.
|
|
887
|
+
|
|
888
|
+
- ``base`` -- (default: `10`) the base of the logarithm if
|
|
889
|
+
a logarithmic scale is set. This must be greater than 1. The base
|
|
890
|
+
can be also given as a list or tuple ``(basex, basey)``.
|
|
891
|
+
``basex`` sets the base of the logarithm along the horizontal
|
|
892
|
+
axis and ``basey`` sets the base along the vertical axis.
|
|
893
|
+
|
|
894
|
+
- ``scale`` -- string (default: ``'linear'``); scale of the axes.
|
|
895
|
+
Possible values are ``'linear'``, ``'loglog'``, ``'semilogx'``,
|
|
896
|
+
``'semilogy'``.
|
|
897
|
+
|
|
898
|
+
The scale can be also be given as single argument that is a list
|
|
899
|
+
or tuple ``(scale, base)`` or ``(scale, basex, basey)``.
|
|
900
|
+
|
|
901
|
+
The ``'loglog'`` scale sets both the horizontal and vertical axes to
|
|
902
|
+
logarithmic scale. The ``'semilogx'`` scale sets the horizontal axis
|
|
903
|
+
to logarithmic scale. The ``'semilogy'`` scale sets the vertical axis
|
|
904
|
+
to logarithmic scale. The ``'linear'`` scale is the default value
|
|
905
|
+
when :class:`~sage.plot.graphics.Graphics` is initialized.
|
|
906
|
+
|
|
907
|
+
- ``xmin`` -- starting x value in the rendered figure. This parameter is
|
|
908
|
+
passed directly to the ``show`` procedure and it could be overwritten.
|
|
909
|
+
|
|
910
|
+
- ``xmax`` -- ending x value in the rendered figure. This parameter is passed
|
|
911
|
+
directly to the ``show`` procedure and it could be overwritten.
|
|
912
|
+
|
|
913
|
+
- ``ymin`` -- starting y value in the rendered figure. This parameter is
|
|
914
|
+
passed directly to the ``show`` procedure and it could be overwritten.
|
|
915
|
+
|
|
916
|
+
- ``ymax`` -- ending y value in the rendered figure. This parameter is passed
|
|
917
|
+
directly to the ``show`` procedure and it could be overwritten.
|
|
918
|
+
|
|
919
|
+
- ``detect_poles`` -- boolean (default: ``False``); if set to ``True`` poles are detected.
|
|
920
|
+
If set to "show" vertical asymptotes are drawn.
|
|
921
|
+
|
|
922
|
+
- ``legend_label`` -- a (TeX) string serving as the label for `X` in the legend.
|
|
923
|
+
If `X` is a list, then this option can be a single string, or a list or dictionary
|
|
924
|
+
with strings as entries/values. If a dictionary, then keys are taken from ``range(len(X))``.
|
|
925
|
+
|
|
926
|
+
.. NOTE::
|
|
927
|
+
|
|
928
|
+
- If the ``scale`` is ``'linear'``, then irrespective of what
|
|
929
|
+
``base`` is set to, it will default to 10 and will remain unused.
|
|
930
|
+
|
|
931
|
+
- If you want to limit the plot along the horizontal axis in the
|
|
932
|
+
final rendered figure, then pass the ``xmin`` and ``xmax``
|
|
933
|
+
keywords to the :meth:`~sage.plot.graphics.Graphics.show` method.
|
|
934
|
+
To limit the plot along the vertical axis, ``ymin`` and ``ymax``
|
|
935
|
+
keywords can be provided to either this ``plot`` command or to
|
|
936
|
+
the ``show`` command.
|
|
937
|
+
|
|
938
|
+
- This function does NOT simply sample equally spaced points
|
|
939
|
+
between xmin and xmax. Instead it computes equally spaced points
|
|
940
|
+
and adds small perturbations to them. This reduces the possibility
|
|
941
|
+
of, e.g., sampling `\sin` only at multiples of `2\pi`, which would
|
|
942
|
+
yield a very misleading graph.
|
|
943
|
+
|
|
944
|
+
- If there is a range of consecutive points where the function has
|
|
945
|
+
no value, then those points will be excluded from the plot. See
|
|
946
|
+
the example below on automatic exclusion of points.
|
|
947
|
+
|
|
948
|
+
- For the other keyword options that the ``plot`` function can
|
|
949
|
+
take, refer to the method :meth:`~sage.plot.graphics.Graphics.show`
|
|
950
|
+
and the further options below.
|
|
951
|
+
|
|
952
|
+
COLOR OPTIONS:
|
|
953
|
+
|
|
954
|
+
- ``color`` -- (default: ``'blue'``) one of:
|
|
955
|
+
|
|
956
|
+
- an RGB tuple (r,g,b) with each of r,g,b between 0 and 1.
|
|
957
|
+
|
|
958
|
+
- a color name as a string (e.g., ``'purple'``).
|
|
959
|
+
|
|
960
|
+
- an HTML color such as '#aaff0b'.
|
|
961
|
+
|
|
962
|
+
- a list or dictionary of colors (valid only if `X` is a list):
|
|
963
|
+
if a dictionary, keys are taken from ``range(len(X))``;
|
|
964
|
+
the entries/values of the list/dictionary may be any of the options above.
|
|
965
|
+
|
|
966
|
+
- ``'automatic'`` -- maps to default ('blue') if `X` is a single Sage object; and
|
|
967
|
+
maps to a fixed sequence of regularly spaced colors if `X` is a list
|
|
968
|
+
|
|
969
|
+
- ``legend_color`` -- the color of the text for `X` (or each item in `X`) in the legend.
|
|
970
|
+
Default color is 'black'. Options are as in ``color`` above, except that the choice 'automatic' maps to 'black' if `X` is a single Sage object
|
|
971
|
+
|
|
972
|
+
- ``fillcolor`` -- the color of the fill for the plot of `X` (or each item in `X`).
|
|
973
|
+
Default color is 'gray' if `X` is a single Sage object or if ``color`` is a single color. Otherwise, options are as in ``color`` above
|
|
974
|
+
|
|
975
|
+
APPEARANCE OPTIONS:
|
|
976
|
+
|
|
977
|
+
The following options affect the appearance of
|
|
978
|
+
the line through the points on the graph of `X` (these are
|
|
979
|
+
the same as for the line function):
|
|
980
|
+
|
|
981
|
+
INPUT:
|
|
982
|
+
|
|
983
|
+
- ``alpha`` -- how transparent the line is
|
|
984
|
+
|
|
985
|
+
- ``thickness`` -- how thick the line is
|
|
986
|
+
|
|
987
|
+
- ``rgbcolor`` -- the color as an RGB tuple
|
|
988
|
+
|
|
989
|
+
- ``hue`` -- the color given as a hue
|
|
990
|
+
|
|
991
|
+
LINE OPTIONS:
|
|
992
|
+
|
|
993
|
+
Any MATPLOTLIB line option may also be passed in. E.g.,
|
|
994
|
+
|
|
995
|
+
- ``linestyle`` -- (default: ``'-'``) the style of the line, which is one of
|
|
996
|
+
|
|
997
|
+
- ``'-'`` or ``'solid'``
|
|
998
|
+
- ``'--'`` or ``'dashed'``
|
|
999
|
+
- ``'-.'`` or ``'dash dot'``
|
|
1000
|
+
- ``':'`` or ``'dotted'``
|
|
1001
|
+
- ``"None"`` or ``" "`` or ``""`` (nothing)
|
|
1002
|
+
- a list or dictionary (see below)
|
|
1003
|
+
|
|
1004
|
+
The linestyle can also be prefixed with a drawing style (e.g., ``'steps--'``)
|
|
1005
|
+
|
|
1006
|
+
- ``'default'`` (connect the points with straight lines)
|
|
1007
|
+
- ``'steps'`` or ``'steps-pre'`` (step function; horizontal
|
|
1008
|
+
line is to the left of point)
|
|
1009
|
+
- ``'steps-mid'`` (step function; points are in the middle of
|
|
1010
|
+
horizontal lines)
|
|
1011
|
+
- ``'steps-post'`` (step function; horizontal line is to the
|
|
1012
|
+
right of point)
|
|
1013
|
+
|
|
1014
|
+
If `X` is a list, then ``linestyle`` may be a list (with entries
|
|
1015
|
+
taken from the strings above) or a dictionary (with keys in ``range(len(X))``
|
|
1016
|
+
and values taken from the strings above).
|
|
1017
|
+
|
|
1018
|
+
- ``marker`` -- the style of the markers, which is one of
|
|
1019
|
+
|
|
1020
|
+
- ``"None"`` or ``" "`` or ``""`` (nothing) -- default
|
|
1021
|
+
- ``","`` (pixel), ``"."`` (point)
|
|
1022
|
+
- ``"_"`` (horizontal line), ``"|"`` (vertical line)
|
|
1023
|
+
- ``"o"`` (circle), ``"p"`` (pentagon), ``"s"`` (square), ``"x"`` (x), ``"+"`` (plus), ``"*"`` (star)
|
|
1024
|
+
- ``"D"`` (diamond), ``"d"`` (thin diamond)
|
|
1025
|
+
- ``"H"`` (hexagon), ``"h"`` (alternative hexagon)
|
|
1026
|
+
- ``"<"`` (triangle left), ``">"`` (triangle right), ``"^"`` (triangle up), ``"v"`` (triangle down)
|
|
1027
|
+
- ``"1"`` (tri down), ``"2"`` (tri up), ``"3"`` (tri left), ``"4"`` (tri right)
|
|
1028
|
+
- ``0`` (tick left), ``1`` (tick right), ``2`` (tick up), ``3`` (tick down)
|
|
1029
|
+
- ``4`` (caret left), ``5`` (caret right), ``6`` (caret up), ``7`` (caret down), ``8`` (octagon)
|
|
1030
|
+
- ``"$...$"`` (math TeX string)
|
|
1031
|
+
- ``(numsides, style, angle)`` to create a custom, regular symbol
|
|
1032
|
+
|
|
1033
|
+
- ``numsides`` -- the number of sides
|
|
1034
|
+
- ``style`` -- ``0`` (regular polygon), ``1`` (star shape), ``2`` (asterisk), ``3`` (circle)
|
|
1035
|
+
- ``angle`` -- the angular rotation in degrees
|
|
1036
|
+
|
|
1037
|
+
- ``markersize`` -- the size of the marker in points
|
|
1038
|
+
|
|
1039
|
+
- ``markeredgecolor`` -- the color of the marker edge
|
|
1040
|
+
|
|
1041
|
+
- ``markerfacecolor`` -- the color of the marker face
|
|
1042
|
+
|
|
1043
|
+
- ``markeredgewidth`` -- the size of the marker edge in points
|
|
1044
|
+
|
|
1045
|
+
- ``exclude`` -- (default: ``None``) values which are excluded from the plot range.
|
|
1046
|
+
Either a list of real numbers, or an equation in one variable.
|
|
1047
|
+
|
|
1048
|
+
FILLING OPTIONS:
|
|
1049
|
+
|
|
1050
|
+
- ``fill`` -- boolean (default: ``False``); one of:
|
|
1051
|
+
|
|
1052
|
+
- "axis" or ``True``: Fill the area between the function and the x-axis.
|
|
1053
|
+
|
|
1054
|
+
- "min": Fill the area between the function and its minimal value.
|
|
1055
|
+
|
|
1056
|
+
- "max": Fill the area between the function and its maximal value.
|
|
1057
|
+
|
|
1058
|
+
- a number c: Fill the area between the function and the horizontal line y = c.
|
|
1059
|
+
|
|
1060
|
+
- a function g: Fill the area between the function that is plotted and g.
|
|
1061
|
+
|
|
1062
|
+
- a dictionary ``d`` (only if a list of functions are plotted):
|
|
1063
|
+
The keys of the dictionary should be integers.
|
|
1064
|
+
The value of ``d[i]`` specifies the fill options for the i-th function
|
|
1065
|
+
in the list. If ``d[i] == [j]``: Fill the area between the i-th and
|
|
1066
|
+
the j-th function in the list. (But if ``d[i] == j``: Fill the area
|
|
1067
|
+
between the i-th function in the list and the horizontal line y = j.)
|
|
1068
|
+
|
|
1069
|
+
- ``fillalpha`` -- (default: 0.5) how transparent the fill is;
|
|
1070
|
+
a number between 0 and 1
|
|
1071
|
+
|
|
1072
|
+
MATPLOTLIB STYLE SHEET OPTION:
|
|
1073
|
+
|
|
1074
|
+
- ``stylesheet`` -- (default: classic) support for loading a full matplotlib style sheet.
|
|
1075
|
+
Any style sheet listed in ``matplotlib.pyplot.style.available`` is acceptable. If a
|
|
1076
|
+
non-existing style is provided the default classic is applied.
|
|
1077
|
+
|
|
1078
|
+
EXAMPLES:
|
|
1079
|
+
|
|
1080
|
+
We plot the `\sin` function::
|
|
1081
|
+
|
|
1082
|
+
sage: P = plot(sin, (0,10)); print(P)
|
|
1083
|
+
Graphics object consisting of 1 graphics primitive
|
|
1084
|
+
sage: len(P) # number of graphics primitives
|
|
1085
|
+
1
|
|
1086
|
+
sage: len(P[0]) # how many points were computed (random)
|
|
1087
|
+
225
|
|
1088
|
+
sage: P # render
|
|
1089
|
+
Graphics object consisting of 1 graphics primitive
|
|
1090
|
+
|
|
1091
|
+
.. PLOT::
|
|
1092
|
+
|
|
1093
|
+
P = plot(sin, (0,10))
|
|
1094
|
+
sphinx_plot(P)
|
|
1095
|
+
|
|
1096
|
+
::
|
|
1097
|
+
|
|
1098
|
+
sage: P = plot(sin, (0,10), plot_points=10); print(P)
|
|
1099
|
+
Graphics object consisting of 1 graphics primitive
|
|
1100
|
+
sage: len(P[0]) # random output
|
|
1101
|
+
32
|
|
1102
|
+
sage: P # render
|
|
1103
|
+
Graphics object consisting of 1 graphics primitive
|
|
1104
|
+
|
|
1105
|
+
.. PLOT::
|
|
1106
|
+
|
|
1107
|
+
P = plot(sin, (0,10), plot_points=10)
|
|
1108
|
+
sphinx_plot(P)
|
|
1109
|
+
|
|
1110
|
+
We plot with ``randomize=False``, which makes the initial sample points
|
|
1111
|
+
evenly spaced (hence always the same). Adaptive plotting might
|
|
1112
|
+
insert other points, however, unless ``adaptive_recursion=0``.
|
|
1113
|
+
|
|
1114
|
+
::
|
|
1115
|
+
|
|
1116
|
+
sage: p = plot(1, (x,0,3), plot_points=4, randomize=False, adaptive_recursion=0)
|
|
1117
|
+
sage: list(p[0])
|
|
1118
|
+
[(0.0, 1.0), (1.0, 1.0), (2.0, 1.0), (3.0, 1.0)]
|
|
1119
|
+
|
|
1120
|
+
Some colored functions::
|
|
1121
|
+
|
|
1122
|
+
sage: plot(sin, 0, 10, color='purple')
|
|
1123
|
+
Graphics object consisting of 1 graphics primitive
|
|
1124
|
+
|
|
1125
|
+
.. PLOT::
|
|
1126
|
+
|
|
1127
|
+
P=plot(sin,0,10,color='purple')
|
|
1128
|
+
sphinx_plot(P)
|
|
1129
|
+
|
|
1130
|
+
::
|
|
1131
|
+
|
|
1132
|
+
sage: plot(sin, 0, 10, color='#ff00ff')
|
|
1133
|
+
Graphics object consisting of 1 graphics primitive
|
|
1134
|
+
|
|
1135
|
+
.. PLOT::
|
|
1136
|
+
|
|
1137
|
+
P=plot(sin, 0, 10, color='#ff00ff')
|
|
1138
|
+
sphinx_plot(P)
|
|
1139
|
+
|
|
1140
|
+
We plot several functions together by passing a list of functions
|
|
1141
|
+
as input::
|
|
1142
|
+
|
|
1143
|
+
sage: plot([x*exp(-n*x^2)/.4 for n in [1..5]], (0, 2), aspect_ratio=.8)
|
|
1144
|
+
Graphics object consisting of 5 graphics primitives
|
|
1145
|
+
|
|
1146
|
+
.. PLOT::
|
|
1147
|
+
|
|
1148
|
+
g = plot([x*exp(-n*x**2)/.4 for n in range(1,6)], (0, 2), aspect_ratio=.8)
|
|
1149
|
+
sphinx_plot(g)
|
|
1150
|
+
|
|
1151
|
+
By default, color will change from one primitive to the next.
|
|
1152
|
+
This may be controlled by modifying ``color`` option::
|
|
1153
|
+
|
|
1154
|
+
sage: g1 = plot([x*exp(-n*x^2)/.4 for n in [1..3]], (0, 2),
|
|
1155
|
+
....: color='blue', aspect_ratio=.8); g1
|
|
1156
|
+
Graphics object consisting of 3 graphics primitives
|
|
1157
|
+
sage: g2 = plot([x*exp(-n*x^2)/.4 for n in [1..3]], (0, 2),
|
|
1158
|
+
....: color=['red','red','green'], linestyle=['-','--','-.'],
|
|
1159
|
+
....: aspect_ratio=.8); g2
|
|
1160
|
+
Graphics object consisting of 3 graphics primitives
|
|
1161
|
+
|
|
1162
|
+
.. PLOT::
|
|
1163
|
+
|
|
1164
|
+
g1 = plot([x*exp(-n*x**2)/.4 for n in range(1,4)], (0, 2), color='blue', aspect_ratio=.8)
|
|
1165
|
+
g2 = plot([x*exp(-n*x**2)/.4 for n in range(1,4)], (0, 2), color=['red','red','green'], linestyle=['-','--','-.'], aspect_ratio=.8)
|
|
1166
|
+
sphinx_plot(graphics_array([[g1], [g2]]))
|
|
1167
|
+
|
|
1168
|
+
While plotting real functions, imaginary numbers that are "almost
|
|
1169
|
+
real" will inevitably arise due to numerical issues. By tweaking
|
|
1170
|
+
the ``imaginary_tolerance``, you can decide how large of an
|
|
1171
|
+
imaginary part you're willing to sweep under the rug in order to
|
|
1172
|
+
plot the corresponding point. If a particular value's imaginary
|
|
1173
|
+
part has magnitude larger than ``imaginary_tolerance``, that point
|
|
1174
|
+
will not be plotted. The default tolerance is ``1e-8``, so the
|
|
1175
|
+
imaginary part in the first example below is ignored, but the
|
|
1176
|
+
second example "fails," emits a warning, and produces an empty
|
|
1177
|
+
graph::
|
|
1178
|
+
|
|
1179
|
+
sage: f = x + I*1e-12
|
|
1180
|
+
sage: plot(f, x, -1, 1)
|
|
1181
|
+
Graphics object consisting of 1 graphics primitive
|
|
1182
|
+
sage: plot(f, x, -1, 1, imaginary_tolerance=0)
|
|
1183
|
+
...WARNING: ...Unable to compute ...
|
|
1184
|
+
Graphics object consisting of 0 graphics primitives
|
|
1185
|
+
|
|
1186
|
+
We can also build a plot step by step from an empty plot::
|
|
1187
|
+
|
|
1188
|
+
sage: a = plot([]); a # passing an empty list returns an empty plot (Graphics() object)
|
|
1189
|
+
Graphics object consisting of 0 graphics primitives
|
|
1190
|
+
sage: a += plot(x**2); a # append another plot
|
|
1191
|
+
Graphics object consisting of 1 graphics primitive
|
|
1192
|
+
|
|
1193
|
+
.. PLOT::
|
|
1194
|
+
|
|
1195
|
+
a = plot([])
|
|
1196
|
+
a = a + plot(x**2)
|
|
1197
|
+
sphinx_plot(a)
|
|
1198
|
+
|
|
1199
|
+
::
|
|
1200
|
+
|
|
1201
|
+
sage: a += plot(x**3); a # append yet another plot
|
|
1202
|
+
Graphics object consisting of 2 graphics primitives
|
|
1203
|
+
|
|
1204
|
+
.. PLOT::
|
|
1205
|
+
|
|
1206
|
+
a = plot([])
|
|
1207
|
+
a = a + plot(x**2)
|
|
1208
|
+
a = a + plot(x**3)
|
|
1209
|
+
sphinx_plot(a)
|
|
1210
|
+
|
|
1211
|
+
The function `\sin(1/x)` wiggles wildly near `0`.
|
|
1212
|
+
Sage adapts to this and plots extra points near the origin.
|
|
1213
|
+
|
|
1214
|
+
::
|
|
1215
|
+
|
|
1216
|
+
sage: plot(sin(1/x), (x, -1, 1))
|
|
1217
|
+
Graphics object consisting of 1 graphics primitive
|
|
1218
|
+
|
|
1219
|
+
.. PLOT::
|
|
1220
|
+
|
|
1221
|
+
g = plot(sin(1/x), (x, -1, 1))
|
|
1222
|
+
sphinx_plot(g)
|
|
1223
|
+
|
|
1224
|
+
Via the matplotlib library, Sage makes it easy to tell whether
|
|
1225
|
+
a graph is on both sides of both axes, as the axes only cross
|
|
1226
|
+
if the origin is actually part of the viewing area::
|
|
1227
|
+
|
|
1228
|
+
sage: plot(x^3, (x,0,2)) # this one has the origin
|
|
1229
|
+
Graphics object consisting of 1 graphics primitive
|
|
1230
|
+
|
|
1231
|
+
.. PLOT::
|
|
1232
|
+
|
|
1233
|
+
g = plot(x**3,(x,0,2))
|
|
1234
|
+
sphinx_plot(g)
|
|
1235
|
+
|
|
1236
|
+
::
|
|
1237
|
+
|
|
1238
|
+
sage: plot(x^3, (x,1,2)) # this one does not
|
|
1239
|
+
Graphics object consisting of 1 graphics primitive
|
|
1240
|
+
|
|
1241
|
+
.. PLOT::
|
|
1242
|
+
|
|
1243
|
+
g = plot(x**3,(x,1,2))
|
|
1244
|
+
sphinx_plot(g)
|
|
1245
|
+
|
|
1246
|
+
Another thing to be aware of with axis labeling is that when
|
|
1247
|
+
the labels have quite different orders of magnitude or are very
|
|
1248
|
+
large, scientific notation (the `e` notation for powers of ten) is used::
|
|
1249
|
+
|
|
1250
|
+
sage: plot(x^2, (x,480,500)) # this one has no scientific notation
|
|
1251
|
+
Graphics object consisting of 1 graphics primitive
|
|
1252
|
+
|
|
1253
|
+
.. PLOT::
|
|
1254
|
+
|
|
1255
|
+
g = plot(x**2,(x,480,500))
|
|
1256
|
+
sphinx_plot(g)
|
|
1257
|
+
|
|
1258
|
+
::
|
|
1259
|
+
|
|
1260
|
+
sage: plot(x^2, (x,300,500)) # this one has scientific notation on y-axis
|
|
1261
|
+
Graphics object consisting of 1 graphics primitive
|
|
1262
|
+
|
|
1263
|
+
.. PLOT::
|
|
1264
|
+
|
|
1265
|
+
g = plot(x**2,(x,300,500))
|
|
1266
|
+
sphinx_plot(g)
|
|
1267
|
+
|
|
1268
|
+
You can put a legend with ``legend_label`` (the legend is only put
|
|
1269
|
+
once in the case of multiple functions)::
|
|
1270
|
+
|
|
1271
|
+
sage: plot(exp(x), 0, 2, legend_label='$e^x$')
|
|
1272
|
+
Graphics object consisting of 1 graphics primitive
|
|
1273
|
+
|
|
1274
|
+
.. PLOT::
|
|
1275
|
+
|
|
1276
|
+
g = plot(exp(x), 0, 2, legend_label='$e^x$')
|
|
1277
|
+
sphinx_plot(g)
|
|
1278
|
+
|
|
1279
|
+
Sage understands TeX, so these all are slightly different, and you can choose
|
|
1280
|
+
one based on your needs::
|
|
1281
|
+
|
|
1282
|
+
sage: plot(sin, legend_label='sin')
|
|
1283
|
+
Graphics object consisting of 1 graphics primitive
|
|
1284
|
+
|
|
1285
|
+
.. PLOT::
|
|
1286
|
+
|
|
1287
|
+
g = plot(sin,legend_label='sin')
|
|
1288
|
+
sphinx_plot(g)
|
|
1289
|
+
|
|
1290
|
+
::
|
|
1291
|
+
|
|
1292
|
+
sage: plot(sin, legend_label='$sin$')
|
|
1293
|
+
Graphics object consisting of 1 graphics primitive
|
|
1294
|
+
|
|
1295
|
+
.. PLOT::
|
|
1296
|
+
|
|
1297
|
+
g = plot(sin,legend_label='$sin$')
|
|
1298
|
+
sphinx_plot(g)
|
|
1299
|
+
|
|
1300
|
+
::
|
|
1301
|
+
|
|
1302
|
+
sage: plot(sin, legend_label=r'$\sin$')
|
|
1303
|
+
Graphics object consisting of 1 graphics primitive
|
|
1304
|
+
|
|
1305
|
+
.. PLOT::
|
|
1306
|
+
|
|
1307
|
+
g = plot(sin,legend_label=r'$\sin$')
|
|
1308
|
+
sphinx_plot(g)
|
|
1309
|
+
|
|
1310
|
+
It is possible to use a different color for the text of each label::
|
|
1311
|
+
|
|
1312
|
+
sage: p1 = plot(sin, legend_label='sin', legend_color='red')
|
|
1313
|
+
sage: p2 = plot(cos, legend_label='cos', legend_color='green')
|
|
1314
|
+
sage: p1 + p2
|
|
1315
|
+
Graphics object consisting of 2 graphics primitives
|
|
1316
|
+
|
|
1317
|
+
.. PLOT::
|
|
1318
|
+
|
|
1319
|
+
p1 = plot(sin, legend_label='sin', legend_color='red')
|
|
1320
|
+
p2 = plot(cos, legend_label='cos', legend_color='green')
|
|
1321
|
+
g = p1 + p2
|
|
1322
|
+
sphinx_plot(g)
|
|
1323
|
+
|
|
1324
|
+
Prior to :issue:`19485`, legends by default had a shadowless gray
|
|
1325
|
+
background. This behavior can be recovered by setting the legend
|
|
1326
|
+
options on your plot object::
|
|
1327
|
+
|
|
1328
|
+
sage: p = plot(sin(x), legend_label=r'$\sin(x)$')
|
|
1329
|
+
sage: p.set_legend_options(back_color=(0.9,0.9,0.9), shadow=False)
|
|
1330
|
+
|
|
1331
|
+
.. PLOT::
|
|
1332
|
+
|
|
1333
|
+
g = plot(sin(x), legend_label=r'$\sin(x)$')
|
|
1334
|
+
g.set_legend_options(back_color=(0.9,0.9,0.9), shadow=False)
|
|
1335
|
+
sphinx_plot(g)
|
|
1336
|
+
|
|
1337
|
+
If `X` is a list of Sage objects and ``legend_label`` is 'automatic', then Sage will
|
|
1338
|
+
create labels for each function according to their internal representation::
|
|
1339
|
+
|
|
1340
|
+
sage: plot([sin(x), tan(x), 1 - x^2], legend_label='automatic')
|
|
1341
|
+
Graphics object consisting of 3 graphics primitives
|
|
1342
|
+
|
|
1343
|
+
.. PLOT::
|
|
1344
|
+
|
|
1345
|
+
g = plot([sin(x), tan(x), 1-x**2], legend_label='automatic')
|
|
1346
|
+
sphinx_plot(g)
|
|
1347
|
+
|
|
1348
|
+
If ``legend_label`` is any single string other than 'automatic',
|
|
1349
|
+
then it is repeated for all members of `X`::
|
|
1350
|
+
|
|
1351
|
+
sage: plot([sin(x), tan(x)], color='blue', legend_label='trig')
|
|
1352
|
+
Graphics object consisting of 2 graphics primitives
|
|
1353
|
+
|
|
1354
|
+
.. PLOT::
|
|
1355
|
+
|
|
1356
|
+
g = plot([sin(x), tan(x)], color='blue', legend_label='trig')
|
|
1357
|
+
sphinx_plot(g)
|
|
1358
|
+
|
|
1359
|
+
Note that the independent variable may be omitted if there is no
|
|
1360
|
+
ambiguity::
|
|
1361
|
+
|
|
1362
|
+
sage: plot(sin(1.0/x), (-1, 1))
|
|
1363
|
+
Graphics object consisting of 1 graphics primitive
|
|
1364
|
+
|
|
1365
|
+
.. PLOT::
|
|
1366
|
+
|
|
1367
|
+
g = plot(sin(1.0/x), (-1, 1))
|
|
1368
|
+
sphinx_plot(g)
|
|
1369
|
+
|
|
1370
|
+
Plotting in logarithmic scale is possible for 2D plots. There
|
|
1371
|
+
are two different syntaxes supported::
|
|
1372
|
+
|
|
1373
|
+
sage: plot(exp, (1, 10), scale='semilogy') # log axis on vertical
|
|
1374
|
+
Graphics object consisting of 1 graphics primitive
|
|
1375
|
+
|
|
1376
|
+
.. PLOT::
|
|
1377
|
+
|
|
1378
|
+
g = plot(exp, (1, 10), scale='semilogy')
|
|
1379
|
+
sphinx_plot(g)
|
|
1380
|
+
|
|
1381
|
+
::
|
|
1382
|
+
|
|
1383
|
+
sage: plot_semilogy(exp, (1, 10)) # same thing
|
|
1384
|
+
Graphics object consisting of 1 graphics primitive
|
|
1385
|
+
|
|
1386
|
+
.. PLOT::
|
|
1387
|
+
|
|
1388
|
+
g = plot_semilogy(exp, (1, 10))
|
|
1389
|
+
sphinx_plot(g)
|
|
1390
|
+
|
|
1391
|
+
::
|
|
1392
|
+
|
|
1393
|
+
sage: plot_loglog(exp, (1, 10)) # both axes are log
|
|
1394
|
+
Graphics object consisting of 1 graphics primitive
|
|
1395
|
+
|
|
1396
|
+
.. PLOT::
|
|
1397
|
+
|
|
1398
|
+
g = plot_loglog(exp, (1, 10))
|
|
1399
|
+
sphinx_plot(g)
|
|
1400
|
+
|
|
1401
|
+
::
|
|
1402
|
+
|
|
1403
|
+
sage: plot(exp, (1, 10), scale='loglog', base=2) # base of log is 2 # long time
|
|
1404
|
+
Graphics object consisting of 1 graphics primitive
|
|
1405
|
+
|
|
1406
|
+
.. PLOT::
|
|
1407
|
+
|
|
1408
|
+
g = plot(exp, (1, 10), scale='loglog', base=2)
|
|
1409
|
+
sphinx_plot(g)
|
|
1410
|
+
|
|
1411
|
+
We can also change the scale of the axes in the graphics just before
|
|
1412
|
+
displaying::
|
|
1413
|
+
|
|
1414
|
+
sage: G = plot(exp, 1, 10) # long time
|
|
1415
|
+
sage: G.show(scale=('semilogy', 2)) # long time
|
|
1416
|
+
|
|
1417
|
+
The algorithm used to insert extra points is actually pretty
|
|
1418
|
+
simple. On the picture drawn by the lines below::
|
|
1419
|
+
|
|
1420
|
+
sage: p = plot(x^2, (-0.5, 1.4)) + line([(0,0), (1,1)], color='green')
|
|
1421
|
+
sage: p += line([(0.5, 0.5), (0.5, 0.5^2)], color='purple')
|
|
1422
|
+
sage: p += point(((0, 0), (0.5, 0.5), (0.5, 0.5^2), (1, 1)),
|
|
1423
|
+
....: color='red', pointsize=20)
|
|
1424
|
+
sage: p += text('A', (-0.05, 0.1), color='red')
|
|
1425
|
+
sage: p += text('B', (1.01, 1.1), color='red')
|
|
1426
|
+
sage: p += text('C', (0.48, 0.57), color='red')
|
|
1427
|
+
sage: p += text('D', (0.53, 0.18), color='red')
|
|
1428
|
+
sage: p.show(axes=False, xmin=-0.5, xmax=1.4, ymin=0, ymax=2)
|
|
1429
|
+
|
|
1430
|
+
.. PLOT::
|
|
1431
|
+
|
|
1432
|
+
g = plot(x**2, (-0.5, 1.4)) + line([(0,0), (1,1)], color='green')
|
|
1433
|
+
g = g + line([(0.5, 0.5), (0.5, 0.5**2)], color='purple')
|
|
1434
|
+
g = g + point(((0, 0), (0.5, 0.5), (0.5, 0.5**2), (1, 1)), color='red', pointsize=20)
|
|
1435
|
+
g = g + text('A', (-0.05, 0.1), color='red')
|
|
1436
|
+
g = g + text('B', (1.01, 1.1), color='red')
|
|
1437
|
+
g = g + text('C', (0.48, 0.57), color='red')
|
|
1438
|
+
g = g + text('D', (0.53, 0.18), color='red')
|
|
1439
|
+
g.axes(False)
|
|
1440
|
+
g.xmin(-0.5)
|
|
1441
|
+
g.xmax(1.4)
|
|
1442
|
+
g.ymin(0)
|
|
1443
|
+
g.ymax(2)
|
|
1444
|
+
sphinx_plot(g)
|
|
1445
|
+
|
|
1446
|
+
You have the function (in blue) and its approximation (in green)
|
|
1447
|
+
passing through the points A and B. The algorithm finds the
|
|
1448
|
+
midpoint C of AB and computes the distance between C and D. If that
|
|
1449
|
+
distance exceeds the ``adaptive_tolerance`` threshold (*relative* to
|
|
1450
|
+
the size of the initial plot subintervals), the point D is
|
|
1451
|
+
added to the curve. If D is added to the curve, then the
|
|
1452
|
+
algorithm is applied recursively to the points A and D, and D and
|
|
1453
|
+
B. It is repeated ``adaptive_recursion`` times (5, by default).
|
|
1454
|
+
|
|
1455
|
+
The actual sample points are slightly randomized, so the above
|
|
1456
|
+
plots may look slightly different each time you draw them.
|
|
1457
|
+
|
|
1458
|
+
We draw the graph of an elliptic curve as the union of graphs of 2
|
|
1459
|
+
functions.
|
|
1460
|
+
|
|
1461
|
+
::
|
|
1462
|
+
|
|
1463
|
+
sage: def h1(x): return abs(sqrt(x^3 - 1))
|
|
1464
|
+
sage: def h2(x): return -abs(sqrt(x^3 - 1))
|
|
1465
|
+
sage: P = plot([h1, h2], 1,4)
|
|
1466
|
+
sage: P # show the result
|
|
1467
|
+
Graphics object consisting of 2 graphics primitives
|
|
1468
|
+
|
|
1469
|
+
.. PLOT::
|
|
1470
|
+
|
|
1471
|
+
def h1(x): return abs(sqrt(x**3 - 1))
|
|
1472
|
+
def h2(x): return -abs(sqrt(x**3 - 1))
|
|
1473
|
+
P = plot([h1, h2], 1,4)
|
|
1474
|
+
sphinx_plot(P)
|
|
1475
|
+
|
|
1476
|
+
It is important to mention that when we draw several graphs at the same time,
|
|
1477
|
+
parameters ``xmin``, ``xmax``, ``ymin`` and ``ymax`` are just passed directly
|
|
1478
|
+
to the ``show`` procedure. In fact, these parameters would be overwritten::
|
|
1479
|
+
|
|
1480
|
+
sage: p=plot(x^3, x, xmin=-1, xmax=1,ymin=-1, ymax=1)
|
|
1481
|
+
sage: q=plot(exp(x), x, xmin=-2, xmax=2, ymin=0, ymax=4)
|
|
1482
|
+
sage: (p+q).show()
|
|
1483
|
+
|
|
1484
|
+
As a workaround, we can perform the trick::
|
|
1485
|
+
|
|
1486
|
+
sage: p1 = line([(a,b) for a, b in zip(p[0].xdata, p[0].ydata)
|
|
1487
|
+
....: if b>=-1 and b<=1])
|
|
1488
|
+
sage: q1 = line([(a,b) for a, b in zip(q[0].xdata, q[0].ydata)
|
|
1489
|
+
....: if b>=0 and b<=4])
|
|
1490
|
+
sage: (p1 + q1).show()
|
|
1491
|
+
|
|
1492
|
+
We can also directly plot the elliptic curve::
|
|
1493
|
+
|
|
1494
|
+
sage: E = EllipticCurve([0,-1]) # needs sage.schemes
|
|
1495
|
+
sage: plot(E, (1, 4), color=hue(0.6)) # needs sage.schemes
|
|
1496
|
+
Graphics object consisting of 1 graphics primitive
|
|
1497
|
+
|
|
1498
|
+
.. PLOT::
|
|
1499
|
+
|
|
1500
|
+
E = EllipticCurve([0,-1])
|
|
1501
|
+
g = plot(E, (1, 4), color=hue(0.6))
|
|
1502
|
+
sphinx_plot(g)
|
|
1503
|
+
|
|
1504
|
+
We can change the line style as well::
|
|
1505
|
+
|
|
1506
|
+
sage: plot(sin(x), (x, 0, 10), linestyle='-.')
|
|
1507
|
+
Graphics object consisting of 1 graphics primitive
|
|
1508
|
+
|
|
1509
|
+
.. PLOT::
|
|
1510
|
+
|
|
1511
|
+
g = plot(sin(x), (x, 0, 10), linestyle='-.')
|
|
1512
|
+
sphinx_plot(g)
|
|
1513
|
+
|
|
1514
|
+
If we have an empty linestyle and specify a marker, we can see the
|
|
1515
|
+
points that are actually being plotted::
|
|
1516
|
+
|
|
1517
|
+
sage: plot(sin(x), (x,0,10), plot_points=20, linestyle='', marker='.')
|
|
1518
|
+
Graphics object consisting of 1 graphics primitive
|
|
1519
|
+
|
|
1520
|
+
.. PLOT::
|
|
1521
|
+
|
|
1522
|
+
g = plot(sin(x), (x, 0, 10), plot_points=20, linestyle='', marker='.')
|
|
1523
|
+
sphinx_plot(g)
|
|
1524
|
+
|
|
1525
|
+
The marker can be a TeX symbol as well::
|
|
1526
|
+
|
|
1527
|
+
sage: plot(sin(x), (x, 0, 10), plot_points=20, linestyle='', marker=r'$\checkmark$')
|
|
1528
|
+
Graphics object consisting of 1 graphics primitive
|
|
1529
|
+
|
|
1530
|
+
.. PLOT::
|
|
1531
|
+
|
|
1532
|
+
g = plot(sin(x), (x, 0, 10), plot_points=20, linestyle='', marker=r'$\checkmark$')
|
|
1533
|
+
sphinx_plot(g)
|
|
1534
|
+
|
|
1535
|
+
Sage currently ignores points that cannot be evaluated
|
|
1536
|
+
|
|
1537
|
+
::
|
|
1538
|
+
|
|
1539
|
+
sage: from sage.misc.verbose import set_verbose
|
|
1540
|
+
sage: set_verbose(-1)
|
|
1541
|
+
sage: plot(-x*log(x), (x, 0, 1)) # this works fine since the failed endpoint is just skipped.
|
|
1542
|
+
Graphics object consisting of 1 graphics primitive
|
|
1543
|
+
sage: set_verbose(0)
|
|
1544
|
+
|
|
1545
|
+
This prints out a warning and plots where it can (we turn off the
|
|
1546
|
+
warning by setting the verbose mode temporarily to -1.)
|
|
1547
|
+
|
|
1548
|
+
::
|
|
1549
|
+
|
|
1550
|
+
sage: set_verbose(-1)
|
|
1551
|
+
sage: plot(x^(1/3), (x, -1, 1))
|
|
1552
|
+
Graphics object consisting of 1 graphics primitive
|
|
1553
|
+
sage: set_verbose(0)
|
|
1554
|
+
|
|
1555
|
+
.. PLOT::
|
|
1556
|
+
|
|
1557
|
+
set_verbose(-1)
|
|
1558
|
+
g = plot(x**(1.0/3.0), (x, -1, 1))
|
|
1559
|
+
sphinx_plot(g)
|
|
1560
|
+
set_verbose(0)
|
|
1561
|
+
|
|
1562
|
+
Plotting the real cube root function for negative input requires avoiding
|
|
1563
|
+
the complex numbers one would usually get. The easiest way is to use
|
|
1564
|
+
:class:`real_nth_root(x, n)<sage.functions.other.Function_real_nth_root>` ::
|
|
1565
|
+
|
|
1566
|
+
sage: plot(real_nth_root(x, 3), (x, -1, 1))
|
|
1567
|
+
Graphics object consisting of 1 graphics primitive
|
|
1568
|
+
|
|
1569
|
+
.. PLOT::
|
|
1570
|
+
|
|
1571
|
+
g = plot(real_nth_root(x, 3), (x, -1, 1))
|
|
1572
|
+
sphinx_plot(g)
|
|
1573
|
+
|
|
1574
|
+
We can also get the same plot in the following way::
|
|
1575
|
+
|
|
1576
|
+
sage: plot(sign(x)*abs(x)^(1/3), (x, -1, 1))
|
|
1577
|
+
Graphics object consisting of 1 graphics primitive
|
|
1578
|
+
|
|
1579
|
+
.. PLOT::
|
|
1580
|
+
|
|
1581
|
+
g = plot(sign(x)*abs(x)**(1./3.), (x, -1, 1))
|
|
1582
|
+
sphinx_plot(g)
|
|
1583
|
+
|
|
1584
|
+
A way to plot other functions without symbolic variants is to use lambda
|
|
1585
|
+
functions::
|
|
1586
|
+
|
|
1587
|
+
sage: plot(lambda x : RR(x).nth_root(3), (x,-1, 1))
|
|
1588
|
+
Graphics object consisting of 1 graphics primitive
|
|
1589
|
+
|
|
1590
|
+
.. PLOT::
|
|
1591
|
+
|
|
1592
|
+
sphinx_plot(plot(lambda x : RR(x).nth_root(3), (x,-1, 1)))
|
|
1593
|
+
|
|
1594
|
+
We can detect the poles of a function::
|
|
1595
|
+
|
|
1596
|
+
sage: plot(gamma, (-3, 4), detect_poles=True).show(ymin=-5, ymax=5)
|
|
1597
|
+
|
|
1598
|
+
.. PLOT::
|
|
1599
|
+
|
|
1600
|
+
g = plot(gamma, (-3, 4), detect_poles=True)
|
|
1601
|
+
g.ymin(-5)
|
|
1602
|
+
g.ymax(5)
|
|
1603
|
+
sphinx_plot(g)
|
|
1604
|
+
|
|
1605
|
+
We draw the Gamma-Function with its poles highlighted::
|
|
1606
|
+
|
|
1607
|
+
sage: plot(gamma, (-3, 4), detect_poles='show').show(ymin=-5, ymax=5)
|
|
1608
|
+
|
|
1609
|
+
.. PLOT::
|
|
1610
|
+
|
|
1611
|
+
g = plot(gamma, (-3, 4), detect_poles='show')
|
|
1612
|
+
g.ymin(-5)
|
|
1613
|
+
g.ymax(5)
|
|
1614
|
+
sphinx_plot(g)
|
|
1615
|
+
|
|
1616
|
+
The basic options for filling a plot::
|
|
1617
|
+
|
|
1618
|
+
sage: p1 = plot(sin(x), -pi, pi, fill='axis')
|
|
1619
|
+
sage: p2 = plot(sin(x), -pi, pi, fill='min', fillalpha=1)
|
|
1620
|
+
sage: p3 = plot(sin(x), -pi, pi, fill='max')
|
|
1621
|
+
sage: p4 = plot(sin(x), -pi, pi, fill=(1-x)/3,
|
|
1622
|
+
....: fillcolor='blue', fillalpha=.2)
|
|
1623
|
+
sage: graphics_array([[p1, p2], # long time
|
|
1624
|
+
....: [p3, p4]]).show(frame=True, axes=False)
|
|
1625
|
+
|
|
1626
|
+
.. PLOT::
|
|
1627
|
+
|
|
1628
|
+
p1 = plot(sin(x), -pi, pi, fill='axis'); print(p1)
|
|
1629
|
+
p2 = plot(sin(x), -pi, pi, fill='min', fillalpha=1)
|
|
1630
|
+
p3 = plot(sin(x), -pi, pi, fill='max')
|
|
1631
|
+
p4 = plot(sin(x), -pi, pi, fill=(1-x)/3, fillcolor='blue', fillalpha=.2)
|
|
1632
|
+
g = graphics_array([[p1, p2], [p3, p4]])
|
|
1633
|
+
sphinx_plot(g, frame=True, axes=False)
|
|
1634
|
+
|
|
1635
|
+
The basic options for filling a list of plots::
|
|
1636
|
+
|
|
1637
|
+
sage: (f1, f2) = x*exp(-1*x^2)/.35, x*exp(-2*x^2)/.35
|
|
1638
|
+
sage: p1 = plot([f1, f2], -pi, pi, fill={1: [0]},
|
|
1639
|
+
....: fillcolor='blue', fillalpha=.25, color='blue')
|
|
1640
|
+
sage: p2 = plot([f1, f2], -pi, pi, fill={0: x/3, 1:[0]}, color=['blue'])
|
|
1641
|
+
sage: p3 = plot([f1, f2], -pi, pi, fill=[0, [0]],
|
|
1642
|
+
....: fillcolor=['orange','red'], fillalpha=1, color={1: 'blue'})
|
|
1643
|
+
sage: p4 = plot([f1, f2], (x,-pi, pi), fill=[x/3, 0],
|
|
1644
|
+
....: fillcolor=['grey'], color=['red', 'blue'])
|
|
1645
|
+
sage: graphics_array([[p1, p2], # long time
|
|
1646
|
+
....: [p3, p4]]).show(frame=True, axes=False)
|
|
1647
|
+
|
|
1648
|
+
.. PLOT::
|
|
1649
|
+
|
|
1650
|
+
(f1, f2) = x*exp(-1*x**2)/.35, x*exp(-2*x**2)/.35
|
|
1651
|
+
p1 = plot([f1, f2], -pi, pi, fill={1: [0]}, fillcolor='blue', fillalpha=.25, color='blue')
|
|
1652
|
+
p2 = plot([f1, f2], -pi, pi, fill={0: x/3, 1:[0]}, color=['blue'])
|
|
1653
|
+
p3 = plot([f1, f2], -pi, pi, fill=[0, [0]], fillcolor=['orange','red'], fillalpha=1, color={1: 'blue'})
|
|
1654
|
+
p4 = plot([f1, f2], (x,-pi, pi), fill=[x/3, 0], fillcolor=['grey'], color=['red', 'blue'])
|
|
1655
|
+
g = graphics_array([[p1, p2], [p3, p4]])
|
|
1656
|
+
sphinx_plot(g, frame=True, axes=False)
|
|
1657
|
+
|
|
1658
|
+
A example about the growth of prime numbers::
|
|
1659
|
+
|
|
1660
|
+
sage: plot(1.13*log(x), 1, 100,
|
|
1661
|
+
....: fill=lambda x: nth_prime(x)/floor(x), fillcolor='red')
|
|
1662
|
+
Graphics object consisting of 2 graphics primitives
|
|
1663
|
+
|
|
1664
|
+
.. PLOT::
|
|
1665
|
+
|
|
1666
|
+
sphinx_plot(plot(1.13*log(x), 1, 100, fill=lambda x: nth_prime(x)/floor(x), fillcolor='red'))
|
|
1667
|
+
|
|
1668
|
+
Fill the area between a function and its asymptote::
|
|
1669
|
+
|
|
1670
|
+
sage: f = (2*x^3+2*x-1)/((x-2)*(x+1))
|
|
1671
|
+
sage: plot([f, 2*x+2], -7, 7, fill={0: [1]}, fillcolor='#ccc').show(ymin=-20, ymax=20)
|
|
1672
|
+
|
|
1673
|
+
.. PLOT::
|
|
1674
|
+
|
|
1675
|
+
f = (2*x**3+2*x-1)/((x-2)*(x+1))
|
|
1676
|
+
g = plot([f, 2*x+2], -7,7, fill={0: [1]}, fillcolor='#ccc')
|
|
1677
|
+
g.ymin(-20)
|
|
1678
|
+
g.ymax(20)
|
|
1679
|
+
sphinx_plot(g)
|
|
1680
|
+
|
|
1681
|
+
Fill the area between a list of functions and the x-axis::
|
|
1682
|
+
|
|
1683
|
+
sage: def b(n): return lambda x: bessel_J(n, x)
|
|
1684
|
+
sage: plot([b(n) for n in [1..5]], 0, 20, fill='axis')
|
|
1685
|
+
Graphics object consisting of 10 graphics primitives
|
|
1686
|
+
|
|
1687
|
+
.. PLOT::
|
|
1688
|
+
|
|
1689
|
+
def b(n): return lambda x: bessel_J(n, x)
|
|
1690
|
+
g = plot([b(n) for n in range(1,6)], 0, 20, fill='axis')
|
|
1691
|
+
sphinx_plot(g)
|
|
1692
|
+
|
|
1693
|
+
Note that to fill between the ith and jth functions, you must use
|
|
1694
|
+
the dictionary key-value syntax ``i:[j]``; using key-value pairs
|
|
1695
|
+
like ``i:j`` will fill between the ith function and the line y=j::
|
|
1696
|
+
|
|
1697
|
+
sage: def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1)
|
|
1698
|
+
sage: plot([b(c) for c in [1..5]], 0, 20, fill={i: [i-1] for i in [1..4]},
|
|
1699
|
+
....: color={i: 'blue' for i in [1..5]}, aspect_ratio=3, ymax=3)
|
|
1700
|
+
Graphics object consisting of 9 graphics primitives
|
|
1701
|
+
sage: plot([b(c) for c in [1..5]], 0, 20, fill={i: i-1 for i in [1..4]}, # long time
|
|
1702
|
+
....: color='blue', aspect_ratio=3)
|
|
1703
|
+
Graphics object consisting of 9 graphics primitives
|
|
1704
|
+
|
|
1705
|
+
.. PLOT::
|
|
1706
|
+
|
|
1707
|
+
def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1)
|
|
1708
|
+
g1 = plot([b(n) for n in range(1,6)], 0, 20, fill={i:[i-1] for i in range(1,5)}, color={i:'blue' for i in range(1,6)}, aspect_ratio=3)
|
|
1709
|
+
g2 = plot([b(n) for n in range(1,6)], 0, 20, fill={i:i-1 for i in range(1,5)}, color='blue', aspect_ratio=3)
|
|
1710
|
+
g1.ymax(3)
|
|
1711
|
+
g = graphics_array([[g1], [g2]])
|
|
1712
|
+
sphinx_plot(g)
|
|
1713
|
+
|
|
1714
|
+
Extra options will get passed on to :meth:`~sage.plot.graphics.Graphics.show`,
|
|
1715
|
+
as long as they are valid::
|
|
1716
|
+
|
|
1717
|
+
sage: plot(sin(x^2), (x, -3, 3), # These labels will be nicely typeset
|
|
1718
|
+
....: title=r'Plot of $\sin(x^2)$', axes_labels=['$x$','$y$'])
|
|
1719
|
+
Graphics object consisting of 1 graphics primitive
|
|
1720
|
+
|
|
1721
|
+
.. PLOT::
|
|
1722
|
+
|
|
1723
|
+
g = plot(sin(x**2), (x, -3, 3), title=r'Plot of $\sin(x^2)$', axes_labels=['$x$','$y$'])
|
|
1724
|
+
sphinx_plot(g)
|
|
1725
|
+
|
|
1726
|
+
::
|
|
1727
|
+
|
|
1728
|
+
sage: plot(sin(x^2), (x, -3, 3), # These will not
|
|
1729
|
+
....: title='Plot of sin(x^2)', axes_labels=['x','y'])
|
|
1730
|
+
Graphics object consisting of 1 graphics primitive
|
|
1731
|
+
|
|
1732
|
+
.. PLOT::
|
|
1733
|
+
|
|
1734
|
+
g = plot(sin(x**2), (x, -3, 3), title='Plot of sin(x^2)', axes_labels=['x','y'])
|
|
1735
|
+
sphinx_plot(g)
|
|
1736
|
+
|
|
1737
|
+
::
|
|
1738
|
+
|
|
1739
|
+
sage: plot(sin(x^2), (x, -3, 3), # Large axes labels (w.r.t. the tick marks)
|
|
1740
|
+
....: axes_labels=['x','y'], axes_labels_size=2.5)
|
|
1741
|
+
Graphics object consisting of 1 graphics primitive
|
|
1742
|
+
|
|
1743
|
+
.. PLOT::
|
|
1744
|
+
|
|
1745
|
+
g = plot(sin(x**2), (x, -3, 3), axes_labels=['x','y'], axes_labels_size=2.5)
|
|
1746
|
+
sphinx_plot(g)
|
|
1747
|
+
|
|
1748
|
+
::
|
|
1749
|
+
|
|
1750
|
+
sage: plot(sin(x^2), (x, -3, 3), figsize=[8,2])
|
|
1751
|
+
Graphics object consisting of 1 graphics primitive
|
|
1752
|
+
sage: plot(sin(x^2), (x, -3, 3)).show(figsize=[8,2]) # These are equivalent
|
|
1753
|
+
|
|
1754
|
+
.. PLOT::
|
|
1755
|
+
|
|
1756
|
+
g = plot(sin(x**2), (x, -3, 3), figsize=[8,2])
|
|
1757
|
+
sphinx_plot(g)
|
|
1758
|
+
|
|
1759
|
+
This includes options for custom ticks and formatting. See documentation
|
|
1760
|
+
for :meth:`show` for more details.
|
|
1761
|
+
|
|
1762
|
+
::
|
|
1763
|
+
|
|
1764
|
+
sage: plot(sin(pi*x), (x, -8, 8), ticks=[[-7,-3,0,3,7], [-1/2,0,1/2]])
|
|
1765
|
+
Graphics object consisting of 1 graphics primitive
|
|
1766
|
+
|
|
1767
|
+
.. PLOT::
|
|
1768
|
+
|
|
1769
|
+
g = plot(sin(pi*x), (x, -8, 8), ticks=[[-7,-3,0,3,7], [-1/2,0,1/2]])
|
|
1770
|
+
sphinx_plot(g)
|
|
1771
|
+
|
|
1772
|
+
::
|
|
1773
|
+
|
|
1774
|
+
sage: plot(2*x + 1, (x, 0, 5),
|
|
1775
|
+
....: ticks=[[0, 1, e, pi, sqrt(20)],
|
|
1776
|
+
....: [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]],
|
|
1777
|
+
....: tick_formatter='latex')
|
|
1778
|
+
Graphics object consisting of 1 graphics primitive
|
|
1779
|
+
|
|
1780
|
+
.. PLOT::
|
|
1781
|
+
|
|
1782
|
+
g = plot(2*x + 1, (x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter='latex')
|
|
1783
|
+
sphinx_plot(g)
|
|
1784
|
+
|
|
1785
|
+
This is particularly useful when setting custom ticks in multiples of `\pi`.
|
|
1786
|
+
|
|
1787
|
+
::
|
|
1788
|
+
|
|
1789
|
+
sage: plot(sin(x), (x,0,2*pi), ticks=pi/3, tick_formatter=pi)
|
|
1790
|
+
Graphics object consisting of 1 graphics primitive
|
|
1791
|
+
|
|
1792
|
+
.. PLOT::
|
|
1793
|
+
|
|
1794
|
+
g = plot(sin(x),(x,0,2*pi),ticks=pi/3,tick_formatter=pi)
|
|
1795
|
+
sphinx_plot(g)
|
|
1796
|
+
|
|
1797
|
+
You can even have custom tick labels along with custom positioning. ::
|
|
1798
|
+
|
|
1799
|
+
sage: plot(x**2, (x,0,3), ticks=[[1,2.5], [0.5,1,2]],
|
|
1800
|
+
....: tick_formatter=[["$x_1$","$x_2$"],["$y_1$","$y_2$","$y_3$"]])
|
|
1801
|
+
Graphics object consisting of 1 graphics primitive
|
|
1802
|
+
|
|
1803
|
+
.. PLOT::
|
|
1804
|
+
|
|
1805
|
+
g = plot(x**2, (x,0,3), ticks=[[1,2.5],[0.5,1,2]], tick_formatter=[["$x_1$","$x_2$"], ["$y_1$","$y_2$","$y_3$"]])
|
|
1806
|
+
sphinx_plot(g)
|
|
1807
|
+
|
|
1808
|
+
You can force Type 1 fonts in your figures by providing the relevant
|
|
1809
|
+
option as shown below. This also requires that LaTeX, dvipng and
|
|
1810
|
+
Ghostscript be installed::
|
|
1811
|
+
|
|
1812
|
+
sage: plot(x, typeset='type1') # optional - latex
|
|
1813
|
+
Graphics object consisting of 1 graphics primitive
|
|
1814
|
+
|
|
1815
|
+
A example with excluded values::
|
|
1816
|
+
|
|
1817
|
+
sage: plot(floor(x), (x, 1, 10), exclude=[1..10])
|
|
1818
|
+
Graphics object consisting of 11 graphics primitives
|
|
1819
|
+
|
|
1820
|
+
.. PLOT::
|
|
1821
|
+
|
|
1822
|
+
g = plot(floor(x), (x, 1, 10), exclude=list(range(1,11)))
|
|
1823
|
+
sphinx_plot(g)
|
|
1824
|
+
|
|
1825
|
+
We exclude all points where :class:`~sage.functions.prime_pi.PrimePi`
|
|
1826
|
+
makes a jump::
|
|
1827
|
+
|
|
1828
|
+
sage: jumps = [n for n in [1..100] if prime_pi(n) != prime_pi(n-1)]
|
|
1829
|
+
sage: plot(lambda x: prime_pi(x), (x, 1, 100), exclude=jumps)
|
|
1830
|
+
Graphics object consisting of 26 graphics primitives
|
|
1831
|
+
|
|
1832
|
+
.. PLOT::
|
|
1833
|
+
|
|
1834
|
+
#jumps = [n for n in [1..100] if prime_pi(n) != prime_pi(n-1)]
|
|
1835
|
+
#syntaxError: invalid syntax, so we need more code
|
|
1836
|
+
jumps=list()
|
|
1837
|
+
for n in range(1,101):
|
|
1838
|
+
if prime_pi(n) != prime_pi(n-1):
|
|
1839
|
+
jumps.append(n)
|
|
1840
|
+
g = plot(lambda x: prime_pi(x), (x, 1, 100), exclude=jumps)
|
|
1841
|
+
sphinx_plot(g)
|
|
1842
|
+
|
|
1843
|
+
Excluded points can also be given by an equation::
|
|
1844
|
+
|
|
1845
|
+
sage: g(x) = x^2 - 2*x - 2
|
|
1846
|
+
sage: plot(1/g(x), (x, -3, 4), exclude=g(x)==0, ymin=-5, ymax=5) # long time
|
|
1847
|
+
Graphics object consisting of 3 graphics primitives
|
|
1848
|
+
|
|
1849
|
+
.. PLOT::
|
|
1850
|
+
|
|
1851
|
+
def g(x): return x**2-2*x-2
|
|
1852
|
+
G = plot(1/g(x), (x, -3, 4), exclude=g(x)==0, ymin=-5, ymax=5)
|
|
1853
|
+
sphinx_plot(G)
|
|
1854
|
+
|
|
1855
|
+
``exclude`` and ``detect_poles`` can be used together::
|
|
1856
|
+
|
|
1857
|
+
sage: f(x) = (floor(x)+0.5) / (1-(x-0.5)^2)
|
|
1858
|
+
sage: plot(f, (x, -3.5, 3.5), detect_poles='show', exclude=[-3..3],
|
|
1859
|
+
....: ymin=-5, ymax=5)
|
|
1860
|
+
Graphics object consisting of 12 graphics primitives
|
|
1861
|
+
|
|
1862
|
+
.. PLOT::
|
|
1863
|
+
|
|
1864
|
+
def f(x): return (floor(x)+0.5) / (1-(x-0.5)**2)
|
|
1865
|
+
g = plot(f, (x, -3.5, 3.5), detect_poles='show', exclude=list(range(-3,4)), ymin=-5, ymax=5)
|
|
1866
|
+
sphinx_plot(g)
|
|
1867
|
+
|
|
1868
|
+
Regions in which the plot has no values are automatically excluded. The
|
|
1869
|
+
regions thus excluded are in addition to the exclusion points present
|
|
1870
|
+
in the ``exclude`` keyword argument.::
|
|
1871
|
+
|
|
1872
|
+
sage: set_verbose(-1)
|
|
1873
|
+
sage: plot(arcsec, (x, -2, 2)) # [-1, 1] is excluded automatically
|
|
1874
|
+
Graphics object consisting of 2 graphics primitives
|
|
1875
|
+
|
|
1876
|
+
.. PLOT::
|
|
1877
|
+
|
|
1878
|
+
set_verbose(-1)
|
|
1879
|
+
g = plot(arcsec, (x, -2, 2))
|
|
1880
|
+
sphinx_plot(g)
|
|
1881
|
+
|
|
1882
|
+
::
|
|
1883
|
+
|
|
1884
|
+
sage: plot(arcsec, (x, -2, 2), exclude=[1.5]) # x=1.5 is also excluded
|
|
1885
|
+
Graphics object consisting of 3 graphics primitives
|
|
1886
|
+
|
|
1887
|
+
.. PLOT::
|
|
1888
|
+
|
|
1889
|
+
set_verbose(-1)
|
|
1890
|
+
g = plot(arcsec, (x, -2, 2), exclude=[1.5])
|
|
1891
|
+
sphinx_plot(g)
|
|
1892
|
+
|
|
1893
|
+
::
|
|
1894
|
+
|
|
1895
|
+
sage: plot(arcsec(x/2), -2, 2) # plot should be empty; no valid points
|
|
1896
|
+
Graphics object consisting of 0 graphics primitives
|
|
1897
|
+
sage: plot(sqrt(x^2 - 1), -2, 2) # [-1, 1] is excluded automatically
|
|
1898
|
+
Graphics object consisting of 2 graphics primitives
|
|
1899
|
+
|
|
1900
|
+
.. PLOT::
|
|
1901
|
+
|
|
1902
|
+
set_verbose(-1)
|
|
1903
|
+
g = plot(sqrt(x**2-1), -2, 2)
|
|
1904
|
+
sphinx_plot(g)
|
|
1905
|
+
|
|
1906
|
+
::
|
|
1907
|
+
|
|
1908
|
+
sage: plot(arccsc, -2, 2) # [-1, 1] is excluded automatically
|
|
1909
|
+
Graphics object consisting of 2 graphics primitives
|
|
1910
|
+
sage: set_verbose(0)
|
|
1911
|
+
|
|
1912
|
+
.. PLOT::
|
|
1913
|
+
|
|
1914
|
+
set_verbose(-1)
|
|
1915
|
+
g = plot(arccsc, -2, 2)
|
|
1916
|
+
sphinx_plot(g)
|
|
1917
|
+
|
|
1918
|
+
TESTS:
|
|
1919
|
+
|
|
1920
|
+
We do not randomize the endpoints::
|
|
1921
|
+
|
|
1922
|
+
sage: p = plot(x, (x,-1,1))
|
|
1923
|
+
sage: p[0].xdata[0] == -1
|
|
1924
|
+
True
|
|
1925
|
+
sage: p[0].xdata[-1] == 1
|
|
1926
|
+
True
|
|
1927
|
+
|
|
1928
|
+
We check to make sure that the x/y min/max data get set correctly
|
|
1929
|
+
when there are multiple functions.
|
|
1930
|
+
|
|
1931
|
+
::
|
|
1932
|
+
|
|
1933
|
+
sage: d = plot([sin(x), cos(x)], 100, 120).get_minmax_data()
|
|
1934
|
+
sage: d['xmin']
|
|
1935
|
+
100.0
|
|
1936
|
+
sage: d['xmax']
|
|
1937
|
+
120.0
|
|
1938
|
+
|
|
1939
|
+
We check various combinations of tuples and functions, ending with
|
|
1940
|
+
tests that lambda functions work properly with explicit variable
|
|
1941
|
+
declaration, without a tuple.
|
|
1942
|
+
|
|
1943
|
+
::
|
|
1944
|
+
|
|
1945
|
+
sage: p = plot(lambda x: x,(x,-1,1))
|
|
1946
|
+
sage: p = plot(lambda x: x,-1,1)
|
|
1947
|
+
sage: p = plot(x,x,-1,1)
|
|
1948
|
+
sage: p = plot(x,-1,1)
|
|
1949
|
+
sage: p = plot(x^2,x,-1,1)
|
|
1950
|
+
sage: p = plot(x^2,xmin=-1,xmax=2)
|
|
1951
|
+
sage: p = plot(lambda x: x,x,-1,1)
|
|
1952
|
+
sage: p = plot(lambda x: x^2,x,-1,1)
|
|
1953
|
+
sage: p = plot(lambda x: 1/x,x,-1,1)
|
|
1954
|
+
sage: f(x) = sin(x+3)-.1*x^3
|
|
1955
|
+
sage: p = plot(lambda x: f(x),x,-1,1)
|
|
1956
|
+
|
|
1957
|
+
We check to handle cases where the function gets evaluated at a
|
|
1958
|
+
point which causes an 'inf' or '-inf' result to be produced.
|
|
1959
|
+
|
|
1960
|
+
::
|
|
1961
|
+
|
|
1962
|
+
sage: p = plot(1/x, 0, 1)
|
|
1963
|
+
sage: p = plot(-1/x, 0, 1)
|
|
1964
|
+
|
|
1965
|
+
Bad options now give better errors::
|
|
1966
|
+
|
|
1967
|
+
sage: P = plot(sin(1/x), (x,-1,3), foo=10)
|
|
1968
|
+
Traceback (most recent call last):
|
|
1969
|
+
...
|
|
1970
|
+
RuntimeError: error in line(): option 'foo' not valid
|
|
1971
|
+
sage: P = plot(x, (x,1,1)) # github issue #11753
|
|
1972
|
+
Traceback (most recent call last):
|
|
1973
|
+
...
|
|
1974
|
+
ValueError: plot start point and end point must be different
|
|
1975
|
+
|
|
1976
|
+
We test that we can plot `f(x)=x` (see :issue:`10246`)::
|
|
1977
|
+
|
|
1978
|
+
sage: f(x)=x; f
|
|
1979
|
+
x |--> x
|
|
1980
|
+
sage: plot(f,(x,-1,1))
|
|
1981
|
+
Graphics object consisting of 1 graphics primitive
|
|
1982
|
+
|
|
1983
|
+
Check that :issue:`15030` is fixed::
|
|
1984
|
+
|
|
1985
|
+
sage: plot(abs(log(x)), x)
|
|
1986
|
+
Graphics object consisting of 1 graphics primitive
|
|
1987
|
+
|
|
1988
|
+
Check that if excluded points are less than xmin then the exclusion
|
|
1989
|
+
still works for polar and parametric plots. The following should
|
|
1990
|
+
show two excluded points::
|
|
1991
|
+
|
|
1992
|
+
sage: set_verbose(-1)
|
|
1993
|
+
sage: polar_plot(sin(sqrt(x^2-1)), (x,0,2*pi), exclude=[1/2,2,3])
|
|
1994
|
+
Graphics object consisting of 3 graphics primitives
|
|
1995
|
+
|
|
1996
|
+
sage: parametric_plot((sqrt(x^2-1),sqrt(x^2-1/2)), (x,0,5), exclude=[1,2,3])
|
|
1997
|
+
Graphics object consisting of 3 graphics primitives
|
|
1998
|
+
|
|
1999
|
+
sage: set_verbose(0)
|
|
2000
|
+
|
|
2001
|
+
Legends can contain variables with long names, :issue:`13543`::
|
|
2002
|
+
|
|
2003
|
+
sage: hello = var('hello')
|
|
2004
|
+
sage: label = '$' + latex(hello) + '$'
|
|
2005
|
+
sage: plot(x, x, 0, 1, legend_label=label)
|
|
2006
|
+
Graphics object consisting of 1 graphics primitive
|
|
2007
|
+
|
|
2008
|
+
Extra keywords should be saved if object has a plot method, :issue:`20924`::
|
|
2009
|
+
|
|
2010
|
+
sage: G = graphs.PetersenGraph()
|
|
2011
|
+
sage: p = G.plot()
|
|
2012
|
+
sage: p.aspect_ratio()
|
|
2013
|
+
1.0
|
|
2014
|
+
sage: pp = plot(G)
|
|
2015
|
+
sage: pp.aspect_ratio()
|
|
2016
|
+
1.0
|
|
2017
|
+
"""
|
|
2018
|
+
if 'color' in kwds and 'rgbcolor' in kwds:
|
|
2019
|
+
raise ValueError('only one of color or rgbcolor should be specified')
|
|
2020
|
+
elif 'color' in kwds:
|
|
2021
|
+
kwds['rgbcolor'] = kwds.pop('color', (0, 0, 1)) # take blue as default ``rgbcolor``
|
|
2022
|
+
G_kwds = Graphics._extract_kwds_for_show(kwds, ignore=['xmin', 'xmax'])
|
|
2023
|
+
if 'scale' in G_kwds:
|
|
2024
|
+
kwds['scale'] = G_kwds['scale'] # pass scaling information to _plot too
|
|
2025
|
+
|
|
2026
|
+
original_opts = kwds.pop('__original_opts', {})
|
|
2027
|
+
do_show = kwds.pop('show', False)
|
|
2028
|
+
|
|
2029
|
+
from sage.structure.element import Vector
|
|
2030
|
+
if kwds.get('parametric', False) and isinstance(funcs, Vector):
|
|
2031
|
+
funcs = tuple(funcs)
|
|
2032
|
+
|
|
2033
|
+
if hasattr(funcs, 'plot'):
|
|
2034
|
+
G = funcs.plot(*args, **original_opts)
|
|
2035
|
+
|
|
2036
|
+
# If we have extra keywords already set, then update them
|
|
2037
|
+
for ext in G._extra_kwds:
|
|
2038
|
+
if ext in G_kwds:
|
|
2039
|
+
G_kwds[ext] = G._extra_kwds[ext]
|
|
2040
|
+
|
|
2041
|
+
# if we are using the generic plotting method
|
|
2042
|
+
else:
|
|
2043
|
+
n = len(args)
|
|
2044
|
+
# if there are no extra args, try to get xmin,xmax from
|
|
2045
|
+
# keyword arguments or pick some silly default
|
|
2046
|
+
if n == 0:
|
|
2047
|
+
xmin = kwds.pop('xmin', -1)
|
|
2048
|
+
xmax = kwds.pop('xmax', 1)
|
|
2049
|
+
G = _plot(funcs, (xmin, xmax), **kwds)
|
|
2050
|
+
|
|
2051
|
+
# if there is one extra arg, then it had better be a tuple
|
|
2052
|
+
elif n == 1:
|
|
2053
|
+
G = _plot(funcs, *args, **kwds)
|
|
2054
|
+
elif n == 2:
|
|
2055
|
+
# if there are two extra args, then pull them out
|
|
2056
|
+
# and pass them as a tuple
|
|
2057
|
+
xmin = args[0]
|
|
2058
|
+
xmax = args[1]
|
|
2059
|
+
args = args[2:]
|
|
2060
|
+
G = _plot(funcs, (xmin, xmax), *args, **kwds)
|
|
2061
|
+
elif n == 3:
|
|
2062
|
+
# if there are three extra args, then pull them out
|
|
2063
|
+
# and pass them as a tuple
|
|
2064
|
+
var = args[0]
|
|
2065
|
+
xmin = args[1]
|
|
2066
|
+
xmax = args[2]
|
|
2067
|
+
args = args[3:]
|
|
2068
|
+
G = _plot(funcs, (var, xmin, xmax), *args, **kwds)
|
|
2069
|
+
elif ('xmin' in kwds) or ('xmax' in kwds):
|
|
2070
|
+
xmin = kwds.pop('xmin', -1)
|
|
2071
|
+
xmax = kwds.pop('xmax', 1)
|
|
2072
|
+
G = _plot(funcs, (xmin, xmax), *args, **kwds)
|
|
2073
|
+
else:
|
|
2074
|
+
sage.misc.verbose.verbose(f"there were {n} extra arguments (besides {funcs})", level=0)
|
|
2075
|
+
|
|
2076
|
+
G._set_extra_kwds(G_kwds)
|
|
2077
|
+
if do_show:
|
|
2078
|
+
G.show()
|
|
2079
|
+
return G
|
|
2080
|
+
|
|
2081
|
+
|
|
2082
|
+
def _plot(funcs, xrange, parametric=False,
|
|
2083
|
+
polar=False, fill=False, label='', randomize=True, **options):
|
|
2084
|
+
"""
|
|
2085
|
+
Internal function which does the actual plotting.
|
|
2086
|
+
|
|
2087
|
+
INPUT:
|
|
2088
|
+
|
|
2089
|
+
- ``funcs`` -- function or list of functions to be plotted
|
|
2090
|
+
- ``xrange`` -- two or three tuple of [input variable], min and max
|
|
2091
|
+
- ``parametric`` -- boolean (default: ``False``); whether
|
|
2092
|
+
this is a parametric plot
|
|
2093
|
+
- ``polar`` -- boolean (default: ``False``); whether
|
|
2094
|
+
this is a polar plot
|
|
2095
|
+
- ``fill`` -- boolean (default: ``False``); whether
|
|
2096
|
+
this plot is filled
|
|
2097
|
+
- ``randomize`` -- boolean (default: ``True``); whether
|
|
2098
|
+
to use random plot points
|
|
2099
|
+
|
|
2100
|
+
The following option is deprecated in favor of ``legend_label``:
|
|
2101
|
+
|
|
2102
|
+
- ``label`` -- (default: ``''``) a string for the label
|
|
2103
|
+
|
|
2104
|
+
All other usual plot options are also accepted, and a number
|
|
2105
|
+
are required (see the example below) which are normally passed
|
|
2106
|
+
through the options decorator to :func:`plot`.
|
|
2107
|
+
|
|
2108
|
+
OUTPUT: a ``Graphics`` object
|
|
2109
|
+
|
|
2110
|
+
EXAMPLES:
|
|
2111
|
+
|
|
2112
|
+
See :func:`plot` for many, many implicit examples.
|
|
2113
|
+
Here is an explicit one::
|
|
2114
|
+
|
|
2115
|
+
sage: from sage.plot.plot import _plot
|
|
2116
|
+
sage: P = _plot(e^(-x^2),(-3,3), fill=True,
|
|
2117
|
+
....: color='red',
|
|
2118
|
+
....: plot_points=50,
|
|
2119
|
+
....: adaptive_tolerance=2,
|
|
2120
|
+
....: adaptive_recursion=True,
|
|
2121
|
+
....: exclude=None,
|
|
2122
|
+
....: imaginary_tolerance=1e-8)
|
|
2123
|
+
sage: P.show(aspect_ratio='automatic')
|
|
2124
|
+
|
|
2125
|
+
TESTS:
|
|
2126
|
+
|
|
2127
|
+
Make sure that we get the right number of legend entries as the number of
|
|
2128
|
+
functions varies (:issue:`10514`)::
|
|
2129
|
+
|
|
2130
|
+
sage: p1 = plot(1*x, legend_label='1x')
|
|
2131
|
+
sage: p2 = plot(2*x, legend_label='2x', color='green')
|
|
2132
|
+
sage: p1+p2
|
|
2133
|
+
Graphics object consisting of 2 graphics primitives
|
|
2134
|
+
|
|
2135
|
+
::
|
|
2136
|
+
|
|
2137
|
+
sage: len(p1.matplotlib().axes[0].legend().texts)
|
|
2138
|
+
1
|
|
2139
|
+
sage: len((p1+p2).matplotlib().axes[0].legend().texts)
|
|
2140
|
+
2
|
|
2141
|
+
sage: q1 = plot([sin(x), tan(x)], color='blue', legend_label='trig')
|
|
2142
|
+
sage: len(q1.matplotlib().axes[0].legend().texts)
|
|
2143
|
+
2
|
|
2144
|
+
sage: q1
|
|
2145
|
+
Graphics object consisting of 2 graphics primitives
|
|
2146
|
+
sage: q2 = plot([sin(x), tan(x)], legend_label={1:'tan'})
|
|
2147
|
+
sage: len(q2.matplotlib().axes[0].legend().texts)
|
|
2148
|
+
2
|
|
2149
|
+
sage: q2
|
|
2150
|
+
Graphics object consisting of 2 graphics primitives
|
|
2151
|
+
|
|
2152
|
+
Make sure that we don't get multiple legend labels for plot segments
|
|
2153
|
+
(:issue:`11998`)::
|
|
2154
|
+
|
|
2155
|
+
sage: p1 = plot(1/(x^2-1),(x,-2,2),legend_label='foo',detect_poles=True)
|
|
2156
|
+
sage: len(p1.matplotlib().axes[0].legend().texts)
|
|
2157
|
+
1
|
|
2158
|
+
sage: p1.show(ymin=-10,ymax=10) # should be one legend
|
|
2159
|
+
|
|
2160
|
+
Parametric plots that get evaluated at invalid points should still
|
|
2161
|
+
plot properly (:issue:`13246`)::
|
|
2162
|
+
|
|
2163
|
+
sage: parametric_plot((x, arcsec(x)), (x, -2, 2))
|
|
2164
|
+
Graphics object consisting of 1 graphics primitive
|
|
2165
|
+
|
|
2166
|
+
Verify that :issue:`31089` is fixed::
|
|
2167
|
+
|
|
2168
|
+
sage: plot(x, -1, 1, detect_poles=True)
|
|
2169
|
+
Graphics object consisting of 1 graphics primitive
|
|
2170
|
+
"""
|
|
2171
|
+
from sage.plot.colors import Color
|
|
2172
|
+
from sage.plot.misc import setup_for_eval_on_grid
|
|
2173
|
+
if funcs == []:
|
|
2174
|
+
return Graphics()
|
|
2175
|
+
orig_funcs = funcs # keep the original functions (for use in legend labels)
|
|
2176
|
+
excluded_points = []
|
|
2177
|
+
imag_tol = options["imaginary_tolerance"]
|
|
2178
|
+
funcs, ranges = setup_for_eval_on_grid(funcs,
|
|
2179
|
+
[xrange],
|
|
2180
|
+
options['plot_points'],
|
|
2181
|
+
imaginary_tolerance=imag_tol)
|
|
2182
|
+
xmin, xmax, delta = ranges[0]
|
|
2183
|
+
xrange = ranges[0][:2]
|
|
2184
|
+
# parametric_plot will be a list or tuple of two functions (f,g)
|
|
2185
|
+
# and will plotted as (f(x), g(x)) for all x in the given range
|
|
2186
|
+
if parametric:
|
|
2187
|
+
f, g = funcs
|
|
2188
|
+
# or we have only a single function to be plotted:
|
|
2189
|
+
else:
|
|
2190
|
+
f = funcs
|
|
2191
|
+
|
|
2192
|
+
# Keep ``rgbcolor`` option 'automatic' only for lists of functions.
|
|
2193
|
+
# Otherwise, let the plot color be 'blue'.
|
|
2194
|
+
if parametric or not isinstance(funcs, (list, tuple)):
|
|
2195
|
+
if 'rgbcolor' in options.keys() and options['rgbcolor'] == 'automatic':
|
|
2196
|
+
options['rgbcolor'] = (0, 0, 1) # default color for a single curve.
|
|
2197
|
+
|
|
2198
|
+
# Check to see if funcs is a list of functions that will be all plotted together.
|
|
2199
|
+
if isinstance(funcs, (list, tuple)) and not parametric:
|
|
2200
|
+
def golden_rainbow(i, lightness=0.4):
|
|
2201
|
+
# note: sage's "blue" has hue-saturation-lightness values (2/3, 1, 1/2).
|
|
2202
|
+
g = 0.61803398875
|
|
2203
|
+
return Color((0.66666666666666 + i*g) % 1, 1, lightness, space='hsl')
|
|
2204
|
+
|
|
2205
|
+
default_line_styles = ("-", "--", "-.", ":")*len(funcs)
|
|
2206
|
+
|
|
2207
|
+
G = Graphics()
|
|
2208
|
+
for i, h in enumerate(funcs):
|
|
2209
|
+
options_temp = options.copy()
|
|
2210
|
+
color_temp = options_temp.pop('rgbcolor', 'automatic')
|
|
2211
|
+
fill_temp = options_temp.pop('fill', fill)
|
|
2212
|
+
fillcolor_temp = options_temp.pop('fillcolor', 'automatic') # perhaps the 2nd argument should be ``options_temp['color']``
|
|
2213
|
+
linestyle_temp = options_temp.pop('linestyle', None)
|
|
2214
|
+
legend_label_temp = options_temp.pop('legend_label', None)
|
|
2215
|
+
legend_color_temp = options_temp.pop('legend_color', None)
|
|
2216
|
+
|
|
2217
|
+
# passed more than one color directive?
|
|
2218
|
+
one_plot_color = False
|
|
2219
|
+
if isinstance(color_temp, dict):
|
|
2220
|
+
if i in color_temp:
|
|
2221
|
+
color_entry = color_temp[i]
|
|
2222
|
+
else:
|
|
2223
|
+
color_entry = golden_rainbow(i)
|
|
2224
|
+
elif isinstance(color_temp, (list, tuple)) and isinstance(color_temp[0], (str, list, tuple)):
|
|
2225
|
+
if i < len(color_temp):
|
|
2226
|
+
color_entry = color_temp[i]
|
|
2227
|
+
else:
|
|
2228
|
+
color_entry = golden_rainbow(i)
|
|
2229
|
+
elif color_temp == 'automatic':
|
|
2230
|
+
color_entry = golden_rainbow(i)
|
|
2231
|
+
else:
|
|
2232
|
+
# assume a single color was assigned for all plots
|
|
2233
|
+
one_plot_color = True
|
|
2234
|
+
color_entry = color_temp
|
|
2235
|
+
|
|
2236
|
+
# passed more than one fill directive?
|
|
2237
|
+
if isinstance(fill_temp, dict):
|
|
2238
|
+
if i in fill_temp:
|
|
2239
|
+
fill_entry_listQ = fill_temp[i]
|
|
2240
|
+
if isinstance(fill_entry_listQ, list):
|
|
2241
|
+
if fill_entry_listQ[0] < len(funcs):
|
|
2242
|
+
fill_entry = funcs[fill_entry_listQ[0]]
|
|
2243
|
+
else:
|
|
2244
|
+
fill_entry = False
|
|
2245
|
+
else:
|
|
2246
|
+
fill_entry = fill_entry_listQ
|
|
2247
|
+
else:
|
|
2248
|
+
fill_entry = False
|
|
2249
|
+
elif isinstance(fill_temp, (list, tuple)):
|
|
2250
|
+
if i < len(fill_temp):
|
|
2251
|
+
fill_entry_listQ = fill_temp[i]
|
|
2252
|
+
if isinstance(fill_entry_listQ, list):
|
|
2253
|
+
if fill_entry_listQ[0] < len(funcs):
|
|
2254
|
+
fill_entry = funcs[fill_entry_listQ[0]]
|
|
2255
|
+
else:
|
|
2256
|
+
fill_entry = False
|
|
2257
|
+
else:
|
|
2258
|
+
fill_entry = fill_entry_listQ
|
|
2259
|
+
else:
|
|
2260
|
+
fill_entry = False
|
|
2261
|
+
else:
|
|
2262
|
+
fill_entry = fill_temp
|
|
2263
|
+
|
|
2264
|
+
# passed more than one fillcolor directive?
|
|
2265
|
+
fillcolor_entry = 'gray' # the default choice
|
|
2266
|
+
if isinstance(fillcolor_temp, dict):
|
|
2267
|
+
if i in fillcolor_temp:
|
|
2268
|
+
fillcolor_entry = fillcolor_temp[i]
|
|
2269
|
+
else:
|
|
2270
|
+
fillcolor_entry = golden_rainbow(i, 0.85)
|
|
2271
|
+
elif isinstance(fillcolor_temp, (list, tuple)):
|
|
2272
|
+
if i < len(fillcolor_temp):
|
|
2273
|
+
fillcolor_entry = fillcolor_temp[i]
|
|
2274
|
+
else:
|
|
2275
|
+
fillcolor_entry = golden_rainbow(i, 0.85)
|
|
2276
|
+
elif fillcolor_temp == 'automatic':
|
|
2277
|
+
# check that we haven't overwritten automatic
|
|
2278
|
+
# multi-colors in color_temp
|
|
2279
|
+
if len(funcs) > 1 and not one_plot_color:
|
|
2280
|
+
fillcolor_entry = golden_rainbow(i, 0.85)
|
|
2281
|
+
elif fillcolor_temp is not None:
|
|
2282
|
+
fillcolor_entry = fillcolor_temp
|
|
2283
|
+
|
|
2284
|
+
# passed more than one linestyle directive?
|
|
2285
|
+
if isinstance(linestyle_temp, dict):
|
|
2286
|
+
if i in linestyle_temp:
|
|
2287
|
+
linestyle_entry = linestyle_temp[i]
|
|
2288
|
+
else:
|
|
2289
|
+
linestyle_entry = default_line_styles[i]
|
|
2290
|
+
elif isinstance(linestyle_temp, (list, tuple)):
|
|
2291
|
+
if i < len(linestyle_temp):
|
|
2292
|
+
linestyle_entry = linestyle_temp[i]
|
|
2293
|
+
else:
|
|
2294
|
+
linestyle_entry = default_line_styles[i]
|
|
2295
|
+
elif linestyle_temp == 'automatic':
|
|
2296
|
+
linestyle_entry = default_line_styles[i]
|
|
2297
|
+
elif linestyle_temp is None:
|
|
2298
|
+
linestyle_entry = 'solid'
|
|
2299
|
+
else:
|
|
2300
|
+
linestyle_entry = linestyle_temp
|
|
2301
|
+
|
|
2302
|
+
# passed more than one legend_label directive?
|
|
2303
|
+
legend_label_entry = orig_funcs[i].__repr__() # the 'automatic' choice
|
|
2304
|
+
if isinstance(legend_label_temp, dict):
|
|
2305
|
+
if i in legend_label_temp:
|
|
2306
|
+
legend_label_entry = legend_label_temp[i]
|
|
2307
|
+
elif isinstance(legend_label_temp, (list, tuple)):
|
|
2308
|
+
if i < len(legend_label_temp):
|
|
2309
|
+
legend_label_entry = legend_label_temp[i]
|
|
2310
|
+
elif legend_label_temp != 'automatic':
|
|
2311
|
+
# assume it is None or str.
|
|
2312
|
+
legend_label_entry = legend_label_temp
|
|
2313
|
+
|
|
2314
|
+
# passed more than one legend_color directive?
|
|
2315
|
+
legend_color_entry = 'black' # the default choice
|
|
2316
|
+
if isinstance(legend_color_temp, dict):
|
|
2317
|
+
if i in legend_color_temp:
|
|
2318
|
+
legend_color_entry = legend_color_temp[i]
|
|
2319
|
+
elif isinstance(legend_color_temp, (list, tuple)) and isinstance(legend_color_temp[0], (str, list, tuple)):
|
|
2320
|
+
if i < len(legend_color_temp):
|
|
2321
|
+
legend_color_entry = legend_color_temp[i]
|
|
2322
|
+
elif legend_color_temp == 'automatic':
|
|
2323
|
+
if len(funcs) > 1:
|
|
2324
|
+
legend_color_entry = golden_rainbow(i)
|
|
2325
|
+
elif legend_color_temp is not None:
|
|
2326
|
+
legend_color_entry = legend_color_temp
|
|
2327
|
+
|
|
2328
|
+
G += plot(h, xrange, polar=polar, fill=fill_entry, fillcolor=fillcolor_entry,
|
|
2329
|
+
rgbcolor=color_entry, linestyle=linestyle_entry,
|
|
2330
|
+
legend_label=legend_label_entry, legend_color=legend_color_entry, **options_temp)
|
|
2331
|
+
return G
|
|
2332
|
+
|
|
2333
|
+
adaptive_tolerance = options.pop('adaptive_tolerance')
|
|
2334
|
+
adaptive_recursion = options.pop('adaptive_recursion')
|
|
2335
|
+
imag_tol = options.pop('imaginary_tolerance')
|
|
2336
|
+
plot_points = int(options.pop('plot_points'))
|
|
2337
|
+
|
|
2338
|
+
exclude = options.pop('exclude')
|
|
2339
|
+
if exclude is not None:
|
|
2340
|
+
if isinstance(exclude, Expression) and exclude.is_relational():
|
|
2341
|
+
if len(exclude.variables()) > 1:
|
|
2342
|
+
raise ValueError('exclude has to be an equation of only one variable')
|
|
2343
|
+
v = exclude.variables()[0]
|
|
2344
|
+
points = [e.right() for e in exclude.solve(v) if e.left() == v and (v not in e.right().variables())]
|
|
2345
|
+
# We are only interested in real solutions
|
|
2346
|
+
for x in points:
|
|
2347
|
+
try:
|
|
2348
|
+
excluded_points.append(float(x))
|
|
2349
|
+
except TypeError:
|
|
2350
|
+
pass
|
|
2351
|
+
excluded_points.sort()
|
|
2352
|
+
|
|
2353
|
+
# We should either have a list in excluded points or exclude
|
|
2354
|
+
# itself must be a list
|
|
2355
|
+
elif isinstance(exclude, (list, tuple)):
|
|
2356
|
+
excluded_points = sorted(exclude)
|
|
2357
|
+
else:
|
|
2358
|
+
raise ValueError('exclude needs to be a list of numbers or an equation')
|
|
2359
|
+
|
|
2360
|
+
# We make sure that points plot points close to the excluded points are computed
|
|
2361
|
+
epsilon = 0.001*(xmax - xmin)
|
|
2362
|
+
initial_points = reduce(lambda a, b: a+b,
|
|
2363
|
+
[[x - epsilon, x + epsilon]
|
|
2364
|
+
for x in excluded_points], [])
|
|
2365
|
+
else:
|
|
2366
|
+
initial_points = None
|
|
2367
|
+
|
|
2368
|
+
# If we are a log scale plot on the x axis, do a change of variables
|
|
2369
|
+
# so we sample the range in log scale
|
|
2370
|
+
is_log_scale = ('scale' in options.keys() and
|
|
2371
|
+
not parametric and
|
|
2372
|
+
options['scale'] in ['loglog', 'semilogx'])
|
|
2373
|
+
if is_log_scale:
|
|
2374
|
+
|
|
2375
|
+
def f_exp(x):
|
|
2376
|
+
return f(exp(x))
|
|
2377
|
+
|
|
2378
|
+
log_xrange = (log(xrange[0]), log(xrange[1]))
|
|
2379
|
+
if initial_points is None:
|
|
2380
|
+
log_initial_points = None
|
|
2381
|
+
else:
|
|
2382
|
+
log_initial_points = [log(x) for x in initial_points]
|
|
2383
|
+
data, extra_excluded = generate_plot_points(
|
|
2384
|
+
f_exp, log_xrange, plot_points,
|
|
2385
|
+
adaptive_tolerance, adaptive_recursion,
|
|
2386
|
+
randomize, log_initial_points,
|
|
2387
|
+
excluded=True, imaginary_tolerance=imag_tol)
|
|
2388
|
+
else:
|
|
2389
|
+
data, extra_excluded = generate_plot_points(
|
|
2390
|
+
f, xrange, plot_points,
|
|
2391
|
+
adaptive_tolerance, adaptive_recursion,
|
|
2392
|
+
randomize, initial_points,
|
|
2393
|
+
excluded=True, imaginary_tolerance=imag_tol)
|
|
2394
|
+
|
|
2395
|
+
excluded_points += extra_excluded
|
|
2396
|
+
|
|
2397
|
+
# If we did a change in variables, undo it now
|
|
2398
|
+
if is_log_scale:
|
|
2399
|
+
for i, (a, fa) in enumerate(data):
|
|
2400
|
+
data[i] = (exp(a), fa)
|
|
2401
|
+
for i, p in enumerate(excluded_points):
|
|
2402
|
+
excluded_points[i] = exp(p)
|
|
2403
|
+
|
|
2404
|
+
if parametric:
|
|
2405
|
+
# We need the original x-values to be able to exclude points
|
|
2406
|
+
# in parametric plots
|
|
2407
|
+
exclude_data = data
|
|
2408
|
+
newdata = []
|
|
2409
|
+
for x, fdata in data:
|
|
2410
|
+
try:
|
|
2411
|
+
newdata.append((fdata, g(x)))
|
|
2412
|
+
except (ValueError, TypeError):
|
|
2413
|
+
newdata.append((fdata, 0)) # append a dummy value 0
|
|
2414
|
+
excluded_points.append(x)
|
|
2415
|
+
data = newdata
|
|
2416
|
+
|
|
2417
|
+
excluded_points.sort(reverse=True)
|
|
2418
|
+
G = Graphics()
|
|
2419
|
+
|
|
2420
|
+
fillcolor = options.pop('fillcolor', 'automatic')
|
|
2421
|
+
fillalpha = options.pop('fillalpha', 0.5)
|
|
2422
|
+
|
|
2423
|
+
# TODO: Use matplotlib's fill and fill_between commands.
|
|
2424
|
+
if fill is not False and fill is not None:
|
|
2425
|
+
if parametric:
|
|
2426
|
+
filldata = data
|
|
2427
|
+
else:
|
|
2428
|
+
if fill == 'axis' or fill is True:
|
|
2429
|
+
base_level = 0
|
|
2430
|
+
elif fill == 'min':
|
|
2431
|
+
base_level = min(t[1] for t in data)
|
|
2432
|
+
elif fill == 'max':
|
|
2433
|
+
base_level = max(t[1] for t in data)
|
|
2434
|
+
elif callable(fill):
|
|
2435
|
+
if fill == max or fill == min:
|
|
2436
|
+
if fill == max:
|
|
2437
|
+
fstr = 'max'
|
|
2438
|
+
else:
|
|
2439
|
+
fstr = 'min'
|
|
2440
|
+
msg = "WARNING: You use the built-in function {} for filling. You probably wanted the string '{}'.".format(fstr, fstr)
|
|
2441
|
+
sage.misc.verbose.verbose(msg, level=0)
|
|
2442
|
+
if not is_fast_float(fill):
|
|
2443
|
+
fill_f = fast_float(fill, expect_one_var=True)
|
|
2444
|
+
else:
|
|
2445
|
+
fill_f = fill
|
|
2446
|
+
|
|
2447
|
+
filldata = generate_plot_points(fill_f,
|
|
2448
|
+
xrange,
|
|
2449
|
+
plot_points,
|
|
2450
|
+
adaptive_tolerance,
|
|
2451
|
+
adaptive_recursion,
|
|
2452
|
+
randomize,
|
|
2453
|
+
imaginary_tolerance=imag_tol)
|
|
2454
|
+
filldata.reverse()
|
|
2455
|
+
filldata += data
|
|
2456
|
+
else:
|
|
2457
|
+
try:
|
|
2458
|
+
base_level = float(fill)
|
|
2459
|
+
except TypeError:
|
|
2460
|
+
base_level = 0
|
|
2461
|
+
|
|
2462
|
+
if not callable(fill) and polar:
|
|
2463
|
+
filldata = generate_plot_points(lambda x: base_level,
|
|
2464
|
+
xrange,
|
|
2465
|
+
plot_points,
|
|
2466
|
+
adaptive_tolerance,
|
|
2467
|
+
adaptive_recursion,
|
|
2468
|
+
randomize,
|
|
2469
|
+
imaginary_tolerance=imag_tol)
|
|
2470
|
+
filldata.reverse()
|
|
2471
|
+
filldata += data
|
|
2472
|
+
if not callable(fill) and not polar:
|
|
2473
|
+
filldata = [(data[0][0], base_level)] + data + [(data[-1][0], base_level)]
|
|
2474
|
+
|
|
2475
|
+
if fillcolor == 'automatic':
|
|
2476
|
+
fillcolor = (0.5, 0.5, 0.5)
|
|
2477
|
+
fill_options = {}
|
|
2478
|
+
fill_options['rgbcolor'] = fillcolor
|
|
2479
|
+
fill_options['alpha'] = fillalpha
|
|
2480
|
+
fill_options['thickness'] = 0
|
|
2481
|
+
if polar:
|
|
2482
|
+
filldata = [(y*cos(x), y*sin(x)) for x, y in filldata]
|
|
2483
|
+
G += polygon(filldata, **fill_options)
|
|
2484
|
+
|
|
2485
|
+
# We need the original data to be able to exclude points in polar plots
|
|
2486
|
+
if not parametric:
|
|
2487
|
+
exclude_data = data
|
|
2488
|
+
if polar:
|
|
2489
|
+
data = [(y*cos(x), y*sin(x)) for x, y in data]
|
|
2490
|
+
|
|
2491
|
+
detect_poles = options.pop('detect_poles', False)
|
|
2492
|
+
legend_label = options.pop('legend_label', None)
|
|
2493
|
+
if excluded_points or detect_poles:
|
|
2494
|
+
start_index = 0
|
|
2495
|
+
# setup for pole detection
|
|
2496
|
+
from sage.rings.real_double import RDF
|
|
2497
|
+
epsilon = 0.0001
|
|
2498
|
+
pole_options = {}
|
|
2499
|
+
pole_options['linestyle'] = '--'
|
|
2500
|
+
pole_options['thickness'] = 1
|
|
2501
|
+
pole_options['rgbcolor'] = '#ccc'
|
|
2502
|
+
|
|
2503
|
+
# setup for exclusion points
|
|
2504
|
+
if excluded_points:
|
|
2505
|
+
exclusion_point = excluded_points.pop()
|
|
2506
|
+
else:
|
|
2507
|
+
# default value of exclusion point must be outside the plot interval
|
|
2508
|
+
# (see :issue:`31089`)
|
|
2509
|
+
exclusion_point = xmax + 1
|
|
2510
|
+
|
|
2511
|
+
flag = True
|
|
2512
|
+
for i in range(len(data)-1):
|
|
2513
|
+
x0, y0 = exclude_data[i]
|
|
2514
|
+
x1, y1 = exclude_data[i+1]
|
|
2515
|
+
|
|
2516
|
+
# detect poles
|
|
2517
|
+
if (not (polar or parametric)) and detect_poles \
|
|
2518
|
+
and ((y1 > 0 and y0 < 0) or (y1 < 0 and y0 > 0)):
|
|
2519
|
+
# calculate the slope of the line segment
|
|
2520
|
+
dy = abs(y1-y0)
|
|
2521
|
+
dx = x1 - x0
|
|
2522
|
+
alpha = (RDF(dy)/RDF(dx)).arctan()
|
|
2523
|
+
if alpha >= RDF(pi/2) - epsilon:
|
|
2524
|
+
G += line(data[start_index:i], **options)
|
|
2525
|
+
if detect_poles == 'show':
|
|
2526
|
+
# draw a vertical asymptote
|
|
2527
|
+
G += line([(x0, y0), (x1, y1)], **pole_options)
|
|
2528
|
+
start_index = i+2
|
|
2529
|
+
|
|
2530
|
+
# exclude points
|
|
2531
|
+
if x0 > exclusion_point:
|
|
2532
|
+
while exclusion_point <= x1:
|
|
2533
|
+
try:
|
|
2534
|
+
exclusion_point = excluded_points.pop()
|
|
2535
|
+
except IndexError:
|
|
2536
|
+
# all excluded points were considered
|
|
2537
|
+
flag = False
|
|
2538
|
+
break
|
|
2539
|
+
|
|
2540
|
+
elif flag and (x0 <= exclusion_point <= x1):
|
|
2541
|
+
G += line(data[start_index:i], **options)
|
|
2542
|
+
start_index = i + 2
|
|
2543
|
+
while exclusion_point <= x1:
|
|
2544
|
+
try:
|
|
2545
|
+
exclusion_point = excluded_points.pop()
|
|
2546
|
+
except IndexError:
|
|
2547
|
+
# all excluded points were considered
|
|
2548
|
+
flag = False
|
|
2549
|
+
break
|
|
2550
|
+
|
|
2551
|
+
G += line(data[start_index:], legend_label=legend_label, **options)
|
|
2552
|
+
else:
|
|
2553
|
+
G += line(data, legend_label=legend_label, **options)
|
|
2554
|
+
|
|
2555
|
+
return G
|
|
2556
|
+
|
|
2557
|
+
|
|
2558
|
+
# ######### misc functions #################
|
|
2559
|
+
|
|
2560
|
+
@options(aspect_ratio=1.0)
|
|
2561
|
+
def parametric_plot(funcs, *args, **kwargs):
|
|
2562
|
+
r"""
|
|
2563
|
+
Plot a parametric curve or surface in 2d or 3d.
|
|
2564
|
+
|
|
2565
|
+
:func:`parametric_plot` takes two or three functions as a
|
|
2566
|
+
list or a tuple and makes a plot with the first function giving the
|
|
2567
|
+
`x` coordinates, the second function giving the `y`
|
|
2568
|
+
coordinates, and the third function (if present) giving the
|
|
2569
|
+
`z` coordinates.
|
|
2570
|
+
|
|
2571
|
+
In the 2d case, :func:`parametric_plot` is equivalent to the :func:`plot` command
|
|
2572
|
+
with the option ``parametric=True``. In the 3d case, :func:`parametric_plot`
|
|
2573
|
+
is equivalent to :func:`~sage.plot.plot3d.parametric_plot3d.parametric_plot3d`.
|
|
2574
|
+
See each of these functions for more help and examples.
|
|
2575
|
+
|
|
2576
|
+
INPUT:
|
|
2577
|
+
|
|
2578
|
+
- ``funcs`` -- 2 or 3-tuple of functions, or a vector of dimension 2 or 3
|
|
2579
|
+
|
|
2580
|
+
- ``other options`` -- passed to :func:`plot` or :func:`~sage.plot.plot3d.parametric_plot3d.parametric_plot3d`
|
|
2581
|
+
|
|
2582
|
+
EXAMPLES: We draw some 2d parametric plots. Note that the default aspect ratio
|
|
2583
|
+
is 1, so that circles look like circles. ::
|
|
2584
|
+
|
|
2585
|
+
sage: t = var('t')
|
|
2586
|
+
sage: parametric_plot((cos(t), sin(t)), (t, 0, 2*pi))
|
|
2587
|
+
Graphics object consisting of 1 graphics primitive
|
|
2588
|
+
|
|
2589
|
+
.. PLOT::
|
|
2590
|
+
|
|
2591
|
+
t = var('t')
|
|
2592
|
+
g = parametric_plot( (cos(t), sin(t)), (t, 0, 2*pi))
|
|
2593
|
+
sphinx_plot(g)
|
|
2594
|
+
|
|
2595
|
+
::
|
|
2596
|
+
|
|
2597
|
+
sage: parametric_plot((sin(t), sin(2*t)), (t, 0, 2*pi), color=hue(0.6))
|
|
2598
|
+
Graphics object consisting of 1 graphics primitive
|
|
2599
|
+
|
|
2600
|
+
.. PLOT::
|
|
2601
|
+
|
|
2602
|
+
t = var('t')
|
|
2603
|
+
g = parametric_plot( (sin(t), sin(2*t)), (t, 0, 2*pi), color=hue(0.6))
|
|
2604
|
+
sphinx_plot(g)
|
|
2605
|
+
|
|
2606
|
+
::
|
|
2607
|
+
|
|
2608
|
+
sage: parametric_plot((1, t), (t, 0, 4))
|
|
2609
|
+
Graphics object consisting of 1 graphics primitive
|
|
2610
|
+
|
|
2611
|
+
.. PLOT::
|
|
2612
|
+
|
|
2613
|
+
t =var('t')
|
|
2614
|
+
g = parametric_plot((1, t), (t, 0, 4))
|
|
2615
|
+
sphinx_plot(g)
|
|
2616
|
+
|
|
2617
|
+
Note that in parametric_plot, there is only fill or no fill.
|
|
2618
|
+
|
|
2619
|
+
::
|
|
2620
|
+
|
|
2621
|
+
sage: parametric_plot((t, t^2), (t, -4, 4), fill=True)
|
|
2622
|
+
Graphics object consisting of 2 graphics primitives
|
|
2623
|
+
|
|
2624
|
+
.. PLOT::
|
|
2625
|
+
|
|
2626
|
+
t = var('t')
|
|
2627
|
+
g = parametric_plot((t, t**2), (t, -4, 4), fill=True)
|
|
2628
|
+
sphinx_plot(g)
|
|
2629
|
+
|
|
2630
|
+
A filled Hypotrochoid::
|
|
2631
|
+
|
|
2632
|
+
sage: parametric_plot([cos(x) + 2 * cos(x/4), sin(x) - 2 * sin(x/4)],
|
|
2633
|
+
....: (x, 0, 8*pi), fill=True)
|
|
2634
|
+
Graphics object consisting of 2 graphics primitives
|
|
2635
|
+
|
|
2636
|
+
.. PLOT::
|
|
2637
|
+
|
|
2638
|
+
g = parametric_plot([cos(x) + 2 * cos(x/4), sin(x) - 2 * sin(x/4)], (x, 0, 8*pi), fill=True)
|
|
2639
|
+
sphinx_plot(g)
|
|
2640
|
+
|
|
2641
|
+
::
|
|
2642
|
+
|
|
2643
|
+
sage: parametric_plot((5*cos(x), 5*sin(x), x), (x, -12, 12), # long time
|
|
2644
|
+
....: plot_points=150, color='red')
|
|
2645
|
+
Graphics3d Object
|
|
2646
|
+
|
|
2647
|
+
.. PLOT::
|
|
2648
|
+
|
|
2649
|
+
#AttributeError: 'Line' object has no attribute 'plot'
|
|
2650
|
+
#g = parametric_plot( (5*cos(x), 5*sin(x), x), (x,-12, 12), plot_points=150, color='red') # long time
|
|
2651
|
+
#sphinx_plot(g)
|
|
2652
|
+
|
|
2653
|
+
::
|
|
2654
|
+
|
|
2655
|
+
sage: y = var('y')
|
|
2656
|
+
sage: parametric_plot((5*cos(x), x*y, cos(x*y)), (x, -4, 4), (y, -4, 4)) # long time
|
|
2657
|
+
Graphics3d Object
|
|
2658
|
+
|
|
2659
|
+
.. PLOT::
|
|
2660
|
+
|
|
2661
|
+
#AttributeError: 'sage.plot.plot3d.parametric_surface.ParametricSurf' object has no attribute 'plot'
|
|
2662
|
+
#y = var('y')
|
|
2663
|
+
#g = parametric_plot( (5*cos(x), x*y, cos(x*y)), (x, -4,4), (y,-4,4)) # long time
|
|
2664
|
+
#sphinx_plot(g)
|
|
2665
|
+
|
|
2666
|
+
::
|
|
2667
|
+
|
|
2668
|
+
sage: t = var('t')
|
|
2669
|
+
sage: parametric_plot(vector((sin(t), sin(2*t))), (t, 0, 2*pi), color='green') # long time
|
|
2670
|
+
Graphics object consisting of 1 graphics primitive
|
|
2671
|
+
|
|
2672
|
+
.. PLOT::
|
|
2673
|
+
|
|
2674
|
+
t = var('t')
|
|
2675
|
+
g = parametric_plot( vector((sin(t), sin(2*t))), (t, 0, 2*pi), color='green') # long time
|
|
2676
|
+
sphinx_plot(g)
|
|
2677
|
+
|
|
2678
|
+
::
|
|
2679
|
+
|
|
2680
|
+
sage: t = var('t')
|
|
2681
|
+
sage: parametric_plot( vector([t, t+1, t^2]), (t, 0, 1)) # long time
|
|
2682
|
+
Graphics3d Object
|
|
2683
|
+
|
|
2684
|
+
.. PLOT::
|
|
2685
|
+
|
|
2686
|
+
#t = var('t')
|
|
2687
|
+
#g = parametric_plot( vector([t, t+1, t**2]), (t, 0, 1)) # long time
|
|
2688
|
+
#sphinx_plot(g)
|
|
2689
|
+
|
|
2690
|
+
Plotting in logarithmic scale is possible with 2D plots. The keyword
|
|
2691
|
+
``aspect_ratio`` will be ignored if the scale is not ``'loglog'`` or
|
|
2692
|
+
``'linear'``.::
|
|
2693
|
+
|
|
2694
|
+
sage: parametric_plot((x, x**2), (x, 1, 10), scale='loglog')
|
|
2695
|
+
Graphics object consisting of 1 graphics primitive
|
|
2696
|
+
|
|
2697
|
+
.. PLOT::
|
|
2698
|
+
|
|
2699
|
+
g = parametric_plot((x, x**2), (x, 1, 10), scale='loglog')
|
|
2700
|
+
sphinx_plot(g)
|
|
2701
|
+
|
|
2702
|
+
We can also change the scale of the axes in the graphics just before
|
|
2703
|
+
displaying. In this case, the ``aspect_ratio`` must be specified as
|
|
2704
|
+
``'automatic'`` if the ``scale`` is set to ``'semilogx'`` or ``'semilogy'``. For
|
|
2705
|
+
other values of the ``scale`` parameter, any ``aspect_ratio`` can be
|
|
2706
|
+
used, or the keyword need not be provided.::
|
|
2707
|
+
|
|
2708
|
+
sage: p = parametric_plot((x, x**2), (x, 1, 10))
|
|
2709
|
+
sage: p.show(scale='semilogy', aspect_ratio='automatic')
|
|
2710
|
+
|
|
2711
|
+
TESTS::
|
|
2712
|
+
|
|
2713
|
+
sage: parametric_plot((x, t^2), (x, -4, 4))
|
|
2714
|
+
Traceback (most recent call last):
|
|
2715
|
+
...
|
|
2716
|
+
ValueError: there are more variables than variable ranges
|
|
2717
|
+
|
|
2718
|
+
sage: parametric_plot((1, x+t), (x, -4, 4))
|
|
2719
|
+
Traceback (most recent call last):
|
|
2720
|
+
...
|
|
2721
|
+
ValueError: there are more variables than variable ranges
|
|
2722
|
+
|
|
2723
|
+
sage: parametric_plot((-t, x+t), (x, -4, 4))
|
|
2724
|
+
Traceback (most recent call last):
|
|
2725
|
+
...
|
|
2726
|
+
ValueError: there are more variables than variable ranges
|
|
2727
|
+
|
|
2728
|
+
sage: parametric_plot((1, x+t, y), (x, -4, 4), (t, -4, 4))
|
|
2729
|
+
Traceback (most recent call last):
|
|
2730
|
+
...
|
|
2731
|
+
ValueError: there are more variables than variable ranges
|
|
2732
|
+
|
|
2733
|
+
sage: parametric_plot((1, x, y), 0, 4)
|
|
2734
|
+
Traceback (most recent call last):
|
|
2735
|
+
...
|
|
2736
|
+
ValueError: there are more variables than variable ranges
|
|
2737
|
+
|
|
2738
|
+
One test for :issue:`7165`::
|
|
2739
|
+
|
|
2740
|
+
sage: m = SR.var('m')
|
|
2741
|
+
sage: parametric_plot([real(exp(i*m)), imaginary(exp(i*m))], (m, 0, 7))
|
|
2742
|
+
Graphics object consisting of 1 graphics primitive
|
|
2743
|
+
"""
|
|
2744
|
+
num_ranges = 0
|
|
2745
|
+
for i in args:
|
|
2746
|
+
if isinstance(i, (list, tuple)):
|
|
2747
|
+
num_ranges += 1
|
|
2748
|
+
else:
|
|
2749
|
+
break
|
|
2750
|
+
|
|
2751
|
+
num_funcs = len(funcs)
|
|
2752
|
+
|
|
2753
|
+
num_vars = len(sage.plot.misc.unify_arguments(funcs)[0])
|
|
2754
|
+
if num_vars > num_ranges:
|
|
2755
|
+
raise ValueError("there are more variables than variable ranges")
|
|
2756
|
+
|
|
2757
|
+
# Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'.
|
|
2758
|
+
# Otherwise matplotlib complains.
|
|
2759
|
+
scale = kwargs.get('scale', None)
|
|
2760
|
+
if isinstance(scale, (list, tuple)):
|
|
2761
|
+
scale = scale[0]
|
|
2762
|
+
if scale == 'semilogy' or scale == 'semilogx':
|
|
2763
|
+
kwargs['aspect_ratio'] = 'automatic'
|
|
2764
|
+
|
|
2765
|
+
if num_funcs == 2 and num_ranges == 1:
|
|
2766
|
+
kwargs['parametric'] = True
|
|
2767
|
+
return plot(funcs, *args, **kwargs)
|
|
2768
|
+
elif (num_funcs == 3 and num_ranges <= 2):
|
|
2769
|
+
return sage.plot.plot3d.parametric_plot3d.parametric_plot3d(funcs, *args, **kwargs)
|
|
2770
|
+
else:
|
|
2771
|
+
raise ValueError("the number of functions and the number of variable ranges is not a supported combination for a 2d or 3d parametric plots")
|
|
2772
|
+
|
|
2773
|
+
|
|
2774
|
+
@options(aspect_ratio=1.0)
|
|
2775
|
+
def polar_plot(funcs, *args, **kwds):
|
|
2776
|
+
r"""
|
|
2777
|
+
``polar_plot`` takes a single function or a list or
|
|
2778
|
+
tuple of functions and plots them with polar coordinates in the given
|
|
2779
|
+
domain.
|
|
2780
|
+
|
|
2781
|
+
This function is equivalent to the :func:`plot` command with the options
|
|
2782
|
+
``polar=True`` and ``aspect_ratio=1``. For more help on options,
|
|
2783
|
+
see the documentation for :func:`plot`.
|
|
2784
|
+
|
|
2785
|
+
INPUT:
|
|
2786
|
+
|
|
2787
|
+
- ``funcs`` -- a function
|
|
2788
|
+
|
|
2789
|
+
- other options are passed to plot
|
|
2790
|
+
|
|
2791
|
+
EXAMPLES:
|
|
2792
|
+
|
|
2793
|
+
Here is a blue 8-leaved petal::
|
|
2794
|
+
|
|
2795
|
+
sage: polar_plot(sin(5*x)^2, (x, 0, 2*pi), color='blue')
|
|
2796
|
+
Graphics object consisting of 1 graphics primitive
|
|
2797
|
+
|
|
2798
|
+
.. PLOT::
|
|
2799
|
+
|
|
2800
|
+
g = polar_plot(sin(5*x)**2, (x, 0, 2*pi), color='blue')
|
|
2801
|
+
sphinx_plot(g)
|
|
2802
|
+
|
|
2803
|
+
A red figure-8::
|
|
2804
|
+
|
|
2805
|
+
sage: polar_plot(abs(sqrt(1 - sin(x)^2)), (x, 0, 2*pi), color='red')
|
|
2806
|
+
Graphics object consisting of 1 graphics primitive
|
|
2807
|
+
|
|
2808
|
+
.. PLOT::
|
|
2809
|
+
|
|
2810
|
+
g = polar_plot(abs(sqrt(1 - sin(x)**2)), (x, 0, 2*pi), color='red')
|
|
2811
|
+
sphinx_plot(g)
|
|
2812
|
+
|
|
2813
|
+
A green limacon of Pascal::
|
|
2814
|
+
|
|
2815
|
+
sage: polar_plot(2 + 2*cos(x), (x, 0, 2*pi), color=hue(0.3))
|
|
2816
|
+
Graphics object consisting of 1 graphics primitive
|
|
2817
|
+
|
|
2818
|
+
.. PLOT::
|
|
2819
|
+
|
|
2820
|
+
g = polar_plot(2 + 2*cos(x), (x, 0, 2*pi), color=hue(0.3))
|
|
2821
|
+
sphinx_plot(g)
|
|
2822
|
+
|
|
2823
|
+
Several polar plots::
|
|
2824
|
+
|
|
2825
|
+
sage: polar_plot([2*sin(x), 2*cos(x)], (x, 0, 2*pi))
|
|
2826
|
+
Graphics object consisting of 2 graphics primitives
|
|
2827
|
+
|
|
2828
|
+
.. PLOT::
|
|
2829
|
+
|
|
2830
|
+
g = polar_plot([2*sin(x), 2*cos(x)], (x, 0, 2*pi))
|
|
2831
|
+
sphinx_plot(g)
|
|
2832
|
+
|
|
2833
|
+
A filled spiral::
|
|
2834
|
+
|
|
2835
|
+
sage: polar_plot(sqrt, 0, 2 * pi, fill=True)
|
|
2836
|
+
Graphics object consisting of 2 graphics primitives
|
|
2837
|
+
|
|
2838
|
+
.. PLOT::
|
|
2839
|
+
|
|
2840
|
+
g = polar_plot(sqrt, 0, 2 * pi, fill=True)
|
|
2841
|
+
sphinx_plot(g)
|
|
2842
|
+
|
|
2843
|
+
Fill the area between two functions::
|
|
2844
|
+
|
|
2845
|
+
sage: polar_plot(cos(4*x) + 1.5, 0, 2*pi, fill=0.5 * cos(4*x) + 2.5,
|
|
2846
|
+
....: fillcolor='orange')
|
|
2847
|
+
Graphics object consisting of 2 graphics primitives
|
|
2848
|
+
|
|
2849
|
+
.. PLOT::
|
|
2850
|
+
|
|
2851
|
+
g = polar_plot(cos(4*x) + 1.5, 0, 2*pi, fill=0.5 * cos(4*x) + 2.5, fillcolor='orange')
|
|
2852
|
+
sphinx_plot(g)
|
|
2853
|
+
|
|
2854
|
+
Fill the area between several spirals::
|
|
2855
|
+
|
|
2856
|
+
sage: polar_plot([(1.2+k*0.2)*log(x) for k in range(6)], 1, 3 * pi,
|
|
2857
|
+
....: fill={0: [1], 2: [3], 4: [5]})
|
|
2858
|
+
Graphics object consisting of 9 graphics primitives
|
|
2859
|
+
|
|
2860
|
+
.. PLOT::
|
|
2861
|
+
|
|
2862
|
+
g = polar_plot([(1.2+k*0.2)*log(x) for k in range(6)], 1, 3 * pi, fill={0: [1], 2: [3], 4: [5]})
|
|
2863
|
+
sphinx_plot(g)
|
|
2864
|
+
|
|
2865
|
+
Exclude points at discontinuities::
|
|
2866
|
+
|
|
2867
|
+
sage: polar_plot(log(floor(x)), (x, 1, 4*pi), exclude=[1..12])
|
|
2868
|
+
Graphics object consisting of 12 graphics primitives
|
|
2869
|
+
|
|
2870
|
+
.. PLOT::
|
|
2871
|
+
|
|
2872
|
+
g = polar_plot(log(floor(x)), (x, 1, 4*pi), exclude=list(range(1,13)))
|
|
2873
|
+
sphinx_plot(g)
|
|
2874
|
+
"""
|
|
2875
|
+
kwds['polar'] = True
|
|
2876
|
+
return plot(funcs, *args, **kwds)
|
|
2877
|
+
|
|
2878
|
+
|
|
2879
|
+
@options(aspect_ratio='automatic')
|
|
2880
|
+
def list_plot(data, plotjoined=False, **kwargs):
|
|
2881
|
+
r"""
|
|
2882
|
+
``list_plot`` takes either a list of numbers, a list of tuples, a numpy
|
|
2883
|
+
array, or a dictionary and plots the corresponding points.
|
|
2884
|
+
|
|
2885
|
+
If given a list of numbers (that is, not a list of tuples or lists),
|
|
2886
|
+
``list_plot`` forms a list of tuples ``(i, x_i)`` where ``i`` goes from
|
|
2887
|
+
0 to ``len(data)-1`` and ``x_i`` is the ``i``-th data value, and puts
|
|
2888
|
+
points at those tuple values.
|
|
2889
|
+
|
|
2890
|
+
``list_plot`` will plot a list of complex numbers in the obvious
|
|
2891
|
+
way; any numbers for which
|
|
2892
|
+
:func:`CC()<sage.rings.complex_mpfr.ComplexField>` makes sense will
|
|
2893
|
+
work.
|
|
2894
|
+
|
|
2895
|
+
``list_plot`` also takes a list of tuples ``(x_i, y_i)`` where ``x_i``
|
|
2896
|
+
and ``y_i`` are the ``i``-th values representing the ``x``- and
|
|
2897
|
+
``y``-values, respectively.
|
|
2898
|
+
|
|
2899
|
+
If given a dictionary, ``list_plot`` interprets the keys as
|
|
2900
|
+
`x`-values and the values as `y`-values.
|
|
2901
|
+
|
|
2902
|
+
The ``plotjoined=True`` option tells ``list_plot`` to plot a line
|
|
2903
|
+
joining all the data.
|
|
2904
|
+
|
|
2905
|
+
For other keyword options that the ``list_plot`` function can
|
|
2906
|
+
take, refer to :func:`~sage.plot.plot.plot`.
|
|
2907
|
+
|
|
2908
|
+
It is possible to pass empty dictionaries, lists, or tuples to
|
|
2909
|
+
``list_plot``. Doing so will plot nothing (returning an empty plot).
|
|
2910
|
+
|
|
2911
|
+
EXAMPLES::
|
|
2912
|
+
|
|
2913
|
+
sage: list_plot([i^2 for i in range(5)]) # long time
|
|
2914
|
+
Graphics object consisting of 1 graphics primitive
|
|
2915
|
+
|
|
2916
|
+
.. PLOT::
|
|
2917
|
+
|
|
2918
|
+
g = list_plot([i**2 for i in range(5)]) # long time
|
|
2919
|
+
sphinx_plot(g)
|
|
2920
|
+
|
|
2921
|
+
Here are a bunch of random red points::
|
|
2922
|
+
|
|
2923
|
+
sage: r = [(random(),random()) for _ in range(20)]
|
|
2924
|
+
sage: list_plot(r, color='red')
|
|
2925
|
+
Graphics object consisting of 1 graphics primitive
|
|
2926
|
+
|
|
2927
|
+
.. PLOT::
|
|
2928
|
+
|
|
2929
|
+
r = [(random(),random()) for _ in range(20)]
|
|
2930
|
+
g = list_plot(r, color='red')
|
|
2931
|
+
sphinx_plot(g)
|
|
2932
|
+
|
|
2933
|
+
This gives all the random points joined in a purple line::
|
|
2934
|
+
|
|
2935
|
+
sage: list_plot(r, plotjoined=True, color='purple')
|
|
2936
|
+
Graphics object consisting of 1 graphics primitive
|
|
2937
|
+
|
|
2938
|
+
.. PLOT::
|
|
2939
|
+
|
|
2940
|
+
r = [(random(),random()) for _ in range(20)]
|
|
2941
|
+
g = list_plot(r, plotjoined=True, color='purple')
|
|
2942
|
+
sphinx_plot(g)
|
|
2943
|
+
|
|
2944
|
+
You can provide a numpy array.::
|
|
2945
|
+
|
|
2946
|
+
sage: import numpy # needs numpy
|
|
2947
|
+
sage: list_plot(numpy.arange(10)) # needs numpy
|
|
2948
|
+
Graphics object consisting of 1 graphics primitive
|
|
2949
|
+
|
|
2950
|
+
.. PLOT::
|
|
2951
|
+
|
|
2952
|
+
import numpy
|
|
2953
|
+
g = list_plot(numpy.arange(10))
|
|
2954
|
+
sphinx_plot(g)
|
|
2955
|
+
|
|
2956
|
+
::
|
|
2957
|
+
|
|
2958
|
+
sage: list_plot(numpy.array([[1,2], [2,3], [3,4]])) # needs numpy
|
|
2959
|
+
Graphics object consisting of 1 graphics primitive
|
|
2960
|
+
|
|
2961
|
+
.. PLOT::
|
|
2962
|
+
|
|
2963
|
+
import numpy
|
|
2964
|
+
g = list_plot(numpy.array([[1,2], [2,3], [3,4]]))
|
|
2965
|
+
sphinx_plot(g)
|
|
2966
|
+
|
|
2967
|
+
Plot a list of complex numbers::
|
|
2968
|
+
|
|
2969
|
+
sage: list_plot([1, I, pi + I/2, CC(.25, .25)])
|
|
2970
|
+
Graphics object consisting of 1 graphics primitive
|
|
2971
|
+
|
|
2972
|
+
.. PLOT::
|
|
2973
|
+
|
|
2974
|
+
g = list_plot([1, I, pi + I/2, CC(.25, .25)])
|
|
2975
|
+
sphinx_plot(g)
|
|
2976
|
+
|
|
2977
|
+
::
|
|
2978
|
+
|
|
2979
|
+
sage: list_plot([exp(I*theta) for theta in [0, .2..pi]])
|
|
2980
|
+
Graphics object consisting of 1 graphics primitive
|
|
2981
|
+
|
|
2982
|
+
.. PLOT::
|
|
2983
|
+
|
|
2984
|
+
g = list_plot([exp(I*theta) for theta in srange(0,pi,0.2)])
|
|
2985
|
+
sphinx_plot(g)
|
|
2986
|
+
|
|
2987
|
+
Note that if your list of complex numbers are all actually real,
|
|
2988
|
+
they get plotted as real values, so this
|
|
2989
|
+
|
|
2990
|
+
::
|
|
2991
|
+
|
|
2992
|
+
sage: list_plot([CDF(1), CDF(1/2), CDF(1/3)])
|
|
2993
|
+
Graphics object consisting of 1 graphics primitive
|
|
2994
|
+
|
|
2995
|
+
.. PLOT::
|
|
2996
|
+
|
|
2997
|
+
g = list_plot([CDF(1), CDF(1/2), CDF(1/3)])
|
|
2998
|
+
sphinx_plot(g)
|
|
2999
|
+
|
|
3000
|
+
is the same as ``list_plot([1, 1/2, 1/3])`` -- it produces a plot of
|
|
3001
|
+
the points `(0,1)`, `(1,1/2)`, and `(2,1/3)`.
|
|
3002
|
+
|
|
3003
|
+
If you have separate lists of `x` values and `y` values which you
|
|
3004
|
+
want to plot against each other, use the ``zip`` command to make a
|
|
3005
|
+
single list whose entries are pairs of `(x,y)` values, and feed
|
|
3006
|
+
the result into ``list_plot``::
|
|
3007
|
+
|
|
3008
|
+
sage: x_coords = [cos(t)^3 for t in srange(0, 2*pi, 0.02)]
|
|
3009
|
+
sage: y_coords = [sin(t)^3 for t in srange(0, 2*pi, 0.02)]
|
|
3010
|
+
sage: list_plot(list(zip(x_coords, y_coords)))
|
|
3011
|
+
Graphics object consisting of 1 graphics primitive
|
|
3012
|
+
|
|
3013
|
+
.. PLOT::
|
|
3014
|
+
|
|
3015
|
+
x_coords = [cos(t)**3 for t in srange(0, 2*pi, 0.02)]
|
|
3016
|
+
y_coords = [sin(t)**3 for t in srange(0, 2*pi, 0.02)]
|
|
3017
|
+
g = list_plot(list(zip(x_coords, y_coords)))
|
|
3018
|
+
sphinx_plot(g)
|
|
3019
|
+
|
|
3020
|
+
If instead you try to pass the two lists as separate arguments,
|
|
3021
|
+
you will get an error message::
|
|
3022
|
+
|
|
3023
|
+
sage: list_plot(x_coords, y_coords)
|
|
3024
|
+
Traceback (most recent call last):
|
|
3025
|
+
...
|
|
3026
|
+
TypeError: The second argument 'plotjoined' should be boolean (True or False).
|
|
3027
|
+
If you meant to plot two lists 'x' and 'y' against each other,
|
|
3028
|
+
use 'list_plot(list(zip(x,y)))'.
|
|
3029
|
+
|
|
3030
|
+
Dictionaries with numeric keys and values can be plotted::
|
|
3031
|
+
|
|
3032
|
+
sage: list_plot({22: 3365, 27: 3295, 37: 3135, 42: 3020, 47: 2880, 52: 2735, 57: 2550})
|
|
3033
|
+
Graphics object consisting of 1 graphics primitive
|
|
3034
|
+
|
|
3035
|
+
.. PLOT::
|
|
3036
|
+
|
|
3037
|
+
g = list_plot({22: 3365, 27: 3295, 37: 3135, 42: 3020, 47: 2880, 52: 2735, 57: 2550})
|
|
3038
|
+
sphinx_plot(g)
|
|
3039
|
+
|
|
3040
|
+
Plotting in logarithmic scale is possible for 2D list plots.
|
|
3041
|
+
There are two different syntaxes available::
|
|
3042
|
+
|
|
3043
|
+
sage: yl = [2**k for k in range(20)]
|
|
3044
|
+
sage: list_plot(yl, scale='semilogy') # long time # log axis on vertical
|
|
3045
|
+
Graphics object consisting of 1 graphics primitive
|
|
3046
|
+
|
|
3047
|
+
.. PLOT::
|
|
3048
|
+
|
|
3049
|
+
yl = [2**k for k in range(20)]
|
|
3050
|
+
g = list_plot(yl, scale='semilogy') # long time # log axis on vertical
|
|
3051
|
+
sphinx_plot(g)
|
|
3052
|
+
|
|
3053
|
+
::
|
|
3054
|
+
|
|
3055
|
+
sage: list_plot_semilogy(yl) # same
|
|
3056
|
+
Graphics object consisting of 1 graphics primitive
|
|
3057
|
+
|
|
3058
|
+
.. warning::
|
|
3059
|
+
|
|
3060
|
+
If ``plotjoined`` is ``False`` then the axis that is in log scale
|
|
3061
|
+
must have all points strictly positive. For instance, the following
|
|
3062
|
+
plot will show no points in the figure since the points in the
|
|
3063
|
+
horizontal axis starts from `(0,1)`. Further, matplotlib will display
|
|
3064
|
+
a user warning.
|
|
3065
|
+
|
|
3066
|
+
::
|
|
3067
|
+
|
|
3068
|
+
sage: list_plot(yl, scale='loglog') # both axes are log
|
|
3069
|
+
doctest:warning
|
|
3070
|
+
...
|
|
3071
|
+
Graphics object consisting of 1 graphics primitive
|
|
3072
|
+
|
|
3073
|
+
Instead this will work. We drop the point `(0,1)`.::
|
|
3074
|
+
|
|
3075
|
+
sage: list_plot(list(zip(range(1,len(yl)), yl[1:])), scale='loglog') # long time
|
|
3076
|
+
Graphics object consisting of 1 graphics primitive
|
|
3077
|
+
|
|
3078
|
+
We use :func:`list_plot_loglog` and plot in a different base.::
|
|
3079
|
+
|
|
3080
|
+
sage: list_plot_loglog(list(zip(range(1,len(yl)), yl[1:])), base=2) # long time
|
|
3081
|
+
Graphics object consisting of 1 graphics primitive
|
|
3082
|
+
|
|
3083
|
+
.. PLOT::
|
|
3084
|
+
|
|
3085
|
+
yl = [2**k for k in range(20)]
|
|
3086
|
+
g = list_plot_loglog(list(zip(range(1,len(yl)), yl[1:])), base=2) # long time
|
|
3087
|
+
sphinx_plot(g)
|
|
3088
|
+
|
|
3089
|
+
We can also change the scale of the axes in the graphics just before
|
|
3090
|
+
displaying::
|
|
3091
|
+
|
|
3092
|
+
sage: G = list_plot(yl) # long time
|
|
3093
|
+
sage: G.show(scale=('semilogy', 2)) # long time
|
|
3094
|
+
|
|
3095
|
+
TESTS:
|
|
3096
|
+
|
|
3097
|
+
We check to see whether elements of the Symbolic Ring are properly
|
|
3098
|
+
handled; see :issue:`16378` ::
|
|
3099
|
+
|
|
3100
|
+
sage: list_plot([1+I, 2+I])
|
|
3101
|
+
Graphics object consisting of 1 graphics primitive
|
|
3102
|
+
|
|
3103
|
+
sage: list_plot([1+I, 2, CC(3+I)])
|
|
3104
|
+
Graphics object consisting of 1 graphics primitive
|
|
3105
|
+
|
|
3106
|
+
sage: list_plot([2, SR(1), CC(1+i)])
|
|
3107
|
+
Graphics object consisting of 1 graphics primitive
|
|
3108
|
+
|
|
3109
|
+
We check to see that the x/y min/max data are set correctly::
|
|
3110
|
+
|
|
3111
|
+
sage: d = list_plot([(100,100), (120, 120)]).get_minmax_data()
|
|
3112
|
+
sage: d['xmin']
|
|
3113
|
+
100.0
|
|
3114
|
+
sage: d['ymin']
|
|
3115
|
+
100.0
|
|
3116
|
+
|
|
3117
|
+
Verify that :issue:`38037` is fixed::
|
|
3118
|
+
|
|
3119
|
+
sage: list_plot([(0,-1),(1,-2),(2,-3),(3,-4),(4,None)])
|
|
3120
|
+
Traceback (most recent call last):
|
|
3121
|
+
...
|
|
3122
|
+
TypeError: unable to coerce to a ComplexNumber:
|
|
3123
|
+
<class 'sage.rings.integer.Integer'>
|
|
3124
|
+
|
|
3125
|
+
Test the codepath where ``list_enumerated`` is ``False``::
|
|
3126
|
+
|
|
3127
|
+
sage: list_plot([3+I, 4, I, 1+5*i, None, 1+i])
|
|
3128
|
+
Graphics object consisting of 1 graphics primitive
|
|
3129
|
+
|
|
3130
|
+
Test the codepath where ``list_enumerated`` is ``True``::
|
|
3131
|
+
|
|
3132
|
+
sage: list_plot([4, 3+I, I, 1+5*i, None, 1+i])
|
|
3133
|
+
Graphics object consisting of 1 graphics primitive
|
|
3134
|
+
"""
|
|
3135
|
+
from sage.plot.all import point
|
|
3136
|
+
try:
|
|
3137
|
+
if not data:
|
|
3138
|
+
return Graphics()
|
|
3139
|
+
except ValueError: # numpy raises ValueError if it is not empty
|
|
3140
|
+
pass
|
|
3141
|
+
if not isinstance(plotjoined, bool):
|
|
3142
|
+
raise TypeError("The second argument 'plotjoined' should be boolean "
|
|
3143
|
+
"(True or False). If you meant to plot two lists 'x' "
|
|
3144
|
+
"and 'y' against each other, use 'list_plot(list(zip(x,y)))'.")
|
|
3145
|
+
if isinstance(data, dict):
|
|
3146
|
+
if plotjoined:
|
|
3147
|
+
list_data = sorted(data.items())
|
|
3148
|
+
else:
|
|
3149
|
+
list_data = list(data.items())
|
|
3150
|
+
return list_plot(list_data, plotjoined=plotjoined, **kwargs)
|
|
3151
|
+
list_enumerated = False
|
|
3152
|
+
try:
|
|
3153
|
+
from sage.rings.real_double import RDF
|
|
3154
|
+
RDF(data[0])
|
|
3155
|
+
data = list(enumerate(data))
|
|
3156
|
+
list_enumerated = True
|
|
3157
|
+
except TypeError:
|
|
3158
|
+
# we can get this TypeError if the element is a list
|
|
3159
|
+
# or tuple or numpy array, or an element of CC, CDF
|
|
3160
|
+
|
|
3161
|
+
# We also want to avoid doing CC(data[0]) here since it will go
|
|
3162
|
+
# through if data[0] is really a tuple and every element of the
|
|
3163
|
+
# data will be converted to a complex and later converted back to
|
|
3164
|
+
# a tuple.
|
|
3165
|
+
# So, the only other check we need to do is whether data[0] is an
|
|
3166
|
+
# element of the Symbolic Ring.
|
|
3167
|
+
if isinstance(data[0], Expression):
|
|
3168
|
+
data = list(enumerate(data))
|
|
3169
|
+
list_enumerated = True
|
|
3170
|
+
|
|
3171
|
+
try:
|
|
3172
|
+
if plotjoined:
|
|
3173
|
+
return line(data, **kwargs)
|
|
3174
|
+
else:
|
|
3175
|
+
return point(data, **kwargs)
|
|
3176
|
+
except (TypeError, IndexError):
|
|
3177
|
+
# Assume we have complex-valued input and plot real and imaginary parts.
|
|
3178
|
+
# Need to catch IndexError because if data is, say, [(0, 1), (1, I)],
|
|
3179
|
+
# point3d() throws an IndexError on the (0,1) before it ever
|
|
3180
|
+
# gets to (1, I).
|
|
3181
|
+
from sage.rings.cc import CC
|
|
3182
|
+
# It is not guaranteed that we enumerated the data so we have two cases
|
|
3183
|
+
if list_enumerated:
|
|
3184
|
+
data = [(z.real(), z.imag()) for z in [CC(z[1]) for z in data]]
|
|
3185
|
+
else:
|
|
3186
|
+
data = [(z.real(), z.imag()) for z in [CC(z) for z in data]]
|
|
3187
|
+
if plotjoined:
|
|
3188
|
+
return line(data, **kwargs)
|
|
3189
|
+
else:
|
|
3190
|
+
return point(data, **kwargs)
|
|
3191
|
+
|
|
3192
|
+
# ------------------------ Graphs on log scale ---------------------------
|
|
3193
|
+
|
|
3194
|
+
|
|
3195
|
+
@options(base=10)
|
|
3196
|
+
def plot_loglog(funcs, *args, **kwds):
|
|
3197
|
+
"""
|
|
3198
|
+
Plot graphics in 'loglog' scale, that is, both the horizontal and the
|
|
3199
|
+
vertical axes will be in logarithmic scale.
|
|
3200
|
+
|
|
3201
|
+
INPUT:
|
|
3202
|
+
|
|
3203
|
+
- ``base`` -- (default: `10`) the base of the logarithm; this must be
|
|
3204
|
+
greater than 1. The base can be also given as a list or tuple
|
|
3205
|
+
``(basex, basey)``. ``basex`` sets the base of the logarithm along the
|
|
3206
|
+
horizontal axis and ``basey`` sets the base along the vertical axis.
|
|
3207
|
+
|
|
3208
|
+
- ``funcs`` -- any Sage object which is acceptable to the :func:`plot`
|
|
3209
|
+
|
|
3210
|
+
For all other inputs, look at the documentation of :func:`plot`.
|
|
3211
|
+
|
|
3212
|
+
EXAMPLES::
|
|
3213
|
+
|
|
3214
|
+
sage: plot_loglog(exp, (1,10)) # plot in loglog scale with base 10
|
|
3215
|
+
Graphics object consisting of 1 graphics primitive
|
|
3216
|
+
|
|
3217
|
+
.. PLOT::
|
|
3218
|
+
|
|
3219
|
+
g = plot_loglog(exp, (1,10)) # plot in loglog scale with base 10
|
|
3220
|
+
sphinx_plot(g)
|
|
3221
|
+
|
|
3222
|
+
::
|
|
3223
|
+
|
|
3224
|
+
sage: plot_loglog(exp, (1,10), base=2.1) # with base 2.1 on both axes # long time
|
|
3225
|
+
Graphics object consisting of 1 graphics primitive
|
|
3226
|
+
|
|
3227
|
+
.. PLOT::
|
|
3228
|
+
|
|
3229
|
+
g = plot_loglog(exp, (1,10), base=2.1)
|
|
3230
|
+
sphinx_plot(g)
|
|
3231
|
+
|
|
3232
|
+
::
|
|
3233
|
+
|
|
3234
|
+
sage: plot_loglog(exp, (1,10), base=(2,3))
|
|
3235
|
+
Graphics object consisting of 1 graphics primitive
|
|
3236
|
+
|
|
3237
|
+
.. PLOT::
|
|
3238
|
+
|
|
3239
|
+
g = plot_loglog(exp, (1,10), base=(2,3))
|
|
3240
|
+
sphinx_plot(g)
|
|
3241
|
+
"""
|
|
3242
|
+
return plot(funcs, *args, scale='loglog', **kwds)
|
|
3243
|
+
|
|
3244
|
+
|
|
3245
|
+
@options(base=10)
|
|
3246
|
+
def plot_semilogx(funcs, *args, **kwds):
|
|
3247
|
+
"""
|
|
3248
|
+
Plot graphics in 'semilogx' scale, that is, the horizontal axis will be
|
|
3249
|
+
in logarithmic scale.
|
|
3250
|
+
|
|
3251
|
+
INPUT:
|
|
3252
|
+
|
|
3253
|
+
- ``base`` -- (default: `10`) the base of the logarithm; this must be
|
|
3254
|
+
greater than 1
|
|
3255
|
+
|
|
3256
|
+
- ``funcs`` -- any Sage object which is acceptable to the :func:`plot`
|
|
3257
|
+
|
|
3258
|
+
For all other inputs, look at the documentation of :func:`plot`.
|
|
3259
|
+
|
|
3260
|
+
EXAMPLES::
|
|
3261
|
+
|
|
3262
|
+
sage: plot_semilogx(exp, (1,10)) # plot in semilogx scale, base 10 # long time
|
|
3263
|
+
Graphics object consisting of 1 graphics primitive
|
|
3264
|
+
|
|
3265
|
+
.. PLOT::
|
|
3266
|
+
|
|
3267
|
+
g = plot_semilogx(exp, (1,10))
|
|
3268
|
+
sphinx_plot(g)
|
|
3269
|
+
|
|
3270
|
+
::
|
|
3271
|
+
|
|
3272
|
+
sage: plot_semilogx(exp, (1,10), base=2) # with base 2
|
|
3273
|
+
Graphics object consisting of 1 graphics primitive
|
|
3274
|
+
|
|
3275
|
+
.. PLOT::
|
|
3276
|
+
|
|
3277
|
+
g = plot_semilogx(exp, (1,10), base=2)
|
|
3278
|
+
sphinx_plot(g)
|
|
3279
|
+
|
|
3280
|
+
::
|
|
3281
|
+
|
|
3282
|
+
sage: s = var('s') # Samples points logarithmically so graph is smooth
|
|
3283
|
+
sage: f = 4000000/(4000000 + 4000*s*i - s*s)
|
|
3284
|
+
sage: plot_semilogx(20*log(abs(f), 10), (s, 1, 1e6))
|
|
3285
|
+
Graphics object consisting of 1 graphics primitive
|
|
3286
|
+
|
|
3287
|
+
.. PLOT::
|
|
3288
|
+
|
|
3289
|
+
s = var('s') # Samples points logarithmically so graph is smooth
|
|
3290
|
+
f = 4000000/(4000000 + 4000*s*i - s*s)
|
|
3291
|
+
g = plot_semilogx(20*log(abs(f), 10), (s, 1, 1e6))
|
|
3292
|
+
sphinx_plot(g)
|
|
3293
|
+
"""
|
|
3294
|
+
return plot(funcs, *args, scale='semilogx', **kwds)
|
|
3295
|
+
|
|
3296
|
+
|
|
3297
|
+
@options(base=10)
|
|
3298
|
+
def plot_semilogy(funcs, *args, **kwds):
|
|
3299
|
+
"""
|
|
3300
|
+
Plot graphics in 'semilogy' scale, that is, the vertical axis will be
|
|
3301
|
+
in logarithmic scale.
|
|
3302
|
+
|
|
3303
|
+
INPUT:
|
|
3304
|
+
|
|
3305
|
+
- ``base`` -- (default: `10`) the base of the logarithm; this must be
|
|
3306
|
+
greater than 1
|
|
3307
|
+
|
|
3308
|
+
- ``funcs`` -- any Sage object which is acceptable to the :func:`plot`
|
|
3309
|
+
|
|
3310
|
+
For all other inputs, look at the documentation of :func:`plot`.
|
|
3311
|
+
|
|
3312
|
+
EXAMPLES::
|
|
3313
|
+
|
|
3314
|
+
sage: plot_semilogy(exp, (1, 10)) # long time # plot in semilogy scale, base 10
|
|
3315
|
+
Graphics object consisting of 1 graphics primitive
|
|
3316
|
+
|
|
3317
|
+
.. PLOT::
|
|
3318
|
+
|
|
3319
|
+
g = plot_semilogy(exp, (1,10)) # long time # plot in semilogy scale, base 10
|
|
3320
|
+
sphinx_plot(g)
|
|
3321
|
+
|
|
3322
|
+
::
|
|
3323
|
+
|
|
3324
|
+
sage: plot_semilogy(exp, (1, 10), base=2) # long time # with base 2
|
|
3325
|
+
Graphics object consisting of 1 graphics primitive
|
|
3326
|
+
|
|
3327
|
+
.. PLOT::
|
|
3328
|
+
|
|
3329
|
+
g = plot_semilogy(exp, (1,10), base=2) # long time # with base 2
|
|
3330
|
+
sphinx_plot(g)
|
|
3331
|
+
"""
|
|
3332
|
+
return plot(funcs, *args, scale='semilogy', **kwds)
|
|
3333
|
+
|
|
3334
|
+
|
|
3335
|
+
@options(base=10)
|
|
3336
|
+
def list_plot_loglog(data, plotjoined=False, **kwds):
|
|
3337
|
+
"""
|
|
3338
|
+
Plot the ``data`` in 'loglog' scale, that is, both the horizontal and the
|
|
3339
|
+
vertical axes will be in logarithmic scale.
|
|
3340
|
+
|
|
3341
|
+
INPUT:
|
|
3342
|
+
|
|
3343
|
+
- ``base`` -- (default: `10`) the base of the logarithm; this must be
|
|
3344
|
+
greater than 1. The base can be also given as a list or tuple
|
|
3345
|
+
``(basex, basey)``. ``basex`` sets the base of the logarithm along the
|
|
3346
|
+
horizontal axis and ``basey`` sets the base along the vertical axis.
|
|
3347
|
+
|
|
3348
|
+
For all other inputs, look at the documentation of :func:`list_plot`.
|
|
3349
|
+
|
|
3350
|
+
EXAMPLES::
|
|
3351
|
+
|
|
3352
|
+
sage: yl = [5**k for k in range(10)]; xl = [2**k for k in range(10)]
|
|
3353
|
+
sage: list_plot_loglog(list(zip(xl, yl))) # use loglog scale with base 10 # long time
|
|
3354
|
+
Graphics object consisting of 1 graphics primitive
|
|
3355
|
+
|
|
3356
|
+
.. PLOT::
|
|
3357
|
+
|
|
3358
|
+
yl = [5**k for k in range(10)]
|
|
3359
|
+
xl = [2**k for k in range(10)]
|
|
3360
|
+
g = list_plot_loglog(list(zip(xl, yl)))
|
|
3361
|
+
sphinx_plot(g)
|
|
3362
|
+
|
|
3363
|
+
::
|
|
3364
|
+
|
|
3365
|
+
sage: list_plot_loglog(list(zip(xl, yl)), # with base 2.1 on both axes # long time
|
|
3366
|
+
....: base=2.1)
|
|
3367
|
+
Graphics object consisting of 1 graphics primitive
|
|
3368
|
+
|
|
3369
|
+
.. PLOT::
|
|
3370
|
+
|
|
3371
|
+
yl = [5**k for k in range(10)]
|
|
3372
|
+
xl = [2**k for k in range(10)]
|
|
3373
|
+
g = list_plot_loglog(list(zip(xl, yl)), base=2.1)
|
|
3374
|
+
sphinx_plot(g)
|
|
3375
|
+
|
|
3376
|
+
::
|
|
3377
|
+
|
|
3378
|
+
sage: list_plot_loglog(list(zip(xl, yl)), base=(2,5)) # long time
|
|
3379
|
+
Graphics object consisting of 1 graphics primitive
|
|
3380
|
+
|
|
3381
|
+
.. warning::
|
|
3382
|
+
|
|
3383
|
+
If ``plotjoined`` is ``False`` then the axis that is in log scale
|
|
3384
|
+
must have all points strictly positive. For instance, the following
|
|
3385
|
+
plot will show no points in the figure since the points in the
|
|
3386
|
+
horizontal axis starts from `(0,1)`.
|
|
3387
|
+
|
|
3388
|
+
::
|
|
3389
|
+
|
|
3390
|
+
sage: yl = [2**k for k in range(20)]
|
|
3391
|
+
sage: list_plot_loglog(yl)
|
|
3392
|
+
Graphics object consisting of 1 graphics primitive
|
|
3393
|
+
|
|
3394
|
+
Instead this will work. We drop the point `(0,1)`.::
|
|
3395
|
+
|
|
3396
|
+
sage: list_plot_loglog(list(zip(range(1,len(yl)), yl[1:])))
|
|
3397
|
+
Graphics object consisting of 1 graphics primitive
|
|
3398
|
+
"""
|
|
3399
|
+
return list_plot(data, plotjoined=plotjoined, scale='loglog', **kwds)
|
|
3400
|
+
|
|
3401
|
+
|
|
3402
|
+
@options(base=10)
|
|
3403
|
+
def list_plot_semilogx(data, plotjoined=False, **kwds):
|
|
3404
|
+
"""
|
|
3405
|
+
Plot ``data`` in 'semilogx' scale, that is, the horizontal axis will be
|
|
3406
|
+
in logarithmic scale.
|
|
3407
|
+
|
|
3408
|
+
INPUT:
|
|
3409
|
+
|
|
3410
|
+
- ``base`` -- (default: `10`) the base of the logarithm; this must be
|
|
3411
|
+
greater than 1
|
|
3412
|
+
|
|
3413
|
+
For all other inputs, look at the documentation of :func:`list_plot`.
|
|
3414
|
+
|
|
3415
|
+
EXAMPLES::
|
|
3416
|
+
|
|
3417
|
+
sage: yl = [2**k for k in range(12)]
|
|
3418
|
+
sage: list_plot_semilogx(list(zip(yl,yl)))
|
|
3419
|
+
Graphics object consisting of 1 graphics primitive
|
|
3420
|
+
|
|
3421
|
+
.. PLOT::
|
|
3422
|
+
|
|
3423
|
+
yl = [2**k for k in range(12)]
|
|
3424
|
+
g = list_plot_semilogx(list(zip(yl,yl)))
|
|
3425
|
+
sphinx_plot(g)
|
|
3426
|
+
|
|
3427
|
+
.. warning::
|
|
3428
|
+
|
|
3429
|
+
If ``plotjoined`` is ``False`` then the horizontal axis must have all
|
|
3430
|
+
points strictly positive. Otherwise the plot will come up empty.
|
|
3431
|
+
For instance the following plot contains a point at `(0,1)`.
|
|
3432
|
+
|
|
3433
|
+
::
|
|
3434
|
+
|
|
3435
|
+
sage: yl = [2**k for k in range(12)]
|
|
3436
|
+
sage: list_plot_semilogx(yl) # plot empty due to (0,1)
|
|
3437
|
+
Graphics object consisting of 1 graphics primitive
|
|
3438
|
+
|
|
3439
|
+
We remove `(0,1)` to fix this.::
|
|
3440
|
+
|
|
3441
|
+
sage: list_plot_semilogx(list(zip(range(1, len(yl)), yl[1:])))
|
|
3442
|
+
Graphics object consisting of 1 graphics primitive
|
|
3443
|
+
|
|
3444
|
+
::
|
|
3445
|
+
|
|
3446
|
+
sage: list_plot_semilogx([(1,2),(3,4),(3,-1),(25,3)], base=2) # with base 2
|
|
3447
|
+
Graphics object consisting of 1 graphics primitive
|
|
3448
|
+
|
|
3449
|
+
.. PLOT::
|
|
3450
|
+
|
|
3451
|
+
g = list_plot_semilogx([(1,2),(3,4),(3,-1),(25,3)], base=2)
|
|
3452
|
+
sphinx_plot(g)
|
|
3453
|
+
"""
|
|
3454
|
+
return list_plot(data, plotjoined=plotjoined, scale='semilogx', **kwds)
|
|
3455
|
+
|
|
3456
|
+
|
|
3457
|
+
@options(base=10)
|
|
3458
|
+
def list_plot_semilogy(data, plotjoined=False, **kwds):
|
|
3459
|
+
"""
|
|
3460
|
+
Plot ``data`` in 'semilogy' scale, that is, the vertical axis will be
|
|
3461
|
+
in logarithmic scale.
|
|
3462
|
+
|
|
3463
|
+
INPUT:
|
|
3464
|
+
|
|
3465
|
+
- ``base`` -- (default: `10`) the base of the logarithm; this must be
|
|
3466
|
+
greater than 1
|
|
3467
|
+
|
|
3468
|
+
For all other inputs, look at the documentation of :func:`list_plot`.
|
|
3469
|
+
|
|
3470
|
+
EXAMPLES::
|
|
3471
|
+
|
|
3472
|
+
sage: yl = [2**k for k in range(12)]
|
|
3473
|
+
sage: list_plot_semilogy(yl) # plot in semilogy scale, base 10
|
|
3474
|
+
Graphics object consisting of 1 graphics primitive
|
|
3475
|
+
|
|
3476
|
+
.. PLOT::
|
|
3477
|
+
|
|
3478
|
+
yl = [2**k for k in range(12)]
|
|
3479
|
+
g = list_plot_semilogy(yl) # plot in semilogy scale, base 10
|
|
3480
|
+
sphinx_plot(g)
|
|
3481
|
+
|
|
3482
|
+
.. warning::
|
|
3483
|
+
|
|
3484
|
+
If ``plotjoined`` is ``False`` then the vertical axis must have all
|
|
3485
|
+
points strictly positive. Otherwise the plot will come up empty.
|
|
3486
|
+
For instance the following plot contains a point at `(1,0)`. Further,
|
|
3487
|
+
matplotlib will display a user warning.
|
|
3488
|
+
|
|
3489
|
+
::
|
|
3490
|
+
|
|
3491
|
+
sage: xl = [2**k for k in range(12)]; yl = range(len(xl))
|
|
3492
|
+
sage: list_plot_semilogy(list(zip(xl, yl))) # plot empty due to (1,0)
|
|
3493
|
+
doctest:warning
|
|
3494
|
+
...
|
|
3495
|
+
Graphics object consisting of 1 graphics primitive
|
|
3496
|
+
|
|
3497
|
+
We remove `(1,0)` to fix this.::
|
|
3498
|
+
|
|
3499
|
+
sage: list_plot_semilogy(list(zip(xl[1:],yl[1:])))
|
|
3500
|
+
Graphics object consisting of 1 graphics primitive
|
|
3501
|
+
|
|
3502
|
+
::
|
|
3503
|
+
|
|
3504
|
+
sage: list_plot_semilogy([2, 4, 6, 8, 16, 31], base=2) # with base 2
|
|
3505
|
+
Graphics object consisting of 1 graphics primitive
|
|
3506
|
+
|
|
3507
|
+
.. PLOT::
|
|
3508
|
+
|
|
3509
|
+
g = list_plot_semilogy([2, 4, 6, 8, 16, 31], base=2)
|
|
3510
|
+
sphinx_plot(g)
|
|
3511
|
+
"""
|
|
3512
|
+
return list_plot(data, plotjoined=plotjoined, scale='semilogy', **kwds)
|
|
3513
|
+
|
|
3514
|
+
|
|
3515
|
+
def to_float_list(v):
|
|
3516
|
+
"""
|
|
3517
|
+
Given a list or tuple or iterable v, coerce each element of v to a
|
|
3518
|
+
float and make a list out of the result.
|
|
3519
|
+
|
|
3520
|
+
EXAMPLES::
|
|
3521
|
+
|
|
3522
|
+
sage: from sage.plot.plot import to_float_list
|
|
3523
|
+
sage: to_float_list([1,1/2,3])
|
|
3524
|
+
[1.0, 0.5, 3.0]
|
|
3525
|
+
"""
|
|
3526
|
+
return [float(x) for x in v]
|
|
3527
|
+
|
|
3528
|
+
|
|
3529
|
+
def reshape(v, n, m):
|
|
3530
|
+
"""
|
|
3531
|
+
Helper function for creating graphics arrays.
|
|
3532
|
+
|
|
3533
|
+
The input array is flattened and turned into an `n\times m`
|
|
3534
|
+
array, with blank graphics object padded at the end, if
|
|
3535
|
+
necessary.
|
|
3536
|
+
|
|
3537
|
+
INPUT:
|
|
3538
|
+
|
|
3539
|
+
- ``v`` -- list of lists or tuples
|
|
3540
|
+
|
|
3541
|
+
- ``n``, ``m`` -- integers
|
|
3542
|
+
|
|
3543
|
+
OUTPUT: list of lists of graphics objects
|
|
3544
|
+
|
|
3545
|
+
EXAMPLES::
|
|
3546
|
+
|
|
3547
|
+
sage: L = [plot(sin(k*x), (x,-pi,pi)) for k in range(10)]
|
|
3548
|
+
sage: graphics_array(L,3,4) # long time (up to 4s on sage.math, 2012)
|
|
3549
|
+
Graphics Array of size 3 x 4
|
|
3550
|
+
|
|
3551
|
+
::
|
|
3552
|
+
|
|
3553
|
+
sage: M = [[plot(sin(k*x), (x,-pi,pi)) for k in range(3)],
|
|
3554
|
+
....: [plot(cos(j*x), (x,-pi,pi)) for j in [3..5]]]
|
|
3555
|
+
sage: graphics_array(M,6,1) # long time (up to 4s on sage.math, 2012)
|
|
3556
|
+
Graphics Array of size 6 x 1
|
|
3557
|
+
|
|
3558
|
+
TESTS::
|
|
3559
|
+
|
|
3560
|
+
sage: L = [plot(sin(k*x), (x,-pi,pi)) for k in [1..3]]
|
|
3561
|
+
sage: graphics_array(L,0,-1) # indirect doctest
|
|
3562
|
+
Traceback (most recent call last):
|
|
3563
|
+
...
|
|
3564
|
+
ValueError: array sizes must be positive
|
|
3565
|
+
"""
|
|
3566
|
+
if not (n > 0 and m > 0):
|
|
3567
|
+
raise ValueError('array sizes must be positive')
|
|
3568
|
+
G = Graphics()
|
|
3569
|
+
G.axes(False)
|
|
3570
|
+
if len(v) == 0:
|
|
3571
|
+
return [[G]*m]*n
|
|
3572
|
+
|
|
3573
|
+
if not isinstance(v[0], Graphics):
|
|
3574
|
+
# a list of lists -- flatten it
|
|
3575
|
+
v = sum([list(x) for x in v], [])
|
|
3576
|
+
|
|
3577
|
+
# Now v should be a single list.
|
|
3578
|
+
# First, make it have the right length.
|
|
3579
|
+
v = list(v) # do not mutate the argument
|
|
3580
|
+
for i in range(n * m - len(v)):
|
|
3581
|
+
v.append(G)
|
|
3582
|
+
|
|
3583
|
+
# Next, create a list of lists out of it.
|
|
3584
|
+
L = []
|
|
3585
|
+
k = 0
|
|
3586
|
+
for i in range(n):
|
|
3587
|
+
w = []
|
|
3588
|
+
for j in range(m):
|
|
3589
|
+
w.append(v[k])
|
|
3590
|
+
k += 1
|
|
3591
|
+
L.append(w)
|
|
3592
|
+
|
|
3593
|
+
return L
|
|
3594
|
+
|
|
3595
|
+
|
|
3596
|
+
def graphics_array(array, nrows=None, ncols=None):
|
|
3597
|
+
r"""
|
|
3598
|
+
Plot a list of lists (or tuples) of graphics objects on one canvas,
|
|
3599
|
+
arranged as an array.
|
|
3600
|
+
|
|
3601
|
+
INPUT:
|
|
3602
|
+
|
|
3603
|
+
- ``array`` -- either a list of lists of
|
|
3604
|
+
:class:`~sage.plot.graphics.Graphics` elements or a
|
|
3605
|
+
single list of :class:`~sage.plot.graphics.Graphics` elements
|
|
3606
|
+
|
|
3607
|
+
- ``nrows``, ``ncols`` -- (optional) integers. If both are given then
|
|
3608
|
+
the input array is flattened and turned into an ``nrows`` x
|
|
3609
|
+
``ncols`` array, with blank graphics objects padded at the end,
|
|
3610
|
+
if necessary. If only one is specified, the other is chosen
|
|
3611
|
+
automatically.
|
|
3612
|
+
|
|
3613
|
+
OUTPUT: an instance of :class:`~sage.plot.multigraphics.GraphicsArray`
|
|
3614
|
+
|
|
3615
|
+
EXAMPLES:
|
|
3616
|
+
|
|
3617
|
+
Make some plots of `\sin` functions::
|
|
3618
|
+
|
|
3619
|
+
sage: # long time
|
|
3620
|
+
sage: f(x) = sin(x)
|
|
3621
|
+
sage: g(x) = sin(2*x)
|
|
3622
|
+
sage: h(x) = sin(4*x)
|
|
3623
|
+
sage: p1 = plot(f, (-2*pi,2*pi), color=hue(0.5))
|
|
3624
|
+
sage: p2 = plot(g, (-2*pi,2*pi), color=hue(0.9))
|
|
3625
|
+
sage: p3 = parametric_plot((f,g), (0,2*pi), color=hue(0.6))
|
|
3626
|
+
sage: p4 = parametric_plot((f,h), (0,2*pi), color=hue(1.0))
|
|
3627
|
+
|
|
3628
|
+
Now make a graphics array out of the plots::
|
|
3629
|
+
|
|
3630
|
+
sage: graphics_array(((p1,p2), (p3,p4))) # long time
|
|
3631
|
+
Graphics Array of size 2 x 2
|
|
3632
|
+
|
|
3633
|
+
.. PLOT::
|
|
3634
|
+
|
|
3635
|
+
def f(x): return sin(x)
|
|
3636
|
+
def g(x): return sin(2*x)
|
|
3637
|
+
def h(x): return sin(4*x)
|
|
3638
|
+
p1 = plot(f, (-2*pi,2*pi), color=hue(0.5))
|
|
3639
|
+
p2 = plot(g, (-2*pi,2*pi), color=hue(0.9))
|
|
3640
|
+
p3 = parametric_plot((f,g), (0,2*pi), color=hue(0.6))
|
|
3641
|
+
p4 = parametric_plot((f,h), (0,2*pi), color=hue(1.0))
|
|
3642
|
+
g = graphics_array(((p1, p2), (p3, p4)))
|
|
3643
|
+
sphinx_plot(g)
|
|
3644
|
+
|
|
3645
|
+
One can also name the array, and then use
|
|
3646
|
+
:meth:`~sage.plot.multigraphics.MultiGraphics.show`
|
|
3647
|
+
or :meth:`~sage.plot.multigraphics.MultiGraphics.save`::
|
|
3648
|
+
|
|
3649
|
+
sage: ga = graphics_array(((p1,p2), (p3,p4))) # long time
|
|
3650
|
+
sage: ga.show() # long time; same output as above
|
|
3651
|
+
|
|
3652
|
+
Here we give only one row::
|
|
3653
|
+
|
|
3654
|
+
sage: p1 = plot(sin, (-4,4))
|
|
3655
|
+
sage: p2 = plot(cos, (-4,4))
|
|
3656
|
+
sage: ga = graphics_array([p1, p2]); ga
|
|
3657
|
+
Graphics Array of size 1 x 2
|
|
3658
|
+
sage: ga.show()
|
|
3659
|
+
|
|
3660
|
+
.. PLOT::
|
|
3661
|
+
|
|
3662
|
+
p1 = plot(sin,(-4,4))
|
|
3663
|
+
p2 = plot(cos,(-4,4))
|
|
3664
|
+
ga = graphics_array([p1, p2])
|
|
3665
|
+
sphinx_plot(ga)
|
|
3666
|
+
|
|
3667
|
+
It is possible to use ``figsize`` to change the size of the plot
|
|
3668
|
+
as a whole::
|
|
3669
|
+
|
|
3670
|
+
sage: x = var('x')
|
|
3671
|
+
sage: L = [plot(sin(k*x), (x,-pi,pi)) for k in [1..3]]
|
|
3672
|
+
sage: ga = graphics_array(L)
|
|
3673
|
+
sage: ga.show(figsize=[5,3]) # smallish and compact
|
|
3674
|
+
|
|
3675
|
+
.. PLOT::
|
|
3676
|
+
|
|
3677
|
+
ga = graphics_array([plot(sin(k*x), (x,-pi,pi)) for k in range(1, 4)])
|
|
3678
|
+
sphinx_plot(ga, figsize=[5,3])
|
|
3679
|
+
|
|
3680
|
+
::
|
|
3681
|
+
|
|
3682
|
+
sage: ga.show(figsize=[5,7]) # tall and thin; long time
|
|
3683
|
+
|
|
3684
|
+
.. PLOT::
|
|
3685
|
+
|
|
3686
|
+
ga = graphics_array([plot(sin(k*x), (x,-pi,pi)) for k in range(1, 4)])
|
|
3687
|
+
sphinx_plot(ga, figsize=[5,7])
|
|
3688
|
+
|
|
3689
|
+
::
|
|
3690
|
+
|
|
3691
|
+
sage: ga.show(figsize=4) # width=4 inches, height fixed from default aspect ratio
|
|
3692
|
+
|
|
3693
|
+
.. PLOT::
|
|
3694
|
+
|
|
3695
|
+
ga = graphics_array([plot(sin(k*x), (x,-pi,pi)) for k in range(1, 4)])
|
|
3696
|
+
sphinx_plot(ga, figsize=4)
|
|
3697
|
+
|
|
3698
|
+
Specifying only the number of rows or the number of columns
|
|
3699
|
+
computes the other dimension automatically::
|
|
3700
|
+
|
|
3701
|
+
sage: ga = graphics_array([plot(sin)] * 10, nrows=3)
|
|
3702
|
+
sage: ga.nrows(), ga.ncols()
|
|
3703
|
+
(3, 4)
|
|
3704
|
+
sage: ga = graphics_array([plot(sin)] * 10, ncols=3)
|
|
3705
|
+
sage: ga.nrows(), ga.ncols()
|
|
3706
|
+
(4, 3)
|
|
3707
|
+
sage: ga = graphics_array([plot(sin)] * 4, nrows=2)
|
|
3708
|
+
sage: ga.nrows(), ga.ncols()
|
|
3709
|
+
(2, 2)
|
|
3710
|
+
sage: ga = graphics_array([plot(sin)] * 6, ncols=2)
|
|
3711
|
+
sage: ga.nrows(), ga.ncols()
|
|
3712
|
+
(3, 2)
|
|
3713
|
+
|
|
3714
|
+
The options like ``fontsize``, ``scale`` or ``frame`` passed to individual
|
|
3715
|
+
plots are preserved::
|
|
3716
|
+
|
|
3717
|
+
sage: p1 = plot(sin(x^2), (x, 0, 6),
|
|
3718
|
+
....: axes_labels=[r'$\theta$', r'$\sin(\theta^2)$'], fontsize=16)
|
|
3719
|
+
sage: p2 = plot(x^3, (x, 1, 100), axes_labels=[r'$x$', r'$y$'],
|
|
3720
|
+
....: scale='semilogy', frame=True, gridlines='minor')
|
|
3721
|
+
sage: ga = graphics_array([p1, p2])
|
|
3722
|
+
sage: ga.show()
|
|
3723
|
+
|
|
3724
|
+
.. PLOT::
|
|
3725
|
+
|
|
3726
|
+
p1 = plot(sin(x**2), (x, 0, 6), \
|
|
3727
|
+
axes_labels=[r'$\theta$', r'$\sin(\theta^2)$'], fontsize=16)
|
|
3728
|
+
p2 = plot(x**3, (x, 1, 100), axes_labels=[r'$x$', r'$y$'], \
|
|
3729
|
+
scale='semilogy', frame=True, gridlines='minor')
|
|
3730
|
+
ga = graphics_array([p1, p2])
|
|
3731
|
+
sphinx_plot(ga)
|
|
3732
|
+
|
|
3733
|
+
.. SEEALSO::
|
|
3734
|
+
|
|
3735
|
+
:class:`~sage.plot.multigraphics.GraphicsArray` for more examples
|
|
3736
|
+
"""
|
|
3737
|
+
# TODO: refactor the whole array flattening and reshaping into a class
|
|
3738
|
+
if nrows is None and ncols is None:
|
|
3739
|
+
pass
|
|
3740
|
+
elif nrows is not None and ncols is not None:
|
|
3741
|
+
nrows = int(nrows)
|
|
3742
|
+
ncols = int(ncols)
|
|
3743
|
+
array = reshape(array, nrows, ncols)
|
|
3744
|
+
else:
|
|
3745
|
+
# nrows is None xor ncols is None
|
|
3746
|
+
if len(array) > 0 and isinstance(array[0], Graphics):
|
|
3747
|
+
length = len(array)
|
|
3748
|
+
else:
|
|
3749
|
+
length = sum(map(len, array))
|
|
3750
|
+
if nrows is None:
|
|
3751
|
+
ncols = int(ncols)
|
|
3752
|
+
nrows = length // ncols
|
|
3753
|
+
if nrows*ncols < length or nrows == 0:
|
|
3754
|
+
nrows += 1
|
|
3755
|
+
elif ncols is None:
|
|
3756
|
+
nrows = int(nrows)
|
|
3757
|
+
ncols = length // nrows
|
|
3758
|
+
if nrows*ncols < length or ncols == 0:
|
|
3759
|
+
ncols += 1
|
|
3760
|
+
else:
|
|
3761
|
+
assert False
|
|
3762
|
+
array = reshape(array, nrows, ncols)
|
|
3763
|
+
return GraphicsArray(array)
|
|
3764
|
+
|
|
3765
|
+
|
|
3766
|
+
def multi_graphics(graphics_list):
|
|
3767
|
+
r"""
|
|
3768
|
+
Plot a list of graphics at specified positions on a single canvas.
|
|
3769
|
+
|
|
3770
|
+
If the graphics positions define a regular array, use
|
|
3771
|
+
:func:`graphics_array` instead.
|
|
3772
|
+
|
|
3773
|
+
INPUT:
|
|
3774
|
+
|
|
3775
|
+
- ``graphics_list`` -- list of graphics along with their
|
|
3776
|
+
positions on the canvas; each element of ``graphics_list`` is either
|
|
3777
|
+
|
|
3778
|
+
- a pair ``(graphics, position)``, where ``graphics`` is a
|
|
3779
|
+
:class:`~sage.plot.graphics.Graphics` object and ``position`` is
|
|
3780
|
+
the 4-tuple ``(left, bottom, width, height)`` specifying the location
|
|
3781
|
+
and size of the graphics on the canvas, all quantities being in
|
|
3782
|
+
fractions of the canvas width and height
|
|
3783
|
+
|
|
3784
|
+
- or a single :class:`~sage.plot.graphics.Graphics` object; its position
|
|
3785
|
+
is then assumed to occupy the whole canvas, except for some padding;
|
|
3786
|
+
this corresponds to the default position
|
|
3787
|
+
``(left, bottom, width, height) = (0.125, 0.11, 0.775, 0.77)``
|
|
3788
|
+
|
|
3789
|
+
OUTPUT: an instance of :class:`~sage.plot.multigraphics.MultiGraphics`
|
|
3790
|
+
|
|
3791
|
+
EXAMPLES:
|
|
3792
|
+
|
|
3793
|
+
``multi_graphics`` is to be used for plot arrangements that cannot be
|
|
3794
|
+
achieved with :func:`graphics_array`, for instance::
|
|
3795
|
+
|
|
3796
|
+
sage: g1 = plot(sin(x), (x, -10, 10), frame=True)
|
|
3797
|
+
sage: g2 = EllipticCurve([0,0,1,-1,0]).plot(color='red', thickness=2,
|
|
3798
|
+
....: axes_labels=['$x$', '$y$']) \
|
|
3799
|
+
....: + text(r"$y^2 + y = x^3 - x$", (1.2, 2), color='red')
|
|
3800
|
+
sage: g3 = matrix_plot(matrix([[1,3,5,1], [2,4,5,6], [1,3,5,7]]))
|
|
3801
|
+
sage: G = multi_graphics([(g1, (0.125, 0.65, 0.775, 0.3)),
|
|
3802
|
+
....: (g2, (0.125, 0.11, 0.4, 0.4)),
|
|
3803
|
+
....: (g3, (0.55, 0.18, 0.4, 0.3))])
|
|
3804
|
+
sage: G
|
|
3805
|
+
Multigraphics with 3 elements
|
|
3806
|
+
|
|
3807
|
+
.. PLOT::
|
|
3808
|
+
|
|
3809
|
+
g1 = plot(sin(x), (x, -10, 10), frame=True)
|
|
3810
|
+
g2 = EllipticCurve([0,0,1,-1,0]).plot(color='red', thickness=2, \
|
|
3811
|
+
axes_labels=['$x$', '$y$']) \
|
|
3812
|
+
+ text(r"$y^2 + y = x^3 - x$", (1.2, 2), color='red')
|
|
3813
|
+
g3 = matrix_plot(matrix([[1,3,5,1], [2,4,5,6], [1,3,5,7]]))
|
|
3814
|
+
G = multi_graphics([(g1, (0.125, 0.65, 0.775, 0.3)), \
|
|
3815
|
+
(g2, (0.125, 0.11, 0.4, 0.4)), \
|
|
3816
|
+
(g3, (0.55, 0.18, 0.4, 0.3))])
|
|
3817
|
+
sphinx_plot(G)
|
|
3818
|
+
|
|
3819
|
+
An example with a list containing a graphics object without any specified
|
|
3820
|
+
position (the graphics, here ``g3``, occupies then the whole canvas)::
|
|
3821
|
+
|
|
3822
|
+
sage: G = multi_graphics([g3, (g1, (0.4, 0.4, 0.2, 0.2))])
|
|
3823
|
+
sage: G
|
|
3824
|
+
Multigraphics with 2 elements
|
|
3825
|
+
|
|
3826
|
+
.. PLOT::
|
|
3827
|
+
|
|
3828
|
+
g1 = plot(sin(x), (x, -10, 10), frame=True)
|
|
3829
|
+
g3 = matrix_plot(matrix([[1,3,5,1], [2,4,5,6], [1,3,5,7]]))
|
|
3830
|
+
G = multi_graphics([g3, (g1, (0.4, 0.4, 0.2, 0.2))])
|
|
3831
|
+
sphinx_plot(G)
|
|
3832
|
+
|
|
3833
|
+
.. SEEALSO::
|
|
3834
|
+
|
|
3835
|
+
:class:`~sage.plot.multigraphics.MultiGraphics` for more examples
|
|
3836
|
+
"""
|
|
3837
|
+
return MultiGraphics(graphics_list)
|
|
3838
|
+
|
|
3839
|
+
|
|
3840
|
+
def minmax_data(xdata, ydata, dict=False) -> tuple | dict:
|
|
3841
|
+
"""
|
|
3842
|
+
Return the minimums and maximums of ``xdata`` and ``ydata``.
|
|
3843
|
+
|
|
3844
|
+
If dict is ``False``, then minmax_data returns the tuple (xmin, xmax,
|
|
3845
|
+
ymin, ymax); otherwise, it returns a dictionary whose keys are
|
|
3846
|
+
'xmin', 'xmax', 'ymin', and 'ymax' and whose values are the
|
|
3847
|
+
corresponding values.
|
|
3848
|
+
|
|
3849
|
+
EXAMPLES::
|
|
3850
|
+
|
|
3851
|
+
sage: from sage.plot.plot import minmax_data
|
|
3852
|
+
sage: minmax_data([], [])
|
|
3853
|
+
(-1, 1, -1, 1)
|
|
3854
|
+
sage: minmax_data([-1, 2], [4, -3])
|
|
3855
|
+
(-1, 2, -3, 4)
|
|
3856
|
+
sage: minmax_data([1, 2], [4, -3])
|
|
3857
|
+
(1, 2, -3, 4)
|
|
3858
|
+
sage: d = minmax_data([-1, 2], [4, -3], dict=True)
|
|
3859
|
+
sage: list(sorted(d.items()))
|
|
3860
|
+
[('xmax', 2), ('xmin', -1), ('ymax', 4), ('ymin', -3)]
|
|
3861
|
+
sage: d = minmax_data([1, 2], [3, 4], dict=True)
|
|
3862
|
+
sage: list(sorted(d.items()))
|
|
3863
|
+
[('xmax', 2), ('xmin', 1), ('ymax', 4), ('ymin', 3)]
|
|
3864
|
+
"""
|
|
3865
|
+
xmin = min(xdata) if len(xdata) else -1
|
|
3866
|
+
xmax = max(xdata) if len(xdata) else 1
|
|
3867
|
+
ymin = min(ydata) if len(ydata) else -1
|
|
3868
|
+
ymax = max(ydata) if len(ydata) else 1
|
|
3869
|
+
if dict:
|
|
3870
|
+
return {'xmin': xmin, 'xmax': xmax,
|
|
3871
|
+
'ymin': ymin, 'ymax': ymax}
|
|
3872
|
+
|
|
3873
|
+
return xmin, xmax, ymin, ymax
|
|
3874
|
+
|
|
3875
|
+
|
|
3876
|
+
def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01,
|
|
3877
|
+
adaptive_recursion=5, level=0, *, excluded=False):
|
|
3878
|
+
r"""
|
|
3879
|
+
The adaptive refinement algorithm for plotting a function ``f``. See
|
|
3880
|
+
the docstring for plot for a description of the algorithm.
|
|
3881
|
+
|
|
3882
|
+
INPUT:
|
|
3883
|
+
|
|
3884
|
+
- ``f`` -- a function of one variable
|
|
3885
|
+
|
|
3886
|
+
- ``p1``, ``p2`` -- two points to refine between
|
|
3887
|
+
|
|
3888
|
+
- ``adaptive_recursion`` -- (default: `5`) how many
|
|
3889
|
+
levels of recursion to go before giving up when doing adaptive
|
|
3890
|
+
refinement. Setting this to 0 disables adaptive refinement.
|
|
3891
|
+
|
|
3892
|
+
- ``adaptive_tolerance`` -- (default: `0.01`) how large
|
|
3893
|
+
a relative difference should be before the adaptive refinement
|
|
3894
|
+
code considers it significant; see documentation for generate_plot_points
|
|
3895
|
+
for more information. See the documentation for :func:`plot` for more
|
|
3896
|
+
information on how the adaptive refinement algorithm works.
|
|
3897
|
+
|
|
3898
|
+
- ``excluded`` -- (default: ``False``) also return locations where it has been
|
|
3899
|
+
discovered that the function is not defined
|
|
3900
|
+
(y-value will be ``'NaN'`` in this case)
|
|
3901
|
+
|
|
3902
|
+
OUTPUT:
|
|
3903
|
+
|
|
3904
|
+
A list of points to insert between ``p1`` and
|
|
3905
|
+
``p2`` to get a better linear approximation between them.
|
|
3906
|
+
If ``excluded``, also x-values for which the calculation failed are given
|
|
3907
|
+
with ``'NaN'`` as y-value.
|
|
3908
|
+
|
|
3909
|
+
TESTS::
|
|
3910
|
+
|
|
3911
|
+
sage: from sage.plot.plot import adaptive_refinement
|
|
3912
|
+
sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01,
|
|
3913
|
+
....: adaptive_recursion=0)
|
|
3914
|
+
[]
|
|
3915
|
+
sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01)
|
|
3916
|
+
[(0.125*pi, 0.3826834323650898), (0.1875*pi, 0.5555702330196022),
|
|
3917
|
+
(0.25*pi, 0.7071067811865475), (0.3125*pi, 0.8314696123025452),
|
|
3918
|
+
(0.375*pi, 0.9238795325112867), (0.4375*pi, 0.9807852804032304),
|
|
3919
|
+
(0.5*pi, 1.0), (0.5625*pi, 0.9807852804032304), (0.625*pi, 0.9238795325112867),
|
|
3920
|
+
(0.6875*pi, 0.8314696123025455), (0.75*pi, 0.7071067811865476),
|
|
3921
|
+
(0.8125*pi, 0.5555702330196022), (0.875*pi, 0.3826834323650899)]
|
|
3922
|
+
|
|
3923
|
+
This shows that lowering ``adaptive_tolerance`` and raising
|
|
3924
|
+
``adaptive_recursion`` both increase the number of subdivision
|
|
3925
|
+
points, though which one creates more points is heavily
|
|
3926
|
+
dependent upon the function being plotted.
|
|
3927
|
+
|
|
3928
|
+
::
|
|
3929
|
+
|
|
3930
|
+
sage: x = var('x')
|
|
3931
|
+
sage: f(x) = sin(1/x)
|
|
3932
|
+
sage: n1 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.01)); n1
|
|
3933
|
+
15
|
|
3934
|
+
sage: n2 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_recursion=10,
|
|
3935
|
+
....: adaptive_tolerance=0.01)); n2
|
|
3936
|
+
79
|
|
3937
|
+
sage: n3 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.001)); n3
|
|
3938
|
+
26
|
|
3939
|
+
|
|
3940
|
+
Exclusion points will be added if ``excluded`` is set::
|
|
3941
|
+
|
|
3942
|
+
sage: f(x) = 1/x
|
|
3943
|
+
sage: adaptive_refinement(f, (-1, -1), (3, 1/3), adaptive_recursion=2, excluded=False)
|
|
3944
|
+
[(1.0, 1.0), (2.0, 0.5)]
|
|
3945
|
+
sage: adaptive_refinement(f, (-1, -1), (3, 1/3), adaptive_recursion=2, excluded=True)
|
|
3946
|
+
[(0.0, 'NaN'), (1.0, 1.0), (2.0, 0.5)]
|
|
3947
|
+
"""
|
|
3948
|
+
if level >= adaptive_recursion:
|
|
3949
|
+
return []
|
|
3950
|
+
|
|
3951
|
+
x = (p1[0] + p2[0])/2.0
|
|
3952
|
+
msg = ''
|
|
3953
|
+
|
|
3954
|
+
try:
|
|
3955
|
+
y = float(f(x))
|
|
3956
|
+
if str(y) in ['nan', 'NaN', 'inf', '-inf']:
|
|
3957
|
+
sage.misc.verbose.verbose(f"{msg}\nUnable to compute f({x})", 1)
|
|
3958
|
+
# give up for this branch
|
|
3959
|
+
if excluded:
|
|
3960
|
+
return [(x, 'NaN')]
|
|
3961
|
+
return []
|
|
3962
|
+
|
|
3963
|
+
except (ZeroDivisionError, TypeError, ValueError, OverflowError) as msg:
|
|
3964
|
+
sage.misc.verbose.verbose(f"{msg}\nUnable to compute f({x})", 1)
|
|
3965
|
+
# give up for this branch
|
|
3966
|
+
if excluded:
|
|
3967
|
+
return [(x, 'NaN')]
|
|
3968
|
+
return []
|
|
3969
|
+
|
|
3970
|
+
# this distance calculation is not perfect.
|
|
3971
|
+
if abs((p1[1] + p2[1])/2.0 - y) > adaptive_tolerance:
|
|
3972
|
+
ref = adaptive_refinement(f, p1, (x, y),
|
|
3973
|
+
adaptive_tolerance=adaptive_tolerance,
|
|
3974
|
+
adaptive_recursion=adaptive_recursion,
|
|
3975
|
+
level=level+1,
|
|
3976
|
+
excluded=excluded)
|
|
3977
|
+
ref += [(x, y)]
|
|
3978
|
+
ref += adaptive_refinement(f, (x, y), p2,
|
|
3979
|
+
adaptive_tolerance=adaptive_tolerance,
|
|
3980
|
+
adaptive_recursion=adaptive_recursion,
|
|
3981
|
+
level=level+1,
|
|
3982
|
+
excluded=excluded)
|
|
3983
|
+
return ref
|
|
3984
|
+
|
|
3985
|
+
return []
|
|
3986
|
+
|
|
3987
|
+
|
|
3988
|
+
def generate_plot_points(f, xrange, plot_points=5, adaptive_tolerance=0.01,
|
|
3989
|
+
adaptive_recursion=5, randomize=True,
|
|
3990
|
+
initial_points=None, *, excluded=False,
|
|
3991
|
+
imaginary_tolerance=1e-8):
|
|
3992
|
+
r"""
|
|
3993
|
+
Calculate plot points for a function f in the interval xrange. The
|
|
3994
|
+
adaptive refinement algorithm is also automatically invoked with a
|
|
3995
|
+
*relative* adaptive tolerance of adaptive_tolerance; see below.
|
|
3996
|
+
|
|
3997
|
+
INPUT:
|
|
3998
|
+
|
|
3999
|
+
- ``f`` -- a function of one variable
|
|
4000
|
+
|
|
4001
|
+
- ``p1``, ``p2`` -- two points to refine between
|
|
4002
|
+
|
|
4003
|
+
- ``plot_points`` -- (default: 5) the minimal number of plot points. (Note
|
|
4004
|
+
however that in any actual plot a number is passed to this, with default
|
|
4005
|
+
value 200.)
|
|
4006
|
+
|
|
4007
|
+
- ``adaptive_recursion`` -- (default: 5) how many levels of recursion to go
|
|
4008
|
+
before giving up when doing adaptive refinement. Setting this to 0
|
|
4009
|
+
disables adaptive refinement.
|
|
4010
|
+
|
|
4011
|
+
- ``adaptive_tolerance`` -- (default: 0.01) how large the relative difference
|
|
4012
|
+
should be before the adaptive refinement code considers it significant. If
|
|
4013
|
+
the actual difference is greater than adaptive_tolerance*delta, where delta
|
|
4014
|
+
is the initial subinterval size for the given xrange and plot_points, then
|
|
4015
|
+
the algorithm will consider it significant.
|
|
4016
|
+
|
|
4017
|
+
- ``initial_points`` -- (default: ``None``) a list of x-values that should be evaluated
|
|
4018
|
+
|
|
4019
|
+
- ``excluded`` -- (default: ``False``) add a list of discovered x-values, for
|
|
4020
|
+
which ``f`` is not defined
|
|
4021
|
+
|
|
4022
|
+
- ``imaginary_tolerance`` -- (default: ``1e-8``) if an imaginary
|
|
4023
|
+
number arises (due, for example, to numerical issues), this
|
|
4024
|
+
tolerance specifies how large it has to be in magnitude before
|
|
4025
|
+
we raise an error. In other words, imaginary parts smaller than
|
|
4026
|
+
this are ignored in your plot points.
|
|
4027
|
+
|
|
4028
|
+
OUTPUT:
|
|
4029
|
+
|
|
4030
|
+
- a list of points (x, f(x)) in the interval xrange, which approximate
|
|
4031
|
+
the function f.
|
|
4032
|
+
|
|
4033
|
+
- if ``excluded`` a tuple consisting of the above and a list of x-values
|
|
4034
|
+
at which ``f`` is not defined
|
|
4035
|
+
|
|
4036
|
+
TESTS::
|
|
4037
|
+
|
|
4038
|
+
sage: from sage.plot.plot import generate_plot_points
|
|
4039
|
+
sage: generate_plot_points(sin, (0, pi), plot_points=2, adaptive_recursion=0)
|
|
4040
|
+
[(0.0, 0.0), (3.141592653589793, 1.2246...e-16)]
|
|
4041
|
+
|
|
4042
|
+
sage: from sage.plot.plot import generate_plot_points
|
|
4043
|
+
sage: generate_plot_points(lambda x: x^2, (0, 6), plot_points=2, adaptive_recursion=0, initial_points=[1,2,3])
|
|
4044
|
+
[(0.0, 0.0), (1.0, 1.0), (2.0, 4.0), (3.0, 9.0), (6.0, 36.0)]
|
|
4045
|
+
|
|
4046
|
+
The delta remains consistent with ``randomize=False`` and no
|
|
4047
|
+
adaptive recursion::
|
|
4048
|
+
|
|
4049
|
+
sage: pps = generate_plot_points(sin(x).function(x),
|
|
4050
|
+
....: (-pi, pi),
|
|
4051
|
+
....: randomize=False,
|
|
4052
|
+
....: adaptive_recursion=0)
|
|
4053
|
+
sage: [pps[k][0] - pps[k-1][0] for k in range(1,len(pps))] # abs tol 1e-10
|
|
4054
|
+
[1.5707963267948966,
|
|
4055
|
+
1.5707963267948966,
|
|
4056
|
+
1.5707963267948966,
|
|
4057
|
+
1.5707963267948966]
|
|
4058
|
+
|
|
4059
|
+
This shows that lowering adaptive_tolerance and raising
|
|
4060
|
+
adaptive_recursion both increase the number of subdivision points.
|
|
4061
|
+
(Note that which creates more points is heavily dependent on the
|
|
4062
|
+
particular function plotted.)
|
|
4063
|
+
|
|
4064
|
+
::
|
|
4065
|
+
|
|
4066
|
+
sage: x = var('x')
|
|
4067
|
+
sage: f(x) = sin(1/x)
|
|
4068
|
+
sage: [len(generate_plot_points(f, (-pi, pi), plot_points=16, adaptive_tolerance=i, randomize=False)) for i in [0.01, 0.001, 0.0001]]
|
|
4069
|
+
[97, 161, 275]
|
|
4070
|
+
|
|
4071
|
+
sage: [len(generate_plot_points(f, (-pi, pi), plot_points=16, adaptive_recursion=i, randomize=False)) for i in [5, 10, 15]]
|
|
4072
|
+
[97, 499, 2681]
|
|
4073
|
+
|
|
4074
|
+
Excluded x-values will be added, if ``exclusion`` is set::
|
|
4075
|
+
|
|
4076
|
+
sage: generate_plot_points(log, (0, 1), plot_points=2, adaptive_recursion=0)
|
|
4077
|
+
[(1.0, 0.0)]
|
|
4078
|
+
sage: generate_plot_points(log, (0, 1), plot_points=2, adaptive_recursion=0, excluded=True)
|
|
4079
|
+
([(1.0, 0.0)], [0.0])
|
|
4080
|
+
"""
|
|
4081
|
+
from sage.plot.misc import setup_for_eval_on_grid
|
|
4082
|
+
f, ranges = setup_for_eval_on_grid(f,
|
|
4083
|
+
[xrange],
|
|
4084
|
+
plot_points,
|
|
4085
|
+
imaginary_tolerance=imaginary_tolerance)
|
|
4086
|
+
xmin, xmax, delta = ranges[0]
|
|
4087
|
+
x_values = srange(*ranges[0], include_endpoint=True)
|
|
4088
|
+
|
|
4089
|
+
random = current_randstate().python_random().random
|
|
4090
|
+
|
|
4091
|
+
for i in range(len(x_values)):
|
|
4092
|
+
xi = x_values[i]
|
|
4093
|
+
# Slightly randomize the interior sample points if
|
|
4094
|
+
# randomize is true
|
|
4095
|
+
if randomize and i > 0 and i < plot_points-1:
|
|
4096
|
+
xi += delta*(random() - 0.5)
|
|
4097
|
+
x_values[i] = xi
|
|
4098
|
+
|
|
4099
|
+
# add initial points
|
|
4100
|
+
if isinstance(initial_points, list):
|
|
4101
|
+
x_values = sorted(x_values + initial_points)
|
|
4102
|
+
|
|
4103
|
+
data = [None]*len(x_values)
|
|
4104
|
+
|
|
4105
|
+
exceptions = 0
|
|
4106
|
+
exception_indices = []
|
|
4107
|
+
for i in range(len(x_values)):
|
|
4108
|
+
xi = x_values[i]
|
|
4109
|
+
|
|
4110
|
+
try:
|
|
4111
|
+
data[i] = (float(xi), float(f(xi)))
|
|
4112
|
+
if str(data[i][1]) in ['nan', 'NaN', 'inf', '-inf']:
|
|
4113
|
+
msg = "Unable to compute f(%s)" % xi
|
|
4114
|
+
sage.misc.verbose.verbose(msg, 1)
|
|
4115
|
+
exceptions += 1
|
|
4116
|
+
exception_indices.append(i)
|
|
4117
|
+
|
|
4118
|
+
except (ArithmeticError, TypeError, ValueError) as m:
|
|
4119
|
+
sage.misc.verbose.verbose(f"{m}\nUnable to compute f({xi})", 1)
|
|
4120
|
+
|
|
4121
|
+
if i == 0: # Given an error for left endpoint, try to move it in slightly
|
|
4122
|
+
for j in range(1, 99):
|
|
4123
|
+
xj = xi + delta*j/100.0
|
|
4124
|
+
try:
|
|
4125
|
+
data[i] = (float(xj), float(f(xj)))
|
|
4126
|
+
# nan != nan
|
|
4127
|
+
if data[i][1] != data[i][1]:
|
|
4128
|
+
continue
|
|
4129
|
+
break
|
|
4130
|
+
except (ArithmeticError, TypeError, ValueError):
|
|
4131
|
+
pass
|
|
4132
|
+
else:
|
|
4133
|
+
msg = m
|
|
4134
|
+
exceptions += 1
|
|
4135
|
+
exception_indices.append(i)
|
|
4136
|
+
|
|
4137
|
+
elif i == plot_points-1: # Given an error for right endpoint, try to move it in slightly
|
|
4138
|
+
for j in range(1, 99):
|
|
4139
|
+
xj = xi - delta*j/100.0
|
|
4140
|
+
try:
|
|
4141
|
+
data[i] = (float(xj), float(f(xj)))
|
|
4142
|
+
# nan != nan
|
|
4143
|
+
if data[i][1] != data[i][1]:
|
|
4144
|
+
continue
|
|
4145
|
+
break
|
|
4146
|
+
except (ArithmeticError, TypeError, ValueError):
|
|
4147
|
+
pass
|
|
4148
|
+
else:
|
|
4149
|
+
msg = m
|
|
4150
|
+
exceptions += 1
|
|
4151
|
+
exception_indices.append(i)
|
|
4152
|
+
else:
|
|
4153
|
+
msg = m
|
|
4154
|
+
exceptions += 1
|
|
4155
|
+
exception_indices.append(i)
|
|
4156
|
+
|
|
4157
|
+
data = [data[i] for i in range(len(data)) if i not in exception_indices]
|
|
4158
|
+
excluded_points = [x_values[i] for i in exception_indices]
|
|
4159
|
+
|
|
4160
|
+
# calls adaptive refinement
|
|
4161
|
+
i, j = 0, 0
|
|
4162
|
+
adaptive_tolerance = delta * float(adaptive_tolerance)
|
|
4163
|
+
adaptive_recursion = int(adaptive_recursion)
|
|
4164
|
+
|
|
4165
|
+
while i < len(data) - 1:
|
|
4166
|
+
for p in adaptive_refinement(f, data[i], data[i+1],
|
|
4167
|
+
adaptive_tolerance=adaptive_tolerance,
|
|
4168
|
+
adaptive_recursion=adaptive_recursion,
|
|
4169
|
+
excluded=True):
|
|
4170
|
+
if p[1] == "NaN":
|
|
4171
|
+
excluded_points.append(p[0])
|
|
4172
|
+
else:
|
|
4173
|
+
data.insert(i+1, p)
|
|
4174
|
+
i += 1
|
|
4175
|
+
i += 1
|
|
4176
|
+
|
|
4177
|
+
if (len(data) == 0 and exceptions > 0) or exceptions > 10:
|
|
4178
|
+
sage.misc.verbose.verbose("WARNING: When plotting, failed to evaluate function at %s points." % exceptions, level=0)
|
|
4179
|
+
sage.misc.verbose.verbose("Last error message: '%s'" % msg, level=0)
|
|
4180
|
+
|
|
4181
|
+
if excluded:
|
|
4182
|
+
return data, excluded_points
|
|
4183
|
+
return data
|