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  
    11701170        z-coordinate.  
    11711171 
    11721172        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 
    11741174        """ 
    11751175        from sage.plot.plot3d.base import Graphics3dGroup 
    11761176        g = Graphics3dGroup([g.plot3d(**kwds) for g in self.__objects]) 
     
    12851285        Add grid lines at specific positions (using iterators). 
    12861286            sage: def maple_leaf(t): 
    12871287            ...     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 
    12901290 
    12911291        Add grid lines at specific positions (using functions). 
    12921292            sage: y = x^5 + 4*x^4 - 10*x^3 - 40*x^2 + 9*x + 36 
     
    33723372 
    33733373    PLOT OPTIONS: 
    33743374    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 
    33803388        xmin -- starting x value 
    33813389        xmax -- ending x value 
    33823390        color -- an rgb-tuple (r,g,b) with each of r,g,b between 0 and 1, or 
     
    34193427        Graphics object consisting of 1 graphics primitive 
    34203428        sage: len(P)     # number of graphics primitives 
    34213429        1 
    3422         sage: len(P[0])  # how many points were computed 
    3423         200 
     3430        sage: len(P[0])  # how many points were computed (random) 
     3431        6369 
    34243432        sage: P          # render 
    34253433         
    34263434        sage: P = plot(sin, (0,10), plot_points=10); print P 
    34273435        Graphics object consisting of 1 graphics primitive 
    34283436        sage: len(P[0])  # random output 
    3429         80 
     3437        32 
    34303438        sage: P          # render 
    34313439 
    34323440    We plot with randomize=False, which makes the initial sample  
    34333441    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) 
    34363444        sage: list(p[0]) 
    34373445        [(0.0, 1.0), (1.0, 1.0), (2.0, 1.0), (3.0, 1.0)] 
    34383446 
     
    34463454        sage: plot([sin(n*x) for n in [1..4]], (0, pi)) 
    34473455 
    34483456 
    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. 
    34523459        sage: plot(sin(1/x), (x, -1, 1)) 
    3453  
    3454     With the \code{plot_points} option you can increase the number 
    3455     of sample points, to obtain a more accurate plot.  
    3456         sage: plot(sin(1/x), (x, -1, 1), plot_points=1000) 
    34573460 
    34583461    Note that the independent variable may be omitted if there is no 
    34593462    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). 
    34613482 
    34623483    The actual sample points are slightly randomized, so the above 
    34633484    plots may look slightly different each time you draw them. 
     
    34873508        sage: set_verbose(0) 
    34883509             
    34893510    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)
    34913512 
    34923513    TESTS: 
    34933514    We do not randomize the endpoints: 
     
    35333554def _plot(funcs, xrange, parametric=False, 
    35343555              polar=False, label='', randomize=True, **kwds): 
    35353556    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) } 
    35383559 
    35393560    if kwds.has_key('color') and not kwds.has_key('rgbcolor'): 
    35403561        kwds['rgbcolor'] = kwds['color'] 
     
    35553576 
    35563577    plot_points = int(options.pop('plot_points')) 
    35573578    x, data = var_and_list_of_values(xrange, plot_points) 
    3558     data = list(data) 
    35593579    xmin = data[0] 
    35603580    xmax = data[-1] 
    35613581 
     
    35643584    if isinstance(funcs, (list, tuple)) and not parametric: 
    35653585        return reduce(operator.add, (plot(f, (xmin, xmax), polar=polar, **kwds) for f in funcs)) 
    35663586 
    3567     if len(data) >= 2: 
    3568         delta = data[1]-data[0] 
    3569     else: 
    3570         delta = 0 
     3587    delta = float(xmin-xmax) / plot_points 
    35713588 
    35723589    random = current_randstate().python_random().random 
    35733590    exceptions = 0; msg='' 
     
    35763593        xi = data[i] 
    35773594        # Slightly randomize the interior sample points if 
    35783595        # 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             
    35873599        try: 
    35883600            data[i] = (float(xi), float(f(xi))) 
    35893601            if str(data[i][1]) in ['nan', 'NaN']: 
     
    35923604                exception_indices.append(i) 
    35933605        except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: 
    35943606            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     
    35953640            exceptions += 1 
    35963641            exception_indices.append(i) 
    35973642 
     
    36003645 
    36013646    # adaptive refinement 
    36023647    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 
    36073651    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) 
    36203656            i += 1 
     3657       i += 1 
    36213658 
    36223659    if (len(data) == 0 and exceptions > 0) or exceptions > 10: 
    36233660        sage.misc.misc.verbose("WARNING: When plotting, failed to evaluate function at %s points."%exceptions, level=0) 
     
    44384475                'ymin':ymin, 'ymax':ymax} 
    44394476    else: 
    44404477        return xmin, xmax, ymin, ymax 
     4478 
     4479def 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 []