In [1]:
# For shits and giggles
using Distributions
import Distributions.rand

# Let's say all variables will have a state and domain

type Domain
	from::Int
	to::Int
end

#Test
function rand(d::Domain)
	du = Distributions.Uniform(d.from, d.to)
	return rand(du)
end


Out[1]:
rand (generic function with 111 methods)

In [5]:


In [7]:


In [607]:


In [1]:
function parse_from_to(expr)
    println(expr)
    tp = nothing
    from = expr.args[2]
    to = expr.args[3]
    if (typeof(from) != Symbol)
        tp = typeof(from)
    elseif (typeof(from) != Symbol)
        tp = typeof(to)
    end

    return from, to, tp
end

function parse_dims(expr)
    dim1 = 1
    dim2 = 1
    if ((typeof(expr) != Expr) && (typeof(expr) == Symbol || isinteger(expr)))
        dim1 = expr
    elseif (length(expr.args) == 2)
        dim1 = expr.args[1]
        dim2 = expr.args[2]
    end

    return (dim1, dim2)
end

function pb(expr::Expr, idx=-1)
    s = string(expr)
    if (contains(s, ">>>=") || contains(s, ".//="))
        error("Sorry, you caught the dirty hack, can't parse that.")
    end
    expr = parse(replace(s, "::", ">>>=")) #Manipulate the precedence priority

    # If it is a block, replace it by the block's contents
    if (expr.head == :block)
        expr = expr.args[length(expr.args)]
    end

    # Left is now new
    l = expr.args[1]
    if (typeof(l) == Symbol) #Great, no array
        var = l
    else #array
        var = l.args[1]
        if idx == -1
            idx = l.args[2]
        end
    end

    # Right is now the type
    # How do I macros in macros?
    expr = parse(replace(string(expr.args[2]), "^", ".//="))
    from = :inf
    to = :inf
    tp = nothing
    dims = nothing

    if (typeof(expr) == Symbol)
        tp = expr
    elseif (expr.head == :.//=)
        println(expr.args)
        # Left we either have the type# or from_to
        if (typeof(expr.args[1]) == Symbol)
            tp = expr.args[1]
        else
            from, to, tp = parse_from_to(expr.args[1])
        end
        # Right we have either one dimension, or 2
        dims = parse_dims(expr.args[2])
    elseif (expr.args[1] == :..)
        from, to, tp = parse_from_to(expr)

    else
        #println(expr.args)
    end

    # Clean up
    if dims == nothing || dims == 1
        dims = (1,1)
    end
    #TODO: Think about concrete types. Inference will be hard for large domains.
    typelookup = Dict()
    typelookup[:float64] = Float64
    typelookup[:Float64] = Float64
    typelookup[:int] = Int
    typelookup[:Integer] = Int64
    typelookup[:integer] = Int64

    if haskey(typelookup, tp)
      tp = typelookup[tp]
    end
    return var, idx, from, to, tp, dims
end


Out[1]:
pb (generic function with 2 methods)

In [19]:
function parse_pt(arg::Expr, consts, hyperparams, params, idx=-1, distvalue=None)

    before = (length(consts), length(hyperparams), length(params))

    if(arg.args[1] in Set{Symbol}([symbol("@constant"),  symbol("@hyperparam"), symbol("@param")]))
        (var, idx, from, to, tp, dims) = pb(arg.args[2], idx)

        if(arg.args[1] == symbol("@constant"))
          if (!haskey(consts, var))
              consts[var] = Set()
          end
          push!(consts[var], (var, idx, from, to, tp, dims, :const))
      
        elseif(arg.args[1] == symbol("@hyperparam"))
          if (!haskey(hyperparams, var))
              hyperparams[var] = Set()
          end
          push!(hyperparams[var], (var, idx, from, to, tp, dims, :hyperparam))
        elseif(arg.args[1] == symbol("@param"))
          if (!haskey(params, var))
              params[var] = Set()
          end
          push!(params[var], (var, idx, from, to, tp, dims, :unk))
        else
          println("This should not happen")
        end
     

    elseif(arg.head == :macrocall)

      if arg.args[1] == symbol("@~") # type foo ~ bar
        println("ARE WE HAVING FUN YET?")
        println(arg.args[2], " -- of type -- ",  typeof(arg.args[2]), " ", arg.args[2].head)
        (var, idx, from, to, tp, dims) = pb(arg.args[2], idx)
        println(arg.args[3], " -- of type -- ",  typeof(arg.args[3]), " ",)
        #(var, idx, from, to, tp, dims, _) = parse_pt(Expr(:macrocall, :@param, arg.args[2]), consts, hyperparams, params, idx, arg.args[3])
        if (!haskey(params, var))
              params[var] = Set()
        end
        push!(params[var], (var, idx, from, to, tp, dims, arg.args[3]))
      end
    
    else
      if arg.head != :line
        println("parse_pt called with something not a line: ", arg.head)
        println(arg.args)
      end
    end

    after = (length(consts), length(hyperparams), length(params))
    if before != after && false
        println("Changed params")
    end
    return consts, hyperparams, params
end
macro model(name, rest...)

    eval(:(model = $(string(name)))) #Set the current model in the global scope
    println(model)

    #TODO: By using arrays instead, and indexing by them you can save much space
    # No need for globalization! :-)

    #eval(:(consts = Dict())) # All of these should be specified
    #eval(:(hyperparams = Dict())) # When initializing for now
    #eval(:(params = Dict()))

    consts = Dict()
    hyperparams = Dict()
    params = Dict()

    c = 0
    u = 0

    # First pass to establish parameters
    for(i in rest) # For every top line
        if (typeof(i) == Expr) # If it is not a comment
            for (arg in i.args)
                if(typeof(arg) == Expr)
                    consts, hyperparams, params = parse_pt(arg, consts, hyperparams, params)
                end
            end
        end
    end

    # This closure expects them to create the function
    fparamnames = {}
    for k in keys(consts)
        push!(fparamnames,k )
    end
    for k in keys(hyperparams)
        push!(fparamnames, k)
    end

    println(fparamnames)

    f = parse("function " * model * " (;ks... ) end")
    f.args[2] =

    quote

        paramdict = Dict{}()
        for (k,v) in ks
            paramdict[k]=v
        end

        # Arguments: the model parameters. Returns success or false or something
        #Initialize the arguments

        #Stupid 2 pass, now parameters are established

        # Replace constants and metaprameters
        for cs in $consts #cs = (:name, {})
          if cs[1] in keys(paramdict)
            s = deepcopy(cs[2]) #Copy set to modify the original
            v = paramdict[cs[1]]
            for c in s
              # See if the current value is :constant instead of its value
              if c[7] == :const
                c_updated = (c[1], c[2], c[3], c[4], c[5], c[6], v) #TODO
                delete!(cs[2], c)
                push!(cs[2], c_updated)
              end
            end
          end
        end
        #Do the same for metaparameters (TODO: DRY) (TODO: merge sets or something with a merge(set1,set2) that works)
        for hp in $hyperparams # = (:name, {})
          if hp[1] in keys(paramdict)
            s = deepcopy(hp[2]) #Copy set to modify the original
            v = paramdict[hp[1]]
            for c in s
              # See if the current value is :hyperparameter instead of its value
              if c[7] == :hyperparam
                c_updated = (c[1], c[2], c[3], c[4], c[5], c[6], v) #TODO
                delete!(hp[2], c)
                push!(hp[2], c_updated)
              end
            end
          end
        end

        for(i in $rest) # For every top line
            if (typeof(i) == Expr) # If it is not a comment
                for (arg in i.args)
                    #println(arg)
                    if(typeof(arg) == Expr)
                        if(arg.head == :for) # We have a loop
                            boundary = arg.args[1]
                            loopvar = boundary.args[1]
                            lfrom = boundary.args[2].args[1]
                            lto = boundary.args[2].args[2]
                            contents = arg.args[2]
                            #println(boundary)
                            println(loopvar," ", typeof(loopvar)," lfrom: ", lfrom, " ", typeof(lfrom), " lto: ", Dict(ks)[symbol(lto)], " ", typeof(lto))
                            #println(contents)

                            # For this part you need the defined fparamnames, which are given to the constructor

                            for k in lfrom:Dict(ks)[symbol(lto)]
                                #println(contents)
                                for l in contents.args
                                    if (typeof(l) == Expr) # If it is not a comment
                                        println("------start------")
                                        println(l, " ", typeof(l.head))
                                        #println("**")
                                        #println(l.head)
                                        #println(l.args)
                                        consts, hyperparams, params = parse_pt(l, $consts, $hyperparams, $params, k)
                                        #TODO: If type == bla ~ blu then ...
                                        #end
                                        println("------end-------")
                                    end
                                #    consts, hyperparams, params = parse_pt(arg, consts, hyperparams, params, idx=k)
                                 end
                            end
                        else
                            #println("\"", arg, "\" which is of type:", typeof(arg)," has args of type: ", typeof(arg.args[1]))
                        end
                    end
                end
            end
        end

        return $consts, $hyperparams, $params

    end # End inner constructor function

    return f

end # End macro

gmm = @model GaussianMixtureModel begin
    # constant declaration
    @constant d::Int   # vector dimension
    @constant n::Int   # number of observations
    @hyperparam K::Int   # number of components

    # parameter declaration
    @param pi :: (0.0..1.0)^K    # prior proportions
    for k in 1 : K
        @param mu[k] :: Float64^d         # component mean
        @param sig[k] :: Float64^(d, d)   # component covariance
    end

    # sample generation process
    for i in 1 : n
        z[i] ~ Categorical(pi)
        x[i] ~ MultivariateNormal(mu[z[i]], sig[z[i]])
    end
end

consts, hyperparams, params = gmm(d=2,n=2,K=5)
println(consts)
print(hyperparams)


GaussianMixtureModel
{:(0.0 .. 1.0),:K}
0.0 .. 1.0
parse_pt called with something not a line: for
{:(k = 1:K),quote  # line 206:
    @param mu[k]::Float64^d # line 207:
    @param sig[k]::Float64^(d,d)
end}
parse_pt called with something not a line: for
{:(i = 1:n),quote  # line 212:
    @~ z[i] Categorical(pi) # line 213:
    @~ x[i] MultivariateNormal(mu[z[i]],sig[z[i]])
end}
{:n,:d,:K}
Replacing consts.
*****************
Replaced consts.
*****************
k Symbol lfrom: 1 Int64 lto: 5 Symbol
------start------
 # line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
 # line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
------start------
 # line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
 # line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
------start------
 # line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
 # line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
------start------
 # line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
 # line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
------start------
 # line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
 # line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
i Symbol lfrom: 1 Int64 lto: 2 Symbol
------start------
 # line 212: Symbol
------end-------
------start------
@~ z[i] Categorical(pi) Symbol
ARE WE HAVING FUN YET?
z[i] -- of type -- Expr ref
Categorical(pi) -- of type -- Expr 
------end-------
------start------
 # line 213: Symbol
------end-------
------start------
@~ x[i] MultivariateNormal(mu[z[i]],sig[z[i]]) Symbol
ARE WE HAVING FUN YET?
x[i] -- of type -- Expr ref
MultivariateNormal(mu[z[i]],sig[z[i]]) -- of type -- Expr 
------end-------
------start------
 # line 212: Symbol
------end-------
------start------
@~ z[i] Categorical(pi) Symbol
ARE WE HAVING FUN YET?
z[i] -- of type -- Expr ref
Categorical(pi) -- of type -- Expr 
------end-------
------start------
 # line 213: Symbol
------end-------
------start------
@~ x[i] MultivariateNormal(mu[z[i]],sig[z[i]]) Symbol
ARE WE HAVING FUN YET?
x[i] -- of type -- Expr ref
MultivariateNormal(mu[z[i]],sig[z[i]]) -- of type -- Expr 
------end-------
{:n=>Set{Any}({(:n,-1,:inf,:inf,:Int,(1,1),2)}),:d=>Set{Any}({(:d,-1,:inf,:inf,:Int,(1,1),2)})}
{:K=>Set{Any}({(:K,-1,:inf,:inf,:Int,(1,1),5)})}

In [44]:
params


Out[44]:
Dict{Any,Any} with 3 entries:
  :pi  => Set{Any}({(:pi,-1,0.0,1.0,Float64,(:K,1))})
  :sig => Set{Any}({(:sig,3,:inf,:inf,:Float64,(:d,:d)),(:sig,1,:inf,:inf,:Floa…
  :mu  => Set{Any}({(:mu,1,:inf,:inf,:Float64,(:d,1)),(:mu,3,:inf,:inf,:Float64…

In [33]:
macroexpand(quote f_test =
@model GaussianMixtureModel begin
    # constant declaration
    @constant d::Int   # vector dimension
    @constant n::Int   # number of observations
    @hyperparam K::Int   # number of components

    # parameter declaration
    @param pi :: (0.0..1.0)^K    # prior proportions
    for k in 1 : K
        @param mu[k] :: Float64^d         # component mean
        @param sig[k] :: Float64^(d, d)   # component covariance
    end

    # sample generation process
    for i in 1 : n
        z[i] ~ Categorical(pi)
        x[i] ~ MultivariateNormal(mu[z[i]], sig[z[i]])
    end
end end)
1


GaussianMixtureModel
{:(0.0 .. 1.0),:K}
0.0 .. 1.0
{:n,:d,:K}
Out[33]:
1

In [8]:
f2 = eval(parse("function f2(x=1) println(x) end"))


Out[8]:
f2 (generic function with 2 methods)

In [12]:
GaussianMixtureModel()


GaussianMixtureModel not defined
while loading In[12], in expression starting on line 1

In [27]:
consts, hyperparams, params = f_test(d=2,n=2,K=5)


k Symbol lfrom: 1 Int64 lto: 5 Symbol
Expr # line 11:
Expr@param mu[k]::Float64^d
consts not defined
while loading In[27], in expression starting on line 1

 in #1277#GaussianMixtureModel at In[25]:74

In [97]:
Dict(dic)[:K]


Out[97]:
5

In [24]:
params


Out[24]:
Dict{Any,Any} with 1 entry:
  :pi => Set{Any}({(:pi,-1,0.0,1.0,Float64,(:K,1))})

In [25]:
hyperparams


Out[25]:
Dict{Any,Any} with 1 entry:
  :K => Set{Any}({(:K,-1,:inf,:inf,:Int,nothing)})

In [26]:
consts


Out[26]:
Dict{Any,Any} with 2 entries:
  :n => Set{Any}({(:n,-1,:inf,:inf,:Int,nothing)})
  :d => Set{Any}({(:d,-1,:inf,:inf,:Int,nothing)})

In [7]:
println(typeof(:(function uu(x=1, y=2) println(x) end).args[1]))


Expr

In [392]:
function uuu(a;args...)
    println(args)
end


Out[392]:
uuu (generic function with 3 methods)

In [395]:
macro mkfun(args)
           quote
               local t0 = time()
               local val = $(esc(ex))
               local t1 = time()
               println("elapsed time: ", t1-t0, " seconds")
               val
           end
       end


unsupported or misplaced expression =>
while loading In[395], in expression starting on line 1

In [39]:
macroexpand(quote f_test(d=2,n=2,K=5) end)


Out[39]:
quote  # In[39], line 1:
    f_test(d=2,n=2,K=5)
end

In [21]:
function parse_name(s)
    in_under = false
    symbols = String[""]
    for c in s
        if in_under
            if c == '_'
                symbols[end] *= "_"
            else
                push!(symbols,string(c))
            end
            in_under = false
        else
            if c == '_'
                in_under = true
            else
                symbols[end] *= string(c)
                in_under = false
            end
        end
    end
    symbols
end

function foo(a)
    return a
end


Out[21]:
foo (generic function with 1 method)

In [24]:
macro funmac(e)
    name = string(e)
    println("Name: ", name)
    syms = parse_name(name)
    syms = map(symbol, syms)
    print(syms)
    quote
        print($(syms[2]))
        $(syms[1])($(syms[2:end]...))
    end
end

In [28]:
macroexpand( quote @funmac(foo_11) end )


Name: foo_11
[:foo,symbol("11")]
Out[28]:
quote  # In[28], line 1:
    begin  # In[24], line 8:
        print(11) # line 9:
        foo(11)
    end
end

In [30]:



2