Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/plot/plot.py
4038 views
1
r"""
2
2D Plotting
3
4
Sage provides extensive 2D plotting functionality. The underlying
5
rendering is done using the matplotlib Python library.
6
7
The following graphics primitives are supported:
8
9
10
- :func:`~sage.plot.arrow.arrow` - an arrow from a min point to a max point.
11
12
- :func:`~sage.plot.circle.circle` - a circle with given radius
13
14
- :func:`~sage.plot.ellipse.ellipse` - an ellipse with given radii
15
and angle
16
17
- :func:`~sage.plot.arc.arc` - an arc of a circle or an ellipse
18
19
- :func:`~sage.plot.disk.disk` - a filled disk (i.e. a sector or wedge of a circle)
20
21
- :func:`~sage.plot.line.line` - a line determined by a sequence of points (this need not
22
be straight!)
23
24
- :func:`~sage.plot.point.point` - a point
25
26
- :func:`~sage.plot.text.text` - some text
27
28
- :func:`~sage.plot.polygon.polygon` - a filled polygon
29
30
31
The following plotting functions are supported:
32
33
34
- :func:`plot` - plot of a function or other Sage object (e.g., elliptic
35
curve).
36
37
- :func:`parametric_plot`
38
39
- :func:`~sage.plot.contour_plot.implicit_plot`
40
41
- :func:`polar_plot`
42
43
- :func:`~sage.plot.contour_plot.region_plot`
44
45
- :func:`list_plot`
46
47
- :func:`~sage.plot.scatter_plot.scatter_plot`
48
49
- :func:`~sage.plot.bar_chart.bar_chart`
50
51
- :func:`~sage.plot.contour_plot.contour_plot`
52
53
- :func:`~sage.plot.density_plot.density_plot`
54
55
- :func:`~sage.plot.plot_field.plot_vector_field`
56
57
- :func:`~sage.plot.plot_field.plot_slope_field`
58
59
- :func:`~sage.plot.matrix_plot.matrix_plot`
60
61
- :func:`~sage.plot.complex_plot.complex_plot`
62
63
- :func:`graphics_array`
64
65
66
The following miscellaneous Graphics functions are included:
67
68
69
- :func:`Graphics`
70
71
- :func:`is_Graphics`
72
73
- :func:`~sage.plot.colors.hue`
74
75
76
Type ``?`` after each primitive in Sage for help and examples.
77
78
EXAMPLES:
79
80
We draw a curve::
81
82
sage: plot(x^2, (x,0,5))
83
84
We draw a circle and a curve::
85
86
sage: circle((1,1), 1) + plot(x^2, (x,0,5))
87
88
Notice that the aspect ratio of the above plot makes the plot very tall because
89
the plot adopts the default aspect ratio of the circle (to make the circle appear
90
like a circle). We can change the aspect ratio to be what we normally expect for a plot
91
by explicitly asking for an 'automatic' aspect ratio::
92
93
sage: show(circle((1,1), 1) + plot(x^2, (x,0,5)), aspect_ratio='automatic')
94
95
The aspect ratio describes the apparently height/width ratio of a unit square. If you want the vertical units to be twice as big as the horizontal units, specify an aspect ratio of 2::
96
97
sage: show(circle((1,1), 1) + plot(x^2, (x,0,5)), aspect_ratio=2)
98
99
The ``figsize`` option adjusts the figure size. The default figsize is 4. To make a figure that is roughly twice as big, use ``figsize=8``::
100
101
sage: show(circle((1,1), 1) + plot(x^2, (x,0,5)), figsize=8)
102
103
You can also give separate horizontal and vertical dimensions::
104
105
sage: show(circle((1,1), 1) + plot(x^2, (x,0,5)), figsize=[4,8])
106
107
Note that the axes will not cross if the data is not on both sides of
108
both axes, even if it is quite close::
109
110
sage: plot(x^3,(x,1,10))
111
112
When the labels have quite different orders of magnitude or are very
113
large, scientific notation (the `e` notation for powers of ten) is used::
114
115
sage: plot(x^2,(x,480,500)) # no scientific notation
116
117
::
118
119
sage: plot(x^2,(x,300,500)) # scientific notation on y-axis
120
121
But you can fix your own tick labels, if you know what to expect and
122
have a preference::
123
124
sage: plot(x^2,(x,300,500),ticks=[None,50000])
125
126
We construct a plot involving several graphics objects::
127
128
sage: G = plot(cos(x), (x, -5, 5), thickness=5, color='green')
129
sage: P = polygon([[1,2], [5,6], [5,0]], color='red')
130
sage: G + P
131
132
Next we construct the reflection of the above polygon about the
133
`y`-axis by iterating over the list of first-coordinates of
134
the first graphic element of `P` (which is the actual
135
Polygon; note that `P` is a Graphics object, which consists
136
of a single polygon)::
137
138
sage: Q = polygon([(-x,y) for x,y in P[0]], color='blue')
139
sage: Q # show it
140
141
We combine together different graphics objects using "+"::
142
143
sage: H = G + P + Q
144
sage: print H
145
Graphics object consisting of 3 graphics primitives
146
sage: type(H)
147
<class 'sage.plot.graphics.Graphics'>
148
sage: H[1]
149
Polygon defined by 3 points
150
sage: list(H[1])
151
[(1.0, 2.0), (5.0, 6.0), (5.0, 0.0)]
152
sage: H # show it
153
154
We can put text in a graph::
155
156
sage: L = [[cos(pi*i/100)^3,sin(pi*i/100)] for i in range(200)]
157
sage: p = line(L, rgbcolor=(1/4,1/8,3/4))
158
sage: t = text('A Bulb', (1.5, 0.25))
159
sage: x = text('x axis', (1.5,-0.2))
160
sage: y = text('y axis', (0.4,0.9))
161
sage: g = p+t+x+y
162
sage: g.show(xmin=-1.5, xmax=2, ymin=-1, ymax=1)
163
164
We plot the Riemann zeta function along the critical line and see
165
the first few zeros::
166
167
sage: i = CDF.0 # define i this way for maximum speed.
168
sage: p1 = plot(lambda t: arg(zeta(0.5+t*i)), 1,27,rgbcolor=(0.8,0,0))
169
sage: p2 = plot(lambda t: abs(zeta(0.5+t*i)), 1,27,color=hue(0.7))
170
sage: print p1 + p2
171
Graphics object consisting of 2 graphics primitives
172
sage: p1 + p2 # display it
173
174
Many concentric circles shrinking toward the origin::
175
176
sage: show(sum(circle((i,0), i, hue=sin(i/10)) for i in [10,9.9,..,0]))
177
178
Here is a pretty graph::
179
180
sage: g = Graphics()
181
sage: for i in range(60):
182
... p = polygon([(i*cos(i),i*sin(i)), (0,i), (i,0)],\
183
... color=hue(i/40+0.4), alpha=0.2)
184
... g = g + p
185
...
186
sage: g.show(dpi=200, axes=False)
187
188
Another graph::
189
190
sage: x = var('x')
191
sage: P = plot(sin(x)/x, -4,4, color='blue') + \
192
... plot(x*cos(x), -4,4, color='red') + \
193
... plot(tan(x),-4,4, color='green')
194
...
195
sage: P.show(ymin=-pi,ymax=pi)
196
197
PYX EXAMPLES: These are some examples of plots similar to some of
198
the plots in the PyX (http://pyx.sourceforge.net) documentation:
199
200
Symbolline::
201
202
sage: y(x) = x*sin(x^2)
203
sage: v = [(x, y(x)) for x in [-3,-2.95,..,3]]
204
sage: show(points(v, rgbcolor=(0.2,0.6, 0.1), pointsize=30) + plot(spline(v), -3.1, 3))
205
206
Cycliclink::
207
208
sage: x = var('x')
209
sage: g1 = plot(cos(20*x)*exp(-2*x), 0, 1)
210
sage: g2 = plot(2*exp(-30*x) - exp(-3*x), 0, 1)
211
sage: show(graphics_array([g1, g2], 2, 1), xmin=0)
212
213
Pi Axis::
214
215
sage: g1 = plot(sin(x), 0, 2*pi)
216
sage: g2 = plot(cos(x), 0, 2*pi, linestyle = "--")
217
sage: (g1+g2).show(ticks=pi/6, tick_formatter=pi) # show their sum, nicely formatted
218
219
An illustration of integration::
220
221
sage: f(x) = (x-3)*(x-5)*(x-7)+40
222
sage: P = line([(2,0),(2,f(2))], color='black')
223
sage: P += line([(8,0),(8,f(8))], color='black')
224
sage: P += polygon([(2,0),(2,f(2))] + [(x, f(x)) for x in [2,2.1,..,8]] + [(8,0),(2,0)], rgbcolor=(0.8,0.8,0.8),aspect_ratio='automatic')
225
sage: P += text("$\\int_{a}^b f(x) dx$", (5, 20), fontsize=16, color='black')
226
sage: P += plot(f, (1, 8.5), thickness=3)
227
sage: P # show the result
228
229
230
231
NUMERICAL PLOTTING:
232
233
Sage includes Matplotlib, which provides 2D plotting with an interface
234
that is a likely very familiar to people doing numerical
235
computation. For example,
236
237
::
238
239
sage: from pylab import *
240
sage: t = arange(0.0, 2.0, 0.01)
241
sage: s = sin(2*pi*t)
242
sage: P = plot(t, s, linewidth=1.0)
243
sage: xl = xlabel('time (s)')
244
sage: yl = ylabel('voltage (mV)')
245
sage: t = title('About as simple as it gets, folks')
246
sage: grid(True)
247
sage: savefig(os.path.join(SAGE_TMP, 'sage.png'))
248
249
We test that ``imshow`` works as well, verifying that
250
Trac ticket 2900 is fixed in Matplotlib.
251
252
::
253
254
sage: imshow([[(0,0,0)]])
255
<matplotlib.image.AxesImage object at ...>
256
sage: savefig(os.path.join(SAGE_TMP, 'foo.png'))
257
258
Since the above overwrites many Sage plotting functions, we reset
259
the state of Sage, so that the examples below work!
260
261
::
262
263
sage: reset()
264
265
See http://matplotlib.sourceforge.net for complete documentation
266
about how to use Matplotlib.
267
268
TESTS: We test dumping and loading a plot.
269
270
::
271
272
sage: p = plot(sin(x), (x, 0,2*pi))
273
sage: Q = loads(dumps(p))
274
275
Verify that a clean sage startup does *not* import matplotlib::
276
277
sage: os.system("sage -c \"if 'matplotlib' in sys.modules: sys.exit(1)\"")
278
0
279
280
AUTHORS:
281
282
- Alex Clemesha and William Stein (2006-04-10): initial version
283
284
- David Joyner: examples
285
286
- Alex Clemesha (2006-05-04) major update
287
288
- William Stein (2006-05-29): fine tuning, bug fixes, better server
289
integration
290
291
- William Stein (2006-07-01): misc polish
292
293
- Alex Clemesha (2006-09-29): added contour_plot, frame axes, misc
294
polishing
295
296
- Robert Miller (2006-10-30): tuning, NetworkX primitive
297
298
- Alex Clemesha (2006-11-25): added plot_vector_field, matrix_plot,
299
arrow, bar_chart, Axes class usage (see axes.py)
300
301
- Bobby Moretti and William Stein (2008-01): Change plot to specify
302
ranges using the (varname, min, max) notation.
303
304
- William Stein (2008-01-19): raised the documentation coverage from a
305
miserable 12 percent to a 'wopping' 35 percent, and fixed and
306
clarified numerous small issues.
307
308
- Jason Grout (2009-09-05): shifted axes and grid functionality over
309
to matplotlib; fixed a number of smaller issues.
310
311
- Jason Grout (2010-10): rewrote aspect ratio portions of the code
312
313
- Jeroen Demeyer (2012-04-19): move parts of this file to graphics.py (:trac:`12857`)
314
315
"""
316
#*****************************************************************************
317
# Copyright (C) 2006 Alex Clemesha <[email protected]>
318
# Copyright (C) 2006-2008 William Stein <[email protected]>
319
# Copyright (C) 2010 Jason Grout
320
#
321
# Distributed under the terms of the GNU General Public License (GPL)
322
# as published by the Free Software Foundation; either version 2 of
323
# the License, or (at your option) any later version.
324
# http://www.gnu.org/licenses/
325
#*****************************************************************************
326
327
328
import os
329
330
## IMPORTANT: Do *not* import matplotlib at module scope. It takes a
331
## surprisingly long time to initialize itself. It's better if it is
332
## imported in functions, so it only gets started if it is actually
333
## going to be used.
334
335
#DEFAULT_FIGSIZE=(6, 3.70820393249937)
336
EMBEDDED_MODE = False
337
DOCTEST_MODE = False
338
import sage.misc.misc
339
from sage.misc.misc import srange
340
341
from sage.misc.randstate import current_randstate #for plot adaptive refinement
342
from math import sin, cos, pi #for polar_plot
343
344
from sage.ext.fast_eval import fast_float, fast_float_constant, is_fast_float
345
346
from sage.misc.decorators import options, rename_keyword
347
348
from graphics import Graphics, GraphicsArray
349
350
#Currently not used - see comment immediately above about
351
#figure.canvas.mpl_connect('draw_event', pad_for_tick_labels)
352
# TODO - figure out how to use this, add documentation
353
#def pad_for_tick_labels(event):
354
# import matplotlib.transforms as mtransforms
355
# figure=event.canvas.figure
356
# bboxes = []
357
# for ax in figure.axes:
358
# bbox = ax.xaxis.get_label().get_window_extent()
359
# # the figure transform goes from relative coords->pixels and we
360
# # want the inverse of that
361
# bboxi = bbox.inverse_transformed(figure.transFigure)
362
# bboxes.append(bboxi)
363
#
364
# bbox = ax.yaxis.get_label().get_window_extent()
365
# bboxi = bbox.inverse_transformed(figure.transFigure)
366
# bboxes.append(bboxi)
367
# for label in (ax.get_xticklabels()+ax.get_yticklabels() \
368
# + ax.get_xticklabels(minor=True) \
369
# +ax.get_yticklabels(minor=True)):
370
# bbox = label.get_window_extent()
371
# bboxi = bbox.inverse_transformed(figure.transFigure)
372
# bboxes.append(bboxi)
373
#
374
# # this is the bbox that bounds all the bboxes, again in relative
375
# # figure coords
376
# bbox = mtransforms.Bbox.union(bboxes)
377
# adjusted=adjust_figure_to_contain_bbox(figure,bbox)
378
#
379
# if adjusted:
380
# figure.canvas.draw()
381
# return False
382
#
383
#Currently not used - see comment above about
384
#figure.canvas.mpl_connect('draw_event', pad_for_tick_labels)
385
# TODO - figure out how to use this, add documentation
386
#def adjust_figure_to_contain_bbox(fig, bbox,pad=1.1):
387
# """
388
# For each amount we are over (in axes coordinates), we adjust by over*pad
389
# to give ourselves a bit of padding.
390
# """
391
# left=fig.subplotpars.left
392
# bottom=fig.subplotpars.bottom
393
# right=fig.subplotpars.right
394
# top=fig.subplotpars.top
395
#
396
# adjusted=False
397
# if bbox.xmin<0:
398
# left-=bbox.xmin*pad
399
# adjusted=True
400
# if bbox.ymin<0:
401
# bottom-=bbox.ymin*pad
402
# adjusted=True
403
# if bbox.xmax>1:
404
# right-=(bbox.xmax-1)*pad
405
# adjusted=True
406
# if bbox.ymax>1:
407
# top-=(bbox.ymax-1)*pad
408
# adjusted=True
409
#
410
# if left<right and bottom<top:
411
# fig.subplots_adjust(left=left, bottom=bottom, right=right, top=top)
412
# return adjusted
413
# else:
414
# return False
415
416
_SelectiveFormatterClass = None
417
418
def SelectiveFormatter(formatter, skip_values):
419
"""
420
This matplotlib formatter selectively omits some tick values and
421
passes the rest on to a specified formatter.
422
423
EXAMPLES:
424
425
This example is almost straight from a matplotlib example.
426
427
::
428
429
sage: from sage.plot.plot import SelectiveFormatter
430
sage: import matplotlib.pyplot as plt
431
sage: import numpy
432
sage: fig=plt.figure()
433
sage: ax=fig.add_subplot(111)
434
sage: t = numpy.arange(0.0, 2.0, 0.01)
435
sage: s = numpy.sin(2*numpy.pi*t)
436
sage: p = ax.plot(t, s)
437
sage: formatter=SelectiveFormatter(ax.xaxis.get_major_formatter(),skip_values=[0,1])
438
sage: ax.xaxis.set_major_formatter(formatter)
439
sage: fig.savefig(os.path.join(SAGE_TMP, 'test.png'))
440
"""
441
global _SelectiveFormatterClass
442
if _SelectiveFormatterClass is None:
443
444
from matplotlib.ticker import Formatter
445
446
class _SelectiveFormatterClass(Formatter):
447
def __init__(self, formatter,skip_values):
448
"""
449
Initialize a SelectiveFormatter object.
450
451
INPUT:
452
453
- formatter -- the formatter object to which we should pass labels
454
455
- skip_values -- a list of values that we should skip when
456
formatting the tick labels
457
458
EXAMPLES::
459
460
sage: from sage.plot.plot import SelectiveFormatter
461
sage: import matplotlib.pyplot as plt
462
sage: import numpy
463
sage: fig=plt.figure()
464
sage: ax=fig.add_subplot(111)
465
sage: t = numpy.arange(0.0, 2.0, 0.01)
466
sage: s = numpy.sin(2*numpy.pi*t)
467
sage: line=ax.plot(t, s)
468
sage: formatter=SelectiveFormatter(ax.xaxis.get_major_formatter(),skip_values=[0,1])
469
sage: ax.xaxis.set_major_formatter(formatter)
470
sage: fig.savefig(os.path.join(SAGE_TMP, 'test.png'))
471
"""
472
self.formatter=formatter
473
self.skip_values=skip_values
474
def set_locs(self, locs):
475
"""
476
Set the locations for the ticks that are not skipped.
477
478
EXAMPLES::
479
sage: from sage.plot.plot import SelectiveFormatter
480
sage: import matplotlib.ticker
481
sage: formatter=SelectiveFormatter(matplotlib.ticker.Formatter(),skip_values=[0,200])
482
sage: formatter.set_locs([i*100 for i in range(10)])
483
"""
484
self.formatter.set_locs([l for l in locs if l not in self.skip_values])
485
def __call__(self, x, *args, **kwds):
486
"""
487
Return the format for tick val *x* at position *pos*
488
489
EXAMPLES::
490
491
sage: from sage.plot.plot import SelectiveFormatter
492
sage: import matplotlib.ticker
493
sage: formatter=SelectiveFormatter(matplotlib.ticker.FixedFormatter(['a','b']),skip_values=[0,2])
494
sage: [formatter(i,1) for i in range(10)]
495
['', 'b', '', 'b', 'b', 'b', 'b', 'b', 'b', 'b']
496
"""
497
if x in self.skip_values:
498
return ''
499
else:
500
return self.formatter(x, *args, **kwds)
501
502
return _SelectiveFormatterClass(formatter, skip_values)
503
504
505
def xydata_from_point_list(points):
506
r"""
507
Returns two lists (xdata, ydata), each coerced to a list of floats,
508
which correspond to the x-coordinates and the y-coordinates of the
509
points.
510
511
The points parameter can be a list of 2-tuples or some object that
512
yields a list of one or two numbers.
513
514
This function can potentially be very slow for large point sets.
515
516
TESTS::
517
518
sage: from sage.plot.plot import xydata_from_point_list
519
sage: xydata_from_point_list([CC(0), CC(1)]) # ticket 8082
520
([0.0, 1.0], [0.0, 0.0])
521
522
This function should work for anything than can be turned into a
523
list, such as iterators and such (see ticket #10478)::
524
525
sage: xydata_from_point_list(iter([(0,0), (sqrt(3), 2)]))
526
([0.0, 1.7320508075688772], [0.0, 2.0])
527
sage: xydata_from_point_list((x, x^2) for x in range(5))
528
([0.0, 1.0, 2.0, 3.0, 4.0], [0.0, 1.0, 4.0, 9.0, 16.0])
529
sage: xydata_from_point_list(enumerate(prime_range(1, 15)))
530
([0.0, 1.0, 2.0, 3.0, 4.0, 5.0], [2.0, 3.0, 5.0, 7.0, 11.0, 13.0])
531
sage: from itertools import izip; xydata_from_point_list(izip([2,3,5,7], [11, 13, 17, 19]))
532
([2.0, 3.0, 5.0, 7.0], [11.0, 13.0, 17.0, 19.0])
533
"""
534
from sage.rings.complex_number import ComplexNumber
535
if not isinstance(points, (list,tuple)):
536
points = list(points)
537
try:
538
points = [[float(z) for z in points]]
539
except TypeError:
540
pass
541
elif len(points)==2 and not isinstance(points[0],(list,tuple,ComplexNumber)):
542
try:
543
points = [[float(z) for z in points]]
544
except TypeError:
545
pass
546
547
if len(points)>0 and len(list(points[0]))!=2:
548
raise ValueError, "points must have 2 coordinates in a 2d line"
549
550
551
xdata = [float(z[0]) for z in points]
552
ydata = [float(z[1]) for z in points]
553
554
return xdata, ydata
555
556
@rename_keyword(color='rgbcolor')
557
@options(alpha=1, thickness=1, fill=False, fillcolor='automatic', fillalpha=0.5, rgbcolor=(0,0,1), plot_points=200,
558
adaptive_tolerance=0.01, adaptive_recursion=5, detect_poles = False, exclude = None, legend_label=None,
559
__original_opts=True, aspect_ratio='automatic')
560
def plot(funcs, *args, **kwds):
561
r"""
562
Use plot by writing
563
564
``plot(X, ...)``
565
566
where `X` is a Sage object (or list of Sage objects) that
567
either is callable and returns numbers that can be coerced to
568
floats, or has a plot method that returns a
569
``GraphicPrimitive`` object.
570
571
There are many other specialized 2D plot commands available
572
in Sage, such as ``plot_slope_field``, as well as various
573
graphics primitives like Arrow; type ``sage.plot.plot?`` for
574
a current list.
575
576
Type ``plot.options`` for a dictionary of the default
577
options for plots. You can change this to change the defaults for
578
all future plots. Use ``plot.reset()`` to reset to the
579
default options.
580
581
PLOT OPTIONS:
582
583
- ``plot_points`` - (default: 200) the minimal number of plot points.
584
585
- ``adaptive_recursion`` - (default: 5) how many levels of recursion to go
586
before giving up when doing adaptive refinement. Setting this to 0
587
disables adaptive refinement.
588
589
- ``adaptive_tolerance`` - (default: 0.01) how large a difference should be
590
before the adaptive refinement code considers it significant. See the
591
documentation further below for more information, starting at "the
592
algorithm used to insert".
593
594
- ``xmin`` - starting x value
595
596
- ``xmax`` - ending x value
597
598
- ``color`` - an RGB tuple (r,g,b) with each of r,g,b between 0 and 1,
599
or a color name as a string (e.g., 'purple'), or an HTML color
600
such as '#aaff0b'.
601
602
- ``detect_poles`` - (Default: False) If set to True poles are detected.
603
If set to "show" vertical asymptotes are drawn.
604
605
- ``legend_label`` - the label for this item in the legend
606
607
APPEARANCE OPTIONS:
608
609
The following options affect the appearance of
610
the line through the points on the graph of `X` (these are
611
the same as for the line function):
612
613
INPUT:
614
615
- ``alpha`` - How transparent the line is
616
617
- ``thickness`` - How thick the line is
618
619
- ``rgbcolor`` - The color as an RGB tuple
620
621
- ``hue`` - The color given as a hue
622
623
Any MATPLOTLIB line option may also be passed in. E.g.,
624
625
- ``linestyle`` - The style of the line, which is one of
626
- ``"-"`` (solid) -- default
627
- ``"--"`` (dashed)
628
- ``"-."`` (dash dot)
629
- ``":"`` (dotted)
630
- ``"None"`` or ``" "`` or ``""`` (nothing)
631
632
The linestyle can also be prefixed with a drawing style (e.g., ``"steps--"``)
633
634
- ``"default"`` (connect the points with straight lines)
635
- ``"steps"`` or ``"steps-pre"`` (step function; horizontal
636
line is to the left of point)
637
- ``"steps-mid"`` (step function; points are in the middle of
638
horizontal lines)
639
- ``"steps-post"`` (step function; horizontal line is to the
640
right of point)
641
642
- ``marker`` - The style of the markers, which is one of
643
- ``"None"`` or ``" "`` or ``""`` (nothing) -- default
644
- ``","`` (pixel), ``"."`` (point)
645
- ``"_"`` (horizontal line), ``"|"`` (vertical line)
646
- ``"o"`` (circle), ``"p"`` (pentagon), ``"s"`` (square), ``"x"`` (x), ``"+"`` (plus), ``"*"`` (star)
647
- ``"D"`` (diamond), ``"d"`` (thin diamond)
648
- ``"H"`` (hexagon), ``"h"`` (alternative hexagon)
649
- ``"<"`` (triangle left), ``">"`` (triangle right), ``"^"`` (triangle up), ``"v"`` (triangle down)
650
- ``"1"`` (tri down), ``"2"`` (tri up), ``"3"`` (tri left), ``"4"`` (tri right)
651
- ``0`` (tick left), ``1`` (tick right), ``2`` (tick up), ``3`` (tick down)
652
- ``4`` (caret left), ``5`` (caret right), ``6`` (caret up), ``7`` (caret down)
653
- ``"$...$"`` (math TeX string)
654
655
- ``markersize`` - the size of the marker in points
656
657
- ``markeredgecolor`` -- the color of the marker edge
658
659
- ``markerfacecolor`` -- the color of the marker face
660
661
- ``markeredgewidth`` - the size of the marker edge in points
662
663
- ``exclude`` - (Default: None) values which are excluded from the plot range.
664
Either a list of real numbers, or an equation in one variable.
665
666
FILLING OPTIONS:
667
668
- ``fill`` - (Default: False) One of:
669
670
- "axis" or True: Fill the area between the function and the x-axis.
671
672
- "min": Fill the area between the function and its minimal value.
673
674
- "max": Fill the area between the function and its maximal value.
675
676
- a number c: Fill the area between the function and the horizontal line y = c.
677
678
- a function g: Fill the area between the function that is plotted and g.
679
680
- a dictionary d (only if a list of functions are plotted):
681
The keys of the dictionary should be integers.
682
The value of d[i] specifies the fill options for the i-th function in the list.
683
If d[i] == [j]: Fill the area between the i-th and the j-th function in the list.
684
(But if d[i] == j: Fill the area between the i-th function in the list and the
685
horizontal line y = j.)
686
687
- ``fillcolor`` - (default: 'automatic') The color of the fill.
688
Either 'automatic' or a color.
689
690
- ``fillalpha`` - (default: 0.5) How transparent the fill is.
691
A number between 0 and 1.
692
693
Note that this function does NOT simply sample equally spaced
694
points between xmin and xmax. Instead it computes equally spaced
695
points and add small perturbations to them. This reduces the
696
possibility of, e.g., sampling sin only at multiples of
697
`2\pi`, which would yield a very misleading graph.
698
699
EXAMPLES: We plot the sin function::
700
701
sage: P = plot(sin, (0,10)); print P
702
Graphics object consisting of 1 graphics primitive
703
sage: len(P) # number of graphics primitives
704
1
705
sage: len(P[0]) # how many points were computed (random)
706
225
707
sage: P # render
708
709
::
710
711
sage: P = plot(sin, (0,10), plot_points=10); print P
712
Graphics object consisting of 1 graphics primitive
713
sage: len(P[0]) # random output
714
32
715
sage: P # render
716
717
We plot with ``randomize=False``, which makes the initial sample points
718
evenly spaced (hence always the same). Adaptive plotting might
719
insert other points, however, unless ``adaptive_recursion=0``.
720
721
::
722
723
sage: p=plot(1, (x,0,3), plot_points=4, randomize=False, adaptive_recursion=0)
724
sage: list(p[0])
725
[(0.0, 1.0), (1.0, 1.0), (2.0, 1.0), (3.0, 1.0)]
726
727
Some colored functions::
728
729
sage: plot(sin, 0, 10, color='purple')
730
sage: plot(sin, 0, 10, color='#ff00ff')
731
732
We plot several functions together by passing a list of functions
733
as input::
734
735
sage: plot([sin(n*x) for n in [1..4]], (0, pi))
736
737
We can also build a plot step by step from an empty plot::
738
739
sage: a = plot([]); a # passing an empty list returns an empty plot (Graphics() object)
740
sage: a += plot(x**2); a # append another plot
741
sage: a += plot(x**3); a # append yet another plot
742
743
744
The function `\sin(1/x)` wiggles wildly near `0`.
745
Sage adapts to this and plots extra points near the origin.
746
747
::
748
749
sage: plot(sin(1/x), (x, -1, 1))
750
751
Via the matplotlib library, Sage makes it easy to tell whether
752
a graph is on both sides of both axes, as the axes only cross
753
if the origin is actually part of the viewing area::
754
755
sage: plot(x^3,(x,0,2)) # this one has the origin
756
sage: plot(x^3,(x,1,2)) # this one does not
757
758
Another thing to be aware of with axis labeling is that when
759
the labels have quite different orders of magnitude or are very
760
large, scientific notation (the `e` notation for powers of ten) is used::
761
762
sage: plot(x^2,(x,480,500)) # this one has no scientific notation
763
sage: plot(x^2,(x,300,500)) # this one has scientific notation on y-axis
764
765
You can put a legend with ``legend_label`` (the legend is only put
766
once in the case of multiple functions)::
767
768
sage: plot(exp(x), 0, 2, legend_label='$e^x$')
769
770
Sage understands TeX, so these all are slightly different, and you can choose
771
one based on your needs::
772
773
sage: plot(sin, legend_label='sin')
774
sage: plot(sin, legend_label='$sin$')
775
sage: plot(sin, legend_label='$\sin$')
776
777
Note that the independent variable may be omitted if there is no
778
ambiguity::
779
780
sage: plot(sin(1/x), (-1, 1))
781
782
The algorithm used to insert extra points is actually pretty
783
simple. On the picture drawn by the lines below::
784
785
sage: p = plot(x^2, (-0.5, 1.4)) + line([(0,0), (1,1)], color='green')
786
sage: p += line([(0.5, 0.5), (0.5, 0.5^2)], color='purple')
787
sage: p += point(((0, 0), (0.5, 0.5), (0.5, 0.5^2), (1, 1)), color='red', pointsize=20)
788
sage: p += text('A', (-0.05, 0.1), color='red')
789
sage: p += text('B', (1.01, 1.1), color='red')
790
sage: p += text('C', (0.48, 0.57), color='red')
791
sage: p += text('D', (0.53, 0.18), color='red')
792
sage: p.show(axes=False, xmin=-0.5, xmax=1.4, ymin=0, ymax=2)
793
794
You have the function (in blue) and its approximation (in green)
795
passing through the points A and B. The algorithm finds the
796
midpoint C of AB and computes the distance between C and D. If that
797
distance exceeds the ``adaptive_tolerance`` threshold (*relative* to
798
the size of the initial plot subintervals), the point D is
799
added to the curve. If D is added to the curve, then the
800
algorithm is applied recursively to the points A and D, and D and
801
B. It is repeated ``adaptive_recursion`` times (5, by default).
802
803
The actual sample points are slightly randomized, so the above
804
plots may look slightly different each time you draw them.
805
806
We draw the graph of an elliptic curve as the union of graphs of 2
807
functions.
808
809
::
810
811
sage: def h1(x): return abs(sqrt(x^3 - 1))
812
sage: def h2(x): return -abs(sqrt(x^3 - 1))
813
sage: P = plot([h1, h2], 1,4)
814
sage: P # show the result
815
816
We can also directly plot the elliptic curve::
817
818
sage: E = EllipticCurve([0,-1])
819
sage: plot(E, (1, 4), color=hue(0.6))
820
821
We can change the line style as well::
822
823
sage: plot(sin(x), (x, 0, 10), linestyle='-.')
824
825
If we have an empty linestyle and specify a marker, we can see the
826
points that are actually being plotted::
827
828
sage: plot(sin(x), (x,0,10), plot_points=20, linestyle='', marker='.')
829
830
The marker can be a TeX symbol as well::
831
832
sage: plot(sin(x), (x,0,10), plot_points=20, linestyle='', marker=r'$\checkmark$')
833
834
Sage currently ignores points that cannot be evaluated
835
836
::
837
838
sage: set_verbose(-1)
839
sage: plot(-x*log(x), (x,0,1)) # this works fine since the failed endpoint is just skipped.
840
sage: set_verbose(0)
841
842
This prints out a warning and plots where it can (we turn off the
843
warning by setting the verbose mode temporarily to -1.)
844
845
::
846
847
sage: set_verbose(-1)
848
sage: plot(x^(1/3), (x,-1,1))
849
sage: set_verbose(0)
850
851
To plot the negative real cube root, use something like the following::
852
853
sage: plot(lambda x : RR(x).nth_root(3), (x,-1, 1))
854
855
We can detect the poles of a function::
856
857
sage: plot(gamma, (-3, 4), detect_poles = True).show(ymin = -5, ymax = 5)
858
859
We draw the Gamma-Function with its poles highlighted::
860
861
sage: plot(gamma, (-3, 4), detect_poles = 'show').show(ymin = -5, ymax = 5)
862
863
The basic options for filling a plot::
864
865
sage: p1 = plot(sin(x), -pi, pi, fill = 'axis')
866
sage: p2 = plot(sin(x), -pi, pi, fill = 'min')
867
sage: p3 = plot(sin(x), -pi, pi, fill = 'max')
868
sage: p4 = plot(sin(x), -pi, pi, fill = 0.5)
869
sage: graphics_array([[p1, p2], [p3, p4]]).show(frame=True, axes=False)
870
871
sage: plot([sin(x), cos(2*x)*sin(4*x)], -pi, pi, fill = {0: 1}, fillcolor = 'red', fillalpha = 1)
872
873
A example about the growth of prime numbers::
874
875
sage: plot(1.13*log(x), 1, 100, fill = lambda x: nth_prime(x)/floor(x), fillcolor = 'red')
876
877
Fill the area between a function and its asymptote::
878
879
sage: f = (2*x^3+2*x-1)/((x-2)*(x+1))
880
sage: plot([f, 2*x+2], -7,7, fill = {0: [1]}, fillcolor='#ccc').show(ymin=-20, ymax=20)
881
882
Fill the area between a list of functions and the x-axis::
883
884
sage: def b(n): return lambda x: bessel_J(n, x)
885
sage: plot([b(n) for n in [1..5]], 0, 20, fill = 'axis')
886
887
Note that to fill between the ith and jth functions, you
888
must use dictionary key-value pairs i:[j]; key-value pairs
889
like i:j will fill between the ith function and the line y=j::
890
891
sage: def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1)
892
sage: plot([b(c) for c in [1..5]], 0, 40, fill = dict([(i, [i+1]) for i in [0..3]]))
893
sage: plot([b(c) for c in [1..5]], 0, 40, fill = dict([(i, i+1) for i in [0..3]]))
894
895
Extra options will get passed on to show(), as long as they are valid::
896
897
sage: plot(sin(x^2), (x, -3, 3), axes_labels=['$x$','$y$']) # These labels will be nicely typeset
898
sage: plot(sin(x^2), (x, -3, 3), axes_labels=['x','y']) # These will not
899
900
::
901
902
sage: plot(sin(x^2), (x, -3, 3), figsize=[8,2])
903
sage: plot(sin(x^2), (x, -3, 3)).show(figsize=[8,2]) # These are equivalent
904
905
This includes options for custom ticks and formatting. See documentation
906
for :meth:`show` for more details.
907
908
::
909
910
sage: plot(sin(pi*x), (x, -8, 8), ticks=[[-7,-3,0,3,7],[-1/2,0,1/2]])
911
sage: plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex")
912
913
This is particularly useful when setting custom ticks in multiples of `pi`.
914
915
::
916
917
sage: plot(sin(x),(x,0,2*pi),ticks=pi/3,tick_formatter=pi)
918
919
A example with excluded values::
920
921
sage: plot(floor(x), (x, 1, 10), exclude = [1..10])
922
923
We exclude all points where prime_pi makes a jump::
924
925
sage: jumps = [n for n in [1..100] if prime_pi(n) != prime_pi(n-1)]
926
sage: plot(lambda x: prime_pi(x), (x, 1, 100), exclude = jumps)
927
928
Excluded points can also be given by an equation::
929
930
sage: g(x) = x^2-2*x-2
931
sage: plot(1/g(x), (x, -3, 4), exclude = g(x) == 0, ymin = -5, ymax = 5)
932
933
``exclude`` and ``detect_poles`` can be used together::
934
935
sage: f(x) = (floor(x)+0.5) / (1-(x-0.5)^2)
936
sage: plot(f, (x, -3.5, 3.5), detect_poles = 'show', exclude = [-3..3], ymin = -5, ymax = 5)
937
938
TESTS:
939
940
We do not randomize the endpoints::
941
942
sage: p = plot(x, (x,-1,1))
943
sage: p[0].xdata[0] == -1
944
True
945
sage: p[0].xdata[-1] == 1
946
True
947
948
We check to make sure that the x/y min/max data get set correctly
949
when there are multiple functions.
950
951
::
952
953
sage: d = plot([sin(x), cos(x)], 100, 120).get_minmax_data()
954
sage: d['xmin']
955
100.0
956
sage: d['xmax']
957
120.0
958
959
We check various combinations of tuples and functions, ending with
960
tests that lambda functions work properly with explicit variable
961
declaration, without a tuple.
962
963
::
964
965
sage: p = plot(lambda x: x,(x,-1,1))
966
sage: p = plot(lambda x: x,-1,1)
967
sage: p = plot(x,x,-1,1)
968
sage: p = plot(x,-1,1)
969
sage: p = plot(x^2,x,-1,1)
970
sage: p = plot(x^2,xmin=-1,xmax=2)
971
sage: p = plot(lambda x: x,x,-1,1)
972
sage: p = plot(lambda x: x^2,x,-1,1)
973
sage: p = plot(lambda x: 1/x,x,-1,1)
974
sage: f(x) = sin(x+3)-.1*x^3
975
sage: p = plot(lambda x: f(x),x,-1,1)
976
977
We check to handle cases where the function gets evaluated at a
978
point which causes an 'inf' or '-inf' result to be produced.
979
980
::
981
982
sage: p = plot(1/x, 0, 1)
983
sage: p = plot(-1/x, 0, 1)
984
985
Bad options now give better errors::
986
987
sage: P = plot(sin(1/x), (x,-1,3), foo=10)
988
Traceback (most recent call last):
989
...
990
RuntimeError: Error in line(): option 'foo' not valid.
991
sage: P = plot(x, (x,1,1)) # trac ticket #11753
992
Traceback (most recent call last):
993
...
994
ValueError: plot start point and end point must be different
995
996
We test that we can plot `f(x)=x` (see Trac 10246)::
997
998
sage: f(x)=x; f
999
x |--> x
1000
sage: plot(f,(x,-1,1))
1001
"""
1002
G_kwds = Graphics._extract_kwds_for_show(kwds, ignore=['xmin', 'xmax'])
1003
1004
original_opts = kwds.pop('__original_opts', {})
1005
do_show = kwds.pop('show',False)
1006
1007
from sage.structure.element import is_Vector
1008
if kwds.get('parametric',False) and is_Vector(funcs):
1009
funcs = tuple(funcs)
1010
1011
1012
if hasattr(funcs, 'plot'):
1013
G = funcs.plot(*args, **original_opts)
1014
# if we are using the generic plotting method
1015
else:
1016
n = len(args)
1017
# if there are no extra args, try to get xmin,xmax from
1018
# keyword arguments or pick some silly default
1019
if n == 0:
1020
xmin = kwds.pop('xmin', -1)
1021
xmax = kwds.pop('xmax', 1)
1022
G = _plot(funcs, (xmin, xmax), **kwds)
1023
1024
# if there is one extra arg, then it had better be a tuple
1025
elif n == 1:
1026
G = _plot(funcs, *args, **kwds)
1027
elif n == 2:
1028
# if there are two extra args, then pull them out and pass them as a tuple
1029
xmin = args[0]
1030
xmax = args[1]
1031
args = args[2:]
1032
G = _plot(funcs, (xmin, xmax), *args, **kwds)
1033
elif n == 3:
1034
# if there are three extra args, then pull them out and pass them as a tuple
1035
var = args[0]
1036
xmin = args[1]
1037
xmax = args[2]
1038
args = args[3:]
1039
G = _plot(funcs, (var, xmin, xmax), *args, **kwds)
1040
elif ('xmin' in kwds) or ('xmax' in kwds):
1041
xmin = kwds.pop('xmin', -1)
1042
xmax = kwds.pop('xmax', 1)
1043
G = _plot(funcs, (xmin, xmax), *args, **kwds)
1044
pass
1045
else:
1046
sage.misc.misc.verbose("there were %s extra arguments (besides %s)" % (n, funcs), level=0)
1047
1048
G._set_extra_kwds(G_kwds)
1049
if do_show:
1050
G.show()
1051
return G
1052
1053
1054
def _plot(funcs, xrange, parametric=False,
1055
polar=False, fill=False, label='', randomize=True, **options):
1056
"""
1057
Internal function which does the actual plotting.
1058
1059
INPUT:
1060
1061
- ``funcs`` - function or list of functions to be plotted
1062
- ``xrange`` - two or three tuple of [input variable], min and max
1063
- ``parametric`` - (default: False) a boolean for whether
1064
this is a parametric plot
1065
- ``polar`` - (default: False) a boolean for whether
1066
this is a polar plot
1067
- ``fill`` - (default: False) an input for whether
1068
this plot is filled
1069
- ``randomize`` - (default: True) a boolean for whether
1070
to use random plot points
1071
1072
The following option is deprecated in favor of ``legend_label``:
1073
1074
- ``label`` - (default: '') a string for the label
1075
1076
All other usual plot options are also accepted, and a number
1077
are required (see the example below) which are normally passed
1078
through the options decorator to :func:`plot`.
1079
1080
OUTPUT:
1081
1082
- A ``Graphics`` object
1083
1084
EXAMPLES::
1085
1086
See :func:`plot` for many, many implicit examples.
1087
Here is an explicit one::
1088
1089
sage: from sage.plot.plot import _plot
1090
sage: P = _plot(e^(-x^2),(-3,3),fill=True,color='red',plot_points=50,adaptive_tolerance=2,adaptive_recursion=True,exclude=None)
1091
sage: P.show(aspect_ratio='automatic')
1092
1093
TESTS:
1094
1095
Make sure that we get the right number of legend entries as the number of
1096
functions varies (:trac:`10514`)::
1097
1098
sage: p1 = plot(1*x, legend_label='1x')
1099
sage: p2 = plot(2*x, legend_label='2x', color='green')
1100
sage: p1+p2
1101
1102
::
1103
1104
sage: len(p1.matplotlib().axes[0].legend().texts)
1105
1
1106
sage: len((p1+p2).matplotlib().axes[0].legend().texts)
1107
2
1108
sage: q1 = plot([sin(x), tan(x)], legend_label='trig')
1109
sage: len((q1).matplotlib().axes[0].legend().texts) # used to raise AttributeError
1110
1
1111
sage: q1
1112
1113
::
1114
1115
Make sure that we don't get multiple legend labels for plot segments
1116
(:trac:`11998`)::
1117
1118
sage: p1 = plot(1/(x^2-1),(x,-2,2),legend_label="foo",detect_poles=True)
1119
sage: len(p1.matplotlib().axes[0].legend().texts)
1120
1
1121
sage: p1.show(ymin=-10,ymax=10) # should be one legend
1122
1123
"""
1124
1125
from sage.plot.misc import setup_for_eval_on_grid
1126
if funcs == []:
1127
return Graphics()
1128
funcs, ranges = setup_for_eval_on_grid(funcs, [xrange], options['plot_points'])
1129
xmin, xmax, delta = ranges[0]
1130
xrange=ranges[0][:2]
1131
#parametric_plot will be a list or tuple of two functions (f,g)
1132
#and will plotted as (f(x), g(x)) for all x in the given range
1133
if parametric:
1134
f, g = funcs
1135
#or we have only a single function to be plotted:
1136
else:
1137
f = funcs
1138
1139
#check to see if funcs is a list of functions that will
1140
#be all plotted together.
1141
if isinstance(funcs, (list, tuple)) and not parametric:
1142
from sage.plot.colors import rainbow
1143
rainbow_colors = rainbow(len(funcs))
1144
1145
G = Graphics()
1146
for i, h in enumerate(funcs):
1147
if isinstance(fill, dict):
1148
if i in fill:
1149
fill_entry = fill[i]
1150
if isinstance(fill_entry, list):
1151
if fill_entry[0] < len(funcs):
1152
fill_temp = funcs[fill_entry[0]]
1153
else:
1154
fill_temp = None
1155
else:
1156
fill_temp = fill_entry
1157
else:
1158
fill_temp = None
1159
else:
1160
fill_temp = fill
1161
1162
options_temp = options.copy()
1163
fillcolor_temp = options_temp.pop('fillcolor', 'automatic')
1164
if i >= 1:
1165
legend_label=options_temp.pop('legend_label', None) # legend_label popped so the label isn't repeated for nothing
1166
if fillcolor_temp == 'automatic':
1167
fillcolor_temp = rainbow_colors[i]
1168
1169
G += plot(h, xrange, polar = polar, fill = fill_temp, \
1170
fillcolor = fillcolor_temp, **options_temp)
1171
return G
1172
1173
adaptive_tolerance = options.pop('adaptive_tolerance')
1174
adaptive_recursion = options.pop('adaptive_recursion')
1175
plot_points = int(options.pop('plot_points'))
1176
1177
exclude = options.pop('exclude')
1178
if exclude is not None:
1179
from sage.symbolic.expression import Expression
1180
if isinstance(exclude, Expression) and exclude.is_relational() == True:
1181
if len(exclude.variables()) > 1:
1182
raise ValueError('exclude has to be an equation of only one variable')
1183
v = exclude.variables()[0]
1184
points = [e.right() for e in exclude.solve(v) if e.left() == v and (v not in e.right().variables())]
1185
# We are only interested in real solutions
1186
exclude = []
1187
for x in points:
1188
try:
1189
exclude.append(float(x))
1190
except TypeError:
1191
pass
1192
1193
if isinstance(exclude, (list, tuple)):
1194
exclude = sorted(exclude)
1195
# We make sure that points plot points close to the excluded points are computed
1196
epsilon = 0.001*(xmax - xmin)
1197
initial_points = reduce(lambda a,b: a+b, [[x - epsilon, x + epsilon] for x in exclude], [])
1198
data = generate_plot_points(f, xrange, plot_points, adaptive_tolerance, adaptive_recursion, randomize, initial_points)
1199
else:
1200
raise ValueError('exclude needs to be a list of numbers or an equation')
1201
1202
if exclude == []:
1203
exclude = None
1204
else:
1205
data = generate_plot_points(f, xrange, plot_points, adaptive_tolerance, adaptive_recursion, randomize)
1206
1207
if parametric:
1208
# We need the original x-values to be able to exclude points in parametric plots
1209
exclude_data = data
1210
data = [(fdata, g(x)) for x, fdata in data]
1211
1212
G = Graphics()
1213
1214
fillcolor = options.pop('fillcolor', 'automatic')
1215
fillalpha = options.pop('fillalpha', 0.5)
1216
1217
# TODO: Use matplotlib's fill and fill_between commands.
1218
if fill is not False and fill is not None:
1219
if parametric:
1220
filldata = data
1221
else:
1222
if fill == 'axis' or fill is True:
1223
base_level = 0
1224
elif fill == 'min':
1225
base_level = min(t[1] for t in data)
1226
elif fill == 'max':
1227
base_level = max(t[1] for t in data)
1228
elif hasattr(fill, '__call__'):
1229
if fill == max or fill == min:
1230
if fill == max:
1231
fstr = 'max'
1232
else:
1233
fstr = 'min'
1234
msg = "WARNING: You use the built-in function %s for filling. You probably wanted the string '%s'." % (fstr, fstr)
1235
sage.misc.misc.verbose(msg, level=0)
1236
if not is_fast_float(fill):
1237
fill_f = fast_float(fill, expect_one_var=True)
1238
else:
1239
fill_f = fill
1240
1241
filldata = generate_plot_points(fill_f, xrange, plot_points, adaptive_tolerance, \
1242
adaptive_recursion, randomize)
1243
filldata.reverse()
1244
filldata += data
1245
else:
1246
try:
1247
base_level = float(fill)
1248
except TypeError:
1249
base_level = 0
1250
1251
if not hasattr(fill, '__call__') and polar:
1252
filldata = generate_plot_points(lambda x: base_level, xrange, plot_points, adaptive_tolerance, \
1253
adaptive_recursion, randomize)
1254
filldata.reverse()
1255
filldata += data
1256
if not hasattr(fill, '__call__') and not polar:
1257
filldata = [(data[0][0], base_level)] + data + [(data[-1][0], base_level)]
1258
1259
if fillcolor == 'automatic':
1260
fillcolor = (0.5, 0.5, 0.5)
1261
fill_options = {}
1262
fill_options['rgbcolor'] = fillcolor
1263
fill_options['alpha'] = fillalpha
1264
fill_options['thickness'] = 0
1265
if polar:
1266
filldata = [(y*cos(x), y*sin(x)) for x, y in filldata]
1267
G += polygon(filldata, **fill_options)
1268
1269
# We need the original data to be able to exclude points in polar plots
1270
if not parametric:
1271
exclude_data = data
1272
if polar:
1273
data = [(y*cos(x), y*sin(x)) for x, y in data]
1274
1275
from sage.plot.all import line, text
1276
1277
detect_poles = options.pop('detect_poles', False)
1278
legend_label = options.pop('legend_label', None)
1279
if exclude is not None or detect_poles != False:
1280
start_index = 0
1281
# setup for pole detection
1282
from sage.rings.all import RDF
1283
epsilon = 0.0001
1284
pole_options = {}
1285
pole_options['linestyle'] = '--'
1286
pole_options['thickness'] = 1
1287
pole_options['rgbcolor'] = '#ccc'
1288
1289
# setup for exclusion points
1290
exclusion_point = 0
1291
if exclude is not None:
1292
exclude.reverse()
1293
exclusion_point = exclude.pop()
1294
1295
for i in range(len(data)-1):
1296
x0, y0 = exclude_data[i]
1297
x1, y1 = exclude_data[i+1]
1298
# detect poles
1299
if (not (polar or parametric)) and detect_poles != False \
1300
and ((y1 > 0 and y0 < 0) or (y1 < 0 and y0 > 0)):
1301
# calculate the slope of the line segment
1302
dy = abs(y1-y0)
1303
dx = x1 - x0
1304
alpha = (RDF(dy)/RDF(dx)).arctan()
1305
if alpha >= RDF(pi/2) - epsilon:
1306
G += line(data[start_index:i], **options)
1307
if detect_poles == 'show':
1308
# draw a vertical asymptote
1309
G += line([(x0, y0), (x1, y1)], **pole_options)
1310
start_index = i+2
1311
1312
# exclude points
1313
if exclude is not None and (x0 <= exclusion_point <= x1):
1314
G += line(data[start_index:i], **options)
1315
start_index = i + 2
1316
try:
1317
exclusion_point = exclude.pop()
1318
except IndexError:
1319
# all excluded points were considered
1320
exclude = None
1321
1322
G += line(data[start_index:], legend_label=legend_label, **options)
1323
else:
1324
G += line(data, legend_label=legend_label, **options)
1325
1326
# Label?
1327
if label:
1328
sage.misc.misc.deprecation("Consider using legend_label instead")
1329
label = ' '+str(label)
1330
G += text(label, data[-1], horizontal_alignment='left',
1331
vertical_alignment='center')
1332
1333
return G
1334
1335
1336
1337
1338
########## misc functions ###################
1339
1340
@options(aspect_ratio=1.0)
1341
def parametric_plot(funcs, *args, **kwargs):
1342
r"""
1343
Plot a parametric curve or surface in 2d or 3d.
1344
1345
:func:`parametric_plot` takes two or three functions as a
1346
list or a tuple and makes a plot with the first function giving the
1347
`x` coordinates, the second function giving the `y`
1348
coordinates, and the third function (if present) giving the
1349
`z` coordinates.
1350
1351
In the 2d case, :func:`parametric_plot` is equivalent to the :func:`plot` command
1352
with the option ``parametric=True``. In the 3d case, :func:`parametric_plot`
1353
is equivalent to :func:`~sage.plot.plot3d.parametric_plot3d.parametric_plot3d`.
1354
See each of these functions for more help and examples.
1355
1356
INPUT:
1357
1358
1359
- ``funcs`` - 2 or 3-tuple of functions, or a vector of dimension 2 or 3.
1360
1361
- ``other options`` - passed to :func:`plot` or :func:`~sage.plot.plot3d.parametric_plot3d.parametric_plot3d`
1362
1363
1364
EXAMPLES: We draw some 2d parametric plots. Note that the default aspect ratio
1365
is 1, so that circles look like circles. ::
1366
1367
sage: t = var('t')
1368
sage: parametric_plot( (cos(t), sin(t)), (t, 0, 2*pi))
1369
1370
::
1371
1372
sage: parametric_plot( (sin(t), sin(2*t)), (t, 0, 2*pi), color=hue(0.6) )
1373
1374
::
1375
1376
sage: parametric_plot((1, t), (t, 0, 4))
1377
1378
Note that in parametric_plot, there is only fill or no fill.
1379
1380
::
1381
1382
sage: parametric_plot((t, t^2), (t, -4, 4), fill = True)
1383
1384
A filled Hypotrochoid::
1385
1386
sage: parametric_plot([cos(x) + 2 * cos(x/4), sin(x) - 2 * sin(x/4)], (x,0, 8*pi), fill = True)
1387
1388
sage: parametric_plot( (5*cos(x), 5*sin(x), x), (x,-12, 12), plot_points=150, color="red")
1389
1390
sage: y=var('y')
1391
sage: parametric_plot( (5*cos(x), x*y, cos(x*y)), (x, -4,4), (y,-4,4))
1392
1393
sage: t=var('t')
1394
sage: parametric_plot( vector((sin(t), sin(2*t))), (t, 0, 2*pi), color='green')
1395
sage: parametric_plot( vector([t, t+1, t^2]), (t, 0, 1))
1396
1397
TESTS::
1398
1399
sage: parametric_plot((x, t^2), (x, -4, 4))
1400
Traceback (most recent call last):
1401
...
1402
ValueError: there are more variables than variable ranges
1403
1404
sage: parametric_plot((1, x+t), (x, -4, 4))
1405
Traceback (most recent call last):
1406
...
1407
ValueError: there are more variables than variable ranges
1408
1409
sage: parametric_plot((-t, x+t), (x, -4, 4))
1410
Traceback (most recent call last):
1411
...
1412
ValueError: there are more variables than variable ranges
1413
1414
sage: parametric_plot((1, x+t, y), (x, -4, 4), (t, -4, 4))
1415
Traceback (most recent call last):
1416
...
1417
ValueError: there are more variables than variable ranges
1418
1419
sage: parametric_plot((1, x, y), 0, 4)
1420
Traceback (most recent call last):
1421
...
1422
ValueError: there are more variables than variable ranges
1423
"""
1424
num_ranges=0
1425
for i in args:
1426
if isinstance(i, (list, tuple)):
1427
num_ranges+=1
1428
else:
1429
break
1430
1431
if num_ranges==0 and len(args)>=2:
1432
from sage.misc.misc import deprecation
1433
deprecation("variable ranges to parametric_plot must be given as tuples, like (2,4) or (t,2,3)")
1434
args=tuple(args)
1435
num_ranges=1
1436
1437
num_funcs = len(funcs)
1438
1439
num_vars=len(sage.plot.misc.unify_arguments(funcs)[0])
1440
if num_vars>num_ranges:
1441
raise ValueError, "there are more variables than variable ranges"
1442
1443
if num_funcs == 2 and num_ranges == 1:
1444
kwargs['parametric'] = True
1445
return plot(funcs, *args, **kwargs)
1446
elif (num_funcs == 3 and num_ranges <= 2):
1447
return sage.plot.plot3d.parametric_plot3d.parametric_plot3d(funcs, *args, **kwargs)
1448
else:
1449
raise ValueError, "the number of functions and the number of variable ranges is not a supported combination for a 2d or 3d parametric plots"
1450
1451
@options(aspect_ratio=1.0)
1452
def polar_plot(funcs, *args, **kwds):
1453
r"""
1454
``polar_plot`` takes a single function or a list or
1455
tuple of functions and plots them with polar coordinates in the given
1456
domain.
1457
1458
This function is equivalent to the :func:`plot` command with the options
1459
``polar=True`` and ``aspect_ratio=1``. For more help on options,
1460
see the documentation for :func:`plot`.
1461
1462
INPUT:
1463
1464
- ``funcs`` - a function
1465
- other options are passed to plot
1466
1467
EXAMPLES:
1468
1469
Here is a blue 8-leaved petal::
1470
1471
sage: polar_plot(sin(5*x)^2, (x, 0, 2*pi), color='blue')
1472
1473
A red figure-8::
1474
1475
sage: polar_plot(abs(sqrt(1 - sin(x)^2)), (x, 0, 2*pi), color='red')
1476
1477
A green limacon of Pascal::
1478
1479
sage: polar_plot(2 + 2*cos(x), (x, 0, 2*pi), color=hue(0.3))
1480
1481
Several polar plots::
1482
1483
sage: polar_plot([2*sin(x), 2*cos(x)], (x, 0, 2*pi))
1484
1485
A filled spiral::
1486
1487
sage: polar_plot(sqrt, 0, 2 * pi, fill = True)
1488
1489
Fill the area between two functions::
1490
1491
sage: polar_plot(cos(4*x) + 1.5, 0, 2*pi, fill=0.5 * cos(4*x) + 2.5, fillcolor='orange')
1492
1493
Fill the area between several spirals::
1494
1495
sage: polar_plot([(1.2+k*0.2)*log(x) for k in range(6)], 1, 3 * pi, fill = {0: [1], 2: [3], 4: [5]})
1496
1497
Exclude points at discontinuities::
1498
1499
sage: polar_plot(log(floor(x)), (x, 1, 4*pi), exclude = [1..12])
1500
1501
"""
1502
kwds['polar']=True
1503
return plot(funcs, *args, **kwds)
1504
1505
@options(aspect_ratio='automatic')
1506
def list_plot(data, plotjoined=False, **kwargs):
1507
r"""
1508
``list_plot`` takes either a list of numbers, a list of tuples,
1509
or a dictionary and plots the corresponding points.
1510
1511
If given a list of numbers (that is, not a list of tuples or lists),
1512
``list_plot`` forms a list of tuples `(i, x_i)` where `i` goes from
1513
0 to ``len(data)-1`` and `x_i` is the `i`-th data value, and puts
1514
points at those tuple values.
1515
1516
``list_plot`` will plot a list of complex numbers in the obvious
1517
way; any numbers for which
1518
:func:`CC()<sage.rings.complex_field.ComplexField>` makes sense will
1519
work.
1520
1521
``list_plot`` also takes a list of tuples `(x_i, y_i)` where `x_i`
1522
and `y_i` are the `i`-th values representing the `x`- and
1523
`y`-values, respectively.
1524
1525
If given a dictionary, ``list_plot`` interprets the keys as
1526
`x`-values and the values as `y`-values.
1527
1528
The ``plotjoined=True`` option tells ``list_plot`` to plot a line
1529
joining all the data.
1530
1531
It is possible to pass empty dictionaries, lists, or tuples to
1532
list_plot. Doing so will plot nothing (returning an empty plot).
1533
1534
EXAMPLES::
1535
1536
sage: list_plot([i^2 for i in range(5)])
1537
1538
Here are a bunch of random red points::
1539
1540
sage: r = [(random(),random()) for _ in range(20)]
1541
sage: list_plot(r,color='red')
1542
1543
This gives all the random points joined in a purple line::
1544
1545
sage: list_plot(r, plotjoined=True, color='purple')
1546
1547
Plot a list of complex numbers::
1548
1549
sage: list_plot([1, I, pi + I/2, CC(.25, .25)])
1550
1551
sage: list_plot([exp(I*theta) for theta in [0, .2..pi]])
1552
1553
Note that if your list of complex numbers are all actually real,
1554
they get plotted as real values, so this
1555
1556
::
1557
1558
sage: list_plot([CDF(1), CDF(1/2), CDF(1/3)])
1559
1560
is the same as ``list_plot([1, 1/2, 1/3])`` -- it produces a plot of
1561
the points `(0,1)`, `(1,1/2)`, and `(2,1/3)`.
1562
1563
If you have separate lists of `x` values and `y` values which you
1564
want to plot against each other, use the ``zip`` command to make a
1565
single list whose entries are pairs of `(x,y)` values, and feed
1566
the result into ``list_plot``::
1567
1568
sage: x_coords = [cos(t)^3 for t in srange(0, 2*pi, 0.02)]
1569
sage: y_coords = [sin(t)^3 for t in srange(0, 2*pi, 0.02)]
1570
sage: list_plot(zip(x_coords, y_coords))
1571
1572
If instead you try to pass the two lists as separate arguments,
1573
you will get an error message::
1574
1575
sage: list_plot(x_coords, y_coords)
1576
Traceback (most recent call last):
1577
...
1578
TypeError: The second argument 'plotjoined' should be boolean (True or False). If you meant to plot two lists 'x' and 'y' against each other, use 'list_plot(zip(x,y))'.
1579
1580
Dictionaries with numeric keys and values can be plotted::
1581
1582
sage: list_plot({22: 3365, 27: 3295, 37: 3135, 42: 3020, 47: 2880, 52: 2735, 57: 2550})
1583
1584
TESTS:
1585
1586
We check to see that the x/y min/max data are set correctly.
1587
1588
::
1589
1590
sage: d = list_plot([(100,100), (120, 120)]).get_minmax_data()
1591
sage: d['xmin']
1592
100.0
1593
sage: d['ymin']
1594
100.0
1595
"""
1596
from sage.plot.all import line, point
1597
if data == {} or data == () or data == []:
1598
return Graphics()
1599
if isinstance(data, dict):
1600
if plotjoined:
1601
list_data = sorted(list(data.iteritems()))
1602
else:
1603
list_data = list(data.iteritems())
1604
return list_plot(list_data, plotjoined=plotjoined, **kwargs)
1605
if not isinstance(data[0], (list, tuple)):
1606
data = zip(range(len(data)), data)
1607
if isinstance(plotjoined, (list, tuple)):
1608
raise TypeError, "The second argument 'plotjoined' should be boolean (True or False). If you meant to plot two lists 'x' and 'y' against each other, use 'list_plot(zip(x,y))'."
1609
try:
1610
if plotjoined:
1611
return line(data, **kwargs)
1612
else:
1613
return point(data, **kwargs)
1614
except (TypeError, IndexError):
1615
# Assume we have complex-valued input and plot real and imaginary parts.
1616
# Need to catch IndexError because if data is, say, [(0, 1), (1, I)],
1617
# point3d() throws an IndexError on the (0,1) before it ever
1618
# gets to (1, I).
1619
from sage.rings.complex_field import ComplexField
1620
CC = ComplexField()
1621
# if we get here, we already did "zip(range(len(data)), data)",
1622
# so look at z[1] in inner list
1623
data = [(z.real(), z.imag()) for z in [CC(z[1]) for z in data]]
1624
if plotjoined:
1625
return line(data, **kwargs)
1626
else:
1627
return point(data, **kwargs)
1628
1629
def to_float_list(v):
1630
"""
1631
Given a list or tuple or iterable v, coerce each element of v to a
1632
float and make a list out of the result.
1633
1634
EXAMPLES::
1635
1636
sage: from sage.plot.plot import to_float_list
1637
sage: to_float_list([1,1/2,3])
1638
[1.0, 0.5, 3.0]
1639
"""
1640
return [float(x) for x in v]
1641
1642
1643
def reshape(v, n, m):
1644
"""
1645
Helper function for creating graphics arrays.
1646
1647
The input array is flattened and turned into an `n\times m`
1648
array, with blank graphics object padded at the end, if
1649
necessary.
1650
1651
INPUT:
1652
1653
- ``v`` - a list of lists or tuples
1654
1655
- ``n, m`` - integers
1656
1657
OUTPUT:
1658
1659
A list of lists of graphics objects
1660
1661
EXAMPLES::
1662
1663
sage: L = [plot(sin(k*x),(x,-pi,pi)) for k in range(10)]
1664
sage: graphics_array(L,3,4) # long time (up to 4s on sage.math, 2012)
1665
1666
::
1667
1668
sage: M = [[plot(sin(k*x),(x,-pi,pi)) for k in range(3)],[plot(cos(j*x),(x,-pi,pi)) for j in [3..5]]]
1669
sage: graphics_array(M,6,1) # long time (up to 4s on sage.math, 2012)
1670
1671
TESTS::
1672
1673
sage: L = [plot(sin(k*x),(x,-pi,pi)) for k in [1..3]]
1674
sage: graphics_array(L,0,-1) # indirect doctest
1675
Traceback (most recent call last):
1676
...
1677
AssertionError: array sizes must be positive
1678
"""
1679
assert n>0 and m>0, 'array sizes must be positive'
1680
G = Graphics()
1681
G.axes(False)
1682
if len(v) == 0:
1683
return [[G]*m]*n
1684
1685
if not isinstance(v[0], Graphics):
1686
# a list of lists -- flatten it
1687
v = sum([list(x) for x in v], [])
1688
1689
# Now v should be a single list.
1690
# First, make it have the right length.
1691
for i in xrange(n*m - len(v)):
1692
v.append(G)
1693
1694
# Next, create a list of lists out of it.
1695
L = []
1696
k = 0
1697
for i in range(n):
1698
w = []
1699
for j in range(m):
1700
w.append(v[k])
1701
k += 1
1702
L.append(w)
1703
1704
return L
1705
1706
def graphics_array(array, n=None, m=None):
1707
r"""
1708
``graphics_array`` take a list of lists (or tuples) of
1709
graphics objects and plots them all on one canvas (single plot).
1710
1711
INPUT:
1712
1713
- ``array`` - a list of lists or tuples
1714
1715
- ``n, m`` - (optional) integers - if n and m are
1716
given then the input array is flattened and turned into an n x m
1717
array, with blank graphics objects padded at the end, if
1718
necessary.
1719
1720
1721
EXAMPLE: Make some plots of `\sin` functions::
1722
1723
sage: f(x) = sin(x)
1724
sage: g(x) = sin(2*x)
1725
sage: h(x) = sin(4*x)
1726
sage: p1 = plot(f,(-2*pi,2*pi),color=hue(0.5))
1727
sage: p2 = plot(g,(-2*pi,2*pi),color=hue(0.9))
1728
sage: p3 = parametric_plot((f,g),(0,2*pi),color=hue(0.6))
1729
sage: p4 = parametric_plot((f,h),(0,2*pi),color=hue(1.0))
1730
1731
Now make a graphics array out of the plots::
1732
1733
sage: graphics_array(((p1,p2),(p3,p4)))
1734
1735
One can also name the array, and then use :meth:`~sage.plot.graphics.GraphicsArray.show`
1736
or :meth:`~sage.plot.graphics.GraphicsArray.save`::
1737
1738
sage: ga = graphics_array(((p1,p2),(p3,p4)))
1739
sage: ga.show()
1740
1741
Here we give only one row::
1742
1743
sage: p1 = plot(sin,(-4,4))
1744
sage: p2 = plot(cos,(-4,4))
1745
sage: g = graphics_array([p1, p2]); print g
1746
Graphics Array of size 1 x 2
1747
sage: g.show()
1748
1749
It is possible to use ``figsize`` to change the size of the plot
1750
as a whole::
1751
1752
sage: L = [plot(sin(k*x),(x,-pi,pi)) for k in [1..3]]
1753
sage: G = graphics_array(L)
1754
sage: G.show(figsize=[5,3]) # smallish and compact
1755
1756
::
1757
1758
sage: G.show(figsize=[10,20]) # bigger and tall and thin; long time (2s on sage.math, 2012)
1759
1760
::
1761
1762
sage: G.show(figsize=8) # figure as a whole is a square
1763
"""
1764
if not n is None:
1765
# Flatten then reshape input
1766
n = int(n)
1767
m = int(m)
1768
array = reshape(array, n, m)
1769
return GraphicsArray(array)
1770
1771
def var_and_list_of_values(v, plot_points):
1772
"""
1773
INPUT:
1774
1775
1776
- ``v`` - (v0, v1) or (var, v0, v1); if the former
1777
return the range of values between v0 and v1 taking plot_points
1778
steps; if var is given, also return var.
1779
1780
- ``plot_points`` - integer = 2 (the endpoints)
1781
1782
1783
OUTPUT:
1784
1785
1786
- ``var`` - a variable or None
1787
1788
- ``list`` - a list of floats
1789
1790
1791
EXAMPLES::
1792
1793
sage: from sage.plot.plot import var_and_list_of_values
1794
sage: var_and_list_of_values((var('theta'), 2, 5), 5)
1795
doctest:...: DeprecationWarning: var_and_list_of_values is deprecated. Please use sage.plot.misc.setup_for_eval_on_grid; note that that function has slightly different calling and return conventions which make it more generally applicable
1796
(theta, [2.0, 2.75, 3.5, 4.25, 5.0])
1797
sage: var_and_list_of_values((2, 5), 5)
1798
(None, [2.0, 2.75, 3.5, 4.25, 5.0])
1799
sage: var_and_list_of_values((var('theta'), 2, 5), 2)
1800
(theta, [2.0, 5.0])
1801
sage: var_and_list_of_values((2, 5), 2)
1802
(None, [2.0, 5.0])
1803
"""
1804
from sage.misc.misc import deprecation
1805
deprecation("var_and_list_of_values is deprecated. Please use sage.plot.misc.setup_for_eval_on_grid; note that that function has slightly different calling and return conventions which make it more generally applicable")
1806
plot_points = int(plot_points)
1807
if plot_points < 2:
1808
raise ValueError, "plot_points must be greater than 1"
1809
if not isinstance(v, (tuple, list)):
1810
raise TypeError, "v must be a tuple or list"
1811
if len(v) == 3:
1812
var = v[0]
1813
a, b = v[1], v[2]
1814
elif len(v) == 2:
1815
var = None
1816
a, b = v
1817
else:
1818
raise ValueError, "parametric value range must be a list or tuple of length 2 or 3."
1819
1820
a = float(a)
1821
b = float(b)
1822
if plot_points == 2:
1823
return var, [a, b]
1824
else:
1825
step = (b-a)/float(plot_points-1)
1826
values = [a + step*i for i in xrange(plot_points)]
1827
return var, values
1828
1829
1830
1831
def setup_for_eval_on_grid(v, xrange, yrange, plot_points):
1832
"""
1833
This function is deprecated. Please use
1834
sage.plot.misc.setup_for_eval_on_grid instead. Please note that
1835
that function has slightly different calling and return
1836
conventions which make it more generally applicable.
1837
1838
INPUT:
1839
1840
1841
- ``v`` - a list of functions
1842
1843
- ``xrange`` - 2 or 3 tuple (if 3, first is a
1844
variable)
1845
1846
- ``yrange`` - 2 or 3 tuple
1847
1848
- ``plot_points`` - a positive integer
1849
1850
1851
OUTPUT:
1852
1853
1854
- ``g`` - tuple of fast callable functions
1855
1856
- ``xstep`` - step size in xdirection
1857
1858
- ``ystep`` - step size in ydirection
1859
1860
- ``xrange`` - tuple of 2 floats
1861
1862
- ``yrange`` - tuple of 2 floats
1863
1864
1865
EXAMPLES::
1866
1867
sage: x,y = var('x,y')
1868
sage: sage.plot.plot.setup_for_eval_on_grid([x^2 + y^2], (x,0,5), (y,0,pi), 11)
1869
doctest:...: DeprecationWarning: sage.plot.plot.setup_for_eval_on_grid is deprecated. Please use sage.plot.misc.setup_for_eval_on_grid; note that that function has slightly different calling and return conventions which make it more generally applicable
1870
([<sage.ext... object at ...>],
1871
0.5,
1872
0.3141592653589793,
1873
(0.0, 5.0),
1874
(0.0, 3.141592653589793))
1875
1876
We always plot at least two points; one at the beginning and one at the end of the ranges.
1877
1878
::
1879
1880
sage: sage.plot.plot.setup_for_eval_on_grid([x^2+y^2], (x,0,1), (y,-1,1), 1)
1881
([<sage.ext... object at ...>],
1882
1.0,
1883
2.0,
1884
(0.0, 1.0),
1885
(-1.0, 1.0))
1886
1887
1888
"""
1889
from sage.misc.misc import deprecation
1890
deprecation("sage.plot.plot.setup_for_eval_on_grid is deprecated. Please use sage.plot.misc.setup_for_eval_on_grid; note that that function has slightly different calling and return conventions which make it more generally applicable")
1891
1892
from sage.plot.misc import setup_for_eval_on_grid as setup
1893
g, ranges=setup(v, [xrange, yrange], plot_points)
1894
return list(g), ranges[0][2], ranges[1][2], ranges[0][:2], ranges[1][:2]
1895
1896
1897
def minmax_data(xdata, ydata, dict=False):
1898
"""
1899
Returns the minimums and maximums of xdata and ydata.
1900
1901
If dict is False, then minmax_data returns the tuple (xmin, xmax,
1902
ymin, ymax); otherwise, it returns a dictionary whose keys are
1903
'xmin', 'xmax', 'ymin', and 'ymax' and whose values are the
1904
corresponding values.
1905
1906
EXAMPLES::
1907
1908
sage: from sage.plot.plot import minmax_data
1909
sage: minmax_data([], [])
1910
(-1, 1, -1, 1)
1911
sage: minmax_data([-1, 2], [4, -3])
1912
(-1, 2, -3, 4)
1913
sage: d = minmax_data([-1, 2], [4, -3], dict=True)
1914
sage: list(sorted(d.items()))
1915
[('xmax', 2), ('xmin', -1), ('ymax', 4), ('ymin', -3)]
1916
"""
1917
xmin = min(xdata) if len(xdata) > 0 else -1
1918
xmax = max(xdata) if len(xdata) > 0 else 1
1919
ymin = min(ydata) if len(ydata) > 0 else -1
1920
ymax = max(ydata) if len(ydata) > 0 else 1
1921
if dict:
1922
return {'xmin':xmin, 'xmax':xmax,
1923
'ymin':ymin, 'ymax':ymax}
1924
else:
1925
return xmin, xmax, ymin, ymax
1926
1927
def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, adaptive_recursion=5, level=0):
1928
r"""
1929
The adaptive refinement algorithm for plotting a function f. See
1930
the docstring for plot for a description of the algorithm.
1931
1932
INPUT:
1933
1934
1935
- ``f`` - a function of one variable
1936
1937
- ``p1, p2`` - two points to refine between
1938
1939
- ``adaptive_recursion`` - (default: 5) how many
1940
levels of recursion to go before giving up when doing adaptive
1941
refinement. Setting this to 0 disables adaptive refinement.
1942
1943
- ``adaptive_tolerance`` - (default: 0.01) how large
1944
a relative difference should be before the adaptive refinement
1945
code considers it significant; see documentation for generate_plot_points
1946
for more information. See the documentation for plot() for more
1947
information on how the adaptive refinement algorithm works.
1948
1949
OUTPUT:
1950
1951
1952
- ``list`` - a list of points to insert between p1 and
1953
p2 to get a better linear approximation between them
1954
1955
1956
TESTS::
1957
1958
sage: from sage.plot.plot import adaptive_refinement
1959
sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01, adaptive_recursion=0)
1960
[]
1961
sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01)
1962
[(0.125*pi, 0.3826834323650898), (0.1875*pi, 0.5555702330196022), (0.25*pi, 0.7071067811865475), (0.3125*pi, 0.8314696123025452), (0.375*pi, 0.9238795325112867), (0.4375*pi, 0.9807852804032304), (0.5*pi, 1.0), (0.5625*pi, 0.9807852804032304), (0.625*pi, 0.9238795325112867), (0.6875*pi, 0.8314696123025455), (0.75*pi, 0.7071067811865476), (0.8125*pi, 0.5555702330196022), (0.875*pi, 0.3826834323650899)]
1963
1964
This shows that lowering adaptive_tolerance and raising
1965
adaptive_recursion both increase the number of subdivision
1966
points, though which one creates more points is heavily
1967
dependent upon the function being plotted.
1968
1969
::
1970
1971
sage: x = var('x')
1972
sage: f(x) = sin(1/x)
1973
sage: n1 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.01)); n1
1974
15
1975
sage: n2 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_recursion=10, adaptive_tolerance=0.01)); n2
1976
79
1977
sage: n3 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.001)); n3
1978
26
1979
"""
1980
if level >= adaptive_recursion:
1981
return []
1982
1983
x = (p1[0] + p2[0])/2.0
1984
msg = ''
1985
1986
try:
1987
y = float(f(x))
1988
if str(y) in ['nan', 'NaN', 'inf', '-inf']:
1989
sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x),1)
1990
# give up for this branch
1991
return []
1992
1993
except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg:
1994
sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x), 1)
1995
# give up for this branch
1996
return []
1997
1998
# this distance calculation is not perfect.
1999
if abs((p1[1] + p2[1])/2.0 - y) > adaptive_tolerance:
2000
return adaptive_refinement(f, p1, (x, y),
2001
adaptive_tolerance=adaptive_tolerance,
2002
adaptive_recursion=adaptive_recursion,
2003
level=level+1) \
2004
+ [(x, y)] + \
2005
adaptive_refinement(f, (x, y), p2,
2006
adaptive_tolerance=adaptive_tolerance,
2007
adaptive_recursion=adaptive_recursion,
2008
level=level+1)
2009
else:
2010
return []
2011
2012
def generate_plot_points(f, xrange, plot_points=5, adaptive_tolerance=0.01, adaptive_recursion=5, randomize = True, initial_points = None):
2013
r"""
2014
Calculate plot points for a function f in the interval xrange. The
2015
adaptive refinement algorithm is also automatically invoked with a
2016
*relative* adaptive tolerance of adaptive_tolerance; see below.
2017
2018
INPUT:
2019
2020
- ``f`` - a function of one variable
2021
2022
- ``p1, p2`` - two points to refine between
2023
2024
- ``plot_points`` - (default: 5) the minimal number of plot points. (Note
2025
however that in any actual plot a number is passed to this, with default
2026
value 200.)
2027
2028
- ``adaptive_recursion`` - (default: 5) how many levels of recursion to go
2029
before giving up when doing adaptive refinement. Setting this to 0
2030
disables adaptive refinement.
2031
2032
- ``adaptive_tolerance`` - (default: 0.01) how large the relative difference
2033
should be before the adaptive refinement code considers it significant. If
2034
the actual difference is greater than adaptive_tolerance*delta, where delta
2035
is the initial subinterval size for the given xrange and plot_points, then
2036
the algorithm will consider it significant.
2037
2038
- ``initial_points`` - (default: None) a list of points that should be evaluated.
2039
2040
OUTPUT:
2041
2042
- a list of points (x, f(x)) in the interval xrange, which approximate
2043
the function f.
2044
2045
TESTS::
2046
2047
sage: from sage.plot.plot import generate_plot_points
2048
sage: generate_plot_points(sin, (0, pi), plot_points=2, adaptive_recursion=0)
2049
[(0.0, 0.0), (3.141592653589793, 1.2246...e-16)]
2050
2051
sage: from sage.plot.plot import generate_plot_points
2052
sage: generate_plot_points(lambda x: x^2, (0, 6), plot_points=2, adaptive_recursion=0, initial_points = [1,2,3])
2053
[(0.0, 0.0), (1.0, 1.0), (2.0, 4.0), (3.0, 9.0), (6.0, 36.0)]
2054
2055
sage: generate_plot_points(sin(x).function(x), (-pi, pi), randomize=False)
2056
[(-3.141592653589793, -1.2246...e-16), (-2.748893571891069,
2057
-0.3826834323650899), (-2.356194490192345, -0.707106781186547...),
2058
(-2.1598449493429825, -0.831469612302545...), (-1.9634954084936207,
2059
-0.9238795325112867), (-1.7671458676442586, -0.9807852804032304),
2060
(-1.5707963267948966, -1.0), (-1.3744467859455345,
2061
-0.9807852804032304), (-1.1780972450961724, -0.9238795325112867),
2062
(-0.9817477042468103, -0.831469612302545...), (-0.7853981633974483,
2063
-0.707106781186547...), (-0.39269908169872414, -0.3826834323650898),
2064
(0.0, 0.0), (0.39269908169872414, 0.3826834323650898),
2065
(0.7853981633974483, 0.707106781186547...), (0.9817477042468103,
2066
0.831469612302545...), (1.1780972450961724, 0.9238795325112867),
2067
(1.3744467859455345, 0.9807852804032304), (1.5707963267948966, 1.0),
2068
(1.7671458676442586, 0.9807852804032304), (1.9634954084936207,
2069
0.9238795325112867), (2.1598449493429825, 0.831469612302545...),
2070
(2.356194490192345, 0.707106781186547...), (2.748893571891069,
2071
0.3826834323650899), (3.141592653589793, 1.2246...e-16)]
2072
2073
This shows that lowering adaptive_tolerance and raising
2074
adaptive_recursion both increase the number of subdivision points.
2075
(Note that which creates more points is heavily dependent on the
2076
particular function plotted.)
2077
2078
::
2079
2080
sage: x = var('x')
2081
sage: f(x) = sin(1/x)
2082
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]]
2083
[97, 161, 275]
2084
2085
sage: [len(generate_plot_points(f, (-pi, pi), plot_points=16, adaptive_recursion=i, randomize=False)) for i in [5, 10, 15]]
2086
[97, 499, 2681]
2087
"""
2088
from sage.plot.misc import setup_for_eval_on_grid
2089
ignore, ranges = setup_for_eval_on_grid([], [xrange], plot_points)
2090
xmin, xmax, delta = ranges[0]
2091
data = srange(*ranges[0], include_endpoint=True)
2092
2093
random = current_randstate().python_random().random
2094
2095
for i in range(len(data)):
2096
xi = data[i]
2097
# Slightly randomize the interior sample points if
2098
# randomize is true
2099
if randomize and i > 0 and i < plot_points-1:
2100
xi += delta*(random() - 0.5)
2101
data[i] = xi
2102
2103
# add initial points
2104
if isinstance(initial_points, list):
2105
data = sorted(data + initial_points)
2106
2107
exceptions = 0; msg=''
2108
exception_indices = []
2109
for i in range(len(data)):
2110
xi = data[i]
2111
2112
try:
2113
data[i] = (float(xi), float(f(xi)))
2114
if str(data[i][1]) in ['nan', 'NaN', 'inf', '-inf']:
2115
sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, xi),1)
2116
exceptions += 1
2117
exception_indices.append(i)
2118
2119
except (ArithmeticError, TypeError, ValueError), msg:
2120
sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, xi),1)
2121
2122
if i == 0: # Given an error for left endpoint, try to move it in slightly
2123
for j in range(1, 99):
2124
xj = xi + delta*j/100.0
2125
try:
2126
data[i] = (float(xj), float(f(xj)))
2127
# nan != nan
2128
if data[i][1] != data[i][1]:
2129
continue
2130
break
2131
except (ArithmeticError, TypeError, ValueError), msg:
2132
pass
2133
else:
2134
exceptions += 1
2135
exception_indices.append(i)
2136
2137
elif i == plot_points-1: # Given an error for right endpoint, try to move it in slightly
2138
for j in range(1, 99):
2139
xj = xi - delta*j/100.0
2140
try:
2141
data[i] = (float(xj), float(f(xj)))
2142
# nan != nan
2143
if data[i][1] != data[i][1]:
2144
continue
2145
break
2146
except (ArithmeticError, TypeError, ValueError), msg:
2147
pass
2148
else:
2149
exceptions += 1
2150
exception_indices.append(i)
2151
else:
2152
exceptions += 1
2153
exception_indices.append(i)
2154
2155
data = [data[i] for i in range(len(data)) if i not in exception_indices]
2156
2157
# calls adaptive refinement
2158
i, j = 0, 0
2159
adaptive_tolerance = delta * float(adaptive_tolerance)
2160
adaptive_recursion = int(adaptive_recursion)
2161
2162
while i < len(data) - 1:
2163
for p in adaptive_refinement(f, data[i], data[i+1],
2164
adaptive_tolerance=adaptive_tolerance,
2165
adaptive_recursion=adaptive_recursion):
2166
data.insert(i+1, p)
2167
i += 1
2168
i += 1
2169
2170
if (len(data) == 0 and exceptions > 0) or exceptions > 10:
2171
sage.misc.misc.verbose("WARNING: When plotting, failed to evaluate function at %s points."%exceptions, level=0)
2172
sage.misc.misc.verbose("Last error message: '%s'"%msg, level=0)
2173
2174
return data
2175
2176
#Lovely cruft to keep the pickle jar working
2177
from line import line, line2d, Line as GraphicPrimitive_Line
2178
from arrow import arrow, Arrow as GraphicPrimitive_Arrow
2179
from bar_chart import bar_chart, BarChart as GraphicPrimitive_BarChart
2180
from disk import disk, Disk as GraphicPrimitive_Disk
2181
from point import point, points, point2d, Point as GraphicPrimitive_Point
2182
from matrix_plot import matrix_plot, MatrixPlot as GraphicPrimitive_MatrixPlot
2183
from plot_field import plot_vector_field, plot_slope_field, PlotField as GraphicPrimitive_PlotField
2184
from text import text, Text as GraphicPrimitive_Text
2185
from polygon import polygon, Polygon as GraphicPrimitive_Polygon
2186
from circle import circle, Circle as GraphicPrimtive_Circle
2187
from contour_plot import contour_plot, implicit_plot, ContourPlot as GraphicPrimitive_ContourPlot
2188
2189
2190