I have the opposite view (but I recognize it is possible for rational people to disagree about these things, it is mostly a matter of taste). To make this concrete, consider the
in the PDF of a normal distribution. For practical purposes, I would always introduce z = X - \mu and code it that way. I find code like this more maintainable.
That’s a good example - I would often prefer the “textbook” mathematical form in the code in such cases - having the code closer to the math helps readability, in my opinion. I don’t see why this would be more readable if broken up into several lines.
Though in this specific case, automatic elimination of common sub-expressions wouldn’t be enough - I would rewrite this completely to also avoid the creation of short-lived temporary arrays (esp. if \Sigma is smallish).
In practical code I would try to work with (ie store) the Cholesky decomposition \Sigma = LL^T, eg
exp(0.5 * sum(abs2, L \ (x .- mu)))
I think that most “textbook” formulas facilitate proof techniques, not computation (unless, of course, the textbook is on numerical methods ) So preserving them in code is not always a good choice. YMMV.
In practical code I would try to work with (ie store) the Cholesky decomposition
Yes, obviously, in this specific case.
I think that most “textbook” formulas facilitate proof techniques … So preserving them in code is not always a good choice
Certainly - not always. An you’re right, in cases like the above, as soon as a bit of linear algebra is concerned, the actual implementation one would choose is often very different from the textbook - and that’s a bit much to ask from the compiler
However, there are certainly also many cases where the only optimization that will happen is manual extraction of common sub-expression into variables. That, a compiler can do.
Of course it’s not just adding an “ispure” tag to a function - with multiple dispatch, the purity may be hard for the function’s author to guarantee, depending on what code the function uses internally. Still conventions like bang-functions may be sufficient to make this workable.
YMMV.
Indeed - i would be surprised if there wouldn’t be quite a lot of cases in which optimizations based on compiler knowledge about purity of functions would be beneficial.
My posting wasn’t intended as a feature request in any way, I was just wondering if there was work going on in this direction.
@pure gives the compiler a hint for the definition of a pure function, helping for type inference.
A pure function can only depend on immutable information. This also means a @pure function cannot use any global mutable state, including generic functions. Calls to generic functions depend on method tables which are mutable global state. Use with caution, incorrect @pure annotation of a function may introduce hard to identify bugs. Double check for calls to generic functions. This macro is intended for internal compiler use and may be subject to changes.
In particular, I think it is very clear that you can’t use generic functions inside @pure functions. Please notice that this is incompatible with your usage:
I think it’s OK to depend on implementation details in some private code or maybe even in public code if you are aware that is an implementation detail and hence cannot rely on the semver promises. For example, ForwardDiff.jl uses Threads.atomic_add! inside @generated generator which also is documented that it must be pure. Also, there are (many?) packages using @generated to hoist out argument checking to compile time. Since throw is a side-effect, this is arguably not a valid use case. CUDAnative.jl is mentioning that it can be a real trouble if you want to use it with GPU: https://juliagpu.gitlab.io/CUDAnative.jl/man/hacking/#Generated-functions-1
@NHDaly’s JuliaCon talk is a great summary of the current status on this topic and explaining why you can’t use @pure or @generated in this way safely:
Personally, I often just lift values to type domain as soon as possible and compute things using recursion.
First of all the number 2 has type Int64
when you raise it to 64th power, it becomes a large number
but the first bit is about whether it is a negative number
so it represent a negative number in Int64
Also the largest number in Int64 is 2^64 - 1
just like the largest number in a byte is 2^8 - 1 (aka 255)
I am in favour of a solutions thread. That is solutions you discovered on your own and think they are useful enough to share with the community and spark further discussions. It would superset one liners.
@StevenSiew - I think it’s funny because a very large number is interpreted as zero. This thing called humor has a Personal element to it. It wasn’t expected to me - in Python if you do ```2**100`` you get a long/BigInt of the appropriate size.
@dataDiver - I like that idea. sharing nifty and elegant solutions could lead to a good learning experience and show off the actual power of Julia (which is not in 1 liners).
Well, I thought it was funny, even though I knew about integer overflow. It’s an odd and interesting way to write zero. Seems to be in the spirit of the thread.