| 3417 | | plot_points -- the number of points to initially plot before |
|---|
| 3418 | | doing adaptive refinement |
|---|
| 3419 | | plot_division -- the maximum number of subdivisions to |
|---|
| 3420 | | introduce in adaptive refinement. |
|---|
| 3421 | | max_bend -- parameter that affects adaptive refinement |
|---|
| | 3417 | plot_points -- (default: 200) the number of points to initially plot |
|---|
| | 3418 | before the adaptive refinement. |
|---|
| | 3419 | adaptive_recursion -- (default: 5) how many levels of recursion to go |
|---|
| | 3420 | before giving up when doing adaptive refinement. |
|---|
| | 3421 | Setting this to 0 disables adaptive refinement. |
|---|
| | 3422 | adaptive_tolerance -- (default: 0.01) how large a difference should be |
|---|
| | 3423 | before the adaptive refinement code considers it |
|---|
| | 3424 | significant. Use a smaller value for smoother |
|---|
| | 3425 | plots and a larger value for coarser plots. In |
|---|
| | 3426 | general, adjust adaptive_recursion before |
|---|
| | 3427 | adaptive_tolerance, and consult the code of |
|---|
| | 3428 | adaptive_refinement for the details. |
|---|
| | 3429 | |
|---|
| 3502 | | sage: plot(sin(1/x), (-1, 1), plot_points=1000) |
|---|
| | 3505 | sage: plot(sin(1/x), (-1, 1)) |
|---|
| | 3506 | |
|---|
| | 3507 | The algorithm used to insert extra points is actually pretty simple. On |
|---|
| | 3508 | the picture drawn by the lines below: |
|---|
| | 3509 | sage: p = plot(x^2, (-0.5, 1.4)) + line([(0,0), (1,1)], rgbcolor='green') |
|---|
| | 3510 | sage: p += line([(0.5, 0.5), (0.5, 0.5^2)], rgbcolor='purple') |
|---|
| | 3511 | sage: p += point(((0, 0), (0.5, 0.5), (0.5, 0.5^2), (1, 1)), rgbcolor='red', pointsize=20) |
|---|
| | 3512 | sage: p += text('A', (-0.05, 0.1), rgbcolor='red') |
|---|
| | 3513 | sage: p += text('B', (1.01, 1.1), rgbcolor='red') |
|---|
| | 3514 | sage: p += text('C', (0.48, 0.57), rgbcolor='red') |
|---|
| | 3515 | sage: p += text('D', (0.53, 0.18), rgbcolor='red') |
|---|
| | 3516 | sage: p.show(axes=False, xmin=-0.5, xmax=1.4, ymin=0, ymax=2) |
|---|
| | 3517 | |
|---|
| | 3518 | You have the function (in blue) and its approximation (in green) passing |
|---|
| | 3519 | through the points A and B. The algorithm finds the midpoint C of AB and |
|---|
| | 3520 | computes the distance between C and D. The point D is added the curve if |
|---|
| | 3521 | it exceeds the (nonzero) adaptive_tolerance threshold. If D is added to |
|---|
| | 3522 | the curve, then the algorithm is applied recursively to the points A and D, |
|---|
| | 3523 | and D and B. It is repeated adaptive_recursion times (10, by default). |
|---|
| 3651 | | exceptions += 1 |
|---|
| 3652 | | exception_indices.append(i) |
|---|
| | 3673 | if i == 0: |
|---|
| | 3674 | for j in range(1, 99): |
|---|
| | 3675 | xj = xi + delta*j/100.0 |
|---|
| | 3676 | try: |
|---|
| | 3677 | data[i] = (float(xj), float(f(xj))) |
|---|
| | 3678 | if data[i][1] != data[i][1]: |
|---|
| | 3679 | raise ValueError, "nan result" |
|---|
| | 3680 | break |
|---|
| | 3681 | except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: |
|---|
| | 3682 | pass |
|---|
| | 3683 | elif i == plot_points-1: |
|---|
| | 3684 | for j in range(1, 99): |
|---|
| | 3685 | xj = xi - delta*j/100.0 |
|---|
| | 3686 | try: |
|---|
| | 3687 | data[i] = (float(xj), float(f(xj))) |
|---|
| | 3688 | if data[i][1] != data[i][1]: |
|---|
| | 3689 | raise ValueError, "nan result" |
|---|
| | 3690 | break |
|---|
| | 3691 | except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: |
|---|
| | 3692 | pass |
|---|
| | 3693 | else: |
|---|
| | 3694 | exceptions += 1 |
|---|
| | 3695 | exception_indices.append(i) |
|---|
| | 4493 | |
|---|
| | 4494 | def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, adaptive_recursion=10, level=0): |
|---|
| | 4495 | r""" |
|---|
| | 4496 | The adaptive refinement algorithm for plotting a function f. See the |
|---|
| | 4497 | docstring for plot or PlotFactory for a description of the algorithm. |
|---|
| | 4498 | |
|---|
| | 4499 | INPUT: |
|---|
| | 4500 | f -- a function of one variable |
|---|
| | 4501 | p1, p2 -- two points to refine between |
|---|
| | 4502 | adaptive_recursion -- (default: 10) how many levels of recursion to go |
|---|
| | 4503 | before giving up when doing adaptive refinement. |
|---|
| | 4504 | Setting this to 0 disables adaptive refinement. |
|---|
| | 4505 | adaptive_tolerance -- (default: 0.01) how large a difference should be |
|---|
| | 4506 | before the adaptive refinement code considers |
|---|
| | 4507 | it significant. See the documentation for |
|---|
| | 4508 | plot() for more information. |
|---|
| | 4509 | |
|---|
| | 4510 | OUTPUT: |
|---|
| | 4511 | list -- a list of points to insert between p1 and p2 to get |
|---|
| | 4512 | a better linear approximation between them |
|---|
| | 4513 | |
|---|
| | 4514 | TESTS: |
|---|
| | 4515 | sage: from sage.plot.plot import adaptive_refinement |
|---|
| | 4516 | sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01, level=10) |
|---|
| | 4517 | [] |
|---|
| | 4518 | sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01) |
|---|
| | 4519 | [(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)] |
|---|
| | 4520 | |
|---|
| | 4521 | This shows that lowering adaptive_tolerance and raising |
|---|
| | 4522 | adaptive_recursion both increase the number of subdivision points: |
|---|
| | 4523 | |
|---|
| | 4524 | sage: x = var('x') |
|---|
| | 4525 | sage: f = sin(1/x) |
|---|
| | 4526 | sage: n1 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.01)); n1 |
|---|
| | 4527 | 79 |
|---|
| | 4528 | sage: n2 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_recursion=5, adaptive_tolerance=0.01)); n2 |
|---|
| | 4529 | 15 |
|---|
| | 4530 | sage: n1 > n2 |
|---|
| | 4531 | True |
|---|
| | 4532 | |
|---|
| | 4533 | sage: n3 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.005)); n3 |
|---|
| | 4534 | 88 |
|---|
| | 4535 | sage: n1 < n3 |
|---|
| | 4536 | True |
|---|
| | 4537 | """ |
|---|
| | 4538 | if level >= adaptive_recursion: |
|---|
| | 4539 | return [] |
|---|
| | 4540 | x = (p1[0] + p2[0])/2.0 |
|---|
| | 4541 | try: |
|---|
| | 4542 | y = float(f(x)) |
|---|
| | 4543 | except (ZeroDivisionError, TypeError, ValueError, OverflowError), msg: |
|---|
| | 4544 | sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x), 1) |
|---|
| | 4545 | # give up for this branch |
|---|
| | 4546 | return [] |
|---|
| | 4547 | # this distance calculation is not perfect. |
|---|
| | 4548 | if abs((p1[1] + p2[1])/2.0 - y) > adaptive_tolerance: |
|---|
| | 4549 | return adaptive_refinement(f, p1, (x, y), |
|---|
| | 4550 | adaptive_tolerance=adaptive_tolerance, |
|---|
| | 4551 | adaptive_recursion=adaptive_recursion, |
|---|
| | 4552 | level=level+1) \ |
|---|
| | 4553 | + [(x, y)] + \ |
|---|
| | 4554 | adaptive_refinement(f, (x, y), p2, |
|---|
| | 4555 | adaptive_tolerance=adaptive_tolerance, |
|---|
| | 4556 | adaptive_recursion=adaptive_recursion, |
|---|
| | 4557 | level=level+1) |
|---|
| | 4558 | else: |
|---|
| | 4559 | return [] |