-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Added fplot to Axes subclass. #737
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
1df29a3
Merge branch 'trisurf'
dmcdougall e4d7a9e
Merge remote-tracking branch 'upstream/master'
dmcdougall 8673b07
Merge remote-tracking branch 'upstream/master'
dmcdougall 22d5e09
Merge remote-tracking branch 'upstream/master'
dmcdougall 241ab8a
Merge remote-tracking branch 'upstream/master'
dmcdougall 646989d
Added fplot to Axes subclass.
dmcdougall a04f581
Added docstring to Axes.fplot
dmcdougall d0b01d5
Scaled minimum step size with domain length.
dmcdougall c6c8b31
Small changes to the algorithm to reduce run time.
dmcdougall f04c2ab
Updated kwargs popping.
dmcdougall 04bb1a0
Speed up computation when f is expensive.
dmcdougall 90af9c9
Account for singularities in f.
dmcdougall File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
""" | ||
1D Callable function plotting. | ||
|
||
""" | ||
import numpy as np | ||
import matplotlib | ||
|
||
|
||
__all__ = ['fplot'] | ||
|
||
|
||
def fplot(axes, f, limits, *args, **kwargs): | ||
"""Plots a callable function f. | ||
|
||
Parameters | ||
---------- | ||
*f* : Python callable, the function that is to be plotted. | ||
*limits* : 2-element array or list of limits: [xmin, xmax]. The function f | ||
is to to be plotted between xmin and xmax. | ||
|
||
Returns | ||
------- | ||
*lines* : `matplotlib.collections.LineCollection` | ||
Line collection with that describes the function *f* between xmin | ||
and xmax. all streamlines as a series of line segments. | ||
""" | ||
|
||
# TODO: Check f is callable. If not callable, support array of callables. | ||
# TODO: Support y limits? | ||
|
||
# Some small number, usually close to machine epsilon | ||
eps = 1e-10 | ||
|
||
# If the gradient is bigger than this, we say it has a singularity | ||
sing_tol = 1e6 | ||
|
||
# The scaling factor used to scale the step size | ||
# as a function of the domain length | ||
scale = max(1.0, abs(limits[1] - limits[0])) | ||
|
||
# 0.2% absolute error | ||
tol = kwargs.pop('tol', 2e-3) | ||
n = kwargs.pop('tol', 50) | ||
|
||
x = np.linspace(limits[0], limits[1], n) | ||
f_vals = [f(xi) for xi in x] | ||
|
||
# Bisect abscissa until the gradient error changes by less than tol | ||
within_tol = False | ||
|
||
while not within_tol: | ||
within_tol = True | ||
new_pts = [] | ||
new_f = [] | ||
for i in xrange(len(x)-1): | ||
if np.isnan(f_vals[i]): | ||
continue | ||
# Make sure the step size is not pointlessly small. | ||
# This is a numerical check to prevent silly roundoff errors. | ||
# | ||
# The temporary variable is to ensure the step size is | ||
# represented properly in binary. | ||
min_step = np.sqrt(eps) * x[i] * scale | ||
|
||
tmp = x[i] + min_step | ||
|
||
# The multiplation by two is just to be conservative. | ||
min_step = 2*(tmp - x[i]) | ||
|
||
# Subdivide | ||
x_new = (x[i+1] + x[i]) / 2.0 | ||
|
||
# If the absicissa points are too close, don't bisect | ||
# since calculation of the gradient will produce mostly | ||
# nonsense values due to roundoff error. | ||
# | ||
# If the function values are too close, the payoff is | ||
# negligible, so skip them. | ||
f_new = f(x_new) # Used later, so store it | ||
if abs(x_new - x[i]) < min_step or abs(f_new - f_vals[i]) < min_step: | ||
continue | ||
|
||
# Compare gradients of actual f and linear approximation | ||
# FIXME: What if f(x[i]) or f(x[i+1]) is nan? | ||
dx = abs(x[i+1] - x[i]) | ||
f_interp = (f_vals[i+1] + f_vals[i]) | ||
|
||
# This line is the absolute error of the gradient | ||
grad_error = np.abs(f_interp - 2.0 * f_new) / dx | ||
|
||
# If the new gradient is not within the tolerance, store | ||
# the subdivision point for merging later | ||
if grad_error > sing_tol: | ||
f_vals[i] = np.nan | ||
f_vals[i+1] = np.nan | ||
elif grad_error > tol: | ||
within_tol = False | ||
new_pts.append(x_new) | ||
new_f.append(f_new) | ||
|
||
if not within_tol: | ||
# Not sure this is the best way to do this... | ||
# Merge the subdivision points into the array of abscissae | ||
x, f_vals = merge_pts(x, new_pts, f_vals, new_f) | ||
|
||
return axes.plot(x, f_vals) | ||
|
||
def merge_pts(xs, xs_sub, fs, fs_sub): | ||
x = [] | ||
f = [] | ||
ia = 0 | ||
ib = 0 | ||
while ib < len(xs_sub): | ||
if xs_sub[ib] < xs[ia]: | ||
x.append(xs_sub[ib]) | ||
f.append(fs_sub[ib]) | ||
ib += 1 | ||
else: | ||
x.append(xs[ia]) | ||
f.append(fs[ia]) | ||
ia += 1 | ||
if ia < len(xs): | ||
return np.append(x, xs[ia::]), np.append(f, fs[ia::]) | ||
else: | ||
return np.array(x), np.array(f) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function needs a doc string -- take a look at contour to see how we share docstrings between module helper code and the axes wrapper funcs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I'll make sure to add that.