fredrik-johansson / fungrim

Fungrim: the Mathematical Functions Grimoire
MIT License
119 stars 15 forks source link

Fungrim-as-a-Python-library #14

Open fredrik-johansson opened 5 years ago

fredrik-johansson commented 5 years ago

Make something like this possible:

>>> import fungrim
>>> fungrim.formula("9ee8bc")
Equal(RiemannZeta(s), Mul(Mul(Mul(Mul(2, Pow(Mul(2, ConstPi), Sub(s, 1))), Sin(Div(Mul(ConstPi, s), 2))), GammaFunction(Sub(1, s))), RiemannZeta(Sub(1, s)))))
follymath commented 5 years ago

Out of curiosity, has the possibility of fungrim-as-a-JS-library been considered, at least to start out with? I know this may sound slightly absurd, but it seems like fungrim has to do an awful lot of work just to build for a web target -- and would have to do even more to support a playground/repl if the proto-interpreter were also python. As it turns out, arb and its deps compile into wasm just fine, with decent performance and a reasonable download size (well under a mb when gzipped, w/o any effort to optimize -- a one-time cost per user-agent per version when served from a CDN).

Here's a little observablejs notebook demonstrating some of arb's special functions I started putting together. It's just complex-double precision for now in my little wasm shim, borrowing from the patterns laid down in arbcmath.

This barely scratches the surface of arb's capabilities, obviously -- but even this meager subset is already far more capable than anything that exists in the js ecosystem. Seriously -- for example, I'm pretty sure there's not a single js implementation of hurwitz zeta to be found. Python already has a wealth of special function goodies and CAS implementations to choose from, but to date, about the best you could muster on the web would be running through something like pyodide, like so (which is pretty slow, as you might guess). Yet in spite of this dearth of quality mathematical powertools on the web, there is so much raw potential.

Of course, the wolfram-lang-flavored AST fungrim is scratching out is plenty generic enough to support interpretation in any language/runtime -- python, js+wasm, or otherwise. It seems to me what fungrim's really aiming to do (I'm completely speculating, correct me if I'm wrong...) is to lay down placeholders for these distinct mathematical concepts, e.g. known functions and their codomains -- solidifying the ground keys necessary to build out an index of candidate implementations in any language, along with a means to verify and compare these implementations over the domain for which they are defined. If that's ~ correct, a case could be made for fungrim-as-a-*-library for any *. I'm just trying to make the case for js as a less-than-crazy first pass at a first-class interpreter.

Even a primitive version of fungrim-as-a-JS-library is a thing the js/web ecosystem has needed for a long time -- and the ecosystem has finally matured enough to accommodate. This is something I've been working toward anyway, leaning heavily on @fredrik-johansson's incredible work with arb, long before I even knew fungrim was a thing. But when I saw this issue it occurred to me that I hadn't actually told anyone that arb compiles so smoothly down to wasm -- so I figured I at least ought to do that.

Either way, a huge thanks, @fredrik-johansson for the awesome projects! As someone with zero formal math education trying to feel my way around this labyrinth of cryptic symbols and dense jargon, your work has been invaluable!

fredrik-johansson commented 5 years ago

Wow, that's amazing! First of all thanks for sharing the arbuckle demo. 1 MB sounds unbelievably small. Just libarb.so and libflint.so together are 15 MB compressed. Does this really compile the whole library, or did you somehow specify the symbols you need to call from JS and have the wasm compiler prune unreachable library code?

It would be stellar to have some dynamic widgets on the Fungrim website. The simplest (and immediately useful) thing I'm picturing would be a widget for each function where, for BesselJ say, you get input fields

nu: [ 0 ] z: [ 1.2345 ] Digits: [ 30 ] [ ] Show radius (checkbox)

and it outputs

0.653791995647314266780498356636

or

[0.653791995647314266780498356636 +/- 1.57e-31]

if you choose to show the radius. (For all of this I would use arb_get_str and arb_set_str instead of doubles.)

You could then extend this to support printing a custom-made table for arguments in an arithmetic progression, and eventually add the option to plot this directly in the browser.

