-
-
Notifications
You must be signed in to change notification settings - Fork 359
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
Plot Recipe for Automatic Adaptive Plotting of Functions #621
Comments
I was bored so I did the simplest thing I could: http://imgur.com/a/SksX3 Script is here: https://gist.github.com/KristofferC/b13f2375e4b7596db7b2a82237151709 |
I'm refining 50% of the intervals each iteration because the total cost of the function evaluation should then be just an extra factor of two compared to the last evaluation. (2^0 + 2^1 + ... 2^n) = 2 * 2^n |
Awesome. We could make this be the new recipe for single-function calls
…On Thu, Dec 22, 2016 at 8:20 AM Kristoffer Carlsson < ***@***.***> wrote:
I'm refining 50% of the intervals each iteration because the total cost of
the function evaluation should then be just a factor of two compared to the
last evaluation. (2^0 + 2^1 + ... 2^n) = 2 * 2^n
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#621 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA492krgGldAbPws9rUbx07UfaY1W0S3ks5rKpUegaJpZM4LT8hR>
.
|
But you still need to give start and end points, I guess? Also, how would you differ between |
This implementation is of course also susceptible to aliasing, for example if you create a sine wave that is zero at two adjacent initial "seed" points. Maybe sprinkling some random points in each iteration could help. There must be tons of literature for this though. Not that I am personally not so interested in reading it :P |
Without looking at your posts I wrote exactly the same algorithm (also refining half the points). Mathematica seems to deal with the aliasing issue with a nontrivial periodic (?) pattern in the initial sampling: https://imgur.com/a/UC1vv (I plotted the differences between consecutive initial sampling points divided by the average interval length, so for a regular sampling everything would be 1). |
Oh wow, you guys really ran with this!
I presume there's some randomness at each step which gives new points. If you look at the picture from Mathematica at the top, you see that the points don't where the midpoints would be, they seem slightly off. I was wondering why this would be the case, and aliasing would be a really good explanation. I think throwing in a little bit of noise in the initial selection of points, and a little bit of noise in each interval subdivision (amount of noise as a percentage of the interval size?), and it should be good should be good enough.
What about |
Actually running Mathematica code suggests that the refinements are always done in the middle of two consecutive points. I suspect that this picture is just an "artist view" and not to be taken too seriously. That said, we are not a Mathematica clone and I don't see any problem with some randomness here (except that this would make plotting nondeterministic, for "nice" functions it shouldn't be too bad but maybe we don't want that). |
I am also thinking that we could easily extend this to parametric plots. |
I think, going along with the idea I had about giving more timepoints, instead it would be really nice to be able to declare known points of discontinuity, and ensure that the initial points straddle it dis+epsilon/2 and dis-epsilon/2 away. Being able to declare discontinuities might reduce the number of points in some cases? But from @KristofferC 's pictures I don't see this as a real problem. |
Unless I am mistaken each discontinuity will generate at most |
I will fix something better than last one. Just gotta dress up the Christmas tree zzz. |
Ok, new version at: https://gist.github.com/KristofferC/ce5169000b29cc6b15588dc4e133ee5c with results: http://imgur.com/a/iBAi2. This one should just estimate the second derivative in each point and also wiggle the points a bit to try to remove degenerate cases. Not sure how to decide which one is best though... Also, like Tom said, this could be used for simply |
I think it is important not to have too fine grid in the start as well. 50 points is probably too much. |
It seems like this second one is safer, so I'd assume it is 👍 . The only way to really know is to let it in the wild for a year and see what crazy things end up causing a problem.
And could that be a kwarg? Along with |
The function right now has a |
I think multiplying the error with the interval should be done as well. Basically integrating the second derivative over the interval. I updated the gist. |
Exit early if |
I think you can just generate (same for |
Yeah it is not optimized but to be honest, it is not really needed. It goes fast enough for simple functions and for complicated functions, time would be spent in evaluating the function. I don't think |
Then what about something like est = sum(abs2,err./(abstol + max(f.(xs))*reltol))/length(err) Then stop when Edit: that max isn't the error, it's the values |
My use of In adaptive finite element, one way of estimating the error is to see what happens to the solution after a hierarchial p- or h-refinement where you project the solution of the coarse mesh onto the fine mesh (or higher polynomial mesh). I will try something like that. |
I see. I thought it was a comparison. Then yes, a dual mesh comparison for an error is the way to go. Though you could estimate errors without resorting to a second mesh by checking the inbetween points against a linear interpolation. |
Updated with error estimator: https://gist.github.com/KristofferC/4b999e8b376d5073f7d4d8f69cf3352e. Results at: http://imgur.com/a/15WsA For linear function we break straight away. Other function breaks at "decent" point density I think. Feel free to test with your own functions. |
Fancy! |
I think that the real test is |
Yeah, that is the evil one :P Does other software handle it "well"?. |
I am getting an error when trying a zero one loss function on 0.5, but that could very well be my fault somehow in my blind ignorance of just calling it with no careful thought whatsoever. Example plot of the function: julia> f = x -> x > 0 ? 0 : 1
(::#18) (generic function with 1 method)
julia> mm = (-2, 2)
(-2,2)
julia> xs = refine_grid(f, mm)
ERROR: InexactError()
in setindex!(::Array{Int64,1}, ::Float64, ::Int64) at ./array.jl:415
in #refine_grid#17(::Int64, ::Float64, ::Function, ::Function, ::Tuple{Int64,Int64}) at ./REPL[87]:67
in refine_grid(::Function, ::Tuple{Int64,Int64}) at ./REPL[87]:3 |
Right now I remove the top 5% of the maximum errors for the termination criteria in order for the error not to blow up at discontinuities. No idea if it is a good idea but otherwise, discontinuities will always lead to max refinement. |
Great stuff! Do you know where to add this code in Plots? (Look for the
single function and function with endpoints recipes, I think in recipes.jl
or series.jl?)
…On Fri, Dec 23, 2016 at 7:15 AM Christopher Rackauckas < ***@***.***> wrote:
Wow, that looks really good.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#621 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA492gpi-6OFq_EHsg3h6PdYT3MjSlElks5rK9eMgaJpZM4LT8hR>
.
|
Maybe this would fit better into PlotUtils? so that freeloaders such as the UnicodePlots guy (i.e. me) can use it as well (if he ever gets around to his refactor that is). Anyhow, big fan either way |
I'd be cool with adding the core logic to PlotUtils and just calling it
from the Plots recipe
…On Fri, Dec 23, 2016 at 7:25 AM Christof Stocker ***@***.***> wrote:
Maybe this would fit better into PlotUtils? so that freeloaders such as
the UnicodePlots guy, can use it as well (if he ever gets around to his
refactor that is). Anyhow, big fan either way
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#621 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA492gZcaV4EmhUJQAbCzEIdJoE-GE-gks5rK9ntgaJpZM4LT8hR>
.
|
Ref JuliaPlots/PlotUtils.jl#6. Regarding explicit y-limits I guess it could be solved by returning many sub intervals where the function is inside the y-limits on all the intervals. However, I don't think I can allow myself to work much more on this (not that it isn't fun). |
Another way of dealing with y-limit is simply not to refine intervals where the function is outside the y-limits. |
FWIW I have collected a small set of horror functions to test plotting algorithms: https://github.com/soegaard/bracket/blob/master/plotting/adaptive-plotting.rkt#L225 A lot of them are from |
Have you tried plotting them with Plots? |
FWIW, I am sure the stuff I implemented is not at all state of the art, quality wise. It was just me applying what sort of makes sense. It would be interesting to see how it works on these horror plots. |
No, I haven't tried Plots - I found this discussion through a comment on https://scicomp.stackexchange.com/q/2377/1541 |
Are you familiar with Julia? Could you describe the racket syntax clearly enough for someone here to port them to Julia? It could be fun to see. |
I'm not familar with Julia. The Racket syntax is just prefix notation. Instead of a+b we write (+ a b).
is tan( x^3 -x + 1) + 1 / (+ x 3 e^x) Here Other functions on the list:
These are simpler. They check specific properties:
-2. 2. -15. 15.) |
That looks awful lol. |
I thought I tested sin(1/x) specifically... |
shouldn't be too hard too parse that as Julia, just need to add commas and put the operator outside the parentheses
|
I think there is a difference with how max deals with NaNs in 0.6 or something... |
@pkofod great that it's that simple! So do all the functions plot as prettily as sin(1/x) after @KristofferC 's update? |
Update what? |
Oh lol nvm. |
I don't know... I've banned myself from using |
What is really needed is to take the ylimits into account when doing the partitioning. |
It would be nice to be able to
plot(f)
forf(t)
some function which returns scalars (or more). Mathematica handles this with a sophisticated algorithm. It's only mentioned on this page:https://reference.wolfram.com/language/ref/Plot.html
and described as
It seems like the algorithm does the following (or at least something like it):
max_recursion
or it hits the tolerance.This form of adaptive plotting would make it easy to plot a function without knowing the details. This is nice for when you don't actually know what the function looks like until plotting it!
@Armavica
The text was updated successfully, but these errors were encountered: