Julia Computing was invited by The Planetary Society to represent the Celeste team at a space research reception for members of Congress, their staff, scientists and space, science and technology policymakers at the Library of Congress in Washington DC on October 25.
Participants included Julia Computing, Boeing, Lockheed Martin, Johns Hopkins Applied Physics Lab, Aerospace Industries Association, The Planetary Society, several members of Congress and more than 100 Congressional staff, current and former NASA employees and contributors, scientists, policymakers and advocates.
Photos by Tushar Dayal for The Planetary Society
“The Celeste project wouldn’t have happened without government support,” affirmed Julia Computing’s Keno Fischer. Two of the Celeste partners – Lawrence Berkeley National Laboratory and the National Energy Research Scientific Computing Center (NERSC) – are federally funded institutions. They contributed thousands of hours of staff time plus the world’s sixth most powerful supercomputer to help the Celeste team achieve the following milestones:
Analyzed 178 terabytes of astronomical image data from the Sloan Digital Sky Survey and catalogued 188 million stars and galaxies in 14.6 minutes
Achieved peak performance of 1.54 petaflop per second using 1.3 million threads on 9,300 Knight Landing (KNL) nodes on the world’s sixth most powerful supercomputer
Achieved a performance improvement of 1,000x in single threaded execution
When the Large Synoptic Survey Telescope (LSST) begins operation in 2019, it will produce more visual data every few days than the Sloan Digital Sky Survey’s Apache Point telescope has produced in 20 years. Using Julia and the Cori supercomputer, the Celeste team will be able to analyze and catalog every object in the LSST nightly images in just 5 minutes.
Julia Computing was invited by The Planetary Society to represent the Celeste team at a space research reception for members of Congress, their staff, scientists and space,…
Rock–paper–scissors is
a popular hand game. However, some nerds may prefer playing this game on their
computer rather than actually moving their hands.
We can write this game in less than 10 lines of code in
the Julia programming language. This implementation
will offer the opportunity to have a closer look to one of Julia’s main
features: multiple dispatch.
That’s all. Nine lines of code, as promised. This is considerably shorter,
simpler, and easier to understand than any other implementation in all languages
over at Rosetta Code.
Explanation
Let’s dissect the code.
abstract typeShapeend
defines Shape as
an abstract type.
This will be the parent of the concrete types Rock, Paper and Scissors
that represent the characters of the game. To be fair, it’s not necessary to
create the Shape abstract type (so they would be eight lines in total!), but
this allows us to define methods for the play function only with Shape
subtypes as arguments, so that one can extended that function to other games
without clashing with our definitions.
Here the concrete shapes are defined
as composite types,
subtypes of Shape (indicated by the <: sign). They don’t actually contain
anything, but that’s OK, we just want to define the elements of the game as
types in order to take advantage of Julia’s type system.
These are the basic rules of the game. We’ve defined
three methods for
the play function, which return a string indicating the winning shape. The
two arguments of these methods are two shapes, for instance Rock and Scissors. If you look carefully to the definitions, we omitted the names of
the arguments (they should come right before the :: in the list of elements),
because they’re not used in the body of the function and we don’t need them.
Instead, what’s important here is the type of both arguments.
With this single line we’ve defined the tie for all shapes. The arguments of
this method are two equal shapes, they have the same type T subtype of Shape, whatever T is. T doesn’t even need to be defined at this point,
because in Julia, dispatch is dynamic on all arguments (in in C++/Java you can
achieve dynamic dispatch on first argument, but it’ll be static for the others).
So far we’ve seen the rules, for example, for the arguments Paper and Rock,
in this order, but there is no rule for the same arguments in the reversed
order. Here comes the magic:
play(a::Type{<:Shape},b::Type{<:Shape})=play(b,a)
Recall that multiple dispatch
is the ability to define function behavior across many combinations of argument
types. What’s crucial here is that the type of all arguments matters, not just
the first one as in object-oriented programming languages. If none of the
previous methods applies (Paper–Rock, Paper–Scissors, Rock–Scissors
and all the combinations of arguments with equal types), this method will be
used, which simply swaps the two arguments so that one of the above methods can
be called.
Besides letting us save four definitions, multiple dispatch makes the program
very efficient. The appropriate method is quickly selected based on the type of
all the arguments. Without multiple dispatch we’d have to write explicitly a
sequence of at least seven if ... elseif ... end,
but branching comes
at a performance cost. Of course this isn’t a big deal in a simple game like
this, but think about your CPU-intensive application.
Play the game
Now that we’ve implemented the game we can play it in the
Julia REPL:
julia>abstract typeShapeendjulia>structRock<:Shapeendjulia>structPaper<:Shapeendjulia>structScissors<:Shapeendjulia>play(::Type{Paper},::Type{Rock})="Paper wins"play(genericfunction with1method)julia>play(::Type{Paper},::Type{Scissors})="Scissors wins"play(genericfunction with2methods)julia>play(::Type{Rock},::Type{Scissors})="Rock wins"play(genericfunction with3methods)julia>play(::Type{T},::Type{T})where{T<:Shape}="Tie, try again"play(genericfunction with4methods)julia>play(a::Type{<:Shape},b::Type{<:Shape})=play(b,a)# Commutativityplay(genericfunction with5methods)julia>play(Paper,Scissors)"Scissors wins"julia>play(Rock,Rock)"Tie, try again"julia>play(Rock,Paper)"Paper wins"julia>@whichplay(Rock,Paper)play(a::Type{#s1} where #s1<:Shape, b::Type{#s2} where #s2<:Shape) in Main at REPL[9]:1
There was no explicit method for the combination of arguments Rock–Paper,
but the commutative rule has been used here, as confirmed by the @which macro.
Play randomly
We can also add some randomness to the game.
The rand
function can pick up a random element from a given collection. Luckily,
the subtypes
function returns the array with all the subtypes of the given abstract type:
We accomplished the extension with just four additional lines, one to define the
new type and three for the new game rules. We don’t need to redefine the tie or
the commutativity methods, thanks to Julia’s dynamic type system.
Here you can see that multiple dispatch let us extend the game very easily,
saving us four more definitions. Out of the total necessary
definitions we had just definitions, without giving up commutativity.