scijs / integrate-adaptive-simpson

Compute a definite integral using Simpson's Rule with adaptive quadrature

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Integration extremely slow or frozen within Atom text editor

b3by opened this issue · comments

Hello there,
I am working on a mathematical package for the Atom Text Editor. I would like to include some support for definite integration, and I was thinking about using this package.

I run some tests with it, and still something does not work as expected.

This package should work in conjunction with math.js, and it actually does. Within a node console, I am able to use it in the fashion I need:

var mathjs    = require('mathjs');
var integrate = require('integrate-adaptive-simpson');
var parser    = mathjs.parser();

parser.eval('f(x) = cos(1/x)/x');
integrate(parser.get('f'), 0.1, 1, 0.001);

---> -0.3425487908665287

When I run this sort of code through Atom, though, execution gets badly stuck, and I just have to brutally kill the process. Before doing that, I get hundreds of log prints warning the maximum recursion depth has been reached.

Note: when running in Atom, the execution actually works for bigger errors and simpler functions, even if not always correctly.

f(x) = cos(1/x)/x
integrate(f, 0.01, 1, 1)
> NaN

g(x) = 3 * x + 10
integrate(g, 0, 10, 1)
> 250
integrate(g, 0, 10, 0.9)
> STUCK, TONS OF LOGS

Is it something you experienced before? May it depend on the node version?

The plain example here has been executed with node v5.10.0, while Atom uses node v4.1.1 to run the code.

I pushed all the related code in a separate branch, but please consider it's still in early development. The integrator is actually used here.

Thank you very much for your support guys!

Thanks for the report! I've been kinda mentally away from this for a while, but trying hard to sit down and give things a much-needed tightening up. I'll look at this this afternoon and see if I can get to the root of it.

(In particular, thanks so much for the detailed report! It helps a lot.)

Thank you very much @rreusser for your interest. In the meantime I'll just sit here or probably mark it as experimental feature!

@b3by — Okay, there was kind of a nasty issue in which I was clearly failing to limit console output. Sorry about that! I added a couple of your tests but had a hard time reproducing the NaNs. I've published version 1.0.4 and updated .travis.yml to test against more versions of node, including 4.1.

First things first, does this solve the problem? I suspect it won't since I wasn't able to reproduce the issue you saw.

Next, it seems odd that integrate(g, 0, 10, 0.9) is hanging. an error tolerance of 0.9 is pretty big and should basically bail out right away saying the tolerance has been met. The relative error tolerance is just a little tricky to manage correctly, so it's possible I'm guilty of some clear oversight here. Do you have any sense for where it's first encountering NaN?

Thank you very much for this, I'm on my way to test it!

And I'm looking further into math.js to see if I'm doing anything incompatible.

Hmm… I'm trying to get to the root of how math.js evaluates expressions. Presumably it performs eval or new Function somewhere such that the equations are actually optimized by the JS engine. If so, it could be a content security policy issue, but that's kind of a shot in the dark and certainly doesn't fully explain it.

Edit: See: https://github.com/josdejong/mathjs/issues?utf8=%E2%9C%93&q=security
Additional edit: Perhaps this is the line where the eval is actually happening: https://github.com/josdejong/typed-function/blob/master/typed-function.js#L1042

Here's what I think is happening:

  • Math.js gives NaN values inside Atom because of eval
  • My library recursed as a result of NaN where it should have bailed out immediately
  • It hangs

I'm going to add NaN and Infinity tests, and if this is indeed the case, then (unfortunately) the remaining issue will be one of getting mathjs to play well with atom.

What I don't get here, is why the integration comes back with the correct result and very quickly when the error is greater than 1.

With the new version of the package, I get slightly different results:

  • Any function integrated with a required error greater or equal to 1 will return immediately, mostly with the correct result.
  • Any function integrated with a required error less than 1 will stuck for ~15 seconds, then give the correct result.
  • cos(1/x)/x function still returns NaN, no matter what.

You are right about loophole, I have to use it otherwise no evaluation is possible within Atom.

Frustrating. Okay, can you confirm: The behavior you described applies to Atom only. CLI works as expected.

Yes, the CLI keeps working great.

I also noticed that different integration intervals for that particular function will behave just as I listed before. So:

f(x) = cos(1/x)/x
> saved
integrate(f, 1, 10, 1)
> 2.010290986870436
integrate(f, 1, 10, 0.9)
// hangs for 10, 15 seconds
> 2.010290986870436

So, I don't think that Math.js returns a NaN, just because of loophole. If that was the case, it would get stuck for every evaluation.

Could it be, smaller errors require more double evaluation, back and forth between Math.js, loophole and Atom, thus increasing the computation time dramatically? Again, another shot in the dark.

I get the first NaN occurrence. It pops up in the Integrator function, when fa is calculated. This is very weird, because math.js does not seem to have any problem in doing it through loophole.

f(x) = cos(1/x)/x
> saved
f(0.1)
> -8.390715290764524

This is getting weirder.
🌵

Still seems suspicious. The error is in terms of order of magnitude, so 1 vs 1e-4 would make sense, but 1 vs 0.9 is basically the same thing unless something more insidious is going on here. Okay. Installing Atom and will see if I can test this out.

This is telling… hmm… is it possible that mathjs tries to infer integer vs. floating point inputs?

integrate(f, 1, 10, 0.9999999999999999999)
> 2.0652723095582646

integrate(f, 1, 10, 1)
> 2.010290986870436

Yes, I think you are right.

Apparently any value between 0 and 1 is automatically converted to 0. Then of course, it returns NaN in 0 because of the non-continuity.

Converted by what? tol is only used indirectly via an inequality.

I put some log in the Integrator function. This is from this line:

console.log('Min: ' + a);
var fa = f(a);
console.log('Getting result for f in min: ' + fa);

A call like `integrate(f, 0.1, 1, 1)' gives:

Min: 0
Getting result for f in min: NaN

This is definitely some weird math.js or loophole behaviour.

Interesting. Okay. I need to head out, but I'll add the nan and infinity check and get that out the door ASAP.

Thanks a lot dude, I'll keep looking into it myself.

Thank you! I know I was a little fast and loose getting some of these modules out the door, and it's been hanging over my head for a long time that I needed to tighten up corner cases in order to be a responsible programmer 😬 So I'm very glad to get this feedback and lock down this stuff!

Okay, now really need to head out. I published v1.0.6 to npm. It uses an equality test, e.g. V1 !== V1 in order to detect NaN and bails out if NaN detected. This should catch both infinity (which will pretty quickly cause a NaN) and NaN. I could tighten up the bailout behavior to achieve an absolute minimum of evaluations when things fail, but I think this will have to do for now and should at least help locate these things.

Yes, it does the trick, and the function does not hang anymore when tolerance is less than one. Thank you very much!
I'm still looking into the NaN issue, but at this point I don't really think it is your package issue.

Awesome I'm gonna close this out, but don't hesitate to open other issues if you run into problems, or, for that matter, don't hesitate to ask for specific modules if there are things you need. 👍🏼