Ticket #3813: trac_3813-final.patch
| File trac_3813-final.patch, 14.2 kB (added by mhansen, 4 months ago) |
|---|
-
a/sage/plot/plot.py
old new 1170 1170 z-coordinate. 1171 1171 1172 1172 EXAMPLES: 1173 sage: sum([plot(z*sin(x), 0, 10).plot3d(z) for z in range(6)]) 1173 sage: sum([plot(z*sin(x), 0, 10).plot3d(z) for z in range(6)]) #long 1174 1174 """ 1175 1175 from sage.plot.plot3d.base import Graphics3dGroup 1176 1176 g = Graphics3dGroup([g.plot3d(**kwds) for g in self.__objects]) … … 1285 1285 Add grid lines at specific positions (using iterators). 1286 1286 sage: def maple_leaf(t): 1287 1287 ... return (100/(100+(t-pi/2)^8))*(2-sin(7*t)-cos(30*t)/2) 1288 sage: p = polar_plot(maple_leaf, -pi/4, 3*pi/2, rgbcolor="red",plot_points=1000) 1289 sage: p.show(gridlines=( [-3,-2.75,..,3], xrange(-1,5,2) )) 1288 sage: p = polar_plot(maple_leaf, -pi/4, 3*pi/2, rgbcolor="red",plot_points=1000) #long 1289 sage: p.show(gridlines=( [-3,-2.75,..,3], xrange(-1,5,2) )) #long 1290 1290 1291 1291 Add grid lines at specific positions (using functions). 1292 1292 sage: y = x^5 + 4*x^4 - 10*x^3 - 40*x^2 + 9*x + 36 … … 3372 3372 3373 3373 PLOT OPTIONS: 3374 3374 The plot options are 3375 plot_points -- the number of points to initially plot before 3376 doing adaptive refinement 3377 plot_division -- the maximum number of subdivisions to 3378 introduce in adaptive refinement. 3379 max_bend -- parameter that affects adaptive refinement 3375 plot_points -- (default: 200) the number of points to initially plot 3376 before the adaptive refinement. 3377 adaptive_recursion -- (default: 5) how many levels of recursion to go 3378 before giving up when doing adaptive refinement. 3379 Setting this to 0 disables adaptive refinement. 3380 adaptive_tolerance -- (default: 0.01) how large a difference should be 3381 before the adaptive refinement code considers it 3382 significant. Use a smaller value for smoother 3383 plots and a larger value for coarser plots. In 3384 general, adjust adaptive_recursion before 3385 adaptive_tolerance, and consult the code of 3386 adaptive_refinement for the details. 3387 3380 3388 xmin -- starting x value 3381 3389 xmax -- ending x value 3382 3390 color -- an rgb-tuple (r,g,b) with each of r,g,b between 0 and 1, or … … 3419 3427 Graphics object consisting of 1 graphics primitive 3420 3428 sage: len(P) # number of graphics primitives 3421 3429 1 3422 sage: len(P[0]) # how many points were computed 3423 2003430 sage: len(P[0]) # how many points were computed (random) 3431 6369 3424 3432 sage: P # render 3425 3433 3426 3434 sage: P = plot(sin, (0,10), plot_points=10); print P 3427 3435 Graphics object consisting of 1 graphics primitive 3428 3436 sage: len(P[0]) # random output 3429 803437 32 3430 3438 sage: P # render 3431 3439 3432 3440 We plot with randomize=False, which makes the initial sample 3433 3441 points evenly spaced (hence always the same). Adaptive plotting 3434 might insert other points, however, unless plot_division=0.3435 sage: p=plot(1, (x,0,3), plot_points=4, randomize=False, plot_division=0)3442 might insert other points, however, unless adaptive_recursion=0. 3443 sage: p=plot(1, (x,0,3), plot_points=4, randomize=False, adaptive_recursion=0) 3436 3444 sage: list(p[0]) 3437 3445 [(0.0, 1.0), (1.0, 1.0), (2.0, 1.0), (3.0, 1.0)] 3438 3446 … … 3446 3454 sage: plot([sin(n*x) for n in [1..4]], (0, pi)) 3447 3455 3448 3456 3449 The function $\sin(1/x)$ wiggles wildly near $0$, so the 3450 first plot below won't look perfect. Sage adapts to this 3451 and plots extra points near the origin. 3457 The function $\sin(1/x)$ wiggles wildly near $0$. Sage adapts 3458 to this and plots extra points near the origin. 3452 3459 sage: plot(sin(1/x), (x, -1, 1)) 3453 3454 With the \code{plot_points} option you can increase the number3455 of sample points, to obtain a more accurate plot.3456 sage: plot(sin(1/x), (x, -1, 1), plot_points=1000)3457 3460 3458 3461 Note that the independent variable may be omitted if there is no 3459 3462 ambiguity: 3460 sage: plot(sin(1/x), (-1, 1), plot_points=1000) 3463 sage: plot(sin(1/x), (-1, 1)) 3464 3465 The algorithm used to insert extra points is actually pretty simple. On 3466 the picture drawn by the lines below: 3467 sage: p = plot(x^2, (-0.5, 1.4)) + line([(0,0), (1,1)], rgbcolor='green') 3468 sage: p += line([(0.5, 0.5), (0.5, 0.5^2)], rgbcolor='purple') 3469 sage: p += point(((0, 0), (0.5, 0.5), (0.5, 0.5^2), (1, 1)), rgbcolor='red', pointsize=20) 3470 sage: p += text('A', (-0.05, 0.1), rgbcolor='red') 3471 sage: p += text('B', (1.01, 1.1), rgbcolor='red') 3472 sage: p += text('C', (0.48, 0.57), rgbcolor='red') 3473 sage: p += text('D', (0.53, 0.18), rgbcolor='red') 3474 sage: p.show(axes=False, xmin=-0.5, xmax=1.4, ymin=0, ymax=2) 3475 3476 You have the function (in blue) and its approximation (in green) passing 3477 through the points A and B. The algorithm finds the midpoint C of AB and 3478 computes the distance between C and D. The point D is added to the curve if 3479 it exceeds the (nonzero) adaptive_tolerance threshold. If D is added to 3480 the curve, then the algorithm is applied recursively to the points A and D, 3481 and D and B. It is repeated adaptive_recursion times (10, by default). 3461 3482 3462 3483 The actual sample points are slightly randomized, so the above 3463 3484 plots may look slightly different each time you draw them. … … 3487 3508 sage: set_verbose(0) 3488 3509 3489 3510 To plot the negative real cube root, use something like the following. 3490 sage: plot(lambda x : RR(x).nth_root(3), (x,-1, 1) )3511 sage: plot(lambda x : RR(x).nth_root(3), (x,-1, 1)) 3491 3512 3492 3513 TESTS: 3493 3514 We do not randomize the endpoints: … … 3533 3554 def _plot(funcs, xrange, parametric=False, 3534 3555 polar=False, label='', randomize=True, **kwds): 3535 3556 options = {'alpha':1,'thickness':1,'rgbcolor':(0,0,1), 3536 'plot_points':200, ' plot_division':1000,3537 ' max_bend': 0.1, 'rgbcolor': (0,0,1) }3557 'plot_points':200, 'adaptive_tolerance':0.01, 3558 'adaptive_recursion':5, 'rgbcolor': (0,0,1) } 3538 3559 3539 3560 if kwds.has_key('color') and not kwds.has_key('rgbcolor'): 3540 3561 kwds['rgbcolor'] = kwds['color'] … … 3555 3576 3556 3577 plot_points = int(options.pop('plot_points')) 3557 3578 x, data = var_and_list_of_values(xrange, plot_points) 3558 data = list(data)3559 3579 xmin = data[0] 3560 3580 xmax = data[-1] 3561 3581 … … 3564 3584 if isinstance(funcs, (list, tuple)) and not parametric: 3565 3585 return reduce(operator.add, (plot(f, (xmin, xmax), polar=polar, **kwds) for f in funcs)) 3566 3586 3567 if len(data) >= 2: 3568 delta = data[1]-data[0] 3569 else: 3570 delta = 0 3587 delta = float(xmin-xmax) / plot_points 3571 3588 3572 3589 random = current_randstate().python_random().random 3573 3590 exceptions = 0; msg='' … … 3576 3593 xi = data[i] 3577 3594 # Slightly randomize the interior sample points if 3578 3595 # randomize is true 3579 if i > 0 and i < plot_points-1: 3580 if randomize: 3581 xi += delta*random() 3582 if xi > xmax: 3583 xi = xmax 3584 elif i == plot_points-1: 3585 xi = xmax # guarantee that we get the last point. 3586 3596 if randomize and i > 0 and i < plot_points-1: 3597 xi += delta*(random() - 0.5) 3598 3587 3599 try: 3588 3600 data[i] = (float(xi), float(f(xi))) 3589 3601 if str(data[i][1]) in ['nan', 'NaN']: … … 3592 3604 exception_indices.append(i) 3593 3605 except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: 3594 3606 sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x),1) 3607 3608 if i == 0: 3609 for j in range(1, 99): 3610 xj = xi + delta*j/100.0 3611 try: 3612 data[i] = (float(xj), float(f(xj))) 3613 # nan != nan 3614 if data[i][1] != data[i][1]: 3615 continue 3616 break 3617 except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: 3618 pass 3619 else: 3620 exceptions += 1 3621 exception_indices.append(i) 3622 elif i == plot_points-1: 3623 for j in range(1, 99): 3624 xj = xi - delta*j/100.0 3625 try: 3626 data[i] = (float(xj), float(f(xj))) 3627 # nan != nan 3628 if data[i][1] != data[i][1]: 3629 continue 3630 break 3631 except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: 3632 pass 3633 else: 3634 exceptions += 1 3635 exception_indices.append(i) 3636 else: 3637 exceptions += 1 3638 exception_indices.append(i) 3639 3595 3640 exceptions += 1 3596 3641 exception_indices.append(i) 3597 3642 … … 3600 3645 3601 3646 # adaptive refinement 3602 3647 i, j = 0, 0 3603 max_bend = float(options['max_bend']) 3604 del options['max_bend'] 3605 plot_division = int(options['plot_division']) 3606 del options['plot_division'] 3648 adaptive_tolerance = delta * float(options.pop('adaptive_tolerance')) 3649 adaptive_recursion = int(options.pop('adaptive_recursion')) 3650 3607 3651 while i < len(data) - 1: 3608 if abs(data[i+1][1] - data[i][1]) > max_bend: 3609 x = float((data[i+1][0] + data[i][0])/2) 3610 try: 3611 y = float(f(x)) 3612 data.insert(i+1, (x, y)) 3613 except (ZeroDivisionError, TypeError, ValueError), msg: 3614 sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x),1) 3615 exceptions += 1 3616 j += 1 3617 if j > plot_division: 3618 break 3619 else: 3652 for p in adaptive_refinement(f, data[i], data[i+1], 3653 adaptive_tolerance=adaptive_tolerance, 3654 adaptive_recursion=adaptive_recursion): 3655 data.insert(i+1, p) 3620 3656 i += 1 3657 i += 1 3621 3658 3622 3659 if (len(data) == 0 and exceptions > 0) or exceptions > 10: 3623 3660 sage.misc.misc.verbose("WARNING: When plotting, failed to evaluate function at %s points."%exceptions, level=0) … … 4438 4475 'ymin':ymin, 'ymax':ymax} 4439 4476 else: 4440 4477 return xmin, xmax, ymin, ymax 4478 4479 def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, adaptive_recursion=5, level=0): 4480 r""" 4481 The adaptive refinement algorithm for plotting a function f. See the 4482 docstring for plot or PlotFactory for a description of the algorithm. 4483 4484 INPUT: 4485 f -- a function of one variable 4486 p1, p2 -- two points to refine between 4487 adaptive_recursion -- (default: 5) how many levels of recursion to go 4488 before giving up when doing adaptive refinement. 4489 Setting this to 0 disables adaptive refinement. 4490 adaptive_tolerance -- (default: 0.01) how large a difference should be 4491 before the adaptive refinement code considers 4492 it significant. See the documentation for 4493 plot() for more information. 4494 4495 OUTPUT: 4496 list -- a list of points to insert between p1 and p2 to get 4497 a better linear approximation between them 4498 4499 TESTS: 4500 sage: from sage.plot.plot import adaptive_refinement 4501 sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01, adaptive_recursion=0) 4502 [] 4503 sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01) 4504 [(0.125000000000000*pi, 0.38268343236508978), (0.187500000000000*pi, 0.55557023301960218), (0.250000000000000*pi, 0.707106781186547...), (0.312500000000000*pi, 0.83146961230254524), (0.375000000000000*pi, 0.92387953251128674), (0.437500000000000*pi, 0.98078528040323043), (0.500000000000000*pi, 1.0), (0.562500000000000*pi, 0.98078528040323043), (0.625000000000000*pi, 0.92387953251128674), (0.687500000000000*pi, 0.83146961230254546), (0.750000000000000*pi, 0.70710678118654757), (0.812500000000000*pi, 0.55557023301960218), (0.875000000000000*pi, 0.38268343236508989)] 4505 4506 This shows that lowering adaptive_tolerance and raising 4507 adaptive_recursion both increase the number of subdivision points: 4508 4509 sage: x = var('x') 4510 sage: f = sin(1/x) 4511 sage: n1 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.01)); n1 4512 15 4513 sage: n2 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_recursion=10, adaptive_tolerance=0.01)); n2 4514 79 4515 sage: n3 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.001)); n3 4516 26 4517 """ 4518 if level >= adaptive_recursion: 4519 return [] 4520 x = (p1[0] + p2[0])/2.0 4521 try: 4522 y = float(f(x)) 4523 except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: 4524 sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x), 1) 4525 # give up for this branch 4526 return [] 4527 4528 # this distance calculation is not perfect. 4529 if abs((p1[1] + p2[1])/2.0 - y) > adaptive_tolerance: 4530 return adaptive_refinement(f, p1, (x, y), 4531 adaptive_tolerance=adaptive_tolerance, 4532 adaptive_recursion=adaptive_recursion, 4533 level=level+1) \ 4534 + [(x, y)] + \ 4535 adaptive_refinement(f, (x, y), p2, 4536 adaptive_tolerance=adaptive_tolerance, 4537 adaptive_recursion=adaptive_recursion, 4538 level=level+1) 4539 else: 4540 return []