Ticket #3813: trac_3813_rebase.patch
| File trac_3813_rebase.patch, 14.5 kB (added by anakha, 5 months ago) |
|---|
-
a/sage/plot/plot.py
old new 3583 3583 3584 3584 PLOT OPTIONS: 3585 3585 The plot options are 3586 plot_points -- the number of points to initially plot before 3587 doing adaptive refinement 3588 plot_division -- the maximum number of subdivisions to 3589 introduce in adaptive refinement. 3590 max_bend -- parameter that affects adaptive refinement 3586 plot_points -- (default: 200) the number of points to initially plot 3587 before the adaptive refinement. 3588 adaptive_recursion -- (default: 5) how many levels of recursion to go 3589 before giving up when doing adaptive refinement. 3590 Setting this to 0 disables adaptive refinement. 3591 adaptive_tolerance -- (default: 0.01) how large a difference should be 3592 before the adaptive refinement code considers it 3593 significant. Use a smaller value for smoother 3594 plots and a larger value for coarser plots. In 3595 general, adjust adaptive_recursion before 3596 adaptive_tolerance, and consult the code of 3597 adaptive_refinement for the details. 3598 3591 3599 xmin -- starting x value 3592 3600 xmax -- ending x value 3593 3601 color -- an rgb-tuple (r,g,b) with each of r,g,b between 0 and 1, or … … 3630 3638 Graphics object consisting of 1 graphics primitive 3631 3639 sage: len(P) # number of graphics primitives 3632 3640 1 3633 sage: len(P[0]) # how many points were computed 3634 2 003641 sage: len(P[0]) # how many points were computed (random) 3642 224 3635 3643 sage: P # render 3636 3644 3637 3645 sage: P = plot(sin, (0,10), plot_points=10); print P 3638 3646 Graphics object consisting of 1 graphics primitive 3639 3647 sage: len(P[0]) # random output 3640 803648 32 3641 3649 sage: P # render 3642 3650 3643 3651 We plot with randomize=False, which makes the initial sample 3644 3652 points evenly spaced (hence always the same). Adaptive plotting 3645 might insert other points, however, unless plot_division=0.3646 sage: p=plot(lambda x: 1, (x,0,3), plot_points=4, randomize=False, plot_division=0)3653 might insert other points, however, unless adaptive_recursion=0. 3654 sage: p=plot(lambda x: 1, (x,0,3), plot_points=4, randomize=False, adaptive_recursion=0) 3647 3655 sage: list(p[0]) 3648 3656 [(0.0, 1.0), (1.0, 1.0), (2.0, 1.0), (3.0, 1.0)] 3649 3657 … … 3657 3665 sage: plot([sin(n*x) for n in [1..4]], (0, pi)) 3658 3666 3659 3667 3660 The function $\sin(1/x)$ wiggles wildly near $0$, so the 3661 first plot below won't look perfect. Sage adapts to this 3662 and plots extra points near the origin. 3668 The function $\sin(1/x)$ wiggles wildly near $0$. Sage adapts 3669 to this and plots extra points near the origin. 3663 3670 sage: plot(sin(1/x), (x, -1, 1)) 3664 3665 With the \code{plot_points} option you can increase the number3666 of sample points, to obtain a more accurate plot.3667 sage: plot(sin(1/x), (x, -1, 1), plot_points=1000)3668 3671 3669 3672 Note that the independent variable may be omitted if there is no 3670 3673 ambiguity: 3671 sage: plot(sin(1/x), (-1, 1), plot_points=1000) 3674 sage: plot(sin(1/x), (-1, 1)) 3675 3676 The algorithm used to insert extra points is actually pretty simple. On 3677 the picture drawn by the lines below: 3678 sage: p = plot(x^2, (-0.5, 1.4)) + line([(0,0), (1,1)], rgbcolor='green') 3679 sage: p += line([(0.5, 0.5), (0.5, 0.5^2)], rgbcolor='purple') 3680 sage: p += point(((0, 0), (0.5, 0.5), (0.5, 0.5^2), (1, 1)), rgbcolor='red', pointsize=20) 3681 sage: p += text('A', (-0.05, 0.1), rgbcolor='red') 3682 sage: p += text('B', (1.01, 1.1), rgbcolor='red') 3683 sage: p += text('C', (0.48, 0.57), rgbcolor='red') 3684 sage: p += text('D', (0.53, 0.18), rgbcolor='red') 3685 sage: p.show(axes=False, xmin=-0.5, xmax=1.4, ymin=0, ymax=2) 3686 3687 You have the function (in blue) and its approximation (in green) passing 3688 through the points A and B. The algorithm finds the midpoint C of AB and 3689 computes the distance between C and D. The point D is added to the curve if 3690 it exceeds the (nonzero) adaptive_tolerance threshold. If D is added to 3691 the curve, then the algorithm is applied recursively to the points A and D, 3692 and D and B. It is repeated adaptive_recursion times (10, by default). 3672 3693 3673 3694 The actual sample points are slightly randomized, so the above 3674 3695 plots may look slightly different each time you draw them. … … 3698 3719 sage: set_verbose(0) 3699 3720 3700 3721 To plot the negative real cube root, use something like the following. 3701 sage: plot(lambda x : RR(x).nth_root(3), (x,-1, 1) )3722 sage: plot(lambda x : RR(x).nth_root(3), (x,-1, 1)) 3702 3723 3703 3724 TESTS: 3704 3725 We do not randomize the endpoints: … … 3712 3733 def _reset(self): 3713 3734 o = self.options 3714 3735 o['plot_points'] = 200 3715 o[' plot_division'] = 10003716 o[' max_bend'] = 0.13717 o['rgbcolor'] = (0,0,1) 3718 3719 def __repr__(self): 3720 """3736 o['adaptive_tolerance'] = 0.01 3737 o['adaptive_recursion'] = 5 3738 o['rgbcolor'] = (0,0,1) 3739 3740 def __repr__(self): 3741 r""" 3721 3742 Returns a string representation of this PlotFactory object. 3722 3743 3723 3744 TESTS: … … 3792 3813 plot_points = int(options['plot_points']) 3793 3814 del options['plot_points'] 3794 3815 x, data = var_and_list_of_values(xrange, plot_points) 3795 data = list(data)3796 3816 xmin = data[0] 3797 3817 xmax = data[-1] 3798 3818 … … 3801 3821 if isinstance(funcs, (list, tuple)) and not parametric: 3802 3822 return reduce(operator.add, (plot(f, (xmin, xmax), polar=polar, **kwds) for f in funcs)) 3803 3823 3804 if len(data) >= 2: 3805 delta = data[1]-data[0] 3806 else: 3807 delta = 0 3824 delta = float(xmin-xmax) / plot_points 3808 3825 3809 3826 random = current_randstate().python_random().random 3810 3827 exceptions = 0; msg='' … … 3813 3830 xi = data[i] 3814 3831 # Slightly randomize the interior sample points if 3815 3832 # randomize is true 3816 if i > 0 and i < plot_points-1: 3817 if randomize: 3818 xi += delta*random() 3819 if xi > xmax: 3820 xi = xmax 3821 elif i == plot_points-1: 3822 xi = xmax # guarantee that we get the last point. 3823 3833 if randomize and i > 0 and i < plot_points-1: 3834 xi += delta*(random() - 0.5) 3835 3824 3836 try: 3825 3837 data[i] = (float(xi), float(f(xi))) 3838 # Only NaN is not equal to NaN 3839 if data[i][1] != data[i][1]: 3840 raise ValueError, "nan result" 3826 3841 except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: 3827 3842 sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x),1) 3828 exceptions += 1 3829 exception_indices.append(i) 3830 3831 if str(data[i][1]) in ['nan', 'NaN']: 3832 sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x),1) 3833 exceptions += 1 3834 exception_indices.append(i) 3835 3843 if i == 0: 3844 for j in range(1, 99): 3845 xj = xi + delta*j/100.0 3846 try: 3847 data[i] = (float(xj), float(f(xj))) 3848 # nan != nan 3849 if data[i][1] != data[i][1]: 3850 continue 3851 break 3852 except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: 3853 pass 3854 else: 3855 exceptions += 1 3856 exception_indices.append(i) 3857 elif i == plot_points-1: 3858 for j in range(1, 99): 3859 xj = xi - delta*j/100.0 3860 try: 3861 data[i] = (float(xj), float(f(xj))) 3862 # nan != nan 3863 if data[i][1] != data[i][1]: 3864 continue 3865 break 3866 except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: 3867 pass 3868 else: 3869 exceptions += 1 3870 exception_indices.append(i) 3871 else: 3872 exceptions += 1 3873 exception_indices.append(i) 3874 3836 3875 data = [data[i] for i in range(len(data)) if i not in exception_indices] 3837 3876 3877 if 'plot_division' in options: 3878 # turn off adaptive refinement if it was requested with the old method, ignore otherwise 3879 if options['plot_division'] == 0: 3880 options['adaptive_recursion'] = 0 3881 del options['plot_division'] 3882 import warnings 3883 warnings.warn("plot_division is deprecated. See adaptive_recursion in the documentation for plot()", DeprecationWarning, stacklevel=3) 3838 3884 # adaptive refinement 3839 3885 i, j = 0, 0 3840 max_bend = float(options['max_bend']) 3841 del options['max_bend'] 3842 plot_division = int(options['plot_division']) 3843 del options['plot_division'] 3886 adaptive_tolerance = delta * float(options['adaptive_tolerance']) 3887 del options['adaptive_tolerance'] 3888 adaptive_recursion = int(options['adaptive_recursion']) 3889 del options['adaptive_recursion'] 3890 3844 3891 while i < len(data) - 1: 3845 if abs(data[i+1][1] - data[i][1]) > max_bend: 3846 x = float((data[i+1][0] + data[i][0])/2) 3847 try: 3848 y = float(f(x)) 3849 data.insert(i+1, (x, y)) 3850 except (ZeroDivisionError, TypeError, ValueError), msg: 3851 sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x),1) 3852 exceptions += 1 3853 j += 1 3854 if j > plot_division: 3855 break 3856 else: 3892 for p in adaptive_refinement(f, data[i], data[i+1], 3893 adaptive_tolerance=adaptive_tolerance, 3894 adaptive_recursion=adaptive_recursion): 3895 data.insert(i+1, p) 3857 3896 i += 1 3858 3897 i += 1 3898 3859 3899 if (len(data) == 0 and exceptions > 0) or exceptions > 10: 3860 3900 sage.misc.misc.verbose("WARNING: When plotting, failed to evaluate function at %s points."%exceptions, level=0) 3861 3901 sage.misc.misc.verbose("Last error message: '%s'"%msg, level=0) … … 4681 4721 'ymin':ymin, 'ymax':ymax} 4682 4722 else: 4683 4723 return xmin, xmax, ymin, ymax 4724 4725 def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, adaptive_recursion=5, level=0): 4726 r""" 4727 The adaptive refinement algorithm for plotting a function f. See the 4728 docstring for plot or PlotFactory for a description of the algorithm. 4729 4730 INPUT: 4731 f -- a function of one variable 4732 p1, p2 -- two points to refine between 4733 adaptive_recursion -- (default: 5) how many levels of recursion to go 4734 before giving up when doing adaptive refinement. 4735 Setting this to 0 disables adaptive refinement. 4736 adaptive_tolerance -- (default: 0.01) how large a difference should be 4737 before the adaptive refinement code considers 4738 it significant. See the documentation for 4739 plot() for more information. 4740 4741 OUTPUT: 4742 list -- a list of points to insert between p1 and p2 to get 4743 a better linear approximation between them 4744 4745 TESTS: 4746 sage: from sage.plot.plot import adaptive_refinement 4747 sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01, adaptive_recursion=0) 4748 [] 4749 sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01) 4750 [(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)] 4751 4752 This shows that lowering adaptive_tolerance and raising 4753 adaptive_recursion both increase the number of subdivision points: 4754 4755 sage: x = var('x') 4756 sage: f = sin(1/x) 4757 sage: n1 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.01)); n1 4758 79 4759 sage: n2 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_recursion=5, adaptive_tolerance=0.01)); n2 4760 15 4761 sage: n3 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.005)); n3 4762 88 4763 """ 4764 if level >= adaptive_recursion: 4765 return [] 4766 x = (p1[0] + p2[0])/2.0 4767 try: 4768 y = float(f(x)) 4769 except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: 4770 sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x), 1) 4771 # give up for this branch 4772 return [] 4773 4774 # this distance calculation is not perfect. 4775 if abs((p1[1] + p2[1])/2.0 - y) > adaptive_tolerance: 4776 return adaptive_refinement(f, p1, (x, y), 4777 adaptive_tolerance=adaptive_tolerance, 4778 adaptive_recursion=adaptive_recursion, 4779 level=level+1) \ 4780 + [(x, y)] + \ 4781 adaptive_refinement(f, (x, y), p2, 4782 adaptive_tolerance=adaptive_tolerance, 4783 adaptive_recursion=adaptive_recursion, 4784 level=level+1) 4785 else: 4786 return []