Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/plot/contour_plot.py
8815 views
1
"""
2
Contour Plots
3
"""
4
5
#*****************************************************************************
6
# Copyright (C) 2006 Alex Clemesha <[email protected]>,
7
# William Stein <[email protected]>,
8
# 2008 Mike Hansen <[email protected]>,
9
#
10
# Distributed under the terms of the GNU General Public License (GPL)
11
#
12
# This code is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# General Public License for more details.
16
#
17
# The full text of the GPL is available at:
18
#
19
# http://www.gnu.org/licenses/
20
#*****************************************************************************
21
from sage.plot.primitive import GraphicPrimitive
22
from sage.misc.decorators import options, suboptions
23
from sage.plot.colors import rgbcolor, get_cmap
24
from sage.misc.misc import xsrange
25
import operator
26
27
class ContourPlot(GraphicPrimitive):
28
"""
29
Primitive class for the contour plot graphics type. See
30
``contour_plot?`` for help actually doing contour plots.
31
32
INPUT:
33
34
- ``xy_data_array`` - list of lists giving evaluated values of the function on the grid
35
36
- ``xrange`` - tuple of 2 floats indicating range for horizontal direction
37
38
- ``yrange`` - tuple of 2 floats indicating range for vertical direction
39
40
- ``options`` - dict of valid plot options to pass to constructor
41
42
EXAMPLES:
43
44
Note this should normally be used indirectly via ``contour_plot``::
45
46
sage: from sage.plot.contour_plot import ContourPlot
47
sage: C = ContourPlot([[1,3],[2,4]],(1,2),(2,3),options={})
48
sage: C
49
ContourPlot defined by a 2 x 2 data grid
50
sage: C.xrange
51
(1, 2)
52
53
TESTS:
54
55
We test creating a contour plot::
56
57
sage: x,y = var('x,y')
58
sage: contour_plot(x^2-y^3+10*sin(x*y), (x, -4, 4), (y, -4, 4),plot_points=121,cmap='hsv')
59
"""
60
def __init__(self, xy_data_array, xrange, yrange, options):
61
"""
62
Initializes base class ContourPlot.
63
64
EXAMPLES::
65
66
sage: x,y = var('x,y')
67
sage: C = contour_plot(x^2-y^3+10*sin(x*y), (x, -4, 4), (y, -4, 4),plot_points=121,cmap='hsv')
68
sage: C[0].xrange
69
(-4.0, 4.0)
70
sage: C[0].options()['plot_points']
71
121
72
"""
73
self.xrange = xrange
74
self.yrange = yrange
75
self.xy_data_array = xy_data_array
76
self.xy_array_row = len(xy_data_array)
77
self.xy_array_col = len(xy_data_array[0])
78
GraphicPrimitive.__init__(self, options)
79
80
def get_minmax_data(self):
81
"""
82
Returns a dictionary with the bounding box data.
83
84
EXAMPLES::
85
86
sage: x,y = var('x,y')
87
sage: f(x,y) = x^2 + y^2
88
sage: d = contour_plot(f, (3, 6), (3, 6))[0].get_minmax_data()
89
sage: d['xmin']
90
3.0
91
sage: d['ymin']
92
3.0
93
"""
94
from sage.plot.plot import minmax_data
95
return minmax_data(self.xrange, self.yrange, dict=True)
96
97
def _allowed_options(self):
98
"""
99
Return the allowed options for the ContourPlot class.
100
101
EXAMPLES::
102
103
sage: x,y = var('x,y')
104
sage: C = contour_plot(x^2-y^2,(x,-2,2),(y,-2,2))
105
sage: isinstance(C[0]._allowed_options(),dict)
106
True
107
"""
108
return {'plot_points':'How many points to use for plotting precision',
109
'cmap':"""the name of a predefined colormap,
110
a list of colors, or an instance of a
111
matplotlib Colormap. Type: import matplotlib.cm; matplotlib.cm.datad.keys()
112
for available colormap names.""",
113
'colorbar': "Include a colorbar indicating the levels",
114
'colorbar_options': "a dictionary of options for colorbars",
115
'fill':'Fill contours or not',
116
'legend_label':'The label for this item in the legend.',
117
'contours':"""Either an integer specifying the number of
118
contour levels, or a sequence of numbers giving
119
the actual contours to use.""",
120
'linewidths':'the width of the lines to be plotted',
121
'linestyles':'the style of the lines to be plotted',
122
'labels':'show line labels or not',
123
'label_options':'a dictionary of options for the labels',
124
'zorder':'The layer level in which to draw'}
125
126
def _repr_(self):
127
"""
128
String representation of ContourPlot primitive.
129
130
EXAMPLES::
131
132
sage: x,y = var('x,y')
133
sage: C = contour_plot(x^2-y^2,(x,-2,2),(y,-2,2))
134
sage: c = C[0]; c
135
ContourPlot defined by a 100 x 100 data grid
136
"""
137
return "ContourPlot defined by a %s x %s data grid"%(self.xy_array_row, self.xy_array_col)
138
139
def _render_on_subplot(self, subplot):
140
"""
141
TESTS:
142
143
A somewhat random plot, but fun to look at::
144
145
sage: x,y = var('x,y')
146
sage: contour_plot(x^2-y^3+10*sin(x*y), (x, -4, 4), (y, -4, 4),plot_points=121,cmap='hsv')
147
"""
148
from sage.rings.integer import Integer
149
options = self.options()
150
fill = options['fill']
151
contours = options['contours']
152
if options.has_key('cmap'):
153
cmap = get_cmap(options['cmap'])
154
elif fill or contours is None:
155
cmap = get_cmap('gray')
156
else:
157
if isinstance(contours, (int, Integer)):
158
cmap = get_cmap([(i,i,i) for i in xsrange(0,1,1/contours)])
159
else:
160
l = Integer(len(contours))
161
cmap = get_cmap([(i,i,i) for i in xsrange(0,1,1/l)])
162
163
x0,x1 = float(self.xrange[0]), float(self.xrange[1])
164
y0,y1 = float(self.yrange[0]), float(self.yrange[1])
165
166
if isinstance(contours, (int, Integer)):
167
contours = int(contours)
168
169
CSF=None
170
if fill:
171
if contours is None:
172
CSF=subplot.contourf(self.xy_data_array, cmap=cmap, extent=(x0,x1,y0,y1), label=options['legend_label'])
173
else:
174
CSF=subplot.contourf(self.xy_data_array, contours, cmap=cmap, extent=(x0,x1,y0,y1),extend='both', label=options['legend_label'])
175
176
linewidths = options.get('linewidths',None)
177
if isinstance(linewidths, (int, Integer)):
178
linewidths = int(linewidths)
179
elif isinstance(linewidths, (list, tuple)):
180
linewidths = tuple(int(x) for x in linewidths)
181
182
from sage.plot.misc import get_matplotlib_linestyle
183
linestyles = options.get('linestyles', None)
184
if isinstance(linestyles, (list, tuple)):
185
linestyles = [get_matplotlib_linestyle(l, 'long') for l in linestyles]
186
else:
187
linestyles = get_matplotlib_linestyle(linestyles, 'long')
188
if contours is None:
189
CS = subplot.contour(self.xy_data_array, cmap=cmap, extent=(x0,x1,y0,y1),
190
linewidths=linewidths, linestyles=linestyles, label=options['legend_label'])
191
else:
192
CS = subplot.contour(self.xy_data_array, contours, cmap=cmap, extent=(x0,x1,y0,y1),
193
linewidths=linewidths, linestyles=linestyles, label=options['legend_label'])
194
if options.get('labels', False):
195
label_options = options['label_options']
196
label_options['fontsize'] = int(label_options['fontsize'])
197
if fill and label_options is None:
198
label_options['inline']=False
199
subplot.clabel(CS, **label_options)
200
if options.get('colorbar', False):
201
colorbar_options = options['colorbar_options']
202
from matplotlib import colorbar
203
cax,kwds=colorbar.make_axes_gridspec(subplot,**colorbar_options)
204
if CSF is None:
205
cb=colorbar.Colorbar(cax,CS, **kwds)
206
else:
207
cb=colorbar.Colorbar(cax,CSF, **kwds)
208
cb.add_lines(CS)
209
210
@suboptions('colorbar', orientation='vertical', format=None, spacing=None)
211
@suboptions('label', fontsize=9, colors='blue', inline=None, inline_spacing=3, fmt="%1.2f")
212
@options(plot_points=100, fill=True, contours=None, linewidths=None, linestyles=None, labels=False, frame=True, axes=False, colorbar=False, legend_label=None, aspect_ratio=1, region=None)
213
def contour_plot(f, xrange, yrange, **options):
214
r"""
215
``contour_plot`` takes a function of two variables, `f(x,y)`
216
and plots contour lines of the function over the specified
217
``xrange`` and ``yrange`` as demonstrated below.
218
219
``contour_plot(f, (xmin, xmax), (ymin, ymax), ...)``
220
221
INPUT:
222
223
- ``f`` -- a function of two variables
224
225
- ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple
226
``(x,xmin,xmax)``
227
228
- ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple
229
``(y,ymin,ymax)``
230
231
The following inputs must all be passed in as named parameters:
232
233
- ``plot_points`` -- integer (default: 100); number of points to plot
234
in each direction of the grid. For old computers, 25 is fine, but
235
should not be used to verify specific intersection points.
236
237
- ``fill`` -- bool (default: ``True``), whether to color in the area
238
between contour lines
239
240
- ``cmap`` -- a colormap (default: ``'gray'``), the name of
241
a predefined colormap, a list of colors or an instance of a matplotlib
242
Colormap. Type: ``import matplotlib.cm; matplotlib.cm.datad.keys()``
243
for available colormap names.
244
245
- ``contours`` -- integer or list of numbers (default: ``None``):
246
If a list of numbers is given, then this specifies the contour levels
247
to use. If an integer is given, then this many contour lines are
248
used, but the exact levels are determined automatically. If ``None``
249
is passed (or the option is not given), then the number of contour
250
lines is determined automatically, and is usually about 5.
251
252
- ``linewidths`` -- integer or list of integer (default: None), if
253
a single integer all levels will be of the width given,
254
otherwise the levels will be plotted with the width in the order
255
given. If the list is shorter than the number of contours, then
256
the widths will be repeated cyclically.
257
258
- ``linestyles`` -- string or list of strings (default: None), the
259
style of the lines to be plotted, one of: ``"solid"``, ``"dashed"``,
260
``"dashdot"``, ``"dotted"``, respectively ``"-"``, ``"--"``,
261
``"-."``, ``":"``. If the list is shorter than the number of
262
contours, then the styles will be repeated cyclically.
263
264
- ``labels`` -- boolean (default: False) Show level labels or not.
265
266
The following options are to adjust the style and placement of
267
labels, they have no effect if no labels are shown.
268
269
- ``label_fontsize`` -- integer (default: 9), the font size of the labels.
270
271
- ``label_colors`` -- string or sequence of colors (default:
272
None) If a string, gives the name of a single color with which
273
to draw all labels. If a sequence, gives the colors of the
274
labels. A color is a string giving the name of one or a
275
3-tuple of floats.
276
277
- ``label_inline`` -- boolean (default: False if fill is True,
278
otherwise True), controls whether the underlying contour is
279
removed or not.
280
281
- ``label_inline_spacing`` -- integer (default: 3), When inline,
282
this is the amount of contour that is removed from each side,
283
in pixels.
284
285
- ``label_fmt`` -- a format string (default: "%1.2f"), this is
286
used to get the label text from the level. This can also be a
287
dictionary with the contour levels as keys and corresponding
288
text string labels as values. It can also be any callable which
289
returns a string when called with a numeric contour level.
290
291
- ``colorbar`` -- boolean (default: False) Show a colorbar or not.
292
293
The following options are to adjust the style and placement of
294
colorbars. They have no effect if a colorbar is not shown.
295
296
- ``colorbar_orientation`` -- string (default: 'vertical'),
297
controls placement of the colorbar, can be either 'vertical'
298
or 'horizontal'
299
300
- ``colorbar_format`` -- a format string, this is used to format
301
the colorbar labels.
302
303
- ``colorbar_spacing`` -- string (default: 'proportional'). If
304
'proportional', make the contour divisions proportional to
305
values. If 'uniform', space the colorbar divisions uniformly,
306
without regard for numeric values.
307
308
- ``legend_label`` -- the label for this item in the legend
309
310
- ``region`` - (default: None) If region is given, it must be a function
311
of two variables. Only segments of the surface where region(x,y) returns a
312
number >0 will be included in the plot.
313
314
EXAMPLES:
315
316
Here we plot a simple function of two variables. Note that
317
since the input function is an expression, we need to explicitly
318
declare the variables in 3-tuples for the range::
319
320
sage: x,y = var('x,y')
321
sage: contour_plot(cos(x^2+y^2), (x, -4, 4), (y, -4, 4))
322
323
Here we change the ranges and add some options::
324
325
sage: x,y = var('x,y')
326
sage: contour_plot((x^2)*cos(x*y), (x, -10, 5), (y, -5, 5), fill=False, plot_points=150)
327
328
An even more complicated plot::
329
330
sage: x,y = var('x,y')
331
sage: contour_plot(sin(x^2 + y^2)*cos(x)*sin(y), (x, -4, 4), (y, -4, 4),plot_points=150)
332
333
Some elliptic curves, but with symbolic endpoints. In the first
334
example, the plot is rotated 90 degrees because we switch the
335
variables `x`, `y`::
336
337
sage: x,y = var('x,y')
338
sage: contour_plot(y^2 + 1 - x^3 - x, (y,-pi,pi), (x,-pi,pi))
339
340
::
341
342
sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi))
343
344
We can play with the contour levels::
345
346
sage: x,y = var('x,y')
347
sage: f(x,y) = x^2 + y^2
348
sage: contour_plot(f, (-2, 2), (-2, 2))
349
350
::
351
352
sage: contour_plot(f, (-2, 2), (-2, 2), contours=2, cmap=[(1,0,0), (0,1,0), (0,0,1)])
353
354
::
355
356
sage: contour_plot(f, (-2, 2), (-2, 2), contours=(0.1, 1.0, 1.2, 1.4), cmap='hsv')
357
358
::
359
360
sage: contour_plot(f, (-2, 2), (-2, 2), contours=(1.0,), fill=False)
361
362
::
363
364
sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,0,1])
365
366
We can change the style of the lines::
367
368
sage: contour_plot(f, (-2,2), (-2,2), fill=False, linewidths=10)
369
370
::
371
372
sage: contour_plot(f, (-2,2), (-2,2), fill=False, linestyles='dashdot')
373
374
::
375
376
sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\
377
... linewidths=[1,5],linestyles=['solid','dashed'],fill=False)
378
sage: P
379
380
::
381
382
sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\
383
... linewidths=[1,5],linestyles=['solid','dashed'])
384
sage: P
385
386
sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\
387
... linewidths=[1,5],linestyles=['-',':'])
388
sage: P
389
390
We can add labels and play with them::
391
392
sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv', labels=True)
393
394
::
395
396
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',\
397
... labels=True, label_fmt="%1.0f", label_colors='black')
398
sage: P
399
400
::
401
402
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\
403
... contours=[-4,0,4], label_fmt={-4:"low", 0:"medium", 4: "hi"}, label_colors='black')
404
sage: P
405
406
::
407
408
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\
409
... contours=[-4,0,4], label_fmt=lambda x: "$z=%s$"%x, label_colors='black', label_inline=True, \
410
... label_fontsize=12)
411
sage: P
412
413
::
414
415
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \
416
... fill=False, cmap='hsv', labels=True, label_fontsize=18)
417
sage: P
418
419
::
420
421
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \
422
... fill=False, cmap='hsv', labels=True, label_inline_spacing=1)
423
sage: P
424
425
::
426
427
sage: P= contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \
428
... fill=False, cmap='hsv', labels=True, label_inline=False)
429
sage: P
430
431
We can change the color of the labels if so desired::
432
433
sage: contour_plot(f, (-2,2), (-2,2), labels=True, label_colors='red')
434
435
We can add a colorbar as well::
436
437
sage: f(x,y)=x^2-y^2
438
sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True)
439
440
::
441
442
sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True,colorbar_orientation='horizontal')
443
444
::
445
446
sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True)
447
448
::
449
450
sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True,colorbar_spacing='uniform')
451
452
::
453
454
sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[0,2,3,6],colorbar=True,colorbar_format='%.3f')
455
456
::
457
458
sage: contour_plot(f, (x,-3,3), (y,-3,3), labels=True,label_colors='red',contours=[0,2,3,6],colorbar=True)
459
460
::
461
462
sage: contour_plot(f, (x,-3,3), (y,-3,3), cmap='winter', contours=20, fill=False, colorbar=True)
463
464
This should plot concentric circles centered at the origin::
465
466
sage: x,y = var('x,y')
467
sage: contour_plot(x^2+y^2-2,(x,-1,1), (y,-1,1))
468
469
Extra options will get passed on to show(), as long as they are valid::
470
471
sage: f(x, y) = cos(x) + sin(y)
472
sage: contour_plot(f, (0, pi), (0, pi), axes=True)
473
474
One can also plot over a reduced region::
475
476
sage: contour_plot(x**2-y**2, (x,-2, 2), (y,-2, 2),region=x-y,plot_points=300)
477
478
::
479
480
sage: contour_plot(f, (0, pi), (0, pi)).show(axes=True) # These are equivalent
481
482
Note that with ``fill=False`` and grayscale contours, there is the
483
possibility of confusion between the contours and the axes, so use
484
``fill=False`` together with ``axes=True`` with caution::
485
486
sage: contour_plot(f, (-pi, pi), (-pi, pi), fill=False, axes=True)
487
488
TESTS:
489
490
To check that ticket 5221 is fixed, note that this has three curves, not two::
491
492
sage: x,y = var('x,y')
493
sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,-2,0], fill=False)
494
"""
495
from sage.plot.all import Graphics
496
from sage.plot.misc import setup_for_eval_on_grid
497
498
region = options.pop('region')
499
ev = [f] if region is None else [f,region]
500
501
F, ranges = setup_for_eval_on_grid(ev, [xrange, yrange], options['plot_points'])
502
g = F[0]
503
xrange,yrange=[r[:2] for r in ranges]
504
505
xy_data_array = [[g(x, y) for x in xsrange(*ranges[0], include_endpoint=True)]
506
for y in xsrange(*ranges[1], include_endpoint=True)]
507
508
if region is not None:
509
import numpy
510
511
xy_data_array = numpy.ma.asarray(xy_data_array,dtype=float)
512
513
m = F[1]
514
515
mask = numpy.asarray([[m(x, y)<=0 for x in xsrange(*ranges[0], include_endpoint=True)]
516
for y in xsrange(*ranges[1], include_endpoint=True)],dtype=bool)
517
518
xy_data_array[mask] = numpy.ma.masked
519
520
g = Graphics()
521
522
# Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'.
523
# Otherwise matplotlib complains.
524
scale = options.get('scale', None)
525
if isinstance(scale, (list, tuple)):
526
scale = scale[0]
527
if scale == 'semilogy' or scale == 'semilogx':
528
options['aspect_ratio'] = 'automatic'
529
530
g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax']))
531
g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, options))
532
return g
533
534
@options(plot_points=150, contours=(0,0), fill=False, cmap=["blue"])
535
def implicit_plot(f, xrange, yrange, **options):
536
r"""
537
``implicit_plot`` takes a function of two variables, `f(x,y)`
538
and plots the curve `f(x,y) = 0` over the specified
539
``xrange`` and ``yrange`` as demonstrated below.
540
541
``implicit_plot(f, (xmin, xmax), (ymin, ymax), ...)``
542
543
``implicit_plot(f, (x, xmin, xmax), (y, ymin, ymax), ...)``
544
545
INPUT:
546
547
- ``f`` -- a function of two variables or equation in two variables
548
549
- ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values or ``(x,xmin,xmax)``
550
551
- ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values or ``(y,ymin,ymax)``
552
553
The following inputs must all be passed in as named parameters:
554
555
- ``plot_points`` -- integer (default: 150); number of points to plot
556
in each direction of the grid
557
558
- ``fill`` -- boolean (default: ``False``); if ``True``, fill the region
559
`f(x,y) < 0`.
560
561
- ``linewidth`` -- integer (default: None), if a single integer all levels
562
will be of the width given, otherwise the levels will be plotted with the
563
widths in the order given.
564
565
- ``linestyle`` -- string (default: None), the style of the line to be
566
plotted, one of: ``"solid"``, ``"dashed"``, ``"dashdot"`` or
567
``"dotted"``, respectively ``"-"``, ``"--"``, ``"-."``, or ``":"``.
568
569
- ``color`` -- string (default: ``blue``), the color of the plot. Colors are
570
defined in :mod:`sage.plot.colors`; try ``colors?`` to see them all.
571
572
- ``legend_label`` -- the label for this item in the legend
573
574
- ``base`` - (default: 10) the base of the logarithm if
575
a logarithmic scale is set. This must be greater than 1. The base
576
can be also given as a list or tuple ``(basex, basey)``.
577
``basex`` sets the base of the logarithm along the horizontal
578
axis and ``basey`` sets the base along the vertical axis.
579
580
- ``scale`` -- (default: ``"linear"``) string. The scale of the axes.
581
Possible values are ``"linear"``, ``"loglog"``, ``"semilogx"``,
582
``"semilogy"``.
583
584
The scale can be also be given as single argument that is a list
585
or tuple ``(scale, base)`` or ``(scale, basex, basey)``.
586
587
The ``"loglog"`` scale sets both the horizontal and vertical axes to
588
logarithmic scale. The ``"semilogx"`` scale sets the horizontal axis
589
to logarithmic scale. The ``"semilogy"`` scale sets the vertical axis
590
to logarithmic scale. The ``"linear"`` scale is the default value
591
when :class:`~sage.plot.graphics.Graphics` is initialized.
592
593
EXAMPLES:
594
595
A simple circle with a radius of 2. Note that
596
since the input function is an expression, we need to explicitly
597
declare the variables in 3-tuples for the range::
598
599
sage: var("x y")
600
(x, y)
601
sage: implicit_plot(x^2+y^2-2, (x,-3,3), (y,-3,3))
602
603
I can do the same thing, but using a callable function so I don't need
604
to explicitly define the variables in the ranges, and filling the inside::
605
606
sage: f(x,y) = x^2 + y^2 - 2
607
sage: implicit_plot(f, (-3, 3), (-3, 3),fill=True)
608
609
The same circle but with a different line width::
610
611
sage: implicit_plot(f, (-3,3), (-3,3), linewidth=6)
612
613
And again the same circle but this time with a dashdot border::
614
615
sage: implicit_plot(f, (-3,3), (-3,3), linestyle='dashdot')
616
617
You can also plot an equation::
618
619
sage: var("x y")
620
(x, y)
621
sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3))
622
623
You can even change the color of the plot::
624
625
sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3), color="red")
626
627
Here is a beautiful (and long) example which also tests that all
628
colors work with this::
629
630
sage: G = Graphics()
631
sage: counter = 0
632
sage: for col in colors.keys(): # long time
633
... G += implicit_plot(x^2+y^2==1+counter*.1, (x,-4,4),(y,-4,4),color=col)
634
... counter += 1
635
sage: G.show(frame=False)
636
637
We can define a level-`n` approximation of the boundary of the
638
Mandelbrot set::
639
640
sage: def mandel(n):
641
... c = polygen(CDF, 'c')
642
... z = 0
643
... for i in range(n):
644
... z = z*z + c
645
... def f(x, y):
646
... val = z(CDF(x, y))
647
... return val.norm() - 4
648
... return f
649
650
The first-level approximation is just a circle::
651
652
sage: implicit_plot(mandel(1), (-3, 3), (-3, 3))
653
654
A third-level approximation starts to get interesting::
655
656
sage: implicit_plot(mandel(3), (-2, 1), (-1.5, 1.5))
657
658
The seventh-level approximation is a degree 64 polynomial, and
659
``implicit_plot`` does a pretty good job on this part of the curve.
660
(``plot_points=200`` looks even better, but it takes over a second.)
661
662
::
663
664
sage: implicit_plot(mandel(7), (-0.3, 0.05), (-1.15, -0.9),plot_points=50)
665
666
When making a filled implicit plot using a python function rather than a
667
symbolic expression the user should increase the number of plot points to
668
avoid artifacts::
669
670
sage: implicit_plot(lambda x,y: x^2+y^2-2, (x,-3,3), (y,-3,3), fill=True, plot_points=500) # long time
671
672
An example of an implicit plot on 'loglog' scale::
673
674
sage: implicit_plot(x^2+y^2 == 200, (x,1,200), (y,1,200), scale='loglog')
675
676
TESTS::
677
678
sage: f(x,y) = x^2 + y^2 - 2
679
sage: implicit_plot(f, (-3, 3), (-3, 3),fill=5)
680
Traceback (most recent call last):
681
...
682
ValueError: fill=5 is not supported
683
"""
684
from sage.symbolic.expression import is_SymbolicEquation
685
if is_SymbolicEquation(f):
686
if f.operator() != operator.eq:
687
raise ValueError, "input to implicit plot must be function or equation"
688
f = f.lhs() - f.rhs()
689
linewidths = options.pop('linewidth', None)
690
linestyles = options.pop('linestyle', None)
691
692
if 'color' in options:
693
options['cmap']=[options.pop('color', None)]
694
695
if options['fill'] is True:
696
options.pop('fill')
697
options.pop('contours',None)
698
options.pop('cmap',None)
699
from sage.symbolic.expression import is_Expression
700
if not is_Expression(f):
701
return region_plot(lambda x,y: f(x,y)<0, xrange, yrange,
702
borderwidth=linewidths, borderstyle=linestyles,
703
**options)
704
else:
705
return region_plot(f<0, xrange, yrange, borderwidth=linewidths,
706
borderstyle=linestyles, **options)
707
elif options['fill'] is False:
708
return contour_plot(f, xrange, yrange, linewidths=linewidths,
709
linestyles=linestyles, **options)
710
else:
711
raise ValueError("fill=%s is not supported" % options['fill'])
712
713
714
@options(plot_points=100, incol='blue', outcol='white', bordercol=None, borderstyle=None, borderwidth=None,frame=False,axes=True, legend_label=None, aspect_ratio=1)
715
def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol, borderstyle, borderwidth,**options):
716
r"""
717
``region_plot`` takes a boolean function of two variables, `f(x,y)`
718
and plots the region where f is True over the specified
719
``xrange`` and ``yrange`` as demonstrated below.
720
721
``region_plot(f, (xmin, xmax), (ymin, ymax), ...)``
722
723
INPUT:
724
725
- ``f`` -- a boolean function of two variables
726
727
- ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple
728
``(x,xmin,xmax)``
729
730
- ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple
731
``(y,ymin,ymax)``
732
733
- ``plot_points`` -- integer (default: 100); number of points to plot
734
in each direction of the grid
735
736
- ``incol`` -- a color (default: ``'blue'``), the color inside the region
737
738
- ``outcol`` -- a color (default: ``'white'``), the color of the outside
739
of the region
740
741
If any of these options are specified, the border will be shown as indicated,
742
otherwise it is only implicit (with color ``incol``) as the border of the
743
inside of the region.
744
745
- ``bordercol`` -- a color (default: ``None``), the color of the border
746
(``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``)
747
748
- ``borderstyle`` -- string (default: 'solid'), one of ``'solid'``,
749
``'dashed'``, ``'dotted'``, ``'dashdot'``, respectively ``'-'``,
750
``'--'``, ``':'``, ``'-.'``.
751
752
- ``borderwidth`` -- integer (default: None), the width of the border in pixels
753
754
- ``legend_label`` -- the label for this item in the legend
755
756
- ``base`` - (default: 10) the base of the logarithm if
757
a logarithmic scale is set. This must be greater than 1. The base
758
can be also given as a list or tuple ``(basex, basey)``.
759
``basex`` sets the base of the logarithm along the horizontal
760
axis and ``basey`` sets the base along the vertical axis.
761
762
- ``scale`` -- (default: ``"linear"``) string. The scale of the axes.
763
Possible values are ``"linear"``, ``"loglog"``, ``"semilogx"``,
764
``"semilogy"``.
765
766
The scale can be also be given as single argument that is a list
767
or tuple ``(scale, base)`` or ``(scale, basex, basey)``.
768
769
The ``"loglog"`` scale sets both the horizontal and vertical axes to
770
logarithmic scale. The ``"semilogx"`` scale sets the horizontal axis
771
to logarithmic scale. The ``"semilogy"`` scale sets the vertical axis
772
to logarithmic scale. The ``"linear"`` scale is the default value
773
when :class:`~sage.plot.graphics.Graphics` is initialized.
774
775
776
EXAMPLES:
777
778
Here we plot a simple function of two variables::
779
780
sage: x,y = var('x,y')
781
sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3))
782
783
Here we play with the colors::
784
785
sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray')
786
787
An even more complicated plot, with dashed borders::
788
789
sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250)
790
791
A disk centered at the origin::
792
793
sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1))
794
795
A plot with more than one condition (all conditions must be true for the statement to be true)::
796
797
sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2))
798
799
Since it doesn't look very good, let's increase ``plot_points``::
800
801
sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400)
802
803
To get plots where only one condition needs to be true, use a function.
804
Using lambda functions, we definitely need the extra ``plot_points``::
805
806
sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2), plot_points=400)
807
808
The first quadrant of the unit circle::
809
810
sage: region_plot([y>0, x>0, x^2+y^2<1], (x,-1.1, 1.1), (y,-1.1, 1.1), plot_points = 400)
811
812
Here is another plot, with a huge border::
813
814
sage: region_plot(x*(x-1)*(x+1)+y^2<0, (x, -3, 2), (y, -3, 3), incol='lightblue', bordercol='gray', borderwidth=10, plot_points=50)
815
816
If we want to keep only the region where x is positive::
817
818
sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50)
819
820
Here we have a cut circle::
821
822
sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200)
823
824
The first variable range corresponds to the horizontal axis and
825
the second variable range corresponds to the vertical axis::
826
827
sage: s,t=var('s,t')
828
sage: region_plot(s>0,(t,-2,2),(s,-2,2))
829
830
::
831
832
sage: region_plot(s>0,(s,-2,2),(t,-2,2))
833
834
An example of a region plot in 'loglog' scale::
835
836
sage: region_plot(x^2+y^2<100, (x,1,10), (y,1,10), scale='loglog')
837
838
"""
839
840
from sage.plot.all import Graphics
841
from sage.plot.misc import setup_for_eval_on_grid
842
import numpy
843
844
if not isinstance(f, (list, tuple)):
845
f = [f]
846
847
f = [equify(g) for g in f]
848
849
g, ranges = setup_for_eval_on_grid(f, [xrange, yrange], plot_points)
850
xrange,yrange=[r[:2] for r in ranges]
851
852
xy_data_arrays = numpy.asarray([[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)]
853
for y in xsrange(*ranges[1], include_endpoint=True)]
854
for func in g],dtype=float)
855
xy_data_array=numpy.abs(xy_data_arrays.prod(axis=0))
856
# Now we need to set entries to negative iff all
857
# functions were negative at that point.
858
neg_indices = (xy_data_arrays<0).all(axis=0)
859
xy_data_array[neg_indices]=-xy_data_array[neg_indices]
860
861
from matplotlib.colors import ListedColormap
862
incol = rgbcolor(incol)
863
outcol = rgbcolor(outcol)
864
cmap = ListedColormap([incol, outcol])
865
cmap.set_over(outcol)
866
cmap.set_under(incol)
867
868
g = Graphics()
869
870
# Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'.
871
# Otherwise matplotlib complains.
872
scale = options.get('scale', None)
873
if isinstance(scale, (list, tuple)):
874
scale = scale[0]
875
if scale == 'semilogy' or scale == 'semilogx':
876
options['aspect_ratio'] = 'automatic'
877
878
g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax']))
879
g.add_primitive(ContourPlot(xy_data_array, xrange,yrange,
880
dict(contours=[-1e307, 0, 1e307], cmap=cmap, fill=True, **options)))
881
882
if bordercol or borderstyle or borderwidth:
883
cmap = [rgbcolor(bordercol)] if bordercol else ['black']
884
linestyles = [borderstyle] if borderstyle else None
885
linewidths = [borderwidth] if borderwidth else None
886
g.add_primitive(ContourPlot(xy_data_array, xrange, yrange,
887
dict(linestyles=linestyles, linewidths=linewidths,
888
contours=[0], cmap=[bordercol], fill=False, **options)))
889
890
return g
891
892
def equify(f):
893
"""
894
Returns the equation rewritten as a symbolic function to give
895
negative values when True, positive when False.
896
897
EXAMPLES::
898
899
sage: from sage.plot.contour_plot import equify
900
sage: var('x, y')
901
(x, y)
902
sage: equify(x^2 < 2)
903
x^2 - 2
904
sage: equify(x^2 > 2)
905
-x^2 + 2
906
sage: equify(x*y > 1)
907
-x*y + 1
908
sage: equify(y > 0)
909
-y
910
sage: f=equify(lambda x,y: x>y)
911
sage: f(1,2)
912
1
913
sage: f(2,1)
914
-1
915
"""
916
import operator
917
from sage.calculus.all import symbolic_expression
918
from sage.symbolic.expression import is_Expression
919
if not is_Expression(f):
920
return lambda x,y: -1 if f(x,y) else 1
921
922
op = f.operator()
923
if op is operator.gt or op is operator.ge:
924
return symbolic_expression(f.rhs() - f.lhs())
925
else:
926
return symbolic_expression(f.lhs() - f.rhs())
927
928