The next level would be to add on

  1. A parser in JS to create an expression tree from a given text representation of a formula in the Fungrim language (this could start with just understanding function calls, later extended to allow infix arithmetic operations)
  2. A simple numerical evaluation routine in JS that traverses the tree, just like https://github.com/fredrik-johansson/fungrim/blob/master/pygrim/numeric.py

Now the user could type in their own formula like BesselJ(0,x^2) + BesselY(0,x) and specify parameters in symbolic form like ConstPi/3.

Then perhaps also

  1. Render dynamically generated Fungrim expressions to TeX in the browser

Fungrim-as-a-Python-library is happening regardless (it's needed to build the website, and the Python users are out there), it makes perfect sense to have both the JS and Python code.

Eventually common code for things like parsing and numerical evaluation could be moved to a common C library with wrappers in JS, Python etc., but that's for the future when things are more stable.

fredrik-johansson commented 5 years ago

It seems to me what fungrim's really aiming to do (I'm completely speculating, correct me if I'm wrong...) is to lay down placeholders for these distinct mathematical concepts, e.g. known functions and their codomains --

Yeah, one vision is that with enough symbolic data, you could actually do a lot of automatic code generation instead of implementing every case by hand. But I think that's still far off; the formula database specifies functions and operators with their codomains at least implicitly but you still have to write code on a function by function basis. Right now my goal is just to have a declarative formula language that is easy to both write and read (for both humans and computers), as a foundation for 1) the formula database itself, 2) symbolic-numerical code, leveraging Arb in a more direct way.

follymath commented 5 years ago

Wow, that's amazing! First of all thanks for sharing the arbuckle demo. 1 MB sounds unbelievably small. Just libarb.so and libflint.so together are 15 MB compressed. Does this really compile the whole library, or did you somehow specify the symbols you need to call from JS and have the wasm compiler prune unreachable library code?

Sorry, I was a bit unclear -- this has LLVM optimizations enabled, which prunes fairly aggressively, yes. I can't recall exactly, but even when compiling w/ all LLVM optimizations disabled it comes in at around 7 to 8 mb -- so probably around 3-4 mb gzipped -- still not too bad, considering it's carrying around all of libarb, libflint, libmpfr and libgmp. And still lots of low-hanging fruit to improve this -- e.g. splitting out a kernel that could be dynamically linked. If we can do all of the arbcmath special functions in ~750kb, it stands to reason that that kernel module could be even smaller.

It would be stellar to have some dynamic widgets on the Fungrim website. The simplest (and immediately useful) thing I'm picturing would be a widget for each function where, for BesselJ say, you get input fields

nu: [ 0 ] z: [ 1.2345 ] Digits: [ 30 ] [ ] Show radius (checkbox)

and it outputs

0.653791995647314266780498356636

or

[0.653791995647314266780498356636 +/- 1.57e-31]

if you choose to show the radius. (For all of this I would use arb_get_str and arb_set_str instead of doubles.)

Ah, good deal -- I'll tack on support for string-based evaluation asap.

You could then extend this to support printing a custom-made table for arguments in an arithmetic progression, and eventually add the option to plot this directly in the browser.

FWIW I've already built out a lot of this (to various levels of completion) -- lately I've been putting this kind of thing into observablehq notebooks -- it's runtime is handy for tinkering and fun to build in, but the real win is that what you end up with is essentially a stable, immutable js module which can be imported from anywhere -- including into fungrim.

The next level would be to add on

  1. A parser in JS to create an expression tree from a given text representation of a formula in the Fungrim language (this could start with just understanding function calls, later extended to allow infix arithmetic operations)

I've got this -- in various forms and levels of doneness. For example, I've got a js implementation of FoxySheep that can handle just about any mm-flavored expression and spit out a StandardForm-inspired AST. Lately I've been cooking up a little adaption of literal-jsx that essentially allows you to write expressions as jsx markup like <N Digits={30}><BesselJ nu={0}>{1.2345} ShowRadius={true} Digits={30} /></N> -- or some such (still futzing around with it -- but there's a beautiful correspondence between math ASTs and generic vdom component trees lurking in there somewhere).

