The package provides a new type, AstroImage to integrate FITS images with
Julia packages for plotting and image processing. The AstroImage function has
the same syntax as load. This command:
If you are working in a Jupyter notebook, an AstroImage object is automatically rendered as a PNG image:
Plotting an AstroImage
An AstroImage object can be plotted with the Plots.jl package. Just use
julia>usingPlotsjulia>plot(img)
and the image will be displayed as a heatmap using your favorite backend.
Feedback welcome
If you test the package and would like to suggest improvements, feel free to
file an issue on GitHub or, even better, send a pull request to add new features
yourself!
Today I realized by chance that in
the Julia programming language, like in C/C++,
assignment returns the assigned value.
To be more precise, assignment returns the right-hand side of the assignment
expression. This doesn’t appear to be documented in the current (as of December
2017) stable version of the manual, but
a FAQ has
been added to its development version. Be aware of this subtlety, because you
can get unexpected results if you rely on the type of the returned value of an
assignment:
julia>letaa=3.0end3.0julia>letaa::Int=3.0end3.0
In both cases the let blocks return 3.0, even though in the former case a
is really 3.0 and in the latter case it’s been marked to be an Int.
Assignment in the REPL
The fact that assignment returns the assigned value is a very handy feature.
First of all, this is probably the reason why in the Julia’s REPL the assigned
value is printed after an assignment, so that you don’t have to type again the
name of the variable to view its value:
julia>x=sin(2.58)^2+cos(2.58)^21.0
Instead in Python, for example, assignment returns None. Thus, in the
interactive mode of the interpreter you have to type again the name of the
variable to check its value:
Another useful consequence of this feature is that you can use assignment as
condition in, e.g., if and while, making them more concise. For example,
consider this brute-force method to compute
the Euler’s number
stopping when the desired precision is reached:
A common objection against having this feature in Python is that it is
error-prone. Quoting from
a Python tutorial:
Note that in Python, unlike C, assignment cannot occur inside expressions. C
programmers may grumble about this, but it avoids a common class of problems
encountered in C programs: typing = in an expression when == was intended.
This is indeed an issue because in C and Python conditions can be pretty much
anything that can be converted to plain-bit 0 and 1, including, e.g.,
characters. In addition, in Python a variable can freely change the type, so
that
Mistyping the condition (= instead of == or vice-versa) in this language can
lead to unexpected results.
Instead, this is mostly a non-issue in Julia because conditions can only be
instances of Bool type. One way to make an error in Julia is the following:
but it is kind of useless to test equality with a Bool as a condition, since
you can use the Bool itself. The only other possible error you can make in
Julia I came up with is the following:
For this case I don’t have an easy replacement that could prevent you from an
error, but this is also an unlikely situation. The Julia style guide suggests
to not parenthesize conditions.
Following this recommendation is a good way to catch the error in this case
because the assignment requires parens:
What makes this formula stand out among other approximations of is that
it allows one to directly extract the -th fractional digit of the
hexadecimal value of without computing the preceding ones.
The Wikipedia article about the Bailey–Borwein–Plouffe formula explains that the
-th fractional digit (well, actually it’s the -th) is
given by
where
Only the fractional part of expression in square brackets on the right side of
is relevant, thus, in order to avoid rounding errors, when we compute
each term of the finite sum above we can take only the fractional part. This
allows us to always use ordinary double precision floating-point arithmetic,
without resorting to arbitrary-precision numbers. In addition note that the
terms of the infinite sum get quickly very small, so we can stop the summation
when they become negligible.
Implementation in Julia language
Here is a Julia implementation of the algorithm to
extract the -th fractional digit of :
# Return the fractional part of x, modulo 1, always positivefpart(x)=mod(x,one(x))functionΣ(n,j)# Compute the finite sums=0.0denom=jforkin0:ns=fpart(s+powermod(16,n-k,denom)/denom)denom+=8end# Compute the infinite sumnum=1/16frac=num/denomwhilefrac>eps(s)s+=fracnum/=16denom+=8frac=num/denomendreturnfpart(s)endpi_digit(n)=floor(Int,16*fpart(4Σ(n-1,1)-2Σ(n-1,4)-Σ(n-1,5)-Σ(n-1,6)))pi_string(n)="0x3."*join(hex.(pi_digit.(1:n)))*"p0"
The pi_digit function gives the -th hexadecimal fractional digit of
as a base-10 integer, and the pi_string function returns the first
hexadecimal digits of as a valid hexadecimal floating-point
literal:
While I was preparing this post I found an unregistered
package PiBBP.jl that implements the
Bailey–Borwein–Plouffe formula. This is faster than my code above, mostly
thanks to a function
for modular exponentiation
more efficient than that available in Julia standard library.
Test results
Let’s check if the function is working correctly. We can use
the parse function
to convert the string to a decimal floating point
number. IEEE 754 double precision
floating-point numbers have a 53-bit mantissa, amounting to hexadecimal digits:
We can use
the arbitrary-precisionBigFloat
to check the correctness of the result for even more digits. By default, BigFloat numbers in Julia have a 256-bit mantissa:
julia> precision(BigFloat)
256
The result is correct for the first hexadecimal
digits:
Since the Bailey–Borwein–Plouffe formula extracts the -th digit of
without computing the other ones, we can write a multi-threaded version of pi_string, taking advantage of native support
for multi-threading
in Julia:
1
2
3
4
5
6
7
function pi_string_threaded(N)digits=Vector{Int}(N)Threads.@threadsfornineachindex(digits)digits[n]=pi_digit(n)endreturn"0x3."*join(hex.(digits))*"p0"end
For example, running Julia with 4 threads gives a 2× speed-up: