Tractables / ProbabilisticCircuits.jl

Probabilistic Circuits from the Juice library
https://tractables.github.io/ProbabilisticCircuits.jl/dev
Apache License 2.0
104 stars 11 forks source link

Marginalization for `learn_strudel` circuits #48

Closed RenatoGeh closed 3 years ago

RenatoGeh commented 3 years ago

Hi,

I'm trying to compute the marginal probability of a circuit learned from learn_strudel, but I keep getting a keyword error. Am I doing something wrong?

Here's a minimal example to reproduce the error.

using ProbabilisticCircuits, LogicCircuits, DataFrames

R, _, T = twenty_datasets("nltcs")
M, _, _ = learn_strudel(R; num_mix = 10, init_maxiter = 10, em_maxiter = 100)
# Setting first value as missing in test set.
Q = allowmissing(T, 1)
Q[:,1] .= missing
p = MAR(M, Q)

The error backtrace:

ERROR: UndefKeywordError: keyword argument component_idx not assigned
Stacktrace:
 [1] ParamBitCircuit(::SharedSumNode, ::DataFrame) at /home/renatogeh/.julia/packages/ProbabilisticCircuits/Uh1Ay/src/param_bit_circuit.jl:45
 [2] marginal(::SharedSumNode, ::DataFrame) at /home/renatogeh/.julia/packages/ProbabilisticCircuits/Uh1Ay/src/queries/marginal_flow.jl:40
 [3] top-level scope at REPL[20]:1
 [4] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.3/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288

Thanks

MhDang commented 3 years ago

hi,

Thanks for the feedback.

The learn_strudel returns a mixture of circuits sharing the same structure but different parameters. Concretely, a tuple (pc, component_weights, lls), pc is a SharedProbCircuit representing the circuit structure, and each column of parameters refers to each component; component_weights as its weights; lls is the log-likelihoods.

Inferences routines are not implemented for SharedProbCircuit for the uniform API, (TODO later), so directly calling EVI, MAR will get error. Instead, you can call MAR for each component by setting component_idx, and then weighted the results in the end.

R, _, T = twenty_datasets("nltcs")
M, W, _ = learn_strudel(R; num_mix = 10, init_maxiter = 10, em_maxiter = 100)
# Setting first value as missing in test set.
Q = allowmissing(T, 1)
Q[:,1] .= missing
lls = zeros(num_examples(T), num_mix)
for i in 1:num_mix
    pbc = ParamBitCircuit(M, Q; component_idx=i)
    lls[:, i] .= MAR(pbc, Q)
end

ll = logsumexp(lls .+ log.(W), 2)

Btw, if you want to learn a single model probabilistic circuit, use learn_circuit instead.

learn_circuit(R; maxiter=20)
RenatoGeh commented 3 years ago

Thanks for the quick reply. :)

I see. I thought, since there was nothing on this on the docs, that mixtures behaved the same way as regular circuits and the weight computation was somehow embedded on SharedProbCircuit, especially since EVI works out-of-the-box with mixtures (though looking at the code I guess Juice assumes uniform weights for standard log-likelihood).

I've opened a draft PR here #49 addressing these issues.

MhDang commented 3 years ago

Thanks for the contribution, the PR has been merged. : )