But all that's just noise -- you can't beat a stupid-simple s-expr parser when it comes to simplicity. I'll start working something up to parse out fungrim's s-expr input.

  1. A simple numerical evaluation routine in JS that traverses the tree, just like https://github.com/fredrik-johansson/fungrim/blob/master/pygrim/numeric.py

Perfect -- I can probably port a lot of this almost verbatim to js.

Now the user could type in their own formula like BesselJ(0,x^2) + BesselY(0,x) and specify parameters in symbolic form like ConstPi/3.

Then perhaps also

  1. Render dynamically generated Fungrim expressions to TeX in the browser

Once you can print an AST to tex, the "rendering" bit is taken care of (thanks to katex, among many other options, e.g. mathquill, the formula editor used by desmos). The tex printing strategy in mathjs is simple and very flexible: essentially each node type can surface a toTex method that takes a set of rendering options and spits a tex-formatted string.

Fungrim-as-a-Python-library is happening regardless (it's needed to build the website, and the Python users are out there), it makes perfect sense to have both the JS and Python code.

Eventually common code for things like parsing and numerical evaluation could be moved to a common C library with wrappers in JS, Python etc., but that's for the future when things are more stable.

Sounds great. I'll spin up a separate issue with any followup to avoid polluting this one further.

follymath commented 5 years ago

It seems to me what fungrim's really aiming to do (I'm completely speculating, correct me if I'm wrong...) is to lay down placeholders for these distinct mathematical concepts, e.g. known functions and their codomains --

Yeah, one vision is that with enough symbolic data, you could actually do a lot of automatic code generation instead of implementing every case by hand. But I think that's still far off; the formula database specifies functions and operators with their codomains at least implicitly but you still have to write code on a function by function basis. Right now my goal is just to have a declarative formula language that is easy to both write and read (for both humans and computers), as a foundation for 1) the formula database itself, 2) symbolic-numerical code, leveraging Arb in a more direct way.

Just to clarify, I wasn't suggesting that the automatic code generation was necessarily the goal -- rather, giving unique, unambiguous keys to the definitions themselves -- primary keys for the records of the formula database.

It's somewhat shocking to me (as a "database" person) the level of ambiguity that the field mathematics as a whole seems willing to tolerate (especially in light of how rigid and formal it strives to be). For example, when I say ζ(s,z), which 2-arg zeta am I talking about? Is it Hurwitz? Is it the other one (I think mm calls this ClassicalZeta? -- but I can never remember)? When does this difference even matter (not when Re(z)>0 -- I think...)? What a mess.

fredrik-johansson commented 5 years ago

Just to clarify, I wasn't suggesting that the automatic code generation was necessarily the goal -- rather, giving unique, unambiguous keys to the definitions themselves -- primary keys for the records of the formula database.

Got it. Yes, that makes sense.

It's somewhat shocking to me (as a "database" person) the level of ambiguity that the field mathematics as a whole seems willing to tolerate (especially in light of how rigid and formal it strives to be). For example, when I say ζ(s,z), which 2-arg zeta am I talking about? Is it Hurwitz? Is it the other one (I think mm calls this ClassicalZeta? -- but I can never remember)? When does this difference even matter (not when Re(z)>0 -- I think...)? What a mess.

Right. Many such ambiguities come from one of three things:

Sometimes it's best just to choose one good convention. At other times, I think you want explicitly named multiple versions of the same function.

fredrik-johansson commented 5 years ago

Perfect -- I can probably port a lot of this almost verbatim to js.

But please don't look too close at this code in its current version. It needs cleanup and has a bunch of dead/duplicated code in it right now!

paulmasson commented 4 years ago

@follymath version 1.4.1 of my JavaScript library Math has a very decent implementation of the complex Hurwitz zeta, among a large number of special functions. It's good in the right-half of the complex plane and the upper half of the left-hand portion to ten significant digits, but differs a bit from mpmath in the lower left-hand plane for reasons to be determined. And the entire unminified library is currently only 117 kB...