zenna / CausalDiscovery.jl

9 stars 4 forks source link

Autumn-to-Julia Compiler #36

Closed riadas closed 4 years ago

riadas commented 4 years ago

One approach for compiling Autumn code (.au) to Julia code (.jl) is demonstrated here on the particles.au example. The final Julia program contains an init and a next function definition that are used to update the program state. These functions are compiled by finding where the init and next keywords are in the Autumn program, and adding those statements to the Julia definitions. The parameters of the next function come from all objects that have the external keyword in their declaration, which in this example is just click.

I first had to make some modifications to/complete particles.au before writing the Julia version. These changes are listed below (the syntax of some of these can definitely be changed):

Historical Tracking

The most difficult part of compiling the Autumn code into Julia is the issue of historical tracking. In this example compilation, the history is stored in a dictionary that maps particle id's to dictionaries that map time indexes to particle objects (in pseudo-code, history : Dict(particle_index => Dict(time => particle_object))). The particle_index comes from a global_counter, which starts at 0 and is incremented each time a particle is created. The global_counter never decreases, so that every particle created is ensured to have a unique id even if particles are later destroyed (particles are never destroyed in this particular example). The time key in the inner dictionary comes from a global variable time that is incremented

The compiler knows that it should track all particle objects because of the #save flag added to the end of the Particle type definition on line 11 of particles.au.* This signals that all particle objects that are created should be tracked. It knows that the global_counter variable should be incremented (i.e. a new particle_id should be created) in the compiled particleGen function because of the #new flag added to the end of the function's type definition. Similarly, it knows that a new particle_id should not be created within the nextParticle because the flag #update is used in the function's type definition instead. These flags signal that the compiled Julia versions of these functions should have the corresponding historical tracking lines in lines 56-58 and 67-70.

Extensions

This example is simple because it only tracks one type of object. In programs where multiple object types need to be tracked, one possible solution is to have multiple history dictionaries (one for each type of object that needs tracking) instead of a single history dictionary. For example, the compiler could create particleHistory and foodHistory for a more complex example that requires tracking of both Particle objects and food objects. The #save, #new, and #update flags would still be relevant here, appended to the end of function definitions that create new/update old objects of each type.

In any case, this is just one simple idea -- we should iterate further/make sure that our final solution supports all of the complexity related to tracking history etc. that we might need.

*I think that #save is actually not necessary/not used at all in this formulation, and is more of just a useful reminder to the program writer, so it could be removed.

zenna commented 4 years ago

that’s what i started with, but since we use these vars so often those two letter savings can add up in terms of reducing visual clutter

On Mon, Jul 13, 2020 at 5:41 PM riadas notifications@github.com wrote:

@riadas commented on this pull request.

In src/Autumn/compile.jl https://github.com/zenna/CausalDiscovery.jl/pull/36#discussion_r453949963 :

+

  • function compile(expr::AbstractArray, parent=nothing)
  • if length(expr) == 0 || (length(expr) > 1 && expr[1] != :List)
  • throw(AutumnCompileError("Invalid Compound Type"))
  • elseif expr[1] == :List
  • :(Array{$(compile(expr[2:end]))})
  • else
  • expr[1]
  • end
  • end
  • function compile(expr, parent=nothing)
  • expr
  • end
  • function compileassign(expr::AExpr, parent::Union{AExpr, Nothing}, data::Dict{String, Any})

Another option would be to replace everything with aexpr, subexpr, sexpr... that's the style I've been using at least.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/zenna/CausalDiscovery.jl/pull/36#discussion_r453949963, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACMUWUDIXMDB2JW3BS7RLTR3N5Q3ANCNFSM4NM22ESA .