passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.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.

Files changed (81) hide show
  1. passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
  2. passagemath_plot-10.6.31rc3.dist-info/RECORD +81 -0
  3. passagemath_plot-10.6.31rc3.dist-info/WHEEL +6 -0
  4. passagemath_plot-10.6.31rc3.dist-info/top_level.txt +2 -0
  5. passagemath_plot.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
  6. passagemath_plot.libs/libgsl-e3525837.so.28.0.0 +0 -0
  7. passagemath_plot.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
  8. sage/all__sagemath_plot.py +15 -0
  9. sage/ext_data/threejs/animation.css +195 -0
  10. sage/ext_data/threejs/animation.html +85 -0
  11. sage/ext_data/threejs/animation.js +273 -0
  12. sage/ext_data/threejs/fat_lines.js +48 -0
  13. sage/ext_data/threejs/threejs-version.txt +1 -0
  14. sage/ext_data/threejs/threejs_template.html +597 -0
  15. sage/interfaces/all__sagemath_plot.py +1 -0
  16. sage/interfaces/gnuplot.py +196 -0
  17. sage/interfaces/jmoldata.py +208 -0
  18. sage/interfaces/povray.py +56 -0
  19. sage/plot/all.py +42 -0
  20. sage/plot/animate.py +1796 -0
  21. sage/plot/arc.py +504 -0
  22. sage/plot/arrow.py +671 -0
  23. sage/plot/bar_chart.py +205 -0
  24. sage/plot/bezier_path.py +400 -0
  25. sage/plot/circle.py +435 -0
  26. sage/plot/colors.py +1606 -0
  27. sage/plot/complex_plot.cpython-314-aarch64-linux-gnu.so +0 -0
  28. sage/plot/complex_plot.pyx +1446 -0
  29. sage/plot/contour_plot.py +1792 -0
  30. sage/plot/density_plot.py +318 -0
  31. sage/plot/disk.py +373 -0
  32. sage/plot/ellipse.py +375 -0
  33. sage/plot/graphics.py +3580 -0
  34. sage/plot/histogram.py +354 -0
  35. sage/plot/hyperbolic_arc.py +404 -0
  36. sage/plot/hyperbolic_polygon.py +416 -0
  37. sage/plot/hyperbolic_regular_polygon.py +296 -0
  38. sage/plot/line.py +626 -0
  39. sage/plot/matrix_plot.py +629 -0
  40. sage/plot/misc.py +509 -0
  41. sage/plot/multigraphics.py +1294 -0
  42. sage/plot/plot.py +4183 -0
  43. sage/plot/plot3d/all.py +23 -0
  44. sage/plot/plot3d/base.cpython-314-aarch64-linux-gnu.so +0 -0
  45. sage/plot/plot3d/base.pxd +12 -0
  46. sage/plot/plot3d/base.pyx +3378 -0
  47. sage/plot/plot3d/implicit_plot3d.py +659 -0
  48. sage/plot/plot3d/implicit_surface.cpython-314-aarch64-linux-gnu.so +0 -0
  49. sage/plot/plot3d/implicit_surface.pyx +1453 -0
  50. sage/plot/plot3d/index_face_set.cpython-314-aarch64-linux-gnu.so +0 -0
  51. sage/plot/plot3d/index_face_set.pxd +32 -0
  52. sage/plot/plot3d/index_face_set.pyx +1873 -0
  53. sage/plot/plot3d/introduction.py +131 -0
  54. sage/plot/plot3d/list_plot3d.py +649 -0
  55. sage/plot/plot3d/parametric_plot3d.py +1130 -0
  56. sage/plot/plot3d/parametric_surface.cpython-314-aarch64-linux-gnu.so +0 -0
  57. sage/plot/plot3d/parametric_surface.pxd +12 -0
  58. sage/plot/plot3d/parametric_surface.pyx +893 -0
  59. sage/plot/plot3d/platonic.py +601 -0
  60. sage/plot/plot3d/plot3d.py +1442 -0
  61. sage/plot/plot3d/plot_field3d.py +162 -0
  62. sage/plot/plot3d/point_c.pxi +148 -0
  63. sage/plot/plot3d/revolution_plot3d.py +309 -0
  64. sage/plot/plot3d/shapes.cpython-314-aarch64-linux-gnu.so +0 -0
  65. sage/plot/plot3d/shapes.pxd +22 -0
  66. sage/plot/plot3d/shapes.pyx +1382 -0
  67. sage/plot/plot3d/shapes2.py +1512 -0
  68. sage/plot/plot3d/tachyon.py +1779 -0
  69. sage/plot/plot3d/texture.py +453 -0
  70. sage/plot/plot3d/transform.cpython-314-aarch64-linux-gnu.so +0 -0
  71. sage/plot/plot3d/transform.pxd +21 -0
  72. sage/plot/plot3d/transform.pyx +268 -0
  73. sage/plot/plot3d/tri_plot.py +589 -0
  74. sage/plot/plot_field.py +362 -0
  75. sage/plot/point.py +624 -0
  76. sage/plot/polygon.py +562 -0
  77. sage/plot/primitive.py +249 -0
  78. sage/plot/scatter_plot.py +199 -0
  79. sage/plot/step.py +85 -0
  80. sage/plot/streamline_plot.py +328 -0
  81. 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