Nx: Multi-dimensional tensors Elixir lib with multi-staged compilation (CPU/GPU)
github.com
github.com
I have just published an article on Dashbit's blog with a bit more context on Nx and the design decisions behind it: https://dashbit.co/blog/nx-numerical-elixir-is-now-publicly-...
It is hopefully a more in-depth reference than the README. If you have any questions, I will be glad to answer them!
A common complaint with Elixir and Erlang is that it isn't suitable for numerical computations, well now it is!
Where do you see Nx going in the future? I saw there's an MNIST example in the repo. Do you plan including higher-level features for neural networks, similar to Keras/PyTorch?
One bit that sticks out to me is that the "standard" math functions like `exp` or `ln` aren't imported despite `defn` being quite maths specific. It seems that they could be imported as default functions inside the scope of `defn`. That leads me to a more important syntax question though.
Do you have any thoughts or plans on operator syntax for elementwise operations vs matrix level operations? One big "win" for numerical Python was having `@` become an infix operator specifically for matrix dots [0, 1]. The syntax in Julia (which follows Matlab) is to have most math operators also have an element wise version. It's generalized to "broadcast" operations in Julia [2]. R appears to have ``, `%%, `%o%` infix operators for elementwise, inner, and outer matrix multiplication.
Really writing any degree of matrix (ahem Tensor) maths and equations becomes very tedious and illegible if you have to use function calls to distinguish elementwise vs matrix operations.
The pipe operator really does help significantly, even with simpler stats oriented code (I've tried a few different formats [4]). Still it's still harder to read if you're familiar with "broadcast" syntax from other mathematics oriented languages.
0: https://alysivji.github.io/python-matrix-multiplication-oper... 1: https://www.python.org/dev/peps/pep-0465/ 2: https://julialang.org/blog/2018/05/extensible-broadcast-fusi... 3: https://www.statmethods.net/advstats/matrix.html 4: https://github.com/elcritch/matrex_numerix/blob/5835a9b477d8...
You can see them here https://github.com/elixir-lang/elixir/blob/master/lib/elixir...
Not all of them are defined in the language, the list of the unused infix operator includes
\\, <-, |, ~>>, <<~, ~>, <~, <~>, <|>, <<<, >>>, |||, &&&, and ^^^
to define a custom infix operator you can use this syntax def left <|> right do
IO.puts("#{left} <|> #{right}")
end
# usage
left MyModule.<|> right
# or
import MyModule
left <|> right
if the operator is already defined in Kernel, you can exclude it from import and use your own import Kernel, except: [{:+, 2}]
import MyModule
left + right
as you can see @ is not among them, @ it's not a binary operator and is defined as Kernel.@/1Given that Nx has its own numerical kernel, we can import whatever we want. :) I have decided to keep the imported keywords to be a subset of the Elixir's built-in Kernel but there is nothing stopping us from changing that. For example, if exp and ln are all automatically imported in Julia, Python, etc, then it is most likely more than enough to justify them being imported in `defn` too.
Regarding custom operators, Elixir has a limited set of operators which have historically been expanded on demand. So it is a matter of looking at other communities, find what is the closest thing to a standard, and send a proposal to the language. :) If you are interested in driving this, you are welcome to submit an initial discussion comparing other languages and an initial set of operators on the Nx issues tracker, then we can discuss it upstream.
Thank you!
> For example, if exp and ln are all automatically imported in Julia, Python, etc, then it is most likely more than enough to justify them being imported in `defn` too.
Matlab and Julia import lots of math defaults because that's their primary target. In Python it varies, and it's pretty common to use `np` to avoid cluttering the namespace. Though partly it's because there isn't a good way (AFAICT) to make a scope with default custom namespace like `defn` gives you! It's a bit of taste really though. I'd suggest it's worth considering.
> Regarding custom operators, Elixir has a limited set of operators which have historically been expanded on demand. So it is a matter of looking at other communities, find what is the closest thing to a standard, and send a proposal to the language. :)
Well now, you give me way too many ideas. ;) I'll at least post some issues on the Nx tracker. My initial thought would be that perhaps a `<>` operator for "dot" operation would fit in with Elixir's other operators and is comparable to R's `%%` operators so there's a similarity and precedence. If it were low risk to add then it'd be a pretty decent option.
It wouldn't solve the whole elementwise issue though. but with syntax trees there could be ways to use Einstein notation instead [1]. That'd be fun to play with!
> If you are interested in driving this, you are welcome to submit an initial discussion comparing other languages and an initial set of operators on the Nx issues tracker, then we can discuss it upstream.
I'll create an issue on Nx project and basically repost my thoughts there. Then see what other people are thinking!
https://github.com/ityonemo/ez
the two languages feel like they were made for each other. Despite the verbosity of doing some things in zig (the dynamic tensor broadcast function is a bit of a beast), due to both language's similar compile-time metaprogramming facilities, getting to the point of plugging in Nx's tensor addition + tensor multiplication is about 200 LoC.
I'd definitely agree that it seems valuable to have an Nx version that comes with a smaller, simpler install for development purposes. XLA is amazing, but it's not obvious that you want to have that toolchain setup for local development in all scenarios.
Would be great to see several different backends created for Nx as a way of exploring what feature sets are really needed and ensuring that the integration mechanisms are sufficiently simple and general to support multiple solutions.
But kudos to José for building an architecture at a very good level of abstraction, you know, just in case xla falls out of favor (it is a Google project), nx won't have a hard dependency, and there will be trivial migration paths out.
I think there could be some other interesting nx backends, like "remote computation", or "transpile elixir and run on a Julia worker node"
defn softmax(t) do
Nx.exp(t) / Nx.sum(Nx.exp(t))
end
See https://ogunlao.github.io/2020/04/26/you_dont_really_know_so... etc.I think my dream job would be something like what José Valim does: designing a gorgeous, performant language that delights developers. Some day…
I know the newest BEAM release has a new JIT compiler for faster calculations. Is that work related to this at all? I'm guessing not, since it seems like the speed up here is from moving the calculation out of the BEAM.
I wonder if Nx will succeed due being "just" a library for a popular language (Elixir) instead of being a separate language entirely.
"Don't make a new language if your idea could be implemented as a library" is good advice indeed.
I'm happy that Elixir is becoming more and more mainstream. It's the best language I've used in my 15 years in the business.
I thought it was a pun on "wombat"
Elixir is a functional language and it even has Lisp-style macros.
https://games.greggman.com/game/dynamic-typing-static-typing...
The hard evidence that static typing is an overall-win is lacking. Period.
or, more succinctly,
Thanks for the interesting sounding talk link. It's "Types are like the Weather, Type Systems are like Weathermen - Matthias Felleisen" from ClojureTV, for those who rarely click on youtube links. Mathias Felleisen a well known Racket guy, author of How to Design Programs, etc.
Perhaps it helps more in certain specific languages or contexts.
The studies found no general “overall cost” improvement; in other words, the extra correctness came at the cost of tons more boilerplate and “fighting the type system”
Combined with function parameters typehinting (which easily beats typespecs) it's a great and productive developer experience I must say.
Second: As someone who uses Elixir for their day job, Racket for PL research, and Python for some CS classes, I can state with 100% confidence that Elixir is much closer to Lisp than Python. Elixir has macros that operate on the level of abstract syntax trees! Indeed, I think that is how most of this project was implemented.
Elixir and Erlang are strongly- BUT DYNAMICALLY- typed. Next time, please check yourself before you wreck yourself.