Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/plot/misc.py
4034 views
1
#*****************************************************************************
2
# Distributed under the terms of the GNU General Public License (GPL)
3
#
4
# This code is distributed in the hope that it will be useful,
5
# but WITHOUT ANY WARRANTY; without even the implied warranty of
6
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
7
# General Public License for more details.
8
#
9
# The full text of the GPL is available at:
10
#
11
# http://www.gnu.org/licenses/
12
#*****************************************************************************
13
14
from functools import wraps
15
16
from sage.ext.fast_eval import fast_float, fast_float_constant, is_fast_float
17
18
from sage.structure.element import is_Vector
19
20
def setup_for_eval_on_grid(funcs, ranges, plot_points=None, return_vars=False):
21
"""
22
Calculate the necessary parameters to construct a list of points,
23
and make the functions fast_callable.
24
25
INPUT:
26
27
- ``funcs`` - a function, or a list, tuple, or vector of functions
28
29
- ``ranges`` - a list of ranges. A range can be a 2-tuple of
30
numbers specifying the minimum and maximum, or a 3-tuple giving
31
the variable explicitly.
32
33
- ``plot_points`` - a tuple of integers specifying the number of
34
plot points for each range. If a single number is specified, it
35
will be the value for all ranges. This defaults to 2.
36
37
- ``return_vars`` - (default False) If True, return the variables,
38
in order.
39
40
41
OUTPUT:
42
43
44
- ``fast_funcs`` - if only one function passed, then a fast
45
callable function. If funcs is a list or tuple, then a tuple
46
of fast callable functions is returned.
47
48
- ``range_specs`` - a list of range_specs: for each range, a
49
tuple is returned of the form (range_min, range_max,
50
range_step) such that ``srange(range_min, range_max,
51
range_step, include_endpoint=True)`` gives the correct points
52
for evaluation.
53
54
EXAMPLES::
55
56
sage: x,y,z=var('x,y,z')
57
sage: f(x,y)=x+y-z
58
sage: g(x,y)=x+y
59
sage: h(y)=-y
60
sage: sage.plot.misc.setup_for_eval_on_grid(f, [(0, 2),(1,3),(-4,1)], plot_points=5)
61
(<sage.ext...>, [(0.0, 2.0, 0.5), (1.0, 3.0, 0.5), (-4.0, 1.0, 1.25)])
62
sage: sage.plot.misc.setup_for_eval_on_grid([g,h], [(0, 2),(-1,1)], plot_points=5)
63
((<sage.ext...>, <sage.ext...>), [(0.0, 2.0, 0.5), (-1.0, 1.0, 0.5)])
64
sage: sage.plot.misc.setup_for_eval_on_grid([sin,cos], [(-1,1)], plot_points=9)
65
((<sage.ext...>, <sage.ext...>), [(-1.0, 1.0, 0.25)])
66
sage: sage.plot.misc.setup_for_eval_on_grid([lambda x: x^2,cos], [(-1,1)], plot_points=9)
67
((<function <lambda> ...>, <sage.ext...>), [(-1.0, 1.0, 0.25)])
68
sage: sage.plot.misc.setup_for_eval_on_grid([x+y], [(x,-1,1),(y,-2,2)])
69
((<sage.ext...>,), [(-1.0, 1.0, 2.0), (-2.0, 2.0, 4.0)])
70
sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,-1,1),(y,-1,1)], plot_points=[4,9])
71
(<sage.ext...>, [(-1.0, 1.0, 0.6666666666666666), (-1.0, 1.0, 0.25)])
72
sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,-1,1),(y,-1,1)], plot_points=[4,9,10])
73
Traceback (most recent call last):
74
...
75
ValueError: plot_points must be either an integer or a list of integers, one for each range
76
sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(1,-1),(y,-1,1)], plot_points=[4,9,10])
77
Traceback (most recent call last):
78
...
79
ValueError: Some variable ranges specify variables while others do not
80
sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(1,-1),(-1,1)], plot_points=5)
81
doctest:...: DeprecationWarning: Unnamed ranges for more than one variable is deprecated and will be removed from a future release of Sage; you can used named ranges instead, like (x,0,2)
82
(<sage.ext...>, [(1.0, -1.0, 0.5), (-1.0, 1.0, 0.5)])
83
sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(y,1,-1),(x,-1,1)], plot_points=5)
84
(<sage.ext...>, [(1.0, -1.0, 0.5), (-1.0, 1.0, 0.5)])
85
sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,1,-1),(x,-1,1)], plot_points=5)
86
Traceback (most recent call last):
87
...
88
ValueError: range variables should be distinct, but there are duplicates
89
sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,1,1),(y,-1,1)])
90
Traceback (most recent call last):
91
...
92
ValueError: plot start point and end point must be different
93
sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,1,-1),(y,-1,1)], return_vars=True)
94
(<sage.ext...>, [(1.0, -1.0, 2.0), (-1.0, 1.0, 2.0)], [x, y])
95
sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(y,1,-1),(x,-1,1)], return_vars=True)
96
(<sage.ext...>, [(1.0, -1.0, 2.0), (-1.0, 1.0, 2.0)], [y, x])
97
"""
98
if max(map(len, ranges)) != min(map(len, ranges)):
99
raise ValueError, "Some variable ranges specify variables while others do not"
100
101
if len(ranges[0])==3:
102
vars = [r[0] for r in ranges]
103
ranges = [r[1:] for r in ranges]
104
if len(set(vars))<len(vars):
105
raise ValueError, "range variables should be distinct, but there are duplicates"
106
else:
107
vars, free_vars = unify_arguments(funcs)
108
if len(free_vars)>1:
109
from sage.misc.misc import deprecation
110
deprecation("Unnamed ranges for more than one variable is deprecated and will be removed from a future release of Sage; you can used named ranges instead, like (x,0,2)")
111
112
# pad the variables if we don't have enough
113
nargs = len(ranges)
114
if len(vars)<nargs:
115
vars += ('_',)*(nargs-len(vars))
116
117
ranges = [[float(z) for z in r] for r in ranges]
118
119
if plot_points is None:
120
plot_points=2
121
122
if not isinstance(plot_points, (list, tuple)):
123
plot_points = [plot_points]*len(ranges)
124
elif len(plot_points)!=nargs:
125
raise ValueError, "plot_points must be either an integer or a list of integers, one for each range"
126
127
plot_points = [int(p) if p>=2 else 2 for p in plot_points]
128
range_steps = [abs(range[1] - range[0])/(p-1) for range, p in zip(ranges, plot_points)]
129
if min(range_steps) == float(0):
130
raise ValueError, "plot start point and end point must be different"
131
132
options={}
133
if nargs==1:
134
options['expect_one_var']=True
135
136
if is_Vector(funcs):
137
funcs = list(funcs)
138
139
#TODO: raise an error if there is a function/method in funcs that takes more values than we have ranges
140
141
if return_vars:
142
return fast_float(funcs, *vars,**options), [tuple(range+[range_step]) for range,range_step in zip(ranges, range_steps)], vars
143
else:
144
return fast_float(funcs, *vars,**options), [tuple(range+[range_step]) for range,range_step in zip(ranges, range_steps)]
145
146
147
def unify_arguments(funcs):
148
"""
149
Returns a tuple of variables of the functions, as well as the
150
number of "free" variables (i.e., variables that defined in a
151
callable function).
152
153
INPUT:
154
155
- ``funcs`` -- a list of functions; these can be symbolic
156
expressions, polynomials, etc
157
158
OUTPUT: functions, expected arguments
159
160
- A tuple of variables in the functions
161
162
- A tuple of variables that were "free" in the functions
163
164
EXAMPLES:
165
166
sage: x,y,z=var('x,y,z')
167
sage: f(x,y)=x+y-z
168
sage: g(x,y)=x+y
169
sage: h(y)=-y
170
sage: sage.plot.misc.unify_arguments((f,g,h))
171
((x, y, z), (z,))
172
sage: sage.plot.misc.unify_arguments((g,h))
173
((x, y), ())
174
sage: sage.plot.misc.unify_arguments((f,z))
175
((x, y, z), (z,))
176
sage: sage.plot.misc.unify_arguments((h,z))
177
((y, z), (z,))
178
sage: sage.plot.misc.unify_arguments((x+y,x-y))
179
((x, y), (x, y))
180
"""
181
from sage.symbolic.callable import is_CallableSymbolicExpression
182
183
vars=set()
184
free_variables=set()
185
if not isinstance(funcs, (list, tuple)):
186
funcs=[funcs]
187
188
for f in funcs:
189
if is_CallableSymbolicExpression(f):
190
f_args=set(f.arguments())
191
vars.update(f_args)
192
else:
193
f_args=set()
194
195
try:
196
free_vars = set(f.variables()).difference(f_args)
197
vars.update(free_vars)
198
free_variables.update(free_vars)
199
except AttributeError:
200
# we probably have a constant
201
pass
202
return tuple(sorted(vars, key=lambda x: str(x))), tuple(sorted(free_variables, key=lambda x: str(x)))
203
204
#For backward compatibility -- see #9907.
205
from sage.misc.decorators import options, suboptions, rename_keyword
206
207
def _multiple_of_constant(n,pos,const):
208
"""
209
Function for internal use in formatting ticks on axes with
210
nice-looking multiples of various symbolic constants, such
211
as `\pi` or `e`. Should only be used via keyword argument
212
`tick_formatter` in :meth:`plot.show`. See documentation
213
for the matplotlib.ticker module for more details.
214
215
EXAMPLES:
216
217
Here is the intended use::
218
219
sage: plot(sin(x), (x,0,2*pi), ticks=pi/3, tick_formatter=pi)
220
221
Here is an unintended use, which yields unexpected (and probably
222
undesired) results::
223
224
sage: plot(x^2, (x, -2, 2), tick_formatter=pi)
225
226
We can also use more unusual constant choices::
227
228
sage: plot(ln(x), (x,0,10), ticks=e, tick_formatter=e)
229
sage: plot(x^2, (x,0,10), ticks=[sqrt(2),8], tick_formatter=sqrt(2))
230
"""
231
from sage.misc.latex import latex
232
from sage.rings.arith import convergents
233
c=[i for i in convergents(n/const.n()) if i.denominator()<12]
234
return '$%s$'%latex(c[-1]*const)
235
236