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]:
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]:
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)
In [44]:
params
Out[44]:
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
Out[33]:
In [8]:
f2 = eval(parse("function f2(x=1) println(x) end"))
Out[8]:
In [12]:
GaussianMixtureModel()
In [27]:
consts, hyperparams, params = f_test(d=2,n=2,K=5)
In [97]:
Dict(dic)[:K]
Out[97]:
In [24]:
params
Out[24]:
In [25]:
hyperparams
Out[25]:
In [26]:
consts
Out[26]:
In [7]:
println(typeof(:(function uu(x=1, y=2) println(x) end).args[1]))
In [392]:
function uuu(a;args...)
println(args)
end
Out[392]:
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
In [39]:
macroexpand(quote f_test(d=2,n=2,K=5) end)
Out[39]:
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]:
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 )
Out[28]:
In [30]: