Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace eval with macros #12

Open
GiggleLiu opened this issue Mar 3, 2023 · 6 comments
Open

Replace eval with macros #12

GiggleLiu opened this issue Mar 3, 2023 · 6 comments

Comments

@GiggleLiu
Copy link
Contributor

The minimum fix should be like

using JunctionTrees

problem_number = "34"
problem_filename = joinpath("Promedus_" * problem_number)
problem_dir = joinpath(@__DIR__, "../examples/problems/Promedus/", problem_number)
uai_filepath = joinpath(problem_dir, problem_filename * ".uai")
uai_evid_filepath = joinpath(problem_dir, problem_filename * ".uai.evid")
uai_mar_filepath = joinpath(problem_dir, problem_filename * ".uai.MAR")
td_filepath = joinpath(problem_dir, problem_filename * ".tamaki.td")

reference_marginals = JunctionTrees.read_uai_mar_file(uai_mar_filepath)
obsvars, obsvals = JunctionTrees.read_uai_evid_file(uai_evid_filepath)

# ------------------------------------------------------------------------------
# Posterior marginals given evidence
# ------------------------------------------------------------------------------

macro inferencemodel(uai_filepath, uai_evid_filepath)
    compile_algo(
            eval(uai_filepath),
            uai_evid_filepath = eval(uai_evid_filepath),
        )
end

algo = @inferencemodel uai_filepath uai_evid_filepath

marginals = algo(obsvars, obsvals) |> x -> map(y -> y.vals, x)
@mroavi
Copy link
Owner

mroavi commented Mar 14, 2023

Would there be any advantage of using a non-standard string literal over a macro?
E.g.:

algo = compile"problems/asia/asia.uai"
obsvars, obsvals = Int64[], Int64[]
marginals = run_algo(algo, obsvars, obsvals)

@GiggleLiu
Copy link
Contributor Author

Would there be any advantage of using a non-standard string literal over a macro? E.g.:

algo = compile"problems/asia/asia.uai"
obsvars, obsvals = Int64[], Int64[]
marginals = run_algo(algo, obsvars, obsvals)

It looks good. The problem is how to include also uai_evid_filepath.

@mroavi
Copy link
Owner

mroavi commented Mar 14, 2023

Right. You have a point. But putting aside the extra arguments that we might want to pass to compile_algo, would this bring benefits over a standard macro?
I think we should go to the approach you propose then. I have a question about macros though. compile_algo can take a series of optional parameters that customize the generation of the algorithm. Do macros support optional arguments as well? I couldn't find anything related to this in the docs section about metaprogramming.

@GiggleLiu
Copy link
Contributor Author

GiggleLiu commented Mar 15, 2023

Right. You have a point. But putting aside the extra arguments that we might want to pass to compile_algo, would this bring benefits over a standard macro? I think we should go to the approach you propose then. I have a question about macros though. compile_algo can take a series of optional parameters that customize the generation of the algorithm. Do macros support optional arguments as well? I couldn't find anything related to this in the docs section about metaprogramming.

It is easy to do, just create a macro like the following

julia> using MLStyle

julia> macro m(ex1, kwargs...)
    d = Dict()
    for kw in kwargs
        @match kw begin
            :($a = $b) => begin
                d[a] = b
            end
        end
    end
    d
end
@m (macro with 1 method)

julia> @m a b=3 c=5
Dict{Any, Any} with 2 entries:
  :b => 3
  :c => 5

@mroavi
Copy link
Owner

mroavi commented Mar 16, 2023

Ahh, macros do support varargs. Great. Then I think we should go with this approach. I also want to rename the run_algo function to infer.

@mroavi
Copy link
Owner

mroavi commented Mar 31, 2023

The minimum fix should be like

using JunctionTrees

problem_number = "34"
problem_filename = joinpath("Promedus_" * problem_number)
problem_dir = joinpath(@__DIR__, "../examples/problems/Promedus/", problem_number)
uai_filepath = joinpath(problem_dir, problem_filename * ".uai")
uai_evid_filepath = joinpath(problem_dir, problem_filename * ".uai.evid")
uai_mar_filepath = joinpath(problem_dir, problem_filename * ".uai.MAR")
td_filepath = joinpath(problem_dir, problem_filename * ".tamaki.td")

reference_marginals = JunctionTrees.read_uai_mar_file(uai_mar_filepath)
obsvars, obsvals = JunctionTrees.read_uai_evid_file(uai_evid_filepath)

# ------------------------------------------------------------------------------
# Posterior marginals given evidence
# ------------------------------------------------------------------------------

macro inferencemodel(uai_filepath, uai_evid_filepath)
    compile_algo(
            eval(uai_filepath),
            uai_evid_filepath = eval(uai_evid_filepath),
        )
end

algo = @inferencemodel uai_filepath uai_evid_filepath

marginals = algo(obsvars, obsvals) |> x -> map(y -> y.vals, x)

There is an issue with how the macro evaluates the uai_filepath. At first, I thought it was just a problem with the macro's hygiene, but it seems to be more complex than that. The macro is limited to only the expression that it's given, and without using eval (which is a global construct and doesn't work in, for example, let or @testset blocks), it cannot access the variable values in that expression.

One approach to solve this is to pass the problem name to the macro as a String literal, e.g. "Promedus_14", and then move the lines

uai_filepath = joinpath(problem_dir, problem * ".uai")
...

But this would have a negative impact on the interface for constructing the posterior marginals algorithms, as it would be unable to handle variables with bounded values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants