diff options
| author | ziejd2 | 2018-03-14 23:23:33 -0500 |
|---|---|---|
| committer | GitHub | 2018-03-14 23:23:33 -0500 |
| commit | 1ff6baa44e22b91eefb48aea6f3befa078c0489b (patch) | |
| tree | e0fd79d2e32fd2aedda2eadaed0f19af3514c520 /sourcecodes/bnt-master/BNT/inference/dynamic | |
| parent | 6882395afdadf4e982b25b5215071a0932730950 (diff) | |
| parent | c80226899f5cdd9f11c163817d59445213f5bef0 (diff) | |
| download | BNW-1ff6baa44e22b91eefb48aea6f3befa078c0489b.tar.gz | |
Merge pull request #1 from ziejd2/octave_php_separate
Octave php separate
Diffstat (limited to 'sourcecodes/bnt-master/BNT/inference/dynamic')
189 files changed, 6844 insertions, 0 deletions
diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Entries new file mode 100644 index 00000000..5cd139de --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Entries @@ -0,0 +1,10 @@ +/bk_ff_hmm_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_init_bel.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_marginal_from_bel.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_predict_bel.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_update_bel.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_update_bel1.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Entries.Log new file mode 100644 index 00000000..b2cd71e0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Entries.Log @@ -0,0 +1 @@ +A D/private//// diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Repository new file mode 100644 index 00000000..af5c9df5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@bk_ff_hmm_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/bk_ff_hmm_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/bk_ff_hmm_inf_engine.m new file mode 100644 index 00000000..c726a1fd --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/bk_ff_hmm_inf_engine.m @@ -0,0 +1,21 @@ +function engine = bk_ff_hmm_inf_engine(bnet) +% BK_FF_HMM_INF_ENGINE Naive (HMM-based) implementation of fully factored form of Boyen-Koller +% engine = bk_ff_hmm_inf_engine(bnet) +% +% This is implemented on top of the forwards-backwards algo for HMMs, +% so it is *less* efficient than exact inference! However, it is good for educational purposes, +% because it illustrates the BK algorithm very clearly. + +[persistent_nodes, transient_nodes] = partition_dbn_nodes(bnet.intra, bnet.inter); +assert(isequal(sort(bnet.observed), transient_nodes)); +[engine.prior, engine.transmat] = dbn_to_hmm(bnet); + +ss = length(bnet.intra); + +engine.bel = []; +engine.bel_marginals = []; +engine.marginals = []; + + +engine = class(engine, 'bk_ff_hmm_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_init_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_init_bel.m new file mode 100644 index 00000000..2ab39d37 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_init_bel.m @@ -0,0 +1,5 @@ +function engine = dbn_init_bel(engine) +% DBN_INIT_BEL Compute the initial belief state (bk_ff_hmm) +% engine = dbn_init_bel(engine) + +engine.bel = engine.prior(:); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_marginal_from_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_marginal_from_bel.m new file mode 100644 index 00000000..a40d43b9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_marginal_from_bel.m @@ -0,0 +1,5 @@ +function marginal = dbn_marginal_from_bel(engine, i) +% DBN_MARGINAL_FROM_BEL Compute the marginal on a node given the current belief state (bk_ff_hmm) +% marginal = dbn_marginal_from_bel(engine, i) + +marginal = pot_to_marginal(engine.bel_marginals{i}); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_predict_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_predict_bel.m new file mode 100644 index 00000000..5195e53f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_predict_bel.m @@ -0,0 +1,19 @@ +function engine = dbn_predict_bel(engine, lag) +% DBN_PREDICT_BEL Predict the belief state 'lag' steps into the future (bk_ff_hmm) +% engine = dbn_predict_bel(engine, lag) +% 'lag' defaults to 1 + +if nargin < 2, lag = 1; end + +for d=1:lag + %newbel = engine.transmat' * engine.bel; + newbel = normalise(engine.transmat' * engine.bel); + + hnodes = engine.hnodes; + bnet = bnet_from_engine(engine); + ns = bnet.node_sizes; + [marginals, marginalsT] = project_joint_onto_marginals(newbel, hnodes, ns); + newbel = combine_marginals_into_joint(marginalsT, hnodes, ns); + engine.bel_marginals = marginalsT; + engine.bel = newbel; +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_update_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_update_bel.m new file mode 100644 index 00000000..8323b38a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_update_bel.m @@ -0,0 +1,19 @@ +function [engine, loglik] = dbn_update_bel(engine, evidence) +% DBN_UPDATE_BEL Update the belief state (bk_ff_hmm) +% [engine, loglik] = dbn_update_bel(engine, evidence) +% +% evidence{i,1} contains the evidence on node i in slice t-1 +% evidence{i,2} contains the evidence on node i in slice t + +oldbel = engine.bel; +bnet = bnet_from_engine(engine); +obslik = mk_hmm_obs_lik_vec(bnet, evidence); +[newbel, lik] = normalise((engine.transmat' * oldbel) .* obslik); +loglik = log(lik); + +hnodes = engine.hnodes; +ns = bnet.node_sizes; +[marginals, marginalsT] = project_joint_onto_marginals(newbel, hnodes, ns); +newbel = combine_marginals_into_joint(marginalsT, hnodes, ns); +engine.bel_marginals = marginalsT; +engine.bel = newbel; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_update_bel1.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_update_bel1.m new file mode 100644 index 00000000..6280ee77 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/dbn_update_bel1.m @@ -0,0 +1,18 @@ +function [engine, loglik] = dbn_update_bel1(engine, evidence) +% DBN_UPDATE_BEL Update the initial belief state (bk_ff_hmm) +% [engine, loglik] = dbn_update_bel(engine, evidence) +% +% evidence{i} contains the evidence on node i in slice 1 + +oldbel = engine.bel; +bnet = bnet_from_engine(engine); +obslik = mk_hmm_obs_lik_vec1(bnet, evidence); +[newbel, lik] = normalise(oldbel .* obslik); +loglik = log(lik); + +hnodes = engine.hnodes; +ns = bnet.node_sizes; +[marginals, marginalsT] = project_joint_onto_marginals(newbel, hnodes, ns); +newbel = combine_marginals_into_joint(marginalsT, hnodes, ns); +engine.bel_marginals = marginalsT; +engine.bel = newbel; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/enter_evidence.m new file mode 100644 index 00000000..4719e0e9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/enter_evidence.m @@ -0,0 +1,60 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (bk_ff_hmm) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product (not yet supported), else sum-product [0] +% filter - if 1, do filtering, else smoothing [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +assert(~maximize); + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +onodes = bnet.observed; +hnodes = mysetdiff(1:ss, onodes); +T = size(evidence, 2); +assertBNT(~any(isemptycell(evidence(onodes,:)))); + +obslik = mk_hmm_obs_lik_mat(bnet, onodes, evidence); + +ns = bnet.node_sizes_slice; +ns(onodes) = 1; + +[gamma, loglik, marginals, marginalsT] = bk_ff_fb(engine.prior, engine.transmat, obslik, filter, hnodes, ns); + +for t=1:T + for i=hnodes(:)' + engine.marginals{i,t} = pot_to_marginal(marginalsT{i,t}); + end + for i=onodes(:)' + m.domain = i + (t-1)*ss; + m.T = 1; + engine.marginals{i,t} = m; + end +end + + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/marginal_family.m new file mode 100644 index 00000000..fe58a0f8 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/marginal_family.m @@ -0,0 +1,5 @@ +function m = marginal_family(engine, i, t) +% MARGINAL_FAMILY Compute the marginal on the specified family (bk_ff_hmm) +% marginal = marginal_family(engine, i, t) + +error('bk_ff_hmm doesn''t support marginal_family'); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..8c2f9e81 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/marginal_nodes.m @@ -0,0 +1,11 @@ +function marginal = marginal_nodes(engine, nodes, t) +% MARGINAL_NODES Compute the marginal on the specified query nodes (bk_ff_hmm) +% marginal = marginal_nodes(engine, i, t) + +assert(length(nodes)==1); +i = nodes(end); +%assert(myismember(i, engine.hnodes)); +marginal = engine.marginals{i,t}; +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +marginal.domain = i + (t-1)*ss; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/CVS/Entries new file mode 100644 index 00000000..b4ab4b45 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/CVS/Entries @@ -0,0 +1,8 @@ +/bk_ff_fb.m/1.1.1.1/Wed May 29 15:59:56 2002// +/combine_marginals_into_joint.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_to_hmm.m/1.1.1.1/Wed May 29 15:59:56 2002// +/mk_hmm_obs_lik_mat.m/1.1.1.1/Wed May 29 15:59:56 2002// +/mk_hmm_obs_lik_vec.m/1.1.1.1/Wed May 29 15:59:56 2002// +/mk_hmm_obs_lik_vec1.m/1.1.1.1/Wed May 29 15:59:56 2002// +/project_joint_onto_marginals.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/CVS/Repository new file mode 100644 index 00000000..3b0b141c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/bk_ff_fb.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/bk_ff_fb.m new file mode 100644 index 00000000..ca41f77c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/bk_ff_fb.m @@ -0,0 +1,59 @@ +function [gamma, loglik, marginals, marginalsT] = bk_ff_fb(prior, transmat, obslik, filter_only, hnodes, ns) +% BK_FF_FB Fully factored Boyen-Koller version of forwards-backwards +% [gamma, loglik, marginals, marginalsT] = bk_ff_hmm(prior, transmat, obslik, filter_only, hnodes, ns) + +ss = length(ns); +S = length(prior); +T = size(obslik, 2); +marginals = cell(ss,T); +marginalsT = cell(ss,T); +scale = zeros(1,T); +alpha = zeros(S, T); + +transmat2 = transmat'; +for t=1:T + if t==1 + [alpha(:,t), scale(t)] = normalise(prior(:) .* obslik(:,t)); + else + [alpha(:,t), scale(t)] = normalise((transmat2 * alpha(:,t-1)) .* obslik(:,t)); + end + [marginals(:,t), marginalsT(:,t)] = project_joint_onto_marginals(alpha(:,t), hnodes, ns); + alpha(:,t) = combine_marginals_into_joint(marginalsT(:,t), hnodes, ns); + %fprintf('alpha t=%d\n', t); + %celldisp(marginals(1:8,t)) +end +loglik = sum(log(scale)); + +if filter_only + gamma = alpha; + return; +end + +beta = zeros(S,T); +gamma = zeros(S,T); +t = T; +beta(:,t) = ones(S,1); +gamma(:,t) = normalise(alpha(:,t) .* beta(:,t)); +[marginals(:,t), marginalsT(:,t)] = project_joint_onto_marginals(gamma(:,t), hnodes, ns); + +for t=T-1:-1:1 + b = beta(:,t+1) .* obslik(:,t+1); + beta(:,t) = normalise((transmat * b)); + [junk, tempT] = project_joint_onto_marginals(beta(:,t), hnodes, ns); + beta(:,t) = combine_marginals_into_joint(tempT, hnodes, ns); + %gamma(:,t) = normalise(alpha(:,t) .* beta(:,t)); + %[marginals(:,t), marginalsT(:,t)] = project_joint_onto_marginals(gamma(:,t), hnodes, ns); +end + +gamma2 = zeros(S,T); +for t=T-1:-1:1 + b = beta(:,t+1) .* obslik(:,t+1); + xi(:,:,t) = normalise((transmat .* (alpha(:,t) * b'))); + if t==T-1 + gamma2(:,T) = sum(xi(:,:,T-1), 1)'; + end + gamma2(:,t) = sum(xi(:,:,t), 2); + [marginals(:,t), marginalsT(:,t)] = project_joint_onto_marginals(gamma2(:,t), hnodes, ns); +end + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/combine_marginals_into_joint.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/combine_marginals_into_joint.m new file mode 100644 index 00000000..74065662 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/combine_marginals_into_joint.m @@ -0,0 +1,8 @@ +function joint = combine_marginals_into_joint(marginalsT, hnodes, ns) + +jointT = dpot(hnodes, ns(hnodes)); +for i=hnodes(:)' + jointT = multiply_by_pot(jointT, marginalsT{i}); +end +m = pot_to_marginal(jointT); +joint = m.T(:); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/dbn_to_hmm.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/dbn_to_hmm.m new file mode 100644 index 00000000..4a921bfd --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/dbn_to_hmm.m @@ -0,0 +1,41 @@ +function [prior, transmat] = dbn_to_hmm(bnet) +% DBN_TO_HMM Compute the discrete HMM matrices from a simple DBN +% [prior, transmat] = dbn_to_hmm(bnet) + +onodes = bnet.observed; +ss = length(bnet.intra); +evidence = cell(1,2*ss); +hnodes = mysetdiff(1:ss, onodes); +prior = multiply_CPTs(bnet, [], hnodes, evidence); +transmat = multiply_CPTs(bnet, hnodes, hnodes+ss, evidence); +%obsmat1 = multiply_CPTs(bnet, hnodes, onodes, evidence); +%obsmat = multiply_CPTs(bnet, hnodes+ss, onodes+ss, evidence); +%obsmat1 = obsmat if the observation matrices are tied across slices + + + +%%%%%%%%%%%% + +function mat = multiply_CPTs(bnet, pdom, cdom, evidence) + +% MULTIPLY_CPTS Make a matrix Pr(Y|X), where X represents all the parents, and Y all the children +% We assume the children have no intra-connections. +% +% e.g., Consider the DBN with interconnectivity i->i', j->j',k', k->i',k' +% Then transition matrix = Pr(i,j,k -> i',j',k') = Pr(i,k->i') Pr(j->j') Pr(j,k->k') + +dom = [pdom cdom]; +ns = bnet.node_sizes; +bigpot = dpot(dom, ns(dom)); +for j=cdom(:)' + e = bnet.equiv_class(j); + fam = family(bnet.dag, j); + pot = convert_to_pot(bnet.CPD{e}, 'd', fam(:), evidence); + bigpot = multiply_by_pot(bigpot, pot); +end +psize = prod(ns(pdom)); +csize = prod(ns(cdom)); +T = pot_to_marginal(bigpot); +mat = reshape(T.T, [psize csize]); + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/mk_hmm_obs_lik_mat.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/mk_hmm_obs_lik_mat.m new file mode 100644 index 00000000..b3e4c4cc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/mk_hmm_obs_lik_mat.m @@ -0,0 +1,34 @@ +function obslik = mk_hmm_obs_lik_mat(bnet, onodes, evidence) +% MK_HMM_OBS_LIK_MAT Make the observation likelihood matrix for all slices +% obslik = mk_hmm_obs_lik_mat(bnet, onodes, evidence) +% +% obslik(i,t) = Pr(Y(t) | X(t)=i) + +[ss T] = size(evidence); + +hnodes = mysetdiff(1:ss, onodes); +ns = bnet.node_sizes_slice; +ns(onodes) = 1; +Q = prod(ns(hnodes)); +obslik = zeros(Q,T); + +dom = 1:ss; +for t=1:T + bigpot = dpot(dom, ns(dom)); + for i=onodes(:)' + if t==1 + e = bnet.equiv_class(i,1); + fam = family(bnet.dag, i); + else + e = bnet.equiv_class(i,2); + fam = family(bnet.dag, i, 2) + ss*(t-2); + end + pot = convert_to_pot(bnet.CPD{e}, 'd', fam(:), evidence); + pot = set_domain_pot(pot, family(bnet.dag, i)); + bigpot = multiply_by_pot(bigpot, pot); + end + m = pot_to_marginal(bigpot); + obslik(:,t) = m.T(:); +end + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/mk_hmm_obs_lik_vec.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/mk_hmm_obs_lik_vec.m new file mode 100644 index 00000000..23247bd5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/mk_hmm_obs_lik_vec.m @@ -0,0 +1,27 @@ +function obslik = mk_hmm_obs_lik_vec(bnet, evidence) +% MK_HMM_OBS_LIK_VEC Make the observation likelihood vector for one slice +% obslik = mk_obs_lik(bnet, evidence) +% +% obslik(i) = Pr(y(t) | X(t)=i) +% evidence{i,1} contains the evidence on node i in slice t-1 +% evidence{i,2} contains the evidence on node i in slice t + +ns = bnet.node_sizes; +ss = length(bnet.intra); +onodes = find(~isemptycell(evidence(:))); +hnodes = find(isemptycell(evidence(:))); +ens = ns; +ens(onodes) = 1; +Q = prod(ens(hnodes)); +obslik = zeros(1,Q); +dom = (1:ss)+ss; +bigpot = dpot(dom, ens(dom)); +onodes1 = find(~isemptycell(evidence(:,1))); +for i=onodes1(:)' + e = bnet.equiv_class(i,2); + fam = family(bnet.dag, i, 2); + pot = convert_to_pot(bnet.CPD{e}, 'd', fam, evidence); + bigpot = multiply_by_pot(bigpot, pot); +end +m = pot_to_marginal(bigpot); +obslik = m.T(:); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/mk_hmm_obs_lik_vec1.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/mk_hmm_obs_lik_vec1.m new file mode 100644 index 00000000..6d0c1d35 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/mk_hmm_obs_lik_vec1.m @@ -0,0 +1,25 @@ +function obslik = mk_hmm_obs_lik_vec1(bnet, evidence) +% MK_HMM_OBS_LIK_VEC1 Make the observation likelihood vector for the first slice +% obslik = mk_hmm_obs_lik_vec1(engine, evidence) +% +% obslik(i) = Pr(y(1) | X(1)=i) +% evidence{i} contains the evidence on node i in slice 1 + +ns = bnet.node_sizes; +ss = length(ns); +onodes = find(~isemptycell(evidence(:))); +hnodes = find(isemptycell(evidence(:))); +ens = ns; +ens(onodes) = 1; +Q = prod(ens(hnodes)); +obslik = zeros(1,Q); +dom = (1:ss); +bigpot = dpot(dom, ens(dom)); +for i=onodes(:)' + e = bnet.equiv_class(i,1); + fam = family(bnet.dag, i); + pot = convert_to_pot(bnet.CPD{e}, 'd', fam(:), evidence); + bigpot = multiply_by_pot(bigpot, pot); +end +m = pot_to_marginal(bigpot); +obslik = m.T(:); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/project_joint_onto_marginals.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/project_joint_onto_marginals.m new file mode 100644 index 00000000..3f4036db --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_ff_hmm_inf_engine/private/project_joint_onto_marginals.m @@ -0,0 +1,11 @@ +function [marginals, marginalsT] = project_joint_onto_marginals(joint, hnodes, ns) + +ss = length(ns); +jointT = dpot(hnodes, ns(hnodes), joint); +marginalsT = cell(1, ss); +marginals = cell(1,ss); +for i=hnodes(:)' + marginalsT{i} = marginalize_pot(jointT, i); + m = pot_to_marginal(marginalsT{i}); + marginals{i} = m.T(:); +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/CVS/Entries new file mode 100644 index 00000000..b071e13e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/CVS/Entries @@ -0,0 +1,11 @@ +/bk_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_init_bel.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_marginal_from_bel.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_update_bel.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dbn_update_bel1.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_soft_evidence.m/1.1.1.1/Sat Jan 11 18:13:50 2003// +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +/update_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/CVS/Repository new file mode 100644 index 00000000..5e810837 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@bk_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/bk_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/bk_inf_engine.m new file mode 100644 index 00000000..2ca0350f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/bk_inf_engine.m @@ -0,0 +1,107 @@ +function engine = bk_inf_engine(bnet, varargin) +% BK_INF_ENGINE Boyen-Koller approximate inference algorithm for DBNs. +% +% In the BK algorithm, the belief state is represented as a product of marginals, +% even though the factors may not be independent. +% +% engine = bk_inf_engine(bnet, ...) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% clusters - if a cell array, clusters{i} specifies the terms in the i'th factor. +% - 'exact' means create one cluster that contains all the nodes in a slice [exact] +% - 'ff' means create one cluster per node (ff = fully factorised). +% +% +% For details, see +% - "Tractable Inference for Complex Stochastic Processes", X. Boyen and D. Koller, UAI 98. +% - "Approximate learning of dynamic models", X. Boyen and D. Koller, NIPS 98. +% (The UAI98 paper discusses filtering and theory, and the NIPS98 paper discusses smoothing.) + +ss = length(bnet.intra); +% set default params +clusters = 'exact'; + + +if nargin >= 2 + args = varargin; + nargs = length(args); + for i=1:2:nargs + switch args{i}, + case 'clusters', clusters = args{i+1}; + otherwise, error(['unrecognized argument ' args{i}]) + end + end +end + +if strcmp(clusters, 'exact') + %clusters = { compute_interface_nodes(bnet.intra, bnet.inter) }; + clusters = { 1:ss }; +elseif strcmp(clusters, 'ff') + clusters = num2cell(1:ss); +end + + +% We need to insert the prior on the clusters in slice 1, +% and extract the posterior on the clusters in slice 2. +C = length(clusters); +clusters2 = cell(1,2*C); +clusters2(1:C) = clusters; +for c=1:C + clusters2{c+C} = clusters{c} + ss; +end + +onodes = bnet.observed; +obs_nodes = [onodes(:) onodes(:)+ss]; +engine.sub_engine = jtree_inf_engine(bnet, 'clusters', clusters2); + +engine.clq_ass_to_cluster = zeros(C, 2); +for c=1:C + engine.clq_ass_to_cluster(c,1) = clq_containing_nodes(engine.sub_engine, clusters{c}); + engine.clq_ass_to_cluster(c,2) = clq_containing_nodes(engine.sub_engine, clusters{c}+ss); +end +engine.clusters = clusters; + +engine.clq_ass_to_node = zeros(ss, 2); +for i=1:ss + engine.clq_ass_to_node(i, 1) = clq_containing_nodes(engine.sub_engine, i); + engine.clq_ass_to_node(i, 2) = clq_containing_nodes(engine.sub_engine, i+ss); +end + + + +% Also create an engine just for slice 1 +bnet1 = mk_bnet(bnet.intra1, bnet.node_sizes_slice, 'discrete', myintersect(bnet.dnodes, 1:ss), ... + 'equiv_class', bnet.equiv_class(:,1), 'observed', onodes); +for i=1:max(bnet1.equiv_class) + bnet1.CPD{i} = bnet.CPD{i}; +end + +engine.sub_engine1 = jtree_inf_engine(bnet1, 'clusters', clusters); + +engine.clq_ass_to_cluster1 = zeros(1,C); +for c=1:C + engine.clq_ass_to_cluster1(c) = clq_containing_nodes(engine.sub_engine1, clusters{c}); +end + +engine.clq_ass_to_node1 = zeros(1, ss); +for i=1:ss + engine.clq_ass_to_node1(i) = clq_containing_nodes(engine.sub_engine1, i); +end + +engine.clpot = []; % this is where we store the results between enter_evidence and marginal_nodes +engine.filter = []; +engine.maximize = []; +engine.T = []; + +engine.bel = []; +engine.bel_clpot = []; +engine.slice1 = []; +%engine.pot_type = 'cg'; +% hack for online inference so we can cope with hidden Gaussians and discrete +% it will not affect the pot type used in enter_evidence +engine.pot_type = determine_pot_type(bnet, onodes); + +engine = class(engine, 'bk_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_init_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_init_bel.m new file mode 100644 index 00000000..fa6a27de --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_init_bel.m @@ -0,0 +1,8 @@ +function engine = dbn_init_bel(engine) +% DBN_INIT_BEL Compute the initial belief state (bk) +% engine = dbn_init_bel(engine)) + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +evidence = cell(1,ss); +engine = dbn_update_bel1(engine, evidence); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_marginal_from_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_marginal_from_bel.m new file mode 100644 index 00000000..7e5a968d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_marginal_from_bel.m @@ -0,0 +1,18 @@ +function marginal = dbn_marginal_from_bel(engine, i) +% DBN_MARGINAL_FROM_BEL Compute the marginal on a node given the current belief state (bk) +% marginal = dbn_marginal_from_bel(engine, i) + +if engine.slice1 + j = i; + c = clq_containing_nodes(engine.sub_engine1, j); +else + bnet = bnet_from_engine(engine); + ss = length(bnet.intra); + j = i+ss; + c = clq_containing_nodes(engine.sub_engine, j); +end +assert(c >= 1); +bigpot = engine.bel_clpot{c}; + +pot = marginalize_pot(bigpot, j); +marginal = pot_to_marginal(pot); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_update_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_update_bel.m new file mode 100644 index 00000000..0c8e02b5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_update_bel.m @@ -0,0 +1,37 @@ +function [engine, loglik] = dbn_update_bel(engine, evidence) +% DBN_UPDATE_BEL Update the belief state (bk) +% [engine, loglik] = dbn_update_bel(engine, evidence) +% +% evidence{i,1} contains the evidence on node i in slice t-1 +% evidence{i,2} contains the evidence on node i in slice t + +oldbel = engine.bel; + +ss = size(evidence, 1); +bnet = bnet_from_engine(engine); +CPDpot = cell(1, ss); +for n=1:ss + fam = family(bnet.dag, n, 2); + e = bnet.equiv_class(n, 2); + CPDpot{n} = convert_to_pot(bnet.CPD{e}, engine.pot_type, fam(:), evidence); +end + +observed = ~isemptycell(evidence); +onodes2 = find(observed(:)); +clqs = [engine.clq_ass_to_cluster(:,1); engine.clq_ass_to_node(:,2)]; +pots = [oldbel(:); CPDpot(:)]; + +[clpot, loglik] = enter_soft_evidence(engine.sub_engine, clqs, pots, onodes2(:), engine.pot_type); + +C = length(engine.clusters); +newbel = cell(1,C); +for c=1:C + k = engine.clq_ass_to_cluster(c,2); + cl = engine.clusters{c}; + newbel{c} = marginalize_pot(clpot{k}, cl+ss); % extract slice 2 posterior + newbel{c} = set_domain_pot(newbel{c}, cl); % shift back to slice 1 for re-use as prior +end + +engine.bel = newbel; +engine.bel_clpot = clpot; +engine.slice1 = 0; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_update_bel1.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_update_bel1.m new file mode 100644 index 00000000..a3b6cc79 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/dbn_update_bel1.m @@ -0,0 +1,30 @@ +function [engine, loglik] = dbn_update_bel1(engine, evidence) +% DBN_UPDATE_BEL1 Update the initial belief state (bk) +% engine = dbn_update_bel1(engine, evidence) +% +% evidence{i} has the evidence on node i for slice 1 + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +CPDpot = cell(1,ss); +t = 1; +for n=1:ss + fam = family(bnet.dag, n); + e = bnet.equiv_class(n, 1); + CPDpot{n} = convert_to_pot(bnet.CPD{e}, engine.pot_type, fam(:), evidence); +end + +onodes = find(~isemptycell(evidence)); + +[clpot, loglik] = enter_soft_evidence(engine.sub_engine1, engine.clq_ass_to_node1, CPDpot, onodes, engine.pot_type); + +C = length(engine.clusters); +newbel = cell(1,C); +for c=1:C + k = engine.clq_ass_to_cluster1(c); + newbel{c} = marginalize_pot(clpot{k}, engine.clusters{c}); +end + +engine.bel = newbel; +engine.bel_clpot = clpot; +engine.slice1 = 1; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/enter_evidence.m new file mode 100644 index 00000000..7008137b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/enter_evidence.m @@ -0,0 +1,48 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (bk) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product instead of sum-product [0] +% filter - if 1, do filtering, else smoothing [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +[ss T] = size(evidence); +engine.filter = filter; +engine.maximize = maximize; +engine.T = T; + +if maximize + error('BK does not yet support max propagation') + % because it calls enter_soft_evidence, not enter_evidence +end + +observed_bitv = ~isemptycell(evidence); +onodes = find(observed_bitv); +bnet = bnet_from_engine(engine); +pot_type = determine_pot_type(bnet, onodes); +CPDpot = convert_dbn_CPDs_to_pots(bnet, evidence, pot_type); +[engine.clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed_bitv, pot_type, filter); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/enter_soft_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/enter_soft_evidence.m new file mode 100644 index 00000000..1cbc634e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/enter_soft_evidence.m @@ -0,0 +1,88 @@ +function [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (bk) +% [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +[ss T] = size(CPDpot); +C = length(engine.clusters); +Q = length(cliques_from_engine(engine.sub_engine)); +Q1 = length(cliques_from_engine(engine.sub_engine1)); +clpot = cell(Q,T); +alpha = cell(C,T); + +% Forwards +% The method is a generalization of the following HMM equation: +% alpha(j,t) = normalise( (sum_i alpha(i,t-1) * transmat(i,j)) * obsmat(j,t) ) +% where alpha(j,t) = Pr(Q(t)=j | y(1:t)) +t = 1; +[clpot(1:Q1,t), logscale(t)] = enter_soft_evidence(engine.sub_engine1, engine.clq_ass_to_node1(:), ... + CPDpot(:,1), find(observed(:,1)), pot_type); +for c=1:C + k = engine.clq_ass_to_cluster1(c); + alpha{c,t} = marginalize_pot(clpot{k,t}, engine.clusters{c}); +end +% For filtering, clpot{1} contains evidence on slice 1 only + +%fprintf('alphas t=%d\n', t); +%for c=1:8 +% temp = pot_to_marginal(alpha{c,t}); +% temp.T +%end + +% clpot{t} contains evidence from slices t-1, t for t > 1 +clqs = [engine.clq_ass_to_cluster(:,1); engine.clq_ass_to_node(:,2)]; +for t=2:T + pots = [alpha(:,t-1); CPDpot(:,t)]; + [clpot(:,t), logscale(t)] = enter_soft_evidence(engine.sub_engine, clqs, pots, find(observed(:,t-1:t)), pot_type); + for c=1:C + k = engine.clq_ass_to_cluster(c,2); + cl = engine.clusters{c}; + alpha{c,t} = marginalize_pot(clpot{k,t}, cl+ss); % extract slice 2 posterior + alpha{c,t} = set_domain_pot(alpha{c,t}, cl); % shift back to slice 1 for re-use as prior + end + +end + +loglik = sum(logscale); + +if filter + return; +end + +% Backwards +% The method is a generalization of the following HMM equation: +% beta(i,t) = (sum_j transmat(i,j) * obsmat(j,t+1) * beta(j,t+1)) +% where beta(i,t) = Pr(y(t+1:T) | Q(t)=i) +t = T; +bnet = bnet_from_engine(engine); +beta = cell(C,T); +for c=1:C + beta{c,t} = mk_initial_pot(pot_type, engine.clusters{c} + ss, bnet.node_sizes(:), bnet.cnodes(:), ... + find(observed(:,t-1:t))); +end +for t=T-1:-1:1 + clqs = [engine.clq_ass_to_cluster(:,2); engine.clq_ass_to_node(:,2)]; + pots = [beta(:,t+1); CPDpot(:,t+1)]; + temp = enter_soft_evidence(engine.sub_engine, clqs, pots, find(observed(:,t:t+1)), pot_type); + for c=1:C + k = engine.clq_ass_to_cluster(c,1); + cl = engine.clusters{c}; + beta{c,t} = marginalize_pot(temp{k}, cl); % extract slice 1 + beta{c,t} = set_domain_pot(beta{c,t}, cl + ss); % shift fwd to slice 2 + end +end + +% Combine +% The method is a generalization of the following HMM equation: +% xi(i,j,t) = normalise( alpha(i,t) * transmat(i,j) * obsmat(j,t+1) * beta(j,t+1) ) +% where xi(i,j,t) = Pr(Q(t)=i, Q(t+1)=j | y(1:T)) +for t=1:T-1 + clqs = [engine.clq_ass_to_cluster(:); engine.clq_ass_to_node(:,2)]; + pots = [alpha(:,t); beta(:,t+1); CPDpot(:,t+1)]; + clpot(:,t+1) = enter_soft_evidence(engine.sub_engine, clqs, pots, find(observed(:,t:t+1)), pot_type); +end +% for smoothing, clpot{1} is undefined +for k=1:Q1 + clpot{k,1} = []; +end + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/marginal_family.m new file mode 100644 index 00000000..e948b836 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/marginal_family.m @@ -0,0 +1,25 @@ +function m = marginal_family(engine, i, t) +% MARGINAL_FAMILY Compute the marginal on the specified family (bk) +% marginal = marginal_family(engine, i, t) + +% This is just like inf_engine/marginal_family, except when we call +% marginal_nodes, we provide a 4th argument, to tell it's a family. + +if nargin < 3, t = 1; end + +bnet = bnet_from_engine(engine); +if t==1 + m = marginal_nodes(engine, family(bnet.dag, i), t, 1); +else + ss = length(bnet.intra); + fam = family(bnet.dag, i+ss); + if any(fam<=ss) + % i has a parent in the preceeding slice + % Hence the lowest numbered slice containing the family is t-1 + m = marginal_nodes(engine, fam, t-1, 1); + else + % The family all fits inside slice t + % Hence shift the indexes back to slice 1 + m = marginal_nodes(engine, fam-ss, t, 1); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..30bf9a9d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/marginal_nodes.m @@ -0,0 +1,67 @@ +function marginal = marginal_nodes(engine, nodes, t, fam) +% MARGINAL_NODES Compute the marginal on the specified query nodes (bk) +% +% marginal = marginal_nodes(engine, i, t) +% returns Pr(X(i,t) | Y(1:T)), where X(i,t) is the i'th node in the t'th slice. +% If enter_evidence used filtering instead of smoothing, this will return Pr(X(i,t) | Y(1:t)). +% +% marginal = marginal_nodes(engine, query, t) +% returns Pr(X(query(1),t), ... X(query(end),t) | Y(1:T)), +% where X(q,t) is the q'th node in the t'th slice. If q > ss (slice size), this is equal +% to X(q mod ss, t+1). That is, 't' specifies the time slice of the earliest node. +% 'query' cannot span more than 2 time slices. +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3. + +if nargin < 3, t = 1; end +if nargin < 4, fam = 0; else fam = 1; end + + +% clpot{t} contains slice t-1 and t +% Example +% clpot #: 1 2 3 +% slices: 1 1,2 2,3 +% For filtering, we must take care not to take future evidence into account. +% For smoothing, clpot{1} does not exist. + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); + +nodes2 = nodes; +if ~engine.filter + if t < engine.T + slice = t+1; + else % earliest t is T, so all nodes fit in one slice + slice = engine.T; + nodes2 = nodes + ss; + end +else + if t == 1 + slice = 1; + else + if all(nodes<=ss) + slice = t; + nodes2 = nodes + ss; + elseif t == engine.T + slice = t; + else + slice = t + 1; + end + end +end + +if engine.filter & t==1 + c = clq_containing_nodes(engine.sub_engine1, nodes2, fam); +else + c = clq_containing_nodes(engine.sub_engine, nodes2, fam); +end +assert(c >= 1); +bigpot = engine.clpot{c, slice}; + +pot = marginalize_pot(bigpot, nodes2); +marginal = pot_to_marginal(pot); + +% we convert the domain to the unrolled numbering system +% so that update_ess extracts the right evidence. +marginal.domain = nodes+(t-1)*ss; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/update_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/update_engine.m new file mode 100644 index 00000000..b36833a1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@bk_inf_engine/update_engine.m @@ -0,0 +1,11 @@ +function engine = update_engine(engine, newCPDs) +% UPDATE_ENGINE Update the engine to take into account the new parameters (bk) +% engine = update_engine(engine, newCPDs) + +engine.inf_engine = update_engine(engine.inf_engine, newCPDs); +engine.sub_engine = update_engine(engine.sub_engine, newCPDs); + +bnet = bnet_from_engine(engine); +eclass1 = bnet.equiv_class(:,1); +engine.sub_engine1 = update_engine(engine.sub_engine1, newCPDs(1:max(eclass1))); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/CVS/Entries new file mode 100644 index 00000000..4aa8d100 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/CVS/Entries @@ -0,0 +1,12 @@ +/cbk_inf_engine.m/1.1.1.1/Mon Nov 22 22:15:34 2004// +/dbn_init_bel.m/1.1.1.1/Tue Jul 29 02:44:58 2003// +/dbn_marginal_from_bel.m/1.1.1.1/Tue Jul 29 02:44:58 2003// +/dbn_update_bel.m/1.1.1.1/Tue Jul 29 02:44:58 2003// +/dbn_update_bel1.m/1.1.1.1/Tue Jul 29 02:44:58 2003// +/enter_evidence.m/1.1.1.1/Mon Jan 12 20:53:54 2004// +/enter_soft_evidence.m/1.1.1.1/Wed Feb 4 07:42:38 2004// +/junk/1.1.1.1/Wed Nov 24 20:12:38 2004// +/marginal_family.m/1.1.1.1/Tue Jul 29 02:44:58 2003// +/marginal_nodes.m/1.1.1.1/Tue Dec 16 06:17:18 2003// +/update_engine.m/1.1.1.1/Tue Jul 29 02:44:58 2003// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/CVS/Repository new file mode 100644 index 00000000..67ff288b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@cbk_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/cbk_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/cbk_inf_engine.m new file mode 100644 index 00000000..20b4c4eb --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/cbk_inf_engine.m @@ -0,0 +1,175 @@ +function engine = cbk_inf_engine(bnet, varargin) +% Just the same as bk_inf_engine, but you can specify overlapping clusters. + +ss = length(bnet.intra); +% set default params +clusters = 'exact'; + +if nargin >= 2 + args = varargin; + nargs = length(args); + for i=1:2:nargs + switch args{i}, + case 'clusters', clusters = args{i+1}; + otherwise, error(['unrecognized argument ' args{i}]) + end + end +end + +if strcmp(clusters, 'exact') + %clusters = { compute_interface_nodes(bnet.intra, bnet.inter) }; + clusters = { 1:ss }; +elseif strcmp(clusters, 'ff') + clusters = num2cell(1:ss); +end + + +% We need to insert the prior on the clusters in slice 1, +% and extract the posterior on the clusters in slice 2. +% We don't need to care about the separators, b/c they're subsets of the clusters. +C = length(clusters); +clusters2 = cell(1,2*C); +clusters2(1:C) = clusters; +for c=1:C + clusters2{c+C} = clusters{c} + ss; +end + +onodes = bnet.observed; +obs_nodes = [onodes(:) onodes(:)+ss]; +engine.sub_engine = jtree_inf_engine(bnet, 'clusters', clusters2); + +%FH >>> +%Compute separators. +ns = bnet.node_sizes(:,1); +ns(onodes) = 1; +[clusters, separators] = build_jt(clusters, 1:length(ns), ns); +S = length(separators); +engine.separators = separators; + +%Compute size of clusters. +cl_sizes = zeros(1,C); +for c=1:C + cl_sizes(c) = prod(ns(clusters{c})); +end + +%Assign separators to the smallest cluster subsuming them. +engine.cluster_ass_to_separator = zeros(S, 1); +for s=1:S + subsuming_clusters = []; + %find smallest cluster containing s + for c=1:C + if mysubset(separators{s}, clusters{c}) + subsuming_clusters(end+1) = c; + end + end + c = argmin(cl_sizes(subsuming_clusters)); + engine.cluster_ass_to_separator(s) = subsuming_clusters(c); +end + +%<<< FH + +engine.clq_ass_to_cluster = zeros(C, 2); +for c=1:C + engine.clq_ass_to_cluster(c,1) = clq_containing_nodes(engine.sub_engine, clusters{c}); + engine.clq_ass_to_cluster(c,2) = clq_containing_nodes(engine.sub_engine, clusters{c}+ss); +end +engine.clusters = clusters; + +engine.clq_ass_to_node = zeros(ss, 2); +for i=1:ss + engine.clq_ass_to_node(i, 1) = clq_containing_nodes(engine.sub_engine, i); + engine.clq_ass_to_node(i, 2) = clq_containing_nodes(engine.sub_engine, i+ss); +end + + + +% Also create an engine just for slice 1 +bnet1 = mk_bnet(bnet.intra1, bnet.node_sizes_slice, 'discrete', myintersect(bnet.dnodes, 1:ss), ... + 'equiv_class', bnet.equiv_class(:,1), 'observed', onodes); +for i=1:max(bnet1.equiv_class) + bnet1.CPD{i} = bnet.CPD{i}; +end + +engine.sub_engine1 = jtree_inf_engine(bnet1, 'clusters', clusters); + +engine.clq_ass_to_cluster1 = zeros(1,C); +for c=1:C + engine.clq_ass_to_cluster1(c) = clq_containing_nodes(engine.sub_engine1, clusters{c}); +end + +engine.clq_ass_to_node1 = zeros(1, ss); +for i=1:ss + engine.clq_ass_to_node1(i) = clq_containing_nodes(engine.sub_engine1, i); +end + +engine.clpot = []; % this is where we store the results between enter_evidence and marginal_nodes +engine.filter = []; +engine.maximize = []; +engine.T = []; + +engine.bel = []; +engine.bel_clpot = []; +engine.slice1 = []; +%engine.pot_type = 'cg'; +% hack for online inference so we can cope with hidden Gaussians and discrete +% it will not affect the pot type used in enter_evidence +engine.pot_type = determine_pot_type(bnet, onodes); + +engine = class(engine, 'cbk_inf_engine', inf_engine(bnet)); + + + + +function [cliques, seps, jt_size] = build_jt(cliques, vars, ns) +% BUILD_JT connects the cliques into a jtree, computes the respective +% separators and the size of the resulting jtree. +% +% [cliques, seps, jt_size] = build_jt(cliques, vars, ns) +% ns(i) has to hold the size of vars(i) +% vars has to be a superset of the union of cliques. + +%======== Compute the jtree with tool from BNT. This wants the vars to be 1:N. +%==== Map from nodes to their indices. +%disp('Computing jtree for cliques with vars and ns:'); +%cliques +%vars +%ns' + +inv_nodes = sparse(1,max(vars)); +N = length(vars); +for i=1:N + inv_nodes(vars(i)) = i; +end + +tmp_cliques = cell(1,length(cliques)); +%==== Temporarily map clique vars to their indices. +for i=1:length(cliques) + tmp_cliques{i} = inv_nodes(cliques{i}); +end + +%=== Compute the jtree, using BNT. +[jtree, root, B, w] = cliques_to_jtree(tmp_cliques, ns); + + +%======== Now, compute the separators between connected cliques and their weights. +seps = {}; +s_w = []; +[is,js] = find(jtree > 0); +for k=1:length(is) + i = is(k); j = js(k); + sep = vars(find(B(i,:) & B(j,:))); % intersect(cliques{i}, cliques{j}); + if i>j | length(sep) == 0, continue; end; + seps{end+1} = sep; + s_w(end+1) = prod(ns(inv_nodes(seps{end}))); +end + +cl_w = sum(w); +sep_w = sum(s_w); +assert(cl_w > sep_w, 'Weight of cliques must be bigger than weight of separators'); + +jt_size = cl_w + sep_w; +% jt.cliques = cliques; +% jt.seps = seps; +% jt.size = jt_size; +% jt.ns = ns'; +% jt; \ No newline at end of file diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_init_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_init_bel.m new file mode 100644 index 00000000..fa6a27de --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_init_bel.m @@ -0,0 +1,8 @@ +function engine = dbn_init_bel(engine) +% DBN_INIT_BEL Compute the initial belief state (bk) +% engine = dbn_init_bel(engine)) + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +evidence = cell(1,ss); +engine = dbn_update_bel1(engine, evidence); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_marginal_from_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_marginal_from_bel.m new file mode 100644 index 00000000..7e5a968d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_marginal_from_bel.m @@ -0,0 +1,18 @@ +function marginal = dbn_marginal_from_bel(engine, i) +% DBN_MARGINAL_FROM_BEL Compute the marginal on a node given the current belief state (bk) +% marginal = dbn_marginal_from_bel(engine, i) + +if engine.slice1 + j = i; + c = clq_containing_nodes(engine.sub_engine1, j); +else + bnet = bnet_from_engine(engine); + ss = length(bnet.intra); + j = i+ss; + c = clq_containing_nodes(engine.sub_engine, j); +end +assert(c >= 1); +bigpot = engine.bel_clpot{c}; + +pot = marginalize_pot(bigpot, j); +marginal = pot_to_marginal(pot); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_update_bel.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_update_bel.m new file mode 100644 index 00000000..0c8e02b5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_update_bel.m @@ -0,0 +1,37 @@ +function [engine, loglik] = dbn_update_bel(engine, evidence) +% DBN_UPDATE_BEL Update the belief state (bk) +% [engine, loglik] = dbn_update_bel(engine, evidence) +% +% evidence{i,1} contains the evidence on node i in slice t-1 +% evidence{i,2} contains the evidence on node i in slice t + +oldbel = engine.bel; + +ss = size(evidence, 1); +bnet = bnet_from_engine(engine); +CPDpot = cell(1, ss); +for n=1:ss + fam = family(bnet.dag, n, 2); + e = bnet.equiv_class(n, 2); + CPDpot{n} = convert_to_pot(bnet.CPD{e}, engine.pot_type, fam(:), evidence); +end + +observed = ~isemptycell(evidence); +onodes2 = find(observed(:)); +clqs = [engine.clq_ass_to_cluster(:,1); engine.clq_ass_to_node(:,2)]; +pots = [oldbel(:); CPDpot(:)]; + +[clpot, loglik] = enter_soft_evidence(engine.sub_engine, clqs, pots, onodes2(:), engine.pot_type); + +C = length(engine.clusters); +newbel = cell(1,C); +for c=1:C + k = engine.clq_ass_to_cluster(c,2); + cl = engine.clusters{c}; + newbel{c} = marginalize_pot(clpot{k}, cl+ss); % extract slice 2 posterior + newbel{c} = set_domain_pot(newbel{c}, cl); % shift back to slice 1 for re-use as prior +end + +engine.bel = newbel; +engine.bel_clpot = clpot; +engine.slice1 = 0; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_update_bel1.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_update_bel1.m new file mode 100644 index 00000000..a3b6cc79 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/dbn_update_bel1.m @@ -0,0 +1,30 @@ +function [engine, loglik] = dbn_update_bel1(engine, evidence) +% DBN_UPDATE_BEL1 Update the initial belief state (bk) +% engine = dbn_update_bel1(engine, evidence) +% +% evidence{i} has the evidence on node i for slice 1 + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +CPDpot = cell(1,ss); +t = 1; +for n=1:ss + fam = family(bnet.dag, n); + e = bnet.equiv_class(n, 1); + CPDpot{n} = convert_to_pot(bnet.CPD{e}, engine.pot_type, fam(:), evidence); +end + +onodes = find(~isemptycell(evidence)); + +[clpot, loglik] = enter_soft_evidence(engine.sub_engine1, engine.clq_ass_to_node1, CPDpot, onodes, engine.pot_type); + +C = length(engine.clusters); +newbel = cell(1,C); +for c=1:C + k = engine.clq_ass_to_cluster1(c); + newbel{c} = marginalize_pot(clpot{k}, engine.clusters{c}); +end + +engine.bel = newbel; +engine.bel_clpot = clpot; +engine.slice1 = 1; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/enter_evidence.m new file mode 100644 index 00000000..f6057ba2 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/enter_evidence.m @@ -0,0 +1,48 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% this is unchanged from bk_inf_engine. +% ENTER_EVIDENCE Add the specified evidence to the network (bk) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product instead of sum-product [0] +% filter - if 1, do filtering, else smoothing [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +[ss T] = size(evidence); +engine.filter = filter; +engine.maximize = maximize; +engine.T = T; + +if maximize + error('BK does not yet support max propagation') + % because it calls enter_soft_evidence, not enter_evidence +end + +observed_bitv = ~isemptycell(evidence); +onodes = find(observed_bitv); +bnet = bnet_from_engine(engine); +pot_type = determine_pot_type(bnet, onodes); +CPDpot = convert_dbn_CPDs_to_pots(bnet, evidence, pot_type); +[engine.clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed_bitv, pot_type, filter); \ No newline at end of file diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/enter_soft_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/enter_soft_evidence.m new file mode 100644 index 00000000..e102a11e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/enter_soft_evidence.m @@ -0,0 +1,115 @@ +function [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (bk) +% [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +[ss T] = size(CPDpot); +C = length(engine.clusters); +S = length(engine.separators); +Q = length(cliques_from_engine(engine.sub_engine)); +Q1 = length(cliques_from_engine(engine.sub_engine1)); +clpot = cell(Q,T); +alpha = cell(C,T); + +% Forwards +% The method is a generalization of the following HMM equation: +% alpha(j,t) = normalise( (sum_i alpha(i,t-1) * transmat(i,j)) * obsmat(j,t) ) +% where alpha(j,t) = Pr(Q(t)=j | y(1:t)) +t = 1; +[clpot(1:Q1,t), logscale(t)] = enter_soft_evidence(engine.sub_engine1, engine.clq_ass_to_node1(:), ... + CPDpot(:,1), find(observed(:,1)), pot_type); +for c=1:C + k = engine.clq_ass_to_cluster1(c); + alpha{c,t} = marginalize_pot(clpot{k,t}, engine.clusters{c}); +end + +%=== FH: For each separator s, divide some cluster potential by s's potential +alpha_orig = alpha(:,t); +for s=1:S + c = engine.cluster_ass_to_separator(s); + alpha{c,t} = divide_by_pot(alpha{c,t}, marginalize_pot(alpha_orig{c}, engine.separators{s})); +end + +% For filtering, clpot{1} contains evidence on slice 1 only + +%fprintf('alphas t=%d\n', t); +%for c=1:8 +% temp = pot_to_marginal(alpha{c,t}); +% temp.T +%end + +% clpot{t} contains evidence from slices t-1, t for t > 1 +clqs = [engine.clq_ass_to_cluster(:,1); engine.clq_ass_to_node(:,2)]; +for t=2:T + pots = [alpha(:,t-1); CPDpot(:,t)]; + [clpot(:,t), logscale(t)] = enter_soft_evidence(engine.sub_engine, clqs, pots, find(observed(:,t-1:t)), pot_type); + for c=1:C + k = engine.clq_ass_to_cluster(c,2); + cl = engine.clusters{c}; + alpha{c,t} = marginalize_pot(clpot{k,t}, cl+ss); % extract slice 2 posterior + alpha{c,t} = set_domain_pot(alpha{c,t}, cl); % shift back to slice 1 for re-use as prior + end + %=== FH: For each separator s, divide some cluster potential by s's potential + alpha_orig = alpha(:,t); + for s=1:S + c = engine.cluster_ass_to_separator(s); + alpha{c,t} = divide_by_pot(alpha{c,t}, marginalize_pot(alpha_orig{c}, engine.separators{s})); + end +end + +loglik = sum(logscale); + +if filter + return; +end + +% Backwards +% The method is a generalization of the following HMM equation: +% beta(i,t) = (sum_j transmat(i,j) * obsmat(j,t+1) * beta(j,t+1)) +% where beta(i,t) = Pr(y(t+1:T) | Q(t)=i) +t = T; +bnet = bnet_from_engine(engine); +beta = cell(C,T); +for c=1:C + beta{c,t} = mk_initial_pot(pot_type, engine.clusters{c} + ss, bnet.node_sizes(:), bnet.cnodes(:), ... + find(observed(:,t-1:t))); +end +%=== FH: For each separator s, divide some cluster potential by s's potential +beta_orig = beta(:,t); +for s=1:S + c = engine.cluster_ass_to_separator(s); + beta{c,t} = divide_by_pot(beta{c,t}, marginalize_pot(beta_orig{c}, engine.separators{s}+ss)); +end + +for t=T-1:-1:1 + clqs = [engine.clq_ass_to_cluster(:,2); engine.clq_ass_to_node(:,2)]; + pots = [beta(:,t+1); CPDpot(:,t+1)]; + temp = enter_soft_evidence(engine.sub_engine, clqs, pots, find(observed(:,t:t+1)), pot_type); + for c=1:C + k = engine.clq_ass_to_cluster(c,1); + cl = engine.clusters{c}; + beta{c,t} = marginalize_pot(temp{k}, cl); % extract slice 1 + beta{c,t} = set_domain_pot(beta{c,t}, cl + ss); % shift fwd to slice 2 + end + %=== FH: For each separator s, divide some cluster potential by s's potential + beta_orig = beta(:,t); + for s=1:S + c = engine.cluster_ass_to_separator(s); + beta{c,t} = divide_by_pot(beta{c,t}, marginalize_pot(beta_orig{c}, engine.separators{s}+ss)); + end +end + +% Combine +% The method is a generalization of the following HMM equation: +% xi(i,j,t) = normalise( alpha(i,t) * transmat(i,j) * obsmat(j,t+1) * beta(j,t+1) ) +% where xi(i,j,t) = Pr(Q(t)=i, Q(t+1)=j | y(1:T)) +for t=1:T-1 + clqs = [engine.clq_ass_to_cluster(:); engine.clq_ass_to_node(:,2)]; + pots = [alpha(:,t); beta(:,t+1); CPDpot(:,t+1)]; + clpot(:,t+1) = enter_soft_evidence(engine.sub_engine, clqs, pots, find(observed(:,t:t+1)), pot_type); +end +% for smoothing, clpot{1} is undefined +for k=1:Q1 + clpot{k,1} = []; +end + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/junk b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/junk new file mode 100644 index 00000000..31e9cab8 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/junk @@ -0,0 +1,176 @@ +function engine = cbk_inf_engine(bnet, varargin) +% Just the same as bk_inf_engine, but you can specify overlapping clusters. + +ss = length(bnet.intra); +% set default params +clusters = 'exact'; + +if nargin >= 2 + args = varargin; + nargs = length(args); + for i=1:2:nargs + switch args{i}, + case 'clusters', clusters = args{i+1}; + otherwise, error(['unrecognized argument ' args{i}]) + end + end +end + +if strcmp(clusters, 'exact') + %clusters = { compute_interface_nodes(bnet.intra, bnet.inter) }; + clusters = { 1:ss }; +elseif strcmp(clusters, 'ff') + clusters = num2cell(1:ss); +end + + +% We need to insert the prior on the clusters in slice 1, +% and extract the posterior on the clusters in slice 2. +% We don't need to care about the separators, b/c they're subsets of the clusters. +C = length(clusters); +clusters2 = cell(1,2*C); +clusters2(1:C) = clusters; +for c=1:C + clusters2{c+C} = clusters{c} + ss; +end + +onodes = bnet.observed; +obs_nodes = [onodes(:) onodes(:)+ss]; +engine.sub_engine = jtree_inf_engine(bnet, 'clusters', clusters2); + +%FH >>> +%Compute separators. +ns = bnet.node_sizes(:,1); +ns(onodes) = 1; +[clusters, separators] = build_jt(clusters, 1:length(ns), ns); +S = length(separators); +engine.separators = separators; + +%Compute size of clusters. +cl_sizes = zeros(1,C); +for c=1:C + cl_sizes(c) = prod(ns(clusters{c})); +end + +%Assign separators to the smallest cluster subsuming them. +engine.cluster_ass_to_separator = zeros(S, 1); +for s=1:S + subsuming_clusters = []; + %find smaunk + + for c=1:C + if mysubset(separators{s}, clusters{c}) + subsuming_clusters(end+1) = c; + end + end + c = argmin(cl_sizes(subsuming_clusters)); + engine.cluster_ass_to_separator(s) = subsuming_clusters(c); +end + +%<<< FH + +engine.clq_ass_to_cluster = zeros(C, 2); +for c=1:C + engine.clq_ass_to_cluster(c,1) = clq_containing_nodes(engine.sub_engine, clusters{c}); + engine.clq_ass_to_cluster(c,2) = clq_containing_nodes(engine.sub_engine, clusters{c}+ss); +end +engine.clusters = clusters; + +engine.clq_ass_to_node = zeros(ss, 2); +for i=1:ss + engine.clq_ass_to_node(i, 1) = clq_containing_nodes(engine.sub_engine, i); + engine.clq_ass_to_node(i, 2) = clq_containing_nodes(engine.sub_engine, i+ss); +end + + + +% Also create an engine just for slice 1 +bnet1 = mk_bnet(bnet.intra1, bnet.node_sizes_slice, 'discrete', myintersect(bnet.dnodes, 1:ss), ... + 'equiv_class', bnet.equiv_class(:,1), 'observed', onodes); +for i=1:max(bnet1.equiv_class) + bnet1.CPD{i} = bnet.CPD{i}; +end + +engine.sub_engine1 = jtree_inf_engine(bnet1, 'clusters', clusters); + +engine.clq_ass_to_cluster1 = zeros(1,C); +for c=1:C + engine.clq_ass_to_cluster1(c) = clq_containing_nodes(engine.sub_engine1, clusters{c}); +end + +engine.clq_ass_to_node1 = zeros(1, ss); +for i=1:ss + engine.clq_ass_to_node1(i) = clq_containing_nodes(engine.sub_engine1, i); +end + +engine.clpot = []; % this is where we store the results between enter_evidence and marginal_nodes +engine.filter = []; +engine.maximize = []; +engine.T = []; + +engine.bel = []; +engine.bel_clpot = []; +engine.slice1 = []; +%engine.pot_type = 'cg'; +% hack for online inference so we can cope with hidden Gaussians and discrete +% it will not affect the pot type used in enter_evidence +engine.pot_type = determine_pot_type(bnet, onodes); + +engine = class(engine, 'cbk_inf_engine', inf_engine(bnet)); + + + + +function [cliques, seps, jt_size] = build_jt(cliques, vars, ns) +% BUILD_JT connects the cliques into a jtree, computes the respective +% separators and the size of the resulting jtree. +% +% [cliques, seps, jt_size] = build_jt(cliques, vars, ns) +% ns(i) has to hold the size of vars(i) +% vars has to be a superset of the union of cliques. + +%======== Compute the jtree with tool from BNT. This wants the vars to be 1:N. +%==== Map from nodes to their indices. +%disp('Computing jtree for cliques with vars and ns:'); +%cliques +%vars +%ns' + +inv_nodes = sparse(1,max(vars)); +N = length(vars); +for i=1:N + inv_nodes(vars(i)) = i; +end + +tmp_cliques = cell(1,length(cliques)); +%==== Temporarily map clique vars to their indices. +for i=1:length(cliques) + tmp_cliques{i} = inv_nodes(cliques{i}); +end + +%=== Compute the jtree, using BNT. +[jtree, root, B, w] = cliques_to_jtree(tmp_cliques, ns); + + +%======== Now, compute the separators between connected cliques and their weights. +seps = {}; +s_w = []; +[is,js] = find(jtree > 0); +for k=1:length(is) + i = is(k); j = js(k); + sep = vars(find(B(i,:) & B(j,:))); % intersect(cliques{i}, cliques{j}); + if i>j | length(sep) == 0, continue; end; + seps{end+1} = sep; + s_w(end+1) = prod(ns(inv_nodes(seps{end}))); +end + +cl_w = sum(w); +sep_w = sum(s_w); +assert(cl_w > sep_w, 'Weight of cliques must be bigger than weight of separators'); + +jt_size = cl_w + sep_w; +% jt.cliques = cliques; +% jt.seps = seps; +% jt.size = jt_size; +% jt.ns = ns'; +% jt; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/marginal_family.m new file mode 100644 index 00000000..e948b836 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/marginal_family.m @@ -0,0 +1,25 @@ +function m = marginal_family(engine, i, t) +% MARGINAL_FAMILY Compute the marginal on the specified family (bk) +% marginal = marginal_family(engine, i, t) + +% This is just like inf_engine/marginal_family, except when we call +% marginal_nodes, we provide a 4th argument, to tell it's a family. + +if nargin < 3, t = 1; end + +bnet = bnet_from_engine(engine); +if t==1 + m = marginal_nodes(engine, family(bnet.dag, i), t, 1); +else + ss = length(bnet.intra); + fam = family(bnet.dag, i+ss); + if any(fam<=ss) + % i has a parent in the preceeding slice + % Hence the lowest numbered slice containing the family is t-1 + m = marginal_nodes(engine, fam, t-1, 1); + else + % The family all fits inside slice t + % Hence shift the indexes back to slice 1 + m = marginal_nodes(engine, fam-ss, t, 1); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..30bf9a9d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/marginal_nodes.m @@ -0,0 +1,67 @@ +function marginal = marginal_nodes(engine, nodes, t, fam) +% MARGINAL_NODES Compute the marginal on the specified query nodes (bk) +% +% marginal = marginal_nodes(engine, i, t) +% returns Pr(X(i,t) | Y(1:T)), where X(i,t) is the i'th node in the t'th slice. +% If enter_evidence used filtering instead of smoothing, this will return Pr(X(i,t) | Y(1:t)). +% +% marginal = marginal_nodes(engine, query, t) +% returns Pr(X(query(1),t), ... X(query(end),t) | Y(1:T)), +% where X(q,t) is the q'th node in the t'th slice. If q > ss (slice size), this is equal +% to X(q mod ss, t+1). That is, 't' specifies the time slice of the earliest node. +% 'query' cannot span more than 2 time slices. +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3. + +if nargin < 3, t = 1; end +if nargin < 4, fam = 0; else fam = 1; end + + +% clpot{t} contains slice t-1 and t +% Example +% clpot #: 1 2 3 +% slices: 1 1,2 2,3 +% For filtering, we must take care not to take future evidence into account. +% For smoothing, clpot{1} does not exist. + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); + +nodes2 = nodes; +if ~engine.filter + if t < engine.T + slice = t+1; + else % earliest t is T, so all nodes fit in one slice + slice = engine.T; + nodes2 = nodes + ss; + end +else + if t == 1 + slice = 1; + else + if all(nodes<=ss) + slice = t; + nodes2 = nodes + ss; + elseif t == engine.T + slice = t; + else + slice = t + 1; + end + end +end + +if engine.filter & t==1 + c = clq_containing_nodes(engine.sub_engine1, nodes2, fam); +else + c = clq_containing_nodes(engine.sub_engine, nodes2, fam); +end +assert(c >= 1); +bigpot = engine.clpot{c, slice}; + +pot = marginalize_pot(bigpot, nodes2); +marginal = pot_to_marginal(pot); + +% we convert the domain to the unrolled numbering system +% so that update_ess extracts the right evidence. +marginal.domain = nodes+(t-1)*ss; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/update_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/update_engine.m new file mode 100644 index 00000000..b36833a1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@cbk_inf_engine/update_engine.m @@ -0,0 +1,11 @@ +function engine = update_engine(engine, newCPDs) +% UPDATE_ENGINE Update the engine to take into account the new parameters (bk) +% engine = update_engine(engine, newCPDs) + +engine.inf_engine = update_engine(engine.inf_engine, newCPDs); +engine.sub_engine = update_engine(engine.sub_engine, newCPDs); + +bnet = bnet_from_engine(engine); +eclass1 = bnet.equiv_class(:,1); +engine.sub_engine1 = update_engine(engine.sub_engine1, newCPDs(1:max(eclass1))); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/CVS/Entries new file mode 100644 index 00000000..fda92284 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/CVS/Entries @@ -0,0 +1,8 @@ +/enter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_soft_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/ff_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +/filter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +/smooth_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +D/Old//// diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/CVS/Repository new file mode 100644 index 00000000..56fb63d3 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@ff_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/CVS/Entries new file mode 100644 index 00000000..dd45ee37 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/CVS/Entries @@ -0,0 +1,4 @@ +/enter_soft_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_soft_evidence1.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/CVS/Repository new file mode 100644 index 00000000..5582c6dd --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@ff_inf_engine/Old diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/enter_soft_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/enter_soft_evidence.m new file mode 100644 index 00000000..1e2acffb --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/enter_soft_evidence.m @@ -0,0 +1,59 @@ +function [marginals, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (bk_ff) +% [marginals, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +assert(pot_type == 'd'); +[ss T] = size(CPDpot); +fwd = cell(ss,T); +hnodes = engine.hnodes(:)'; +onodes = engine.onodes(:)'; +bnet = bnet_from_engine(engine); +ns = bnet.node_sizes; +onodes2 = [onodes onodes+ss]; +ns(onodes2) = 1; + +logscale = zeros(1,T); +local_logscale = zeros(1,length(hnodes)); + +t = 1; +for i=hnodes + fwd{i,t} = CPDpot{i,t}; +end +for i=onodes + p = parents(bnet.dag, i); + assert(length(p)==1); + ev = marginalize_pot(CPDpot{i,t}, p); + fwd{p,t} = multiply_by_pot(fwd{p,t}, ev); +end +for i=hnodes + [fwd{i,t}, local_logscale(i)] = normalize_pot(fwd{i,t}); +end +logscale(t) = sum(local_logscale); + +for t=2:T + for i=hnodes + ps = parents(bnet.dag, i+ss); + assert(all(ps<=ss)); % in previous slice + prior = CPDpot{i,t}; + for p=ps(:)' + prior = multiply_by_pot(prior, fwd{p,t-1}); + end + fwd{i,t} = marginalize_pot(prior, i+ss); + fwd{i,t} = set_domain_pot(fwd{i,t}, i); + end + for i=onodes + p = parents(bnet.dag, i); + assert(length(p)==1); + temp = pot_to_marginal(CPDpot{i,t}); + ev = dpot(p, ns(p), temp.T); + fwd{p,t} = multiply_by_pot(fwd{p,t}, ev); + end + + for i=hnodes + [fwd{i,t}, local_logscale(i)] = normalize_pot(fwd{i,t}); + end + logscale(t) = sum(local_logscale); +end + +marginals = fwd; +loglik = sum(logscale); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/enter_soft_evidence1.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/enter_soft_evidence1.m new file mode 100644 index 00000000..b4ff1a02 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/enter_soft_evidence1.m @@ -0,0 +1,94 @@ +function [marginals, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (ff) +% [marginals, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +assert(pot_type == 'd'); +[ss T] = size(CPDpot); +fwd = cell(ss,T); +hnodes = engine.hnodes(:)'; +onodes = engine.onodes(:)'; +bnet = bnet_from_engine(engine); +ns = bnet.node_sizes; +onodes2 = [onodes onodes+ss]; +ns(onodes2) = 1; + +logscale = zeros(1,T); +H = length(hnodes); +local_logscale = zeros(1,ss); + +obschild = zeros(1,ss); +for i=hnodes + ocs = myintersect(children(bnet.dag, i), onodes); + assert(length(ocs)==1); + obschild(i) = ocs(1); +end + +t = 1; +for i=hnodes + fwd{i,t} = CPDpot{i,t}; + c = obschild(i); + temp = pot_to_marginal(CPDpot{c,t}); + ev = dpot(i, ns(i), temp.T); + fwd{i,t} = multiply_by_pot(fwd{i,t}, ev); + [fwd{i,t}, local_logscale(i)] = normalize_pot(fwd{i,t}); +end +logscale(t) = sum(local_logscale); + +for t=2:T + for i=hnodes + ps = parents(bnet.dag, i+ss); + assert(all(ps<=ss)); % in previous slice + prior = CPDpot{i,t}; + for p=ps(:)' + prior = multiply_by_pot(prior, fwd{p,t-1}); + end + fwd{i,t} = marginalize_pot(prior, i+ss); + fwd{i,t} = set_domain_pot(fwd{i,t}, i); + c = obschild(i); + temp = pot_to_marginal(CPDpot{c,t}); + ev = dpot(i, ns(i), temp.T); + fwd{i,t} = multiply_by_pot(fwd{i,t}, ev); + [fwd{i,t}, local_logscale(i)] = normalize_pot(fwd{i,t}); + end + logscale(t) = sum(local_logscale); +end + +loglik = sum(logscale); + + +if filter + marginals = fwd; + return; +end + +back = cell(ss,T); +t = T; +for i=hnodes + back{i,t} = dpot(i, ns(i)); + back{i,t} = set_domain_pot(back{i,t}, i+ss); +end +for t=T-1:-1:1 + for i=hnodes + pot = CPDpot{i,t+1}; + pot = multiply_by_pot(pot, back{i,t+1}); + c = obschild(i); + temp = pot_to_marginal(CPDpot{c,t+1}); + ev = dpot(i, ns(i), temp.T); + pot = multiply_by_pot(pot, ev); + back{i,t} = marginalize_pot(pot, i); + back{i,t} = normalize_pot(back{i,t}); + back{i,t} = set_domain_pot(back{i,t}, i+ss); + end +end + + + +% COMBINE +for t=1:T + for i=hnodes + back{i,t} = set_domain_pot(back{i,t}, i); + fwd{i,t} = multiply_by_pot(fwd{i,t}, back{i,t}); + marginals{i,t} = normalize_pot(fwd{i,t}); + %fwdback{i,t} = normalize_pot(multiply_pots(fwd{i,t}, back{i,t})); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/marginal_family.m new file mode 100644 index 00000000..99813571 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/Old/marginal_family.m @@ -0,0 +1,38 @@ +function marginal = marginal_family(engine, i, t) +% MARGINAL_FAMILY Compute the marginal on the specified family (ff) +% marginal = marginal_family(engine, i, t) + +if nargin < 3, t = 1; end + +% The method is similar to the following HMM equation: +% xi(i,j,t) = normalise( alpha(i,t) * transmat(i,j) * obsmat(j,t+1) * beta(j,t+1) ) +% where xi(i,j,t) = Pr(Q(t)=i, Q(t+1)=j | y(1:T)) + +bnet = bnet_from_engine(engine); + +if myismember(i, engine.onodes) + ps = parents(bnet.dag, i); + p = ps(1); + marginal = pot_to_marginal(engine.marginals{p,t}); + marginal.domain = [p i]; + return; +end + +if t==1 + marginal = pot_to_marginal(engine.marginals{i,t}); + return; +end + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +pot = engine.CPDpot{i,t}; +c = engine.obschild(i); +pot = multiply_by_pot(pot, engine.CPDpot{c,t}); +pot = multiply_by_pot(pot, engine.back{i,t}); +ps = parents(bnet.dag, i+ss); +for p=ps(:)' + pot = multiply_by_pot(pot, engine.fwd{p,t-1}); +end +marginal = pot_to_marginal(normalize_pot(pot)); + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/enter_evidence.m new file mode 100644 index 00000000..7fa9fa9b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/enter_evidence.m @@ -0,0 +1,62 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (ff) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or +% column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product (not yet supported), else sum-product [0] +% filter - if 1, do filtering, else smoothing [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +assert(~maximize); + + +[ss T] = size(evidence); +observed = ~isemptycell(evidence); +bnet = bnet_from_engine(engine); +%pot_type = determine_pot_type(find(observed(:,1)), bnet.cnodes_slice, bnet.intra); +pot_type = determine_pot_type(bnet, observed); +% we assume we can use the same pot_type in all slices + +CPDpot = convert_dbn_CPDs_to_pots(bnet, evidence, pot_type); + +% Now convert CPDs on observed nodes to be potentials just on their parents +assert(pot_type == 'd'); +onodes = bnet.observed(:); +ns = bnet.node_sizes_slice; +ns(onodes) = 1; +for t=1:T + for i=onodes + p = parents(bnet.dag, i); + %CPDpot{i,t} = set_domain_pot(CPDpot{i,t}, p); % leaves size too long + temp = pot_to_marginal(CPDpot{i,t}); + CPDpot{i,t} = dpot(p, ns(p), temp.T); % assumes pot_type = d + end +end + +[engine.marginals, engine.fwd, engine.back, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter); + +engine.CPDpot = CPDpot; +engine.filter = filter; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/enter_soft_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/enter_soft_evidence.m new file mode 100644 index 00000000..db16f39b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/enter_soft_evidence.m @@ -0,0 +1,11 @@ +function [marginals, fwd, back, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (ff) +% [marginals, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +if filter + [fwd, loglik] = filter_evidence(engine, CPDpot, observed, pot_type); + marginals = fwd; + back = []; +else + [marginals, fwd, back, loglik] = smooth_evidence(engine, CPDpot, observed, pot_type); +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/ff_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/ff_inf_engine.m new file mode 100644 index 00000000..ade26106 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/ff_inf_engine.m @@ -0,0 +1,44 @@ +function engine = ff_inf_engine(bnet) +% FF_INF_ENGINE Factored frontier inference engine for DBNs +% engine = ff_inf_engine(bnet) +% +% The model must be topologically isomorphic to an HMM. +% In addition, each hidden node is assumed to have at most one observed child, +% and each observed child is assumed to have exactly one hidden parent. +% +% For details of this algorithm, see +% "The Factored Frontier Algorithm for Approximate Inference in DBNs", +% Kevin Murphy and Yair Weiss, UAI 2001. +% +% THIS IS HIGHLY EXPERIMENTAL CODE! + +ss = length(bnet.intra); +onodes = bnet.observed; +hnodes = mysetdiff(1:ss, onodes); + +[persistent_nodes, transient_nodes] = partition_dbn_nodes(bnet.intra, bnet.inter); +assert(isequal(onodes, transient_nodes)); +assert(isequal(hnodes, persistent_nodes)); + +engine.onodes = onodes; +engine.hnodes = hnodes; +engine.marginals = []; +engine.fwd = []; +engine.back = []; +engine.CPDpot = []; +engine.filter = []; + +obschild = zeros(1,ss); +for i=engine.hnodes(:)' + %ocs = myintersect(children(bnet.dag, i), onodes); + ocs = children(bnet.intra, i); + assert(length(ocs) <= 1); + if length(ocs)==1 + obschild(i) = ocs(1); + end +end +engine.obschild = obschild; + + +engine = class(engine, 'ff_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/filter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/filter_evidence.m new file mode 100644 index 00000000..3dd4835c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/filter_evidence.m @@ -0,0 +1,48 @@ +function [fwd, loglik] = filter_evidence(engine, CPDpot, observed, pot_type) +% [fwd, loglik] = filter_evidence(engine, CPDpot, observed, pot_type) (ff) + +[ss T] = size(CPDpot); +fwd = cell(ss,T); +hnodes = engine.hnodes(:)'; +onodes = engine.onodes(:)'; +bnet = bnet_from_engine(engine); +ns = bnet.node_sizes; +onodes2 = [onodes onodes+ss]; +ns(onodes2) = 1; + +logscale = zeros(1,T); +H = length(hnodes); +local_logscale = zeros(1,ss); + +t = 1; +for i=hnodes + fwd{i,t} = CPDpot{i,t}; + c = engine.obschild(i); + if c > 0 + fwd{i,t} = multiply_by_pot(fwd{i,t}, CPDpot{c, t}); + end + [fwd{i,t}, local_logscale(i)] = normalize_pot(fwd{i,t}); +end +logscale(t) = sum(local_logscale); + +for t=2:T + for i=hnodes + ps = parents(bnet.dag, i+ss); + assert(all(ps<=ss)); % in previous slice + prior = CPDpot{i,t}; + for p=ps(:)' + prior = multiply_by_pot(prior, fwd{p,t-1}); + end + fwd{i,t} = marginalize_pot(prior, i+ss); + fwd{i,t} = set_domain_pot(fwd{i,t}, i); + c = engine.obschild(i); + if c > 0 + fwd{i,t} = multiply_by_pot(fwd{i,t}, CPDpot{c,t}); + end + [fwd{i,t}, local_logscale(i)] = normalize_pot(fwd{i,t}); + end + logscale(t) = sum(local_logscale); +end + +loglik = sum(logscale); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/marginal_family.m new file mode 100644 index 00000000..bdc783b7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/marginal_family.m @@ -0,0 +1,44 @@ +function marginal = marginal_family(engine, i, t) +% MARGINAL_FAMILY Compute the marginal on the specified family (ff) +% marginal = marginal_family(engine, i, t) + + +if engine.filter + error('can''t currently use marginal_family when filtering with ff'); +end + +if nargin < 3, t = 1; end + +% The method is similar to the following HMM equation: +% xi(i,j,t) = normalise( alpha(i,t) * transmat(i,j) * obsmat(j,t+1) * beta(j,t+1) ) +% where xi(i,j,t) = Pr(Q(t)=i, Q(t+1)=j | y(1:T)) + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); + +if myismember(i, engine.onodes) + ps = parents(bnet.dag, i); + p = ps(1); + marginal = pot_to_marginal(engine.marginals{ps(1),t}); + fam = ([ps i]) + (t-1)*ss; +elseif t==1 + marginal = pot_to_marginal(engine.marginals{i,t}); + fam = i + (t-1)*ss; +else + pot = engine.CPDpot{i,t}; + c = engine.obschild(i); + if c>0 + pot = multiply_by_pot(pot, engine.CPDpot{c,t}); + end + pot = multiply_by_pot(pot, engine.back{i,t}); + ps = parents(bnet.dag, i+ss); + for p=ps(:)' + pot = multiply_by_pot(pot, engine.fwd{p,t-1}); + end + marginal = pot_to_marginal(normalize_pot(pot)); + fam = ([ps i+ss]) + (t-2)*ss; +end + +% we convert the domain to the unrolled numbering system +% so that update_ess extracts the right evidence. +marginal.domain = fam; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..f65a3bec --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/marginal_nodes.m @@ -0,0 +1,20 @@ +function marginal = marginal_nodes(engine, nodes, t) +% MARGINAL_NODES Compute the marginal on the specified query nodes (ff) +% marginal = marginal_nodes(engine, i, t) +% returns Pr(X(i,t) | Y(1:T)), where X(i,t) is the i'th node in the t'th slice. +% If enter_evidence used filtering instead of smoothing, this will return Pr(X(i,t) | Y(1:t)). + +if nargin < 3, t = 1; end +assert(length(nodes)==1); +i = nodes(end); +if myismember(i, engine.hnodes) + marginal = pot_to_marginal(engine.marginals{i,t}); +else + marginal = pot_to_marginal(dpot(i, 1, 1)); % observed +end + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +% we convert the domain to the unrolled numbering system +% so that update_ess extracts the right evidence. +marginal.domain = nodes+(t-1)*ss; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/smooth_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/smooth_evidence.m new file mode 100644 index 00000000..782f07aa --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@ff_inf_engine/smooth_evidence.m @@ -0,0 +1,89 @@ +function [marginals, fwd, back, loglik] = smooth_evidence(engine, CPDpot, observed, pot_type) +% [marginals, fwd, back, loglik] = smooth_evidence(engine, CPDpot, observed, pot_type) (ff) + +error('ff smoothing is broken'); + +[ss T] = size(CPDpot); +fwd = cell(ss,T); +hnodes = engine.hnodes(:)'; +onodes = engine.onodes(:)'; +bnet = bnet_from_engine(engine); +ns = bnet.node_sizes; +onodes2 = [onodes onodes+ss]; +ns(onodes2) = 1; + +logscale = zeros(1,T); +H = length(hnodes); +local_logscale = zeros(1,ss); + +t = 1; +for i=hnodes + fwd{i,t} = CPDpot{i,t}; + c = engine.obschild(i); + if 0 % c > 0 + fwd{i,t} = multiply_by_pot(fwd{i,t}, CPDpot{c, t}); + end + [fwd{i,t}, local_logscale(i)] = normalize_pot(fwd{i,t}); +end +logscale(t) = sum(local_logscale); + +for t=2:T + for i=hnodes + ps = parents(bnet.dag, i+ss); + assert(all(ps<=ss)); % in previous slice + prior = CPDpot{i,t}; + for p=ps(:)' + prior = multiply_by_pot(prior, fwd{p,t-1}); + end + fwd{i,t} = marginalize_pot(prior, i+ss); + fwd{i,t} = set_domain_pot(fwd{i,t}, i); + c = engine.obschild(i); + if 0 % c > 0 + fwd{i,t} = multiply_by_pot(fwd{i,t}, CPDpot{c,t}); + end + [fwd{i,t}, local_logscale(i)] = normalize_pot(fwd{i,t}); + end + logscale(t) = sum(local_logscale); +end + +loglik = sum(logscale); + +back = cell(ss,T); +t = T; +for i=hnodes + pot = dpot(i, ns(i)); + cs = children(bnet.intra, i); + for c=cs(:)' + pot = multiply_pots(pot, CPDpot{c,t}); + end + back{i,t} = marginalize_pot(pot, i); + back{i,t} = normalize_pot(back{i,t}); + back{i,t} = set_domain_pot(back{i,t}, i+ss); +end +for t=T-1:-1:1 + for i=hnodes + pot = dpot(i, ns(i)); + cs = children(bnet.inter, i); + for c=cs(:)' + pot = multiply_pots(pot, back{c,t+1}); + pot = multiply_pots(pot, CPDpot{c,t+1}); + end + cs = children(bnet.intra, i); + for c=cs(:)' + pot = multiply_pots(pot, CPDpot{c,t}); + end + back{i,t} = marginalize_pot(pot, i); + back{i,t} = normalize_pot(back{i,t}); + back{i,t} = set_domain_pot(back{i,t}, i+ss); + end +end + + +% COMBINE +for t=1:T + for i=hnodes + back{i,t} = set_domain_pot(back{i,t}, i); + fwd{i,t} = multiply_by_pot(fwd{i,t}, back{i,t}); + marginals{i,t} = normalize_pot(fwd{i,t}); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/CVS/Entries new file mode 100644 index 00000000..79297e05 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/CVS/Entries @@ -0,0 +1,7 @@ +/enter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_soft_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/frontier_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +/set_fwdback.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/CVS/Repository new file mode 100644 index 00000000..0e85f66f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@frontier_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/enter_evidence.m new file mode 100644 index 00000000..bd30a57c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/enter_evidence.m @@ -0,0 +1,44 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (frontier) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product (not yet supported), else sum-product [0] +% filter - if 1, do filtering, else smoothing [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +assert(~maximize); + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +onodes = find(~isemptycell(evidence)); +cnodes = unroll_set(bnet.cnodes(:), ss, T); +pot_type = determine_pot_type(bnet, onodes); + +CPDpot = convert_dbn_CPDs_to_pots(bnet, evidence, pot_type); + +[engine.fwdback, loglik, engine.fwd_frontier, engine.back_frontier] = ... + enter_soft_evidence(engine, CPDpot, onodes, pot_type, filter); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/enter_soft_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/enter_soft_evidence.m new file mode 100644 index 00000000..8da339c7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/enter_soft_evidence.m @@ -0,0 +1,142 @@ +function [fwdback, loglik, fwd_frontier, back_frontier] = enter_soft_evidence(engine, CPD, onodes, pot_type, filter) +% ENTER_SOFT_EVIDENCE Add soft evidence to network (frontier) +% [fwdback, loglik] = enter_soft_evidence(engine, CPDpot, onodes, filter) + +if nargin < 3, filter = 0; end + +[ss T] = size(CPD); +bnet = bnet_from_engine(engine); +ns = repmat(bnet.node_sizes_slice(:), 1, T); +cnodes = unroll_set(bnet.cnodes(:), ss, T); + +% FORWARDS +fwd = cell(ss,T); +ll = zeros(1,T); +S = 2*ss; % num. intermediate frontiers to get from t to t+1 +frontier = cell(S,T); + +% Start with empty frontier, and add each node in slice 1 +init = mk_initial_pot(pot_type, [], ns, cnodes, onodes); +t = 1; +s = 1; +j = 1; +frontier{s,t} = update(init, j, 1, CPD{j}, engine.fdom1{s}, pot_type, ns, cnodes, onodes); +fwd{j} = frontier{s,t}; +for s=2:ss + j = s; % add node j at step s + frontier{s,t} = update(frontier{s-1,t}, j, 1, CPD{j}, engine.fdom1{s}, pot_type, ns, cnodes, onodes); + fwd{j} = frontier{s,t}; +end +frontier{S,t} = frontier{ss,t}; +[frontier{S,t}, ll(1)] = normalize_pot(frontier{S,t}); + +% Now move frontier from slice to slice +OPS = engine.ops; +add = OPS>0; +nodes = [zeros(S,1) unroll_set(abs(OPS(:)), ss, T-1)]; +for t=2:T + offset = (t-2)*ss; + for s=1:S + if s==1 + prev_ndx = (t-2)*S + S; % S,t-1 + else + prev_ndx = (t-1)*S + s-1; % s-1,t + end + j = nodes(s,t); + frontier{s,t} = update(frontier{prev_ndx}, j, add(s), CPD{j}, engine.fdom{s}+offset, pot_type, ns, cnodes, onodes); + if add(s) + fwd{j} = frontier{s,t}; + end + end + [frontier{S,t}, ll(t)] = normalize_pot(frontier{S,t}); +end +loglik = sum(ll); + + +fwd_frontier = frontier; + +if filter + fwdback = fwd; + return; +end + + +% BACKWARDS +back = cell(ss,T); +add = ~add; % forwards add = backwards remove +frontier = cell(S,T+1); +t = T; +dom = (1:ss) + (t-1)*ss; +frontier{1,T+1} = mk_initial_pot(pot_type, dom, ns, cnodes, onodes); % all 1s for last slice +for t=T:-1:2 + offset = (t-2)*ss; + for s=S:-1:1 % reverse order + if s==S + prev_ndx = t*S + 1; % 1,t+1 + else + prev_ndx = (t-1)*S + (s+1); % s+1,t + end + j = nodes(s,t); + if ~add(s) + back{j} = frontier{prev_ndx}; % save frontier before removing + end + frontier{s,t} = rev_update(frontier{prev_ndx}, t, s, j, add(s), CPD{j}, engine.fdom{s}+offset, pot_type, ns, cnodes, onodes); + end + frontier{1,t} = normalize_pot(frontier{1,t}); +end +% Remove each node in first slice until left with empty set +t = 1; +frontier{ss+1,t} = frontier{1,2}; +add = 0; +for s=ss:-1:1 + j = s; % remove node j at step s + back{j} = frontier{s+1,t}; + frontier{s,t} = rev_update(frontier{s+1,t}, t, s, j, add, CPD{j}, 1:s, pot_type, ns, cnodes, onodes); +end + +% COMBINE +for t=1:T + for i=1:ss + %fwd{i,t} = multiply_by_pot(fwd{i,t}, back{i,t}); + %fwdback{i,t} = normalize_pot(fwd{i,t}); + fwdback{i,t} = normalize_pot(multiply_pots(fwd{i,t}, back{i,t})); + end +end + +back_frontier = frontier; + +%%%%%%%%%% +function new_frontier = update(old_frontier, j, add, CPD, newdom, pot_type, ns, cnodes, onodes) + +if add + new_frontier = mk_initial_pot(pot_type, newdom, ns, cnodes, onodes); + new_frontier = multiply_by_pot(new_frontier, old_frontier); + new_frontier = multiply_by_pot(new_frontier, CPD); +else + new_frontier = marginalize_pot(old_frontier, mysetdiff(domain_pot(old_frontier), j)); +end + + +%%%%%% +function new_frontier = rev_update(old_frontier, t, s, j, add, CPD, junk, pot_type, ns, cnodes, onodes) + +olddom = domain_pot(old_frontier); +assert(isequal(junk, olddom)); + +if add + % add: extend domain to include j by multiplying by 1 + newdom = myunion(olddom, j); + new_frontier = mk_initial_pot(pot_type, newdom, ns, cnodes, onodes); + new_frontier = multiply_by_pot(new_frontier, old_frontier); + %fprintf('t=%d, s=%d, add %d to %s to make %s\n', t, s, j, num2str(olddom), num2str(newdom)); +else + % remove: multiply in CPT and then marginalize out j + % parents of j are guaranteed to be in old_frontier, else couldn't have added j on fwds pass + old_frontier = multiply_by_pot(old_frontier, CPD); + newdom = mysetdiff(olddom, j); + new_frontier = marginalize_pot(old_frontier, newdom); + %newdom2 = domain_pot(new_frontier); + %fprintf('t=%d, s=%d, rem %d from %s to make %s\n', t, s, j, num2str(olddom), num2str(newdom2)); +end + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/frontier_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/frontier_inf_engine.m new file mode 100644 index 00000000..fd550579 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/frontier_inf_engine.m @@ -0,0 +1,121 @@ +function engine = frontier_inf_engine(bnet) +% FRONTIER_INF_ENGINE Inference engine for DBNs which which uses the frontier algorithm. +% engine = frontier_inf_engine(bnet) +% +% The frontier algorithm extends the forwards-backwards algorithm to DBNs in the obvious way, +% maintaining a joint distribution (frontier) over all the nodes in a time slice. +% When all the hidden nodes in the DBN are persistent (have children in the next time slice), +% its theoretical running time is often similar to that of the junction tree algorithm, +% although in practice, this algorithm seems to very slow (at least in matlab). +% However, it is extremely simple to describe and implement. +% +% Suppose there are n binary nodes per slice, so the frontier takes O(2^n) space. +% Each time step takes between O(n 2^{n+1}) and O(n 2^{2n}) operations, depending on the graph structure. +% The lower bound is achieved by a set of n independent chains, as in a factorial HMM. +% The upper bound is achieved by a set of n fully interconnected chains, as in an HMM. +% +% The factor of n arises because we need to multiply in each CPD from slice t+1. +% The second factor depends on the size of the frontier to which we add the new node. +% In an FHMM, once we have added X(i,t+1), we can marginalize out X(i,t) from the frontier, since +% no other nodes depend on it; hence the frontier never contains more than n+1 nodes. +% In a fully coupled HMM, we must leave X(i,t) in the frontier until all X(j,t+1) have been +% added; hence the frontier will contain 2*n nodes at its peak. +% +% For details, see +% "The Factored Frontier Algorithm for Approximate Inference in DBNs", +% Kevin Murphy and Yair Weiss, UAI 01. + +ns = bnet.node_sizes_slice; +onodes = bnet.observed; +ns(onodes) = 1; +ss = length(bnet.intra); + +[engine.ops, engine.fdom] = best_first_frontier_seq(ns, bnet.dag); +engine.ops1 = 1:ss; + +engine.fwdback = []; +engine.fwd_frontier = []; +engine.back_frontier = []; + +engine.fdom1 = cell(1,ss); +for s=1:ss + engine.fdom1{s} = 1:s; +end + +engine = class(engine, 'frontier_inf_engine', inf_engine(bnet)); + + +%%%%%%%%% + +function [ops, frontier_set] = best_first_frontier_seq(ns, dag) +% BEST_FIRST_FRONTIER_SEQ Do a greedy search for the sequence of additions/removals to the frontier. +% [ops, frontier_set] = best_first_frontier_seq(ns, dag) +% +% We maintain 3 sets: the frontier (F), the right set (R), and the left set (L). +% The invariant is that the nodes in R are d-separated from L given F. +% We start with slice 1 in F and slice 2 in R. +% The goal is to move slice 1 from F to L, and slice 2 from R to F, so as to minimize the size +% of the frontier at each step, where the size(F) = product of the node-sizes of nodes in F. +% A node may be removed (from F to L) if it has no children in R. +% A node may be added (from R to F) if its parents are in F. +% +% ns(i) = num. discrete values node i can take on (i=1..ss, where ss = slice size) +% dag is the (2*ss) x (2*ss) adjacency matrix for the 2-slice DBN. + +% Example: +% +% 4 9 +% ^ ^ +% | | +% 2 -> 7 +% ^ ^ +% | | +% 1 -> 6 +% | | +% v v +% 3 -> 8 +% | | +% v V +% 5 10 +% +% ops = -4, -5, 6, -1, 7, -2, 8, -3, 9, 10 + +ss = length(ns); +ns = [ns(:)' ns(:)']; +ops = zeros(1,ss); +L = []; F = 1:ss; R = (1:ss)+ss; +frontier_set = cell(1,2*ss); +for s=1:2*ss + remcost = inf*ones(1,2*ss); + %disp(['L: ' num2str(L) ', F: ' num2str(F) ', R: ' num2str(R)]); + maybe_removable = myintersect(F, 1:ss); + for n=maybe_removable(:)' + cs = children(dag, n); + if isempty(myintersect(cs, R)) + remcost(n) = prod(ns(mysetdiff(F, n))); + end + end + %remcost + if any(remcost < inf) + n = argmin(remcost); + ops(s) = -n; + L = myunion(L, n); + F = mysetdiff(F, n); + else + addcost = inf*ones(1,2*ss); + for n=R(:)' + ps = parents(dag, n); + if mysubset(ps, F) + addcost(n) = prod(ns(myunion(F, [ps n]))); + end + end + %addcost + assert(any(addcost < inf)); + n = argmin(addcost); + ops(s) = n; + R = mysetdiff(R, n); + F = myunion(F, n); + end + %fprintf('op at step %d = %d\n\n', s, ops(s)); + frontier_set{s} = F; +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/marginal_family.m new file mode 100644 index 00000000..4d28263b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/marginal_family.m @@ -0,0 +1,7 @@ +function marginal = marginal_family(engine, i, t) +% MARGINAL_FAMILY Compute the marginal on node i in slice t and its parents (frontier) +% marginal = marginal_family(engine, i, t) + +bnet = bnet_from_engine(engine); +fam = family(bnet.dag, i, t); +marginal = pot_to_marginal(normalize_pot(marginalize_pot(engine.fwdback{i,t}, fam))); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..898d1130 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/marginal_nodes.m @@ -0,0 +1,21 @@ +function marginal = marginal_nodes(engine, nodes, t) +% MARGINAL_NODES Compute the marginal on the specified query nodes (frontier) +% marginal = marginal_nodes(engine, nodes, t) +% +% 't' specifies the time slice of the earliest node in 'nodes'. +% 'nodes' cannot span more than 2 time slices. +% +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3, +% i.e., nodes 3 and 5 in the unrolled network, + +if nargin < 3, t = 1; end +assert(length(nodes)==1); +i = nodes(1); +bigpot = engine.fwdback{i,t}; +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +nodes = nodes + (t-1)*ss; +%if t > 1, nodes = nodes + ss; end +marginal = pot_to_marginal(marginalize_pot(bigpot, nodes)); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/set_fwdback.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/set_fwdback.m new file mode 100644 index 00000000..6752d827 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@frontier_inf_engine/set_fwdback.m @@ -0,0 +1,8 @@ +function engine = set_fwdback(engine, fb) +% SET_FWDBACK Set the field 'fwdback', which contains the frontiers after propagation +% engine = set_fwdback(engine, fb) +% +% This is used by frontier_fast_inf_engine/enter_evidence +% as a workaround for Matlab's annoying privacy control + +engine.fwdback = fb; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/CVS/Entries new file mode 100644 index 00000000..e1ab6d00 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/CVS/Entries @@ -0,0 +1,9 @@ +/enter_evidence.m/1.2/Sat Sep 17 17:00:30 2005// +/find_mpe.m/1.1.1.1/Thu Jun 20 00:18:24 2002// +/fwdback_twoslice.m/1.1/Sat Nov 26 01:24:09 2005// +/hmm_inf_engine.m/1.1.1.1/Thu Nov 14 20:05:36 2002// +/marginal_family.m/1.1.1.1/Thu Nov 14 20:05:36 2002// +/marginal_nodes.m/1.1.1.1/Thu Nov 14 20:03:28 2002// +/update_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +D/Old//// +D/private//// diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/CVS/Repository new file mode 100644 index 00000000..b7392efa --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@hmm_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/CVS/Entries new file mode 100644 index 00000000..528b5843 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/CVS/Entries @@ -0,0 +1,4 @@ +/dhmm_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/CVS/Repository new file mode 100644 index 00000000..f82b2bea --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@hmm_inf_engine/Old diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/dhmm_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/dhmm_inf_engine.m new file mode 100644 index 00000000..2b0c8810 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/dhmm_inf_engine.m @@ -0,0 +1,34 @@ +function engine = dhmm_inf_engine(bnet, onodes) +% DHMM_INF_ENGINE Inference engine for discrete DBNs which uses the forwards-backwards algorithm. +% engine = dhmm_inf_engine(bnet, onodes) +% +% 'onodes' specifies which nodes are observed; these must be leaves, and can be discrete or continuous. +% The remaining nodes are all hidden, and must be discrete. +% The DBN is converted to an HMM, with a single meganode, but which may have factored obs. + +ss = length(bnet.intra); +hnodes = mysetdiff(1:ss, onodes); +evidence = cell(ss, 2); +ns = bnet.node_sizes; +Q = prod(ns(hnodes)); +tmp = dpot_to_table(compute_joint_pot(bnet, hnodes, evidence)); +engine.startprob = reshape(tmp, Q, 1); +tmp = dpot_to_table(compute_joint_pot(bnet, [hnodes hnodes+ss], evidence)); +engine.transprob = mk_stochastic(reshape(tmp, Q, Q)); +engine.obsprob = cell(1, length(onodes)); +for i=1:length(onodes) + tmp = dpot_to_table(compute_joint_pot(bnet, [hnodes onodes(i)], evidence)); + O = ns(onodes(i)); + engine.obsprob{i} = mk_stochastic(reshape(tmp, Q, O)); +end + +% This is where we will store the results between enter_evidence and marginal_nodes +engine.gamma = []; +engine.xi = []; + +engine.onodes = onodes; +engine.hnodes = hnodes; +engine.maximize = []; + +engine = class(engine, 'dhmm_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/marginal_family.m new file mode 100644 index 00000000..681e1591 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/marginal_family.m @@ -0,0 +1,31 @@ +function marginal = marginal_family(engine, i, t, add_ev) +% MARGINAL_FAMILY Compute the marginal on the specified family (hmm) +% marginal = marginal_nodes(engine, i, t, add_ev) +% + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +if t==1 + fam = family(bnet.dag, i); + bigpot = engine.one_slice_marginal{t}; + nodes = fam; +else + fam = family(bnet.dag, i+ss); + if any(fam <= ss) % family spans 2 slices + bigpot = engine.two_slice_marginal{t-1}; % t-1 and t + nodes = fam + (t-2)*ss; + else + bigpot = engine.one_slice_marginal{t}; + nodes = fam-ss + (t-1)*ss; + end +end + +marginal = pot_to_marginal(marginalize_pot(bigpot, nodes, engine.maximize)); + +if add_ev + marginal = add_ev_to_dmarginal(marginal, engine.evidence, engine.node_sizes); +end + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/marginal_nodes.m new file mode 100644 index 00000000..4b8d6008 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/Old/marginal_nodes.m @@ -0,0 +1,30 @@ +function marginal = marginal_nodes(engine, nodes, t, add_ev) +% MARGINAL_NODES Compute the marginal on the specified query nodes (hmm) +% marginal = marginal_nodes(engine, nodes, t, add_ev) +% +% 't' specifies the time slice of the earliest node in 'nodes'. +% 'nodes' cannot span more than 2 time slices. +% +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3, +% i.e., nodes 3 and 5 in the unrolled network, + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +if all(nodes <= ss) + bigpot = engine.one_slice_marginal{t}; +else + bigpot = engine.two_slice_marginal{t}; +end + +nodes = nodes + (t-1)*ss; +marginal = pot_to_marginal(marginalize_pot(bigpot, nodes, engine.maximize)); + +if add_ev + marginal = add_ev_to_dmarginal(marginal, engine.evidence, engine.node_sizes); +end + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/enter_evidence.m new file mode 100644 index 00000000..8af97edf --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/enter_evidence.m @@ -0,0 +1,64 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (hmm) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product (not yet supported), else sum-product [0] +% filter - if 1, does filtering, else smoothing [0] +% oneslice - 1 means only compute marginals on nodes within a single slice [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; +oneslice = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + case 'oneslice', oneslice = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +[ss T] = size(evidence); +engine.maximize = maximize; +engine.evidence = evidence; +bnet = bnet_from_engine(engine); +engine.node_sizes = repmat(bnet.node_sizes_slice(:), [1 T]); + +obs_bitv = ~isemptycell(evidence(:)); +bitv = reshape(obs_bitv, ss, T); +for t=1:T + onodes = find(bitv(:,t)); + if ~isequal(onodes, bnet.observed(:)) + error(['dbn was created assuming observed nodes per slice were '... + num2str(bnet.observed(:)') ' but the evidence in slice ' num2str(t) ... + ' has observed nodes ' num2str(onodes(:)')]); + end +end + +obslik = mk_hmm_obs_lik_matrix(engine, evidence); + +%[alpha, beta, gamma, loglik, xi] = fwdback(engine.startprob, engine.transprob, obslik, ... +[alpha, beta, gamma, loglik, xi] = fwdback_twoslice(engine, engine.startprob,... + engine.transprob, obslik, ... + 'maximize', maximize, 'fwd_only', filter, ... + 'compute_xi', ~oneslice); + +engine.one_slice_marginal = gamma; % gamma(:,t) for t=1:T +if ~oneslice + Q = size(gamma,1); + engine.two_slice_marginal = reshape(xi, [Q*Q T-1]); % xi(:,t) for t=1:T-1 +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/find_mpe.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/find_mpe.m new file mode 100644 index 00000000..ba2cba74 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/find_mpe.m @@ -0,0 +1,16 @@ +function mpe = find_mpe(engine, evidence) +% FIND_MPE Find the most probable explanation (Viterbi) +% mpe = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% + +obslik = mk_hmm_obs_lik_matrix(engine, evidence); +path = viterbi_path(engine.startprob, engine.transprob, obslik); +bnet = bnet_from_engine(engine); +ns = bnet.node_sizes_slice; +ns(bnet.observed) = 1; +ass = ind2subv(ns, path); +mpe = num2cell(ass'); +mpe(bnet.observed,:) = evidence(bnet.observed,:); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/fwdback_twoslice.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/fwdback_twoslice.m new file mode 100644 index 00000000..0565e727 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/fwdback_twoslice.m @@ -0,0 +1,198 @@ +function [alpha, beta, gamma, loglik, xi, gamma2] = fwdback_twoslice(engine, init_state_distrib, transmat, obslik, varargin) +% FWDBACK Compute the posterior probs. in an HMM using the forwards backwards algo. +% +% [alpha, beta, gamma, loglik, xi, gamma2] = fwdback(init_state_distrib, transmat, obslik, ...) +% +% Notation: +% Y(t) = observation, Q(t) = hidden state, M(t) = mixture variable (for MOG outputs) +% A(t) = discrete input (action) (for POMDP models) +% +% INPUT: +% init_state_distrib(i) = Pr(Q(1) = i) +% transmat(i,j) = Pr(Q(t) = j | Q(t-1)=i) +% or transmat{a}(i,j) = Pr(Q(t) = j | Q(t-1)=i, A(t-1)=a) if there are discrete inputs +% obslik(i,t) = Pr(Y(t)| Q(t)=i) +% (Compute obslik using eval_pdf_xxx on your data sequence first.) +% +% Optional parameters may be passed as 'param_name', param_value pairs. +% Parameter names are shown below; default values in [] - if none, argument is mandatory. +% +% For HMMs with MOG outputs: if you want to compute gamma2, you must specify +% 'obslik2' - obslik(i,j,t) = Pr(Y(t)| Q(t)=i,M(t)=j) [] +% 'mixmat' - mixmat(i,j) = Pr(M(t) = j | Q(t)=i) [] +% +% For HMMs with discrete inputs: +% 'act' - act(t) = action performed at step t +% +% Optional arguments: +% 'fwd_only' - if 1, only do a forwards pass and set beta=[], gamma2=[] [0] +% 'scaled' - if 1, normalize alphas and betas to prevent underflow [1] +% 'maximize' - if 1, use max-product instead of sum-product [0] +% +% OUTPUTS: +% alpha(i,t) = p(Q(t)=i | y(1:t)) (or p(Q(t)=i, y(1:t)) if scaled=0) +% beta(i,t) = p(y(t+1:T) | Q(t)=i)*p(y(t+1:T)|y(1:t)) (or p(y(t+1:T) | Q(t)=i) if scaled=0) +% gamma(i,t) = p(Q(t)=i | y(1:T)) +% loglik = log p(y(1:T)) +% xi(i,j,t-1) = p(Q(t-1)=i, Q(t)=j | y(1:T)) +% gamma2(j,k,t) = p(Q(t)=j, M(t)=k | y(1:T)) (only for MOG outputs) +% +% If fwd_only = 1, these become +% alpha(i,t) = p(Q(t)=i | y(1:t)) +% beta = [] +% gamma(i,t) = p(Q(t)=i | y(1:t)) +% xi(i,j,t-1) = p(Q(t-1)=i, Q(t)=j | y(1:t)) +% gamma2 = [] +% +% Note: we only compute xi if it is requested as a return argument, since it can be very large. +% Similarly, we only compute gamma2 on request (and if using MOG outputs). +% +% Examples: +% +% [alpha, beta, gamma, loglik] = fwdback(pi, A, multinomial_prob(sequence, B)); +% +% [B, B2] = mixgauss_prob(data, mu, Sigma, mixmat); +% [alpha, beta, gamma, loglik, xi, gamma2] = fwdback(pi, A, B, 'obslik2', B2, 'mixmat', mixmat); + + +if nargout >= 5, compute_xi = 1; else compute_xi = 0; end +if nargout >= 6, compute_gamma2 = 1; else compute_gamma2 = 0; end + +[obslik2, mixmat, fwd_only, scaled, act, maximize, compute_xi, compute_gamma2] = process_options(varargin, 'obslik2', [], 'mixmat', [], 'fwd_only', 0, 'scaled', 1, 'act', [], 'maximize', 0, 'compute_xi', compute_xi, 'compute_gamma2', compute_gamma2); + + +[Q T] = size(obslik); + +if isempty(obslik2) + compute_gamma2 = 0; +end + +if isempty(act) + act = ones(1,T); + transmat = { transmat } ; +end + +scale = ones(1,T); + +% scale(t) = Pr(O(t) | O(1:t-1)) = 1/c(t) as defined by Rabiner (1989). +% Hence prod_t scale(t) = Pr(O(1)) Pr(O(2)|O(1)) Pr(O(3) | O(1:2)) = Pr(O(1), ... ,O(T)) +% or log P = sum_t log scale(t). +% Rabiner suggests multiplying beta(t) by scale(t), but we can instead +% normalise beta(t) - the constants will cancel when we compute gamma. + +loglik = 0; + +alpha = zeros(Q,T); +gamma = zeros(Q,T); +if compute_xi + xi = zeros(Q,Q,T-1); +else + xi = []; +end + + +%%%%%%%%% Forwards %%%%%%%%%% + +t = 1; +alpha(:,1) = init_state_distrib(:) .* obslik(:,t); +if scaled + %[alpha(:,t), scale(t)] = normaliseC(alpha(:,t)); + [alpha(:,t), scale(t)] = normalise(alpha(:,t)); +end +if scaled, assert(approxeq(sum(alpha(:,t)),1)), end +for t=2:T + %trans = transmat(:,:,act(t-1))'; + trans = transmat{act(t-1)}; + if maximize + m = max_mult(trans', alpha(:,t-1)); + %A = repmat(alpha(:,t-1), [1 Q]); + %m = max(trans .* A, [], 1); + else + m = trans' * alpha(:,t-1); + end + alpha(:,t) = m(:) .* obslik(:,t); + if scaled + %[alpha(:,t), scale(t)] = normaliseC(alpha(:,t)); + [alpha(:,t), scale(t)] = normalise(alpha(:,t)); + end + if compute_xi & fwd_only % useful for online EM + %xi(:,:,t-1) = normaliseC((alpha(:,t-1) * obslik(:,t)') .* trans); + xi(:,:,t-1) = normalise((alpha(:,t-1) * obslik(:,t)') .* trans); + end + if scaled, assert(approxeq(sum(alpha(:,t)),1)), end +end +if scaled + if any(scale==0) + loglik = -inf; + else + loglik = sum(log(scale)); + end +else + loglik = log(sum(alpha(:,T))); +end + +if fwd_only + gamma = alpha; + beta = []; + gamma2 = []; + return; +end + + +%%%%%%%%% Backwards %%%%%%%%%% + +beta = zeros(Q,T); +if compute_gamma2 + M = size(mixmat, 2); + gamma2 = zeros(Q,M,T); +else + gamma2 = []; +end + +beta(:,T) = ones(Q,1); +%gamma(:,T) = normaliseC(alpha(:,T) .* beta(:,T)); +gamma(:,T) = normalise(alpha(:,T) .* beta(:,T)); +t=T; +if compute_gamma2 + denom = obslik(:,t) + (obslik(:,t)==0); % replace 0s with 1s before dividing + gamma2(:,:,t) = obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M]) ./ repmat(denom, [1 M]); + %gamma2(:,:,t) = normaliseC(obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M])); % wrong! +end +for t=T-1:-1:1 + b = beta(:,t+1) .* obslik(:,t+1); + %trans = transmat(:,:,act(t)); + trans = transmat{act(t)}; + if maximize + B = repmat(b(:)', Q, 1); + beta(:,t) = max(trans .* B, [], 2); + else + beta(:,t) = trans * b; + end + if scaled + %beta(:,t) = normaliseC(beta(:,t)); + beta(:,t) = normalise(beta(:,t)); + end + %gamma(:,t) = normaliseC(alpha(:,t) .* beta(:,t)); + gamma(:,t) = normalise(alpha(:,t) .* beta(:,t)); + if compute_xi + %xi(:,:,t) = normaliseC((trans .* (alpha(:,t) * b'))); + xi(:,:,t) = normalise((trans .* (alpha(:,t) * b'))); + %xi(:,:,t) = (trans .* (alpha(:,t) * b')); + end + if compute_gamma2 + denom = obslik(:,t) + (obslik(:,t)==0); % replace 0s with 1s before dividing + gamma2(:,:,t) = obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M]) ./ repmat(denom, [1 M]); + %gamma2(:,:,t) = normaliseC(obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M])); + end +end + + +% We now explain the equation for gamma2 +% Let zt=y(1:t-1,t+1:T) be all observations except y(t) +% gamma2(Q,M,t) = P(Qt,Mt|yt,zt) = P(yt|Qt,Mt,zt) P(Qt,Mt|zt) / P(yt|zt) +% = P(yt|Qt,Mt) P(Mt|Qt) P(Qt|zt) / P(yt|zt) +% Now gamma(Q,t) = P(Qt|yt,zt) = P(yt|Qt) P(Qt|zt) / P(yt|zt) +% hence +% P(Qt,Mt|yt,zt) = P(yt|Qt,Mt) P(Mt|Qt) [P(Qt|yt,zt) P(yt|zt) / P(yt|Qt)] / P(yt|zt) +% = P(yt|Qt,Mt) P(Mt|Qt) P(Qt|yt,zt) / P(yt|Qt) +% \ No newline at end of file diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/hmm_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/hmm_inf_engine.m new file mode 100644 index 00000000..3de17b40 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/hmm_inf_engine.m @@ -0,0 +1,71 @@ +function engine = hmm_inf_engine(bnet, varargin) +% HMM_INF_ENGINE Inference engine for DBNs which uses the forwards-backwards algorithm. +% engine = hmm_inf_engine(bnet, ...) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - 1 means max-product, 0 means sum-product [0] +% +% The DBN is converted to an HMM with a single meganode, but the observed nodes remain factored. +% This can be faster than jtree if the num. hidden nodes is low, because of lower constant factors. +% +% All hidden nodes must be discrete. +% All observed nodes are assumed to be leaves, i.e., they cannot be parents of anything. +% The parents of each observed leaf are assumed to be a subset of the hidden nodes within the same slice. +% The only exception is if bnet is an AR-HMM, where the parents are assumed to be self in the +% previous slice (continuous), plus all the discrete nodes in the current slice. + +ss = bnet.nnodes_per_slice; + +engine.maximize = 0; +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', engine.maximize = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +% Stuff to do with speeding up marginal_family +[int, engine.persist, engine.transient] = compute_interface_nodes(bnet.intra, bnet.inter); +engine.persist_bitv = zeros(1, ss); +engine.persist_bitv(engine.persist) = 1; + + +ns = bnet.node_sizes(:); +ns(bnet.observed) = 1; +ns(bnet.observed+ss) = 1; +engine.eff_node_sizes = ns; + +for o=bnet.observed(:)' + %if bnet.equiv_class(o,1) ~= bnet.equiv_class(o,2) + % error(['observed node ' num2str(o) ' is not tied']) + %end + cs = children(bnet.dag, o); + if ~isempty(cs) + error(['observed node ' num2str(o) ' is not allowed children']) + end +end + +[engine.startprob, engine.transprob, engine.obsprob] = dbn_to_hmm(bnet); + +% This is where we will store the results between enter_evidence and marginal_nodes +engine.one_slice_marginal = []; +engine.two_slice_marginal = []; + +ss = length(bnet.intra); +engine.evidence = []; +engine.node_sizes = []; + +% avoid the need to do bnet_from_engine, which is slow +engine.slice_size = ss; +engine.parents = bnet.parents; + +engine = class(engine, 'hmm_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/marginal_family.m new file mode 100644 index 00000000..56b9fb6c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/marginal_family.m @@ -0,0 +1,35 @@ +function marginal = marginal_family(engine, i, t, add_ev) +% MARGINAL_FAMILY Compute the marginal on the specified family (hmm) +% marginal = marginal_family(engine, i, t, add_ev) + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end + +ns = engine.eff_node_sizes(:); +ss = engine.slice_size; + +if t==1 | ~engine.persist_bitv(i) + bigT = engine.one_slice_marginal(:,t); + ps = engine.parents{i}; + dom = [ps i] + (t-1)*ss; + bigdom = 1:ss; + bigsz = ns(bigdom); + bigdom = bigdom + (t-1)*ss; +else % some parents are in previous slice + bigT = engine.two_slice_marginal(:,t-1); % t-1 and t + ps = engine.parents{i+ss}; + dom = [ps i+ss] + (t-2)*ss; + bigdom = 1:(2*ss); % domain of xi(:,:,t) + bigsz = ns(bigdom); + bigdom = bigdom + (t-2)*ss; +end +marginal.domain = dom; + +marginal.T = marg_table(bigT, bigdom, bigsz, dom, engine.maximize); +marginal.mu = []; +marginal.Sigma = []; + +if add_ev + marginal = add_ev_to_dmarginal(marginal, engine.evidence, engine.node_sizes); +end + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..0a2bec4f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/marginal_nodes.m @@ -0,0 +1,29 @@ +function marginal = marginal_nodes(engine, nodes, t, add_ev) +% MARGINAL_NODES Compute the marginal on the specified query nodes (hmm) +% marginal = marginal_nodes(engine, nodes, t, add_ev) +% +% 'nodes' must be a single node. +% t is the time slice. + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end + +assert(length(nodes)==1) +ss = engine.slice_size; + +i = nodes(1); +bigT = engine.one_slice_marginal(:,t); +dom = i + (t-1)*ss; + +ns = engine.eff_node_sizes(:); +bigdom = 1:ss; +marginal.T = marg_table(bigT, bigdom + (t-1)*ss, ns(bigdom), dom, engine.maximize); + +marginal.domain = dom; +marginal.mu = []; +marginal.Sigma = []; + +if add_ev + marginal = add_ev_to_dmarginal(marginal, engine.evidence, engine.node_sizes); +end + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/CVS/Entries new file mode 100644 index 00000000..a35185ea --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/CVS/Entries @@ -0,0 +1,3 @@ +/mk_hmm_obs_lik_matrix.m/1.1.1.1/Sun May 4 21:42:26 2003// +/mk_hmm_obs_lik_vec.m/1.1.1.1/Thu Jan 23 18:50:10 2003// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/CVS/Repository new file mode 100644 index 00000000..20dfc6fd --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@hmm_inf_engine/private diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/mk_hmm_obs_lik_matrix.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/mk_hmm_obs_lik_matrix.m new file mode 100644 index 00000000..441f3c0a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/mk_hmm_obs_lik_matrix.m @@ -0,0 +1,30 @@ +function obslik = mk_hmm_obs_lik_matrix(engine, evidence) + +T = size(evidence,2); +Q = length(engine.startprob); +obslik = ones(Q, T); +bnet = bnet_from_engine(engine); +% P(o1,o2| Q1,Q2) = P(o1|Q1,Q2) * P(o2|Q1,Q2) +onodes = bnet.observed; +for i=1:length(onodes) + data = cell2num(evidence(onodes(i),:)); + if bnet.auto_regressive(onodes(i)) + params = engine.obsprob{i}; + mu = params.big_mu; + Sigma = params.big_Sigma, + W = params.big_W; + mu0 = params.big_mu0; + Sigma0 = params.big_Sigma0; + %obslik_i = mk_arhmm_obs_lik(data, mu, Sigma, W, mu0, Sigma0 + obslik_i = clg_prob(data(:,1:T-1), data(:,2:T), mu, Sigma, W); + obslik_i = [mixgauss_prob(data(:,1), mu0, Sigma0) obslik_i]; + elseif myismember(onodes(i), bnet.dnodes) + %obslik_i = eval_pdf_cond_multinomial(data, engine.obsprob{i}.big_CPT); + obslik_i = multinomial_prob(data, engine.obsprob{i}.big_CPT); + else + %obslik_i = eval_pdf_cond_gauss(data, engine.obsprob{i}.big_mu, engine.obsprob{i}.big_Sigma); + obslik_i = mixgauss_prob(data, engine.obsprob{i}.big_mu, engine.obsprob{i}.big_Sigma); + end + obslik = obslik .* obslik_i; +end + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/mk_hmm_obs_lik_vec.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/mk_hmm_obs_lik_vec.m new file mode 100644 index 00000000..16d30aec --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/private/mk_hmm_obs_lik_vec.m @@ -0,0 +1,52 @@ +function obslik = mk_hmm_obs_lik_vec(engine, evidence) + +% P(o1,o2| h) = P(o1|h) * P(o2|h) where h = Q1,Q2,... + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +onodes = bnet.observed; +hnodes = mysetdiff(1:ss, onodes); +ns = bnet.node_sizes(:); +ns(onodes) = 1; + +Q = length(engine.startprob); +obslik = ones(Q, 1); + +for i=1:length(onodes) + o = onodes(i); + %data = cell2num(evidence(o,1)); + data = evidence{o,1}; + if myismember(o, bnet.dnodes) + obslik_i = eval_pdf_cond_multinomial(data, engine.obsprob{i}.CPT); + else + if bnet.auto_regressive(o) + error('can''t handle AR nodes') + end + %% calling mk_ghmm_obs_lik, which calls gaussian_prob, is slow, so we inline it + %% and use the pre-computed inverse matrix + %obslik_i = mk_ghmm_obs_lik(data, engine.obsprob{i}.mu, engine.obsprob{i}.Sigma); + x = data(:); + m = engine.obsprob{i}.mu; + Qi = size(m, 2); + obslik_i = size(Qi, 1); + invC = engine.obsprob{i}.inv_Sigma; + denom = engine.obsprob{i}.denom; + for j=1:Qi + numer = exp(-0.5 * (x-m(:,j))' * invC(:,:,j) * (x-m(:,j))); + obslik_i(j) = numer / denom(j); + end + end + % convert P(o|ps) into P(o|h) by multiplying onto a (h,o) potential of all 1s + ps = bnet.parents{o}; + dom = [ps o]; + obspot_i = dpot(dom, ns(dom), obslik_i); + dom = [hnodes o]; + obspot = dpot(dom, ns(dom)); + obspot = multiply_by_pot(obspot, obspot_i); + % compute p(oi|h) * p(oj|h) + S = struct(obspot); + obslik = obslik .* S.T(:); +end + + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/update_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/update_engine.m new file mode 100644 index 00000000..e6cd1f79 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@hmm_inf_engine/update_engine.m @@ -0,0 +1,8 @@ +function engine = update_engine(engine, newCPDs) +% UPDATE_ENGINE Update the engine to take into account the new parameters (hmm) +% engine = update_engine(engine, newCPDs) + +%engine.inf_engine.bnet.CPD = newCPDs; +engine.inf_engine = update_engine(engine.inf_engine, newCPDs); +[engine.startprob, engine.transprob, engine.obsprob] = dbn_to_hmm(bnet_from_engine(engine)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/CVS/Entries new file mode 100644 index 00000000..3baa09c7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/CVS/Entries @@ -0,0 +1,6 @@ +/enter_soft_evidence1.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_soft_evidence2.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_soft_evidence3.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_soft_evidence4.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/CVS/Repository new file mode 100644 index 00000000..a9b61e36 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence1.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence1.m new file mode 100644 index 00000000..8f82b57e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence1.m @@ -0,0 +1,119 @@ +function [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (jtree_dbn) +% [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +[ss T] = size(CPDpot); +Q = length(engine.jtree_struct.cliques); +clpot = cell(Q,T); % clpot{t} contains evidence from slices (t-1, t) +seppot = cell(Q,Q,T); +ll = zeros(1,Q); +logscale = zeros(1,T); +bnet = bnet_from_engine(engine); + +% Forwards pass. +% Compute distribution on clq C, +% where C is the out interface to (t-1,t). +% Then pass this to clq D, where D is the in inferface to (t+1,t). +% Then propagate from D to later slices. + +C = engine.out_clq; +assert(C==engine.jtree_struct.root_clq); +D = engine.in_clq; +slice1 = 1:ss; +slice2 = slice1 + ss; +for t=2:T + if t==2 + clqs = engine.jtree_struct.clq_ass_to_node([slice1 slice2]); + pots = CPDpot(:,t-1:t); + else + %clqs = [D; engine.clq_ass_to_node(:,2)]; + clqs = [D engine.jtree_struct.clq_ass_to_node(slice2)]; + phiC = set_domain_pot(phiC, engine.interface); % shift back to slice 1 + pots = [ {phiC}; CPDpot(:,t)]; % CPDpot domains are always slice 2 + end + [clpot(:,t), seppot(:,:,t)] = init_pot(engine.jtree_struct.cliques, clqs, pots, pot_type, ... + find(observed(:,t-1:t)), bnet.node_sizes(:), bnet.cnodes); + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(C); + + phiC = marginalize_pot(clpot{C,t}, engine.interface+ss, engine.maximize); +end + + + +% Backwards pass. +% Pass evidence from clq C to clq D, +% where C is the in interface to (t,t+1) and D is the out inferface to (t-1,t) +% Then propagate evidence from D to earlier slices. +C = engine.in_clq; +D = engine.out_clq; +for t=T:-1:2 + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(C); + + if t >= 3 + phiC = marginalize_pot(clpot{C,t}, engine.interface, engine.maximize); + phiC = set_domain_pot(phiC, engine.interface+ss); % shift forward to slice 2 + phiD = marginalize_pot(clpot{D,t-1}, engine.interface+ss, engine.maximize); + ratio = divide_by_pot(phiC, phiD); + clpot{D,t-1} = multiply_by_pot(clpot{D,t-1}, ratio); + end +end + +loglik = sum(logscale); + + +%%%%%%% +function [clpot, seppot] = init_pot(cliques, clqs, pots, pot_type, onodes, ns, cnodes); + +% Set the clique potentials to all 1s +C = length(cliques); +clpot = cell(1,C); +for i=1:C + clpot{i} = mk_initial_pot(pot_type, cliques{i}, ns, cnodes, onodes); +end + +% Multiply on specified potentials +for i=1:length(clqs) + c = clqs(i); + clpot{c} = multiply_by_pot(clpot{c}, pots{i}); +end + +seppot = cell(C,C); % implicitely initialized to 1 + + +%%%% +function [clpot, seppot] = collect_evidence(clpot, seppot, maximize, postorder, postorder_parents,... + separator) +for n=postorder %postorder(1:end-1) + for p=postorder_parents{n} + %clpot{p} = divide_by_pot(clpot{n}, seppot{p,n}); % dividing by 1 is redundant + seppot{p,n} = marginalize_pot(clpot{n}, separator{p,n}, maximize); + clpot{p} = multiply_by_pot(clpot{p}, seppot{p,n}); + end +end + + +%%%% +function [clpot, seppot] = distribute_evidence(clpot, seppot, maximize, preorder, preorder_children,... + separator) +for n=preorder + for c=preorder_children{n} + clpot{c} = divide_by_pot(clpot{c}, seppot{n,c}); + seppot{n,c} = marginalize_pot(clpot{n}, separator{n,c}, maximize); + clpot{c} = multiply_by_pot(clpot{c}, seppot{n,c}); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence2.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence2.m new file mode 100644 index 00000000..0adfef0d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence2.m @@ -0,0 +1,143 @@ +function [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (jtree_dbn) +% [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +[ss T] = size(CPDpot); +Q = length(engine.jtree_struct.cliques); +clpot = cell(Q,T); % clpot{t} contains evidence from slices (t-1, t) +seppot = cell(Q,Q,T); +ll = zeros(1,Q); +logscale = ones(1,T); % log(logscale(1)) = 0 +bnet = bnet_from_engine(engine); + +slice1 = 1:ss; +slice2 = slice1+ss; + +% calibrate each 2-slice jtree in isolation +for t=2:T + if t==2 + clqs = engine.jtree_struct.clq_ass_to_node([slice1 slice2]); + pots = CPDpot(:,t-1:t); + else + clqs = engine.jtree_struct.clq_ass_to_node(slice2); + pots = CPDpot(:,t); % CPDpot domains are always slice 2 + end + [clpot(:,t), sepot(:,:,t)] = init_pot(engine.jtree_struct.cliques, clqs, pots, pot_type, ... + find(observed(:,t-1:t)), bnet.node_sizes(:), bnet.cnodes); + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); +end + +% Forwards pass. +% Compute distribution on clq C, +% where C is the out interface to (t-1,t). +% Then pass this to clq D, where D is the in inferface to (t+1,t). +% Then propagate from D to later slices. + +C = engine.out_clq; +D = engine.in_clq; +for t=2:T-1 + phiC = marginalize_pot(clpot{C,t}, engine.interface+ss, engine.maximize); + phiC = set_domain_pot(phiC, engine.interface); % shift back to slice 1 + phiD = marginalize_pot(clpot{D,t+1}, engine.interface, engine.maximize); + ratio = divide_by_pot(phiC, phiD); + clpot{D,t+1} = multiply_by_pot(clpot{D,t+1}, ratio); + + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(1); +end + +% Backwards pass. +% Pass evidence from clq C to clq D, +% where C is the in interface to (t,t+1) and D is the out inferface to (t-1,t) +% Then propagate evidence from D to earlier slices. +C = engine.in_clq; +D = engine.out_clq; +for t=T:-1:2 + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(1); + + if t >= 3 + phiC = marginalize_pot(clpot{C,t}, engine.interface, engine.maximize); + phiC = set_domain_pot(phiC, engine.interface+ss); % shift forward to slice 2 + phiD = marginalize_pot(clpot{D,t-1}, engine.interface+ss, engine.maximize); + ratio = divide_by_pot(phiC, phiD); + clpot{D,t-1} = multiply_by_pot(clpot{D,t-1}, ratio); + end +end + +loglik = sum(logscale); + +%%%%%%%%%% + +function [clpot, seppot] = calibrate(engine, clpot, seppot) + + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); + + +%%%%%%% +function [clpot, seppot] = init_pot(cliques, clqs, pots, pot_type, onodes, ns, cnodes); + +% Set the clique potentials to all 1s +C = length(cliques); +clpot = cell(1,C); +for i=1:C + clpot{i} = mk_initial_pot(pot_type, cliques{i}, ns, cnodes, onodes); +end + +% Multiply on specified potentials +for i=1:length(clqs) + c = clqs(i); + clpot{c} = multiply_by_pot(clpot{c}, pots{i}); +end + +seppot = cell(C,C); % implicitely initialized to 1 + + +%%%% +function [clpot, seppot] = collect_evidence(clpot, seppot, maximize, postorder, postorder_parents,... + separator) +for n=postorder %postorder(1:end-1) + for p=postorder_parents{n} + %clpot{p} = divide_by_pot(clpot{n}, seppot{p,n}); % dividing by 1 is redundant + seppot{p,n} = marginalize_pot(clpot{n}, separator{p,n}, maximize); + clpot{p} = multiply_by_pot(clpot{p}, seppot{p,n}); + end +end + + +%%%% +function [clpot, seppot] = distribute_evidence(clpot, seppot, maximize, preorder, preorder_children,... + separator) +for n=preorder + for c=preorder_children{n} + clpot{c} = divide_by_pot(clpot{c}, seppot{n,c}); + seppot{n,c} = marginalize_pot(clpot{n}, separator{n,c}, maximize); + clpot{c} = multiply_by_pot(clpot{c}, seppot{n,c}); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence3.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence3.m new file mode 100644 index 00000000..c1189460 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence3.m @@ -0,0 +1,125 @@ +function [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (jtree_dbn) +% [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +[ss T] = size(CPDpot); +Q = length(engine.jtree_struct.cliques); +clpot = cell(Q,T); % clpot{t} contains evidence from slices (t-1, t) +seppot = cell(Q,Q,T); +ll = zeros(1,Q); +logscale = zeros(1,T); +bnet = bnet_from_engine(engine); + +% Forwards pass. +% Compute distribution on clq C, +% where C is the out interface to (t-1,t). +% Then pass this to clq D, where D is the in inferface to (t+1,t). +% Then propagate from D to later slices. + +C = engine.out_clq; +assert(C==engine.jtree_struct.root_clq); +D = engine.in_clq; +slice1 = 1:ss; +slice2 = slice1 + ss; +Ntransient = length(engine.transient); +trans = cell(Ntransient,1); +for t=2:T + if t==2 + clqs = engine.jtree_struct.clq_ass_to_node([slice1 slice2]); + pots = CPDpot(:,t-1:t); + else + %clqs = [D; engine.clq_ass_to_node(:,2)]; + clqs = [D engine.jtree_struct.clq_ass_to_node([engine.transient slice2])]; + phiC = set_domain_pot(phiC, engine.interface); % shift back to slice 1 + for i=1:Ntransient + trans{i} = CPDpot{engine.transient(i), t-1}; + trans{i} = set_domain_pot(trans{i}, domain_pot(trans{i})-ss); % shift back to slice 1 + end + pots = [ {phiC}; trans; CPDpot(:,t)]; + end + [clpot(:,t), seppot(:,:,t)] = init_pot(engine.jtree_struct.cliques, clqs, pots, pot_type, ... + find(observed(:,t-1:t)), bnet.node_sizes(:), bnet.cnodes); + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(C); + + phiC = marginalize_pot(clpot{C,t}, engine.interface+ss, engine.maximize); +end + + + +% Backwards pass. +% Pass evidence from clq C to clq D, +% where C is the in interface to (t,t+1) and D is the out inferface to (t-1,t) +% Then propagate evidence from D to earlier slices. +C = engine.in_clq; +D = engine.out_clq; +for t=T:-1:2 + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(C); + + if t >= 3 + phiC = marginalize_pot(clpot{C,t}, engine.interface, engine.maximize); + phiC = set_domain_pot(phiC, engine.interface+ss); % shift forward to slice 2 + phiD = marginalize_pot(clpot{D,t-1}, engine.interface+ss, engine.maximize); + ratio = divide_by_pot(phiC, phiD); + clpot{D,t-1} = multiply_by_pot(clpot{D,t-1}, ratio); + end +end + +loglik = sum(logscale); + + +%%%%%%% +function [clpot, seppot] = init_pot(cliques, clqs, pots, pot_type, onodes, ns, cnodes); + +% Set the clique potentials to all 1s +C = length(cliques); +clpot = cell(1,C); +for i=1:C + clpot{i} = mk_initial_pot(pot_type, cliques{i}, ns, cnodes, onodes); +end + +% Multiply on specified potentials +for i=1:length(clqs) + c = clqs(i); + clpot{c} = multiply_by_pot(clpot{c}, pots{i}); +end + +seppot = cell(C,C); % implicitely initialized to 1 + + +%%%% +function [clpot, seppot] = collect_evidence(clpot, seppot, maximize, postorder, postorder_parents,... + separator) +for n=postorder %postorder(1:end-1) + for p=postorder_parents{n} + %clpot{p} = divide_by_pot(clpot{n}, seppot{p,n}); % dividing by 1 is redundant + seppot{p,n} = marginalize_pot(clpot{n}, separator{p,n}, maximize); + clpot{p} = multiply_by_pot(clpot{p}, seppot{p,n}); + end +end + + +%%%% +function [clpot, seppot] = distribute_evidence(clpot, seppot, maximize, preorder, preorder_children,... + separator) +for n=preorder + for c=preorder_children{n} + clpot{c} = divide_by_pot(clpot{c}, seppot{n,c}); + seppot{n,c} = marginalize_pot(clpot{n}, separator{n,c}, maximize); + clpot{c} = multiply_by_pot(clpot{c}, seppot{n,c}); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence4.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence4.m new file mode 100644 index 00000000..a4ec90c6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/enter_soft_evidence4.m @@ -0,0 +1,149 @@ +function [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (jtree_dbn) +% [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +[ss T] = size(CPDpot); +Q = length(engine.jtree_struct.cliques); +clpot = cell(Q,T); % clpot{t} contains evidence from slices (t-1, t) +seppot = cell(Q,Q,T); +ll = zeros(1,Q); +logscale = ones(1,T); % log(logscale(1)) = 0 +bnet = bnet_from_engine(engine); + +slice1 = 1:ss; +slice2 = slice1+ss; +Ntransient = length(engine.transient); +trans = cell(Ntransient,1); + +% calibrate each 2-slice jtree in isolation +for t=2:T + if t==2 + clqs = engine.jtree_struct.clq_ass_to_node([slice1 slice2]); + pots = CPDpot(:,t-1:t); + else + clqs = engine.jtree_struct.clq_ass_to_node([engine.transient slice2]); + for i=1:Ntransient + trans{i} = CPDpot{engine.transient(i), t-1}; + trans{i} = set_domain_pot(trans{i}, domain_pot(trans{i})-ss); % shift back to slice 1 + end + pots = [ trans; CPDpot(:,t)]; + end + [clpot(:,t), sepot(:,:,t)] = init_pot(engine.jtree_struct.cliques, clqs, pots, pot_type, ... + find(observed(:,t-1:t)), bnet.node_sizes(:), bnet.cnodes); + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); +end + +% Forwards pass. +% Compute distribution on clq C, +% where C is the out interface to (t-1,t). +% Then pass this to clq D, where D is the in inferface to (t+1,t). +% Then propagate from D to later slices. + +C = engine.out_clq; +D = engine.in_clq; +for t=2:T-1 + phiC = marginalize_pot(clpot{C,t}, engine.interface+ss, engine.maximize); + phiC = set_domain_pot(phiC, engine.interface); % shift back to slice 1 + phiD = marginalize_pot(clpot{D,t+1}, engine.interface, engine.maximize); + ratio = divide_by_pot(phiC, phiD); + clpot{D,t+1} = multiply_by_pot(clpot{D,t+1}, ratio); + + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(1); +end + +% Backwards pass. +% Pass evidence from clq C to clq D, +% where C is the in interface to (t,t+1) and D is the out inferface to (t-1,t) +% Then propagate evidence from D to earlier slices. +C = engine.in_clq; +D = engine.out_clq; +for t=T:-1:2 + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(1); + + if t >= 3 + phiC = marginalize_pot(clpot{C,t}, engine.interface, engine.maximize); + phiC = set_domain_pot(phiC, engine.interface+ss); % shift forward to slice 2 + phiD = marginalize_pot(clpot{D,t-1}, engine.interface+ss, engine.maximize); + ratio = divide_by_pot(phiC, phiD); + clpot{D,t-1} = multiply_by_pot(clpot{D,t-1}, ratio); + end +end + +loglik = sum(logscale); + +%%%%%%%%%% + +function [clpot, seppot] = calibrate(engine, clpot, seppot) + + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); + + +%%%%%%% +function [clpot, seppot] = init_pot(cliques, clqs, pots, pot_type, onodes, ns, cnodes); + +% Set the clique potentials to all 1s +C = length(cliques); +clpot = cell(1,C); +for i=1:C + clpot{i} = mk_initial_pot(pot_type, cliques{i}, ns, cnodes, onodes); +end + +% Multiply on specified potentials +for i=1:length(clqs) + c = clqs(i); + clpot{c} = multiply_by_pot(clpot{c}, pots{i}); +end + +seppot = cell(C,C); % implicitely initialized to 1 + + +%%%% +function [clpot, seppot] = collect_evidence(clpot, seppot, maximize, postorder, postorder_parents,... + separator) +for n=postorder %postorder(1:end-1) + for p=postorder_parents{n} + %clpot{p} = divide_by_pot(clpot{n}, seppot{p,n}); % dividing by 1 is redundant + seppot{p,n} = marginalize_pot(clpot{n}, separator{p,n}, maximize); + clpot{p} = multiply_by_pot(clpot{p}, seppot{p,n}); + end +end + + +%%%% +function [clpot, seppot] = distribute_evidence(clpot, seppot, maximize, preorder, preorder_children,... + separator) +for n=preorder + for c=preorder_children{n} + clpot{c} = divide_by_pot(clpot{c}, seppot{n,c}); + seppot{n,c} = marginalize_pot(clpot{n}, separator{n,c}, maximize); + clpot{c} = multiply_by_pot(clpot{c}, seppot{n,c}); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/marginal_nodes.m new file mode 100644 index 00000000..fe1d38f6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Broken/marginal_nodes.m @@ -0,0 +1,51 @@ +function marginal = marginal_nodes(engine, nodes, t, fam) +% MARGINAL_NODES Compute the marginal on the specified query nodes (bk) +% +% marginal = marginal_nodes(engine, i, t) +% returns Pr(X(i,t) | Y(1:T)), where X(i,t) is the i'th node in the t'th slice. +% +% marginal = marginal_nodes(engine, query, t) +% returns Pr(X(query(1),t), ... X(query(end),t) | Y(1:T)), +% where 't' specifies the time slice of the earliest node in the query. +% 'query' cannot span more than 2 time slices. +% +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3. + +if nargin < 3, t = 1; end +if nargin < 4, fam = 0; else fam = 1; end + + +% clpot{t} contains slice t-1 and t +% Example +% clpot #: 1 2 3 +% slices: 1 1,2 2,3 +% For filtering, we must take care not to take future evidence into account. +% For smoothing, clpot{1} does not exist. + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); + +if t < engine.T + slice = t+1; + nodes2 = nodes; +else % earliest t is T, so all nodes fit in one slice + slice = engine.T; + nodes2 = nodes + ss; +end + +c = clq_containing_nodes(engine.jtree_engine, nodes2, fam); +assert(c >= 1); + +%disp(['computing marginal on ' num2str(nodes) ' t = ' num2str(t)]); +%disp(['using ' num2str(nodes2) ' slice = ' num2str(slice) 'clq = ' num2str(c)]); + +bigpot = engine.clpot{c, slice}; + +pot = marginalize_pot(bigpot, nodes2, engine.maximize); +marginal = pot_to_marginal(pot); + +% we convert the domain to the unrolled numbering system +% so that update_ess extracts the right evidence. +marginal.domain = nodes+(t-1)*ss; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Entries new file mode 100644 index 00000000..aea2a602 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Entries @@ -0,0 +1,6 @@ +/enter_evidence.m/1.1.1.1/Sat Jan 11 18:41:30 2003// +/enter_soft_evidence.m/1.1.1.1/Thu Feb 19 01:12:08 2004// +/jtree_dbn_inf_engine.m/1.1.1.1/Thu Nov 14 16:32:00 2002// +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Fri Nov 22 23:51:58 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Entries.Log new file mode 100644 index 00000000..2fb9e4b6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Broken//// +A D/Old//// diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Repository new file mode 100644 index 00000000..590182fa --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@jtree_dbn_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/CVS/Entries new file mode 100644 index 00000000..0bc2d941 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/CVS/Entries @@ -0,0 +1,6 @@ +/enter_soft_evidence_nonint.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_soft_evidence_trans.m/1.1.1.1/Wed May 29 15:59:56 2002// +/jtree_dbn_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +/jtree_dbn_inf_engine1.m/1.1.1.1/Wed May 29 15:59:56 2002// +/jtree_dbn_inf_engine2.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/CVS/Repository new file mode 100644 index 00000000..0b90d866 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/enter_soft_evidence_nonint.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/enter_soft_evidence_nonint.m new file mode 100644 index 00000000..72641580 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/enter_soft_evidence_nonint.m @@ -0,0 +1,135 @@ +function [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (jtree_dbn) +% [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +[ss T] = size(CPDpot); +Q = length(engine.jtree_struct.cliques); +clpot = cell(Q,T); % clpot{t} contains evidence from slices (t-1, t) +seppot = cell(Q,Q,T); +ll = zeros(1,Q); +logscale = zeros(1,T); +bnet = bnet_from_engine(engine); + +% Forwards pass. +% Compute distribution on clq C, +% where C is the out interface to (t-1,t). +% Then pass this to clq D, where D is the in inferface to (t+1,t). +% Then propagate from D to later slices. + +C = engine.out_clq; +assert(C==engine.jtree_struct.root_clq); +D = engine.in_clq; +slice1 = 1:ss; +slice2 = slice1 + ss; +Nnonint = length(engine.nonint); +nonint = cell(Nnonint, 1); +for t=1:T + if t==1 + pots = [CPDpot(:,1); CPDpot(engine.interface, 2)]; + clqs = engine.jtree_struct.clq_ass_to_node([slice1 engine.interface+ss]); + obs = find(observed(:,1:2)); + elseif t==T + clqs = [D engine.jtree_struct.clq_ass_to_node(engine.nonint)]; + phiC = set_domain_pot(phiC, engine.interface); % shift back to slice 1 + for i=1:Nnonint + nonint{i} = CPDpot{engine.nonint(i), t}; + nonint{i} = set_domain_pot(nonint{i}, domain_pot(nonint{i})-ss); % shift back to slice 1 + end + pots = [ {phiC}; nonint]; + obs = find(observed(:,T)); + else + clqs = [D engine.jtree_struct.clq_ass_to_node([engine.nonint engine.interface+ss])]; + phiC = set_domain_pot(phiC, engine.interface); % shift back to slice 1 + for i=1:Nnonint + nonint{i} = CPDpot{engine.nonint(i), t}; + nonint{i} = set_domain_pot(nonint{i}, domain_pot(nonint{i})-ss); % shift back to slice 1 + end + pots = [ {phiC}; nonint; CPDpot(engine.interface, t+1)]; + obs = find(observed(:,t:t+1)); + end + [clpot(:,t), seppot(:,:,t)] = init_pot(engine.jtree_struct.cliques, clqs, pots, pot_type, ... + obs, bnet.node_sizes(:), bnet.cnodes); + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(C); + + phiC = marginalize_pot(clpot{C,t}, engine.interface+ss, engine.maximize); +end + + + +% Backwards pass. +% Pass evidence from clq C to clq D, +% where C is the in interface to (t,t+1) and D is the out inferface to (t-1,t) +% Then propagate evidence from D to earlier slices. +C = engine.in_clq; +D = engine.out_clq; +for t=T:-1:1 + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + %logscale(t) = ll(C); + + if t >= 2 + phiC = marginalize_pot(clpot{C,t}, engine.interface, engine.maximize); + phiC = set_domain_pot(phiC, engine.interface+ss); % shift forward to slice 2 + phiD = marginalize_pot(clpot{D,t-1}, engine.interface+ss, engine.maximize); + ratio = divide_by_pot(phiC, phiD); + clpot{D,t-1} = multiply_by_pot(clpot{D,t-1}, ratio); + end +end + +loglik = sum(logscale); + + +%%%%%%% +function [clpot, seppot] = init_pot(cliques, clqs, pots, pot_type, onodes, ns, cnodes); + +% Set the clique potentials to all 1s +C = length(cliques); +clpot = cell(1,C); +for i=1:C + clpot{i} = mk_initial_pot(pot_type, cliques{i}, ns, cnodes, onodes); +end + +% Multiply on specified potentials +for i=1:length(clqs) + c = clqs(i); + clpot{c} = multiply_by_pot(clpot{c}, pots{i}); +end + +seppot = cell(C,C); % implicitely initialized to 1 + + +%%%% +function [clpot, seppot] = collect_evidence(clpot, seppot, maximize, postorder, postorder_parents,... + separator) +for n=postorder %postorder(1:end-1) + for p=postorder_parents{n} + %clpot{p} = divide_by_pot(clpot{n}, seppot{p,n}); % dividing by 1 is redundant + seppot{p,n} = marginalize_pot(clpot{n}, separator{p,n}, maximize); + clpot{p} = multiply_by_pot(clpot{p}, seppot{p,n}); + end +end + + +%%%% +function [clpot, seppot] = distribute_evidence(clpot, seppot, maximize, preorder, preorder_children,... + separator) +for n=preorder + for c=preorder_children{n} + clpot{c} = divide_by_pot(clpot{c}, seppot{n,c}); + seppot{n,c} = marginalize_pot(clpot{n}, separator{n,c}, maximize); + clpot{c} = multiply_by_pot(clpot{c}, seppot{n,c}); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/enter_soft_evidence_trans.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/enter_soft_evidence_trans.m new file mode 100644 index 00000000..b9c85b80 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/enter_soft_evidence_trans.m @@ -0,0 +1,135 @@ +function [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (jtree_dbn) +% [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type, filter) + +[ss T] = size(CPDpot); +Q = length(engine.jtree_struct.cliques); +clpot = cell(Q,T); % clpot{t} contains evidence from slices (t-1, t) +seppot = cell(Q,Q,T); +ll = zeros(1,Q); +logscale = zeros(1,T); +bnet = bnet_from_engine(engine); + +% Forwards pass. +% Compute distribution on clq C, +% where C is the out interface to (t-1,t). +% Then pass this to clq D, where D is the in inferface to (t+1,t). +% Then propagate from D to later slices. + +C = engine.out_clq; +assert(C==engine.jtree_struct.root_clq); +D = engine.in_clq; +slice1 = 1:ss; +slice2 = slice1 + ss; +Ntransient = length(engine.transient); +trans = cell(Ntransient,1); +for t=1:T + if t==1 + pots = [CPDpot(:,1); CPDpot(engine.persist, 2)]; + clqs = engine.jtree_struct.clq_ass_to_node([slice1 engine.persist+ss]); + obs = find(observed(:,1:2)); + elseif t==T + clqs = [D engine.jtree_struct.clq_ass_to_node(engine.transient)]; + phiC = set_domain_pot(phiC, engine.interface); % shift back to slice 1 + for i=1:Ntransient + trans{i} = CPDpot{engine.transient(i), t}; + trans{i} = set_domain_pot(trans{i}, domain_pot(trans{i})-ss); % shift back to slice 1 + end + pots = [ {phiC}; trans]; + obs = find(observed(:,T)); + else + clqs = [D engine.jtree_struct.clq_ass_to_node([engine.transient engine.persist+ss])]; + phiC = set_domain_pot(phiC, engine.interface); % shift back to slice 1 + for i=1:Ntransient + trans{i} = CPDpot{engine.transient(i), t}; + trans{i} = set_domain_pot(trans{i}, domain_pot(trans{i})-ss); % shift back to slice 1 + end + pots = [ {phiC}; trans; CPDpot(engine.persist, t+1)]; + obs = find(observed(:,t:t+1)); + end + [clpot(:,t), seppot(:,:,t)] = init_pot(engine.jtree_struct.cliques, clqs, pots, pot_type, ... + obs, bnet.node_sizes(:), bnet.cnodes); + [clpot(:,t), seppot(:,:,t)] = collect_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.postorder, ... + engine.jtree_struct.postorder_parents,... + engine.jtree_struct.separator); + + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(C); + + phiC = marginalize_pot(clpot{C,t}, engine.interface+ss, engine.maximize); +end + + + +% Backwards pass. +% Pass evidence from clq C to clq D, +% where C is the in interface to (t,t+1) and D is the out inferface to (t-1,t) +% Then propagate evidence from D to earlier slices. +C = engine.in_clq; +D = engine.out_clq; +for t=T:-1:1 + [clpot(:,t), seppot(:,:,t)] = distribute_evidence(clpot(:,t), seppot(:,:,t), engine.maximize, ... + engine.jtree_struct.preorder, ... + engine.jtree_struct.preorder_children, ... + engine.jtree_struct.separator); + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + %logscale(t) = ll(C); + + if t >= 2 + phiC = marginalize_pot(clpot{C,t}, engine.interface, engine.maximize); + phiC = set_domain_pot(phiC, engine.interface+ss); % shift forward to slice 2 + phiD = marginalize_pot(clpot{D,t-1}, engine.interface+ss, engine.maximize); + ratio = divide_by_pot(phiC, phiD); + clpot{D,t-1} = multiply_by_pot(clpot{D,t-1}, ratio); + end +end + +loglik = sum(logscale); + + +%%%%%%% +function [clpot, seppot] = init_pot(cliques, clqs, pots, pot_type, onodes, ns, cnodes); + +% Set the clique potentials to all 1s +C = length(cliques); +clpot = cell(1,C); +for i=1:C + clpot{i} = mk_initial_pot(pot_type, cliques{i}, ns, cnodes, onodes); +end + +% Multiply on specified potentials +for i=1:length(clqs) + c = clqs(i); + clpot{c} = multiply_by_pot(clpot{c}, pots{i}); +end + +seppot = cell(C,C); % implicitely initialized to 1 + + +%%%% +function [clpot, seppot] = collect_evidence(clpot, seppot, maximize, postorder, postorder_parents,... + separator) +for n=postorder %postorder(1:end-1) + for p=postorder_parents{n} + %clpot{p} = divide_by_pot(clpot{n}, seppot{p,n}); % dividing by 1 is redundant + seppot{p,n} = marginalize_pot(clpot{n}, separator{p,n}, maximize); + clpot{p} = multiply_by_pot(clpot{p}, seppot{p,n}); + end +end + + +%%%% +function [clpot, seppot] = distribute_evidence(clpot, seppot, maximize, preorder, preorder_children,... + separator) +for n=preorder + for c=preorder_children{n} + clpot{c} = divide_by_pot(clpot{c}, seppot{n,c}); + seppot{n,c} = marginalize_pot(clpot{n}, separator{n,c}, maximize); + clpot{c} = multiply_by_pot(clpot{c}, seppot{n,c}); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/jtree_dbn_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/jtree_dbn_inf_engine.m new file mode 100644 index 00000000..5b5cc8ba --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/jtree_dbn_inf_engine.m @@ -0,0 +1,67 @@ +function engine = jtree_dbn_inf_engine(bnet, varargin) +% JTREE_DBN_INF_ENGINE Junction tree inference algorithm for DBNs. + +ss = length(bnet.intra); + +onodes = []; + +if nargin >= 2 + args = varargin; + nargs = length(args); + for i=1:2:nargs + switch args{i}, + case 'observed', onodes = args{i+1}; + end + end +end + +[int, engine.persist, engine.transient] = compute_interface_nodes(bnet.intra, bnet.inter); +%engine.interface = engine.persist; % WRONG! +engine.interface = int; +engine.nonint = mysetdiff(1:ss, int); + +if 0 + % Create a 2 slice jtree + % We force there to be cliques containing the in and out interfaces for slices t and t+1. + obs_nodes = [onodes(:) onodes(:)+ss]; + engine.jtree_engine = jtree_inf_engine(bnet, 'observed', obs_nodes(:), ... + 'clusters', {int, int+ss}, 'root', int+ss); +else + % Create a "1.5 slice" jtree, containing slice 1 and the interface nodes of slice 2 + nodes15 = [1:ss int+ss]; + N = length(nodes15); + dag15 = bnet.dag(nodes15, nodes15); + ns15 = bnet.node_sizes(nodes15); + eclass15 = bnet.equiv_class(nodes15); + discrete_bitv = zeros(1,2*ss); + discrete_bitv(bnet.dnodes) = 1; + discrete15 = find(discrete_bitv(nodes15)); + bnet15 = mk_bnet(dag15, ns15, 'equiv_class', eclass15, 'discrete', discrete15); + bnet15.CPD = bnet.CPD; % CPDs for non-interface nodes in slice 2 will not be used + obs_bitv = zeros(1, 2*ss); + obs_bitv([onodes onodes+ss]) = 1; + obs_nodes15 = find(obs_bitv(nodes15)); + int_bitv = zeros(1,ss); + int_bitv(int) = 1; + engine.jtree_engine = jtree_inf_engine(bnet15, 'observed', obs_nodes15(:), ... + 'clusters', {int, int+ss}, 'root', int+ss); +end + +engine.in_clq = clq_containing_nodes(engine.jtree_engine, int); +engine.out_clq = clq_containing_nodes(engine.jtree_engine, int+ss); + +engine.clq_ass_to_node = zeros(ss, 2); +for i=1:ss + engine.clq_ass_to_node(i, 1) = clq_containing_nodes(engine.jtree_engine, i); + engine.clq_ass_to_node(i, 2) = clq_containing_nodes(engine.jtree_engine, i+ss); +end + +engine.jtree_struct = struct(engine.jtree_engine); % violate object privacy + +% stuff needed by marginal_nodes +engine.clpot = []; +engine.maximize = []; +engine.T = []; + +engine = class(engine, 'jtree_dbn_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/jtree_dbn_inf_engine1.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/jtree_dbn_inf_engine1.m new file mode 100644 index 00000000..5edad836 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/jtree_dbn_inf_engine1.m @@ -0,0 +1,62 @@ +function engine = jtree_dbn_inf_engine(bnet, varargin) +% JTREE_DBN_INF_ENGINE Junction tree inference algorithm for DBNs. + +ss = length(bnet.intra); + +onodes = []; + +if nargin >= 2 + args = varargin; + nargs = length(args); + for i=1:2:nargs + switch args{i}, + case 'observed', onodes = args{i+1}; + end + end +end + +[int, engine.persist, engine.transient] = compute_interface_nodes(bnet.intra, bnet.inter); +%engine.interface = engine.persist; % WRONG! +engine.interface = int; +engine.nonint = mysetdiff(1:ss, int); + +if 1 + % Create a 2 slice jtree + % We force there to be cliques containing the in and out interfaces for slices t and t+1. + obs_nodes = [onodes(:) onodes(:)+ss]; + engine.jtree_engine = jtree_inf_engine(bnet, 'observed', obs_nodes(:), ... + 'clusters', {int, int+ss}, 'root', int+ss); +else + % Create a "1.5 slice" jtree, containing slice 1 and the interface nodes of slice 2 + % To keep the node numbering the same, we simply disconnect the non-interface nodes + % from slice 2. + intra15 = bnet.intra; + for i=engine.nonint(:)' + intra15(i,:) = 0; + intra15(:,i) = 0; + end + bnet15 = mk_dbn(intra15, bnet.inter, bnet.node_sizes_slice, bnet.dnodes_slice, ... + bnet.equiv_class(:,1), bnet.equiv_class(:,2), bnet.intra); + obs_nodes = [onodes(:) onodes(:)+ss]; + engine.jtree_engine = jtree_inf_engine(bnet15, 'observed', obs_nodes(:), ... + 'clusters', {int, int+ss}, 'root', int+ss); +end + +engine.in_clq = clq_containing_nodes(engine.jtree_engine, int); +engine.out_clq = clq_containing_nodes(engine.jtree_engine, int+ss); + +engine.clq_ass_to_node = zeros(ss, 2); +for i=1:ss + engine.clq_ass_to_node(i, 1) = clq_containing_nodes(engine.jtree_engine, i); + engine.clq_ass_to_node(i, 2) = clq_containing_nodes(engine.jtree_engine, i+ss); +end + +engine.jtree_struct = struct(engine.jtree_engine); % violate object privacy + +% stuff needed by marginal_nodes +engine.clpot = []; +engine.maximize = []; +engine.T = []; + +engine = class(engine, 'jtree_dbn_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/jtree_dbn_inf_engine2.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/jtree_dbn_inf_engine2.m new file mode 100644 index 00000000..68ac2aff --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/Old/jtree_dbn_inf_engine2.m @@ -0,0 +1,57 @@ +function engine = jtree_dbn_inf_engine(bnet, varargin) +% JTREE_DBN_INF_ENGINE Junction tree inference algorithm for DBNs. + +ss = length(bnet.intra); + +onodes = []; + +if nargin >= 2 + args = varargin; + nargs = length(args); + for i=1:2:nargs + switch args{i}, + case 'observed', onodes = args{i+1}; + end + end +end + +[int, engine.persist, engine.transient] = compute_interface_nodes(bnet.intra, bnet.inter); +%engine.interface = engine.persist; % WRONG! +engine.interface = int; +engine.nonint = mysetdiff(1:ss, int); + + +% Create a 2 slice jtree +% We force there to be cliques containing the in and out interfaces for slices t and t+1. +obs_nodes = [onodes(:) onodes(:)+ss]; +engine.jtree_engine = jtree_inf_engine(bnet, 'observed', obs_nodes(:), ... + 'clusters', {int, int+ss}, 'root', int+ss); + +engine.in_clq = clq_containing_nodes(engine.jtree_engine, int); +engine.out_clq = clq_containing_nodes(engine.jtree_engine, int+ss); +engine.jtree_struct = struct(engine.jtree_engine); % violate object privacy + + + +% Also create an engine just for slice 1 +bnet1 = mk_bnet(bnet.intra1, bnet.node_sizes_slice, bnet.dnodes, bnet.equiv_class(:,1)); +for i=1:max(bnet1.equiv_class) + bnet1.CPD{i} = bnet.CPD{i}; +end + +engine.jtree_engine1 = jtree_inf_engine(bnet1, 'observed', onodes, 'clusters', {int}, ... + 'root', int); + +engine.in_clq1 = clq_containing_nodes(engine.jtree_engine1, int); +engine.jtree_struct1 = struct(engine.jtree_engine1); % violate object privacy + + + + +% stuff needed by marginal_nodes +engine.clpot = []; +engine.T = []; +engine.maximize = []; + +engine = class(engine, 'jtree_dbn_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/enter_evidence.m new file mode 100644 index 00000000..e21bb07d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/enter_evidence.m @@ -0,0 +1,70 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (jtree_dbn) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product instead of sum-product [engine.maximize] +% softCPDpot{n,t} - use soft potential for node n instead of its CPD; set to [] to use CPD +% soft_evidence_nodes(i,1:2) = [n t] means the i'th piece of soft evidence is on node n in slice t +% soft_evidence{i} - prob distribution over values for soft_evidence_nodes(i,:) +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + + +% for add_ev in marginal_nodes +T = size(evidence, 2); +engine.evidence = evidence; +bnet = bnet_from_engine(engine); +ss = length(bnet.node_sizes_slice); +ns = bnet.node_sizes_slice(:); +engine.node_sizes = repmat(ns, [1 T]); +softCPDpot = cell(ss,T); +soft_evidence = {}; +soft_evidence_nodes = []; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', engine.maximize = args{i+1}; + case 'softCPDpot', softCPDpot = args{i+1}; + case 'soft_evidence', soft_evidence = args{i+1}; + case 'soft_evidence_nodes', soft_evidence_nodes = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +engine.jtree_engine = set_fields(engine.jtree_engine, 'maximize', engine.maximize); +engine.jtree_engine1 = set_fields(engine.jtree_engine1, 'maximize', engine.maximize); + +[ss T] = size(evidence); +engine.T = T; +observed_bitv = ~isemptycell(evidence); +onodes = find(observed_bitv); +pot_type = determine_pot_type(bnet, onodes); +CPDpot = convert_dbn_CPDs_to_pots(bnet, evidence, pot_type, softCPDpot); + +if ~isempty(soft_evidence_nodes) + nsoft = size(soft_evidence_nodes,1); + for i=1:nsoft + n = soft_evidence_nodes(i,1); + t = soft_evidence_nodes(i,2); + if t==1 + dom = n; + else + dom = n+ss; + end + pot = dpot(dom, ns(n), soft_evidence{i}); + CPDpot{n,t} = multiply_by_pot(CPDpot{n,t}, pot); + end +end + +[engine.clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed_bitv, pot_type); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/enter_soft_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/enter_soft_evidence.m new file mode 100644 index 00000000..5ffc55b0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/enter_soft_evidence.m @@ -0,0 +1,126 @@ +function [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type) +% ENTER_SOFT_EVIDENCE Add the specified soft evidence to the network (jtree_dbn) +% [clpot, loglik] = enter_soft_evidence(engine, CPDpot, observed, pot_type) + +scale = 1; +verbose = 0; + +[ss T] = size(CPDpot); +Q = length(engine.jtree_struct.cliques); +clpot = cell(Q,T); % clpot{t} contains evidence from slices (t-1, t) +seppot = cell(Q,Q,T); +ll = zeros(1,Q); +logscale = zeros(1,T); +bnet = bnet_from_engine(engine); +root = engine.jtree_struct.root_clq; + +% Forwards pass. +% Compute distribution on clq C, +% where C is the out interface to (t-1,t). +% Then pass this to clq D, where D is the in inferface to (t+1,t). + +% Then propagate from D to later slices. + +slice1 = 1:ss; +slice2 = slice1 + ss; +transient = engine.transient; +persist = engine.persist; +Ntransient = length(transient); +trans = cell(Ntransient,1); +if verbose, fprintf('forward pass\n'); end +for t=1:T + if verbose, fprintf('%d ', t); end + if t==1 + pots = [CPDpot(:,1); CPDpot(persist, 2)]; + clqs = engine.jtree_struct.clq_ass_to_node([slice1 persist+ss]); + obs = find(observed(:,1:2)); + elseif t==T + clqs = [engine.in_clq1 engine.jtree_struct1.clq_ass_to_node(transient)]; + phi = set_domain_pot(phi, engine.interface); % shift back to slice 1 + for i=1:Ntransient + trans{i} = CPDpot{transient(i), t}; + trans{i} = set_domain_pot(trans{i}, domain_pot(trans{i})-ss); % shift back to slice 1 + end + pots = [ {phi}; trans]; + obs = find(observed(:,T)); + else + clqs = [engine.in_clq engine.jtree_struct.clq_ass_to_node([transient persist+ss])]; + phi = set_domain_pot(phi, engine.interface); % shift back to slice 1 + for i=1:Ntransient + trans{i} = CPDpot{transient(i), t}; + trans{i} = set_domain_pot(trans{i}, domain_pot(trans{i})-ss); % shift back to slice 1 + end + pots = [ {phi}; trans; CPDpot(persist, t+1)]; + obs = find(observed(:,t:t+1)); + end + + if t < T + [clpot(1:Q,t), seppot(1:Q,1:Q,t)] = init_pot(engine.jtree_engine, clqs, pots, pot_type, obs); + [clpot(1:Q,t), seppot(1:Q,1:Q,t)] = collect_evidence(engine.jtree_engine, clpot(1:Q,t), seppot(1:Q,1:Q,t)); + else + Q = length(engine.jtree_struct1.cliques); + root = engine.jtree_struct1.root_clq; + [clpot(1:Q,t), seppot(1:Q,1:Q,t)] = init_pot(engine.jtree_engine1, clqs, pots, pot_type, obs); + [clpot(1:Q,t), seppot(1:Q,1:Q,t)] = collect_evidence(engine.jtree_engine1, clpot(1:Q,t), seppot(1:Q,1:Q,t)); + end + + + if scale + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + logscale(t) = ll(root); + end + + if t < T + % bug fix by Bob Welch 30 Jan 04 + phi = marginalize_pot(clpot{engine.out_clq,t}, engine.interface+ss,engine.maximize); + %phi = marginalize_pot(clpot{root,t}, engine.interface+ss, engine.maximize); + end +end + +if scale +loglik = sum(logscale); +else +loglik = []; +end + + +% Backwards pass. +% Pass evidence from clq C to clq D, +% where C is the in interface to (t,t+1) and D is the out inferface to (t-1,t) +% Then propagate evidence from D to earlier slices. +% (C and D are reversed names from the tech report!) +D = engine.out_clq; +if verbose, fprintf('\nbackwards pass\n'); end +for t=T:-1:1 + if verbose, fprintf('%d ', t); end + + if t == T + Q = length(engine.jtree_struct1.cliques); + C = engine.in_clq1; + [clpot(1:Q,t), seppot(1:Q,1:Q,t)] = distribute_evidence(engine.jtree_engine1, clpot(1:Q,t), seppot(1:Q,1:Q,t)); + else + Q = length(engine.jtree_struct.cliques); + C = engine.in_clq; + [clpot(1:Q,t), seppot(1:Q,1:Q,t)] = distribute_evidence(engine.jtree_engine, clpot(1:Q,t), seppot(1:Q,1:Q,t)); + end + + if scale + for c=1:Q + [clpot{c,t}, ll(c)] = normalize_pot(clpot{c,t}); + end + end + + if t >= 2 + phiC = marginalize_pot(clpot{C,t}, engine.interface, engine.maximize); + phiC = set_domain_pot(phiC, engine.interface+ss); % shift forward to slice 2 + phiD = marginalize_pot(clpot{D,t-1}, engine.interface+ss, engine.maximize); + ratio = divide_by_pot(phiC, phiD); + clpot{D,t-1} = multiply_by_pot(clpot{D,t-1}, ratio); + end +end +if verbose, fprintf('\n'); end + + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/jtree_dbn_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/jtree_dbn_inf_engine.m new file mode 100644 index 00000000..c49ba4c4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/jtree_dbn_inf_engine.m @@ -0,0 +1,109 @@ +function engine = jtree_dbn_inf_engine(bnet, varargin) +% JTREE_DBN_INF_ENGINE Junction tree inference algorithm for DBNs. +% engine = jtree_inf_engine(bnet, ...) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% clusters - specifies variables that must be grouped in the 1.5 slice DBN +% maximize - 1 means max-product, 0 means sum-product [0] +% +% e.g., engine = jtree_dbn_inf_engine(dbn, 'clusters', {[1 2]}); +% +% This uses all of slice t-1 plus the backwards interface of slice t. +% By contrast, jtree_2TBN_inf_engine in the online directory uses +% the forwards interface of slice t-1 plus all of slice t. +% See my thesis for details. + +ss = length(bnet.intra); + +engine.maximize = 0; +clusters = {}; + +args = varargin; +for i=1:2:length(args) + switch args{i}, + case 'clusters', clusters = args{i+1}; + case 'maximize', engine.maximize = args{i+1}; + otherwise, error(['unrecognized argument ' args{i}]) + end +end + + +engine.evidence = []; +engine.node_sizes = []; + +[int, engine.persist, engine.transient] = compute_interface_nodes(bnet.intra, bnet.inter); +engine.interface = int; +engine.nonint = mysetdiff(1:ss, int); + +onodes = bnet.observed; + +if 0 + % Create a 2 slice jtree + % We force there to be cliques containing the in and out interfaces for slices t and t+1. + obs_nodes = [onodes(:) onodes(:)+ss]; + engine.jtree_engine = jtree_inf_engine(bnet, 'observed', obs_nodes(:), ... + 'clusters', {int, int+ss}, 'root', int+ss); +else + % Create a "1.5 slice" jtree, containing slice 1 and the interface nodes of slice 2 + % To keep the node numbering the same, we simply disconnect the non-interface nodes + % from slice 2, and set their size to 1. + % We do this to speed things up, and so that the likelihood is computed correctly - we do not need to do + % this if we just want to compute marginals. + intra15 = bnet.intra; + for i=engine.nonint(:)' + intra15(i,:) = 0; + intra15(:,i) = 0; + end + dag15 = [bnet.intra bnet.inter; + zeros(ss) intra15]; + ns = bnet.node_sizes(:); + ns(engine.nonint+ss) = 1; % disconnected nodes get size 1 + obs_nodes = [onodes(:) onodes(:)+ss]; + bnet15 = mk_bnet(dag15, ns, 'discrete', bnet.dnodes, 'equiv_class', bnet.equiv_class(:), ... + 'observed', obs_nodes(:)); + + %bnet15 = mk_dbn(intra15, bnet.inter, bnet.node_sizes_slice, bnet.dnodes_slice, ... + % bnet.equiv_class(:,1), bnet.equiv_class(:,2), bnet.intra); + % with the dbn, we can't independently control the sizes of slice 2 nodes + + if 1 + % use unconstrained elimination, + % but force there to be a clique containing both interfaces + clusters(end+1:end+2) = {int, int+ss}; + engine.jtree_engine = jtree_inf_engine(bnet15, 'clusters', clusters, 'root', int+ss); + else + % Use constrained elimination - this induces a clique that contain the 2nd interface, + % but not the first. + % Hence we throw in the first interface as an extra. + stages = {1:ss, [1:ss]+ss}; + clusters(end+1:end+2) = {int, int+ss}; + engine.jtree_engine = jtree_inf_engine(bnet15, 'clusters', clusters, ... + 'stages', stages, 'root', int+ss); + end +end + +engine.in_clq = clq_containing_nodes(engine.jtree_engine, int); +engine.out_clq = clq_containing_nodes(engine.jtree_engine, int+ss); +engine.jtree_struct = struct(engine.jtree_engine); % violate object privacy + + +% Also create an engine just for slice 1 +bnet1 = mk_bnet(bnet.intra1, bnet.node_sizes_slice, 'discrete', myintersect(bnet.dnodes,1:ss), ... + 'equiv_class', bnet.equiv_class(:,1), 'observed', onodes); +for i=1:max(bnet1.equiv_class) + bnet1.CPD{i} = bnet.CPD{i}; +end + +engine.jtree_engine1 = jtree_inf_engine(bnet1, 'clusters', {int}, 'root', int); + +engine.in_clq1 = clq_containing_nodes(engine.jtree_engine1, int); +engine.jtree_struct1 = struct(engine.jtree_engine1); % violate object privacy + +% stuff needed by marginal_nodes +engine.clpot = []; +engine.T = []; + +engine = class(engine, 'jtree_dbn_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/marginal_family.m new file mode 100644 index 00000000..1fe59f61 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/marginal_family.m @@ -0,0 +1,26 @@ +function m = marginal_family(engine, i, t, add_ev) +% MARGINAL_FAMILY Compute the marginal on the specified family (jtree_dbn) +% marginal = marginal_family(engine, i, t) + +% This is just like inf_engine/marginal_family, except when we call +% marginal_nodes, we provide a 4th argument, to tell it's a family. + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end + +bnet = bnet_from_engine(engine); +if t==1 + m = marginal_nodes(engine, family(bnet.dag, i), t, add_ev, 1); +else + ss = length(bnet.intra); + fam = family(bnet.dag, i+ss); + if any(fam<=ss) + % i has a parent in the preceeding slice + % Hence the lowest numbered slice containing the family is t-1 + m = marginal_nodes(engine, fam, t-1, add_ev, 1); + else + % The family all fits inside slice t + % Hence shift the indexes back to slice 1 + m = marginal_nodes(engine, fam-ss, t, add_ev, 1); + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..c9a40488 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_dbn_inf_engine/marginal_nodes.m @@ -0,0 +1,66 @@ +function marginal = marginal_nodes(engine, nodes, t, add_ev, fam) +% MARGINAL_NODES Compute the marginal on the specified query nodes (bk) +% +% marginal = marginal_nodes(engine, i, t) +% returns Pr(X(i,t) | Y(1:T)), where X(i,t) is the i'th node in the t'th slice. +% +% marginal = marginal_nodes(engine, query, t) +% returns Pr(X(query(1),t), ... X(query(end),t) | Y(1:T)), +% where 't' specifies the time slice of the earliest node in the query. +% 'query' cannot span more than 2 time slices. +% +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3. +% +% marginal = marginal_nodes(engine, nodes, t, add_ev, fam) +% add_ev is an optional argument; if 1, we will "inflate" the marginal of observed nodes +% to their original size, adding 0s to the positions which contradict the evidence + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end +if nargin < 5, fam = 0; end + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); + +if t==1 | t==engine.T + slice = t; + nodes2 = nodes; +elseif mysubset(nodes, engine.persist) + slice = t-1; + nodes2 = nodes+ss; +else + slice = t; + nodes2 = nodes; +end + +%disp(['computing marginal on ' num2str(nodes) ' t = ' num2str(t) ' fam = ' num2str(fam)]); + +if t==engine.T + c = clq_containing_nodes(engine.jtree_engine1, nodes2, fam); +else + c = clq_containing_nodes(engine.jtree_engine, nodes2, fam); +end +if c == -1 + error(['no clique contains ' nodes2]) +end + + +%disp(['using ' num2str(nodes2) ' slice = ' num2str(slice) ' clq = ' num2str(c)]); + +bigpot = engine.clpot{c, slice}; + +pot = marginalize_pot(bigpot, nodes2, engine.maximize); +%pot = normalize_pot(pot); +marginal = pot_to_marginal(pot); + + +% we convert the domain to the unrolled numbering system +% so that add_ev_to_dmarginal (maybe called in update_ess) extracts the right evidence. +marginal.domain = nodes+(t-1)*ss; + +if add_ev + marginal = add_ev_to_dmarginal(marginal, engine.evidence, engine.node_sizes); +end + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/CVS/Entries new file mode 100644 index 00000000..2809c39f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/CVS/Entries @@ -0,0 +1,6 @@ +/enter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/jtree_unrolled_dbn_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +/update_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +D/Old//// diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/CVS/Repository new file mode 100644 index 00000000..e9fd6fe5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/CVS/Entries new file mode 100644 index 00000000..eafb1997 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/CVS/Entries @@ -0,0 +1,3 @@ +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/CVS/Repository new file mode 100644 index 00000000..2ad645e2 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/marginal_family.m new file mode 100644 index 00000000..efb38b26 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/marginal_family.m @@ -0,0 +1,10 @@ +function marginal = marginal_family(engine, i, t) +% MARGINAL_FAMILY Compute the marginal on the specified family (jtree_unrolled_dbn) +% marginal = marginal_family(engine, i, t) + +if nargin < 3, t = 1; end + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +marginal = marginal_family(engine.sub_engine, i + (t-1)*ss); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/marginal_nodes.m new file mode 100644 index 00000000..b0cfb04e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/Old/marginal_nodes.m @@ -0,0 +1,18 @@ +function marginal = marginal_nodes(engine, nodes, t) +% MARGINAL_NODES Compute the marginal on the specified query nodes (jtree_unrolled_dbn) +% marginal = marginal_nodes(engine, nodes, t) +% +% 't' specifies the time slice of the earliest node in 'nodes'. +% 'nodes' must occur in some clique. +% +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3, +% i.e., nodes 3 and 5 in the unrolled network, + +if nargin < 3, t = 1; end + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +query = nodes + (t-1)*ss; +marginal = marginal_nodes(engine.sub_engine, query); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/enter_evidence.m new file mode 100644 index 00000000..48b230c9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/enter_evidence.m @@ -0,0 +1,43 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (jtree_unrolled_dbn) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product instead of sum-product [0] +% filter - if 1, does filtering (not supported), else smoothing [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +if filter + error('jtree_unrolled_dbn does not support filtering') +end + +if size(evidence,2) ~= engine.nslices + error(['engine was created assuming there are ' num2str(engine.nslices) ... + ' slices, but evidence has ' num2str(size(evidence,2))]) +end + +[engine.unrolled_engine, loglik] = enter_evidence(engine.unrolled_engine, evidence, 'maximize', maximize); + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/jtree_unrolled_dbn_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/jtree_unrolled_dbn_inf_engine.m new file mode 100644 index 00000000..156c6ee2 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/jtree_unrolled_dbn_inf_engine.m @@ -0,0 +1,57 @@ +function engine = jtree_unrolled_dbn_inf_engine(bnet, T, varargin) +% JTREE_UNROLLED_DBN_INF_ENGINE Unroll the DBN for T time-slices and apply jtree to the resulting static net +% engine = jtree_unrolled_dbn_inf_engine(bnet, T, ...) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% useC - 1 means use jtree_C_inf_engine instead of jtree_inf_engine [0] +% constrained - 1 means we constrain ourselves to eliminate slice t before t+1 [1] +% +% e.g., engine = jtree_unrolled_inf_engine(bnet, 'useC', 1); + +% set default params +N = length(bnet.intra); +useC = 0; +constrained = 1; + +if nargin >= 3 + args = varargin; + nargs = length(args); + if isstr(args{1}) + for i=1:2:nargs + switch args{i}, + case 'useC', useC = args{i+1}; + case 'constrained', constrained = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end + else + error(['invalid argument name ' args{1}]); + end +end + +bnet2 = dbn_to_bnet(bnet, T); +ss = length(bnet.intra); +engine.ss = ss; + +% If constrained_order = 1 we constrain ourselves to eliminate slice t before t+1. +% This prevents cliques containing nodes from far-apart time-slices. +if constrained + stages = num2cell(unroll_set(1:ss, ss, T), 1); +else + stages = { 1:length(bnet2.dag) }; +end +if useC + jengine = jtree_C_inf_engine(bnet2, 'stages', stages); +else + jengine = jtree_inf_engine(bnet2, 'stages', stages); +end + +engine.unrolled_engine = jengine; +% we don't inherit from jtree_inf_engine, because that would only store bnet2, +% and we would lose access to the DBN-specific fields like intra/inter + +engine.nslices = T; +engine = class(engine, 'jtree_unrolled_dbn_inf_engine', inf_engine(bnet)); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/marginal_family.m new file mode 100644 index 00000000..a40f2974 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/marginal_family.m @@ -0,0 +1,11 @@ +function marginal = marginal_family(engine, i, t, add_ev) +% MARGINAL_FAMILY Compute the marginal on the specified family (jtree_unrolled_dbn) +% marginal = marginal_family(engine, i, t) + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end +assert(~add_ev); + +%marginal = marginal_family(engine.unrolled_engine, i + (t-1)*engine.ss, add_ev); +marginal = marginal_family(engine.unrolled_engine, i + (t-1)*engine.ss); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..0fb095e5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/marginal_nodes.m @@ -0,0 +1,16 @@ +function marginal = marginal_nodes(engine, nodes, t, add_ev) +% MARGINAL_NODES Compute the marginal on the specified query nodes (loopy_unrolled_dbn) +% marginal = marginal_nodes(engine, nodes, t) +% +% 't' specifies the time slice of the earliest node in 'nodes'. +% 'nodes' must occur in some clique. +% +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3, +% i.e., nodes 3 and 5 in the unrolled network, + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end + +marginal = marginal_nodes(engine.unrolled_engine, nodes + (t-1)*engine.ss, add_ev); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/update_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/update_engine.m new file mode 100644 index 00000000..5c42d4f6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@jtree_unrolled_dbn_inf_engine/update_engine.m @@ -0,0 +1,7 @@ +function engine = update_engine(engine, newCPDs) +% UPDATE_ENGINE Update the engine to take into account the new parameters (jtree_unrolled_dbn) +% engine = update_engine(engine, newCPDs) + +engine.inf_engine = update_engine(engine.inf_engine, newCPDs); +engine.unrolled_engine = update_engine(engine.unrolled_engine, newCPDs); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/CVS/Entries new file mode 100644 index 00000000..dce274e1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/CVS/Entries @@ -0,0 +1,5 @@ +/enter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/kalman_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +/update_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +D/private//// diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/CVS/Repository new file mode 100644 index 00000000..674f2eea --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@kalman_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/enter_evidence.m new file mode 100644 index 00000000..4ba51942 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/enter_evidence.m @@ -0,0 +1,83 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (kalman) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product (same as sum-product for Gaussians!), else sum-product [0] +% filter - if 1, do filtering, else smoothing [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +assert(~maximize); + +bnet = bnet_from_engine(engine); +n = length(bnet.intra); +onodes = bnet.observed; +hnodes = mysetdiff(1:n, onodes); +T = size(evidence, 2); +ns = bnet.node_sizes; +O = sum(ns(onodes)); +data = reshape(cat(1, evidence{onodes,:}), [O T]); + +A = engine.trans_mat; +C = engine.obs_mat; +Q = engine.trans_cov; +R = engine.obs_cov; +init_x = engine.init_state; +init_V = engine.init_cov; + +if filter + [x, V, VV, loglik] = kalman_filter(data, A, C, Q, R, init_x, init_V); +else + [x, V, VV, loglik] = kalman_smoother(data, A, C, Q, R, init_x, init_V); +end + + +% Wrap the posterior inside a potential, so it can be marginalized easily +engine.one_slice_marginal = cell(1,T); +engine.two_slice_marginal = cell(1,T); +ns(onodes) = 0; +ns(onodes+n) = 0; +ss = length(bnet.intra); +for t=1:T + dom = (1:n); + engine.one_slice_marginal{t} = mpot(dom+(t-1)*ss, ns(dom), 1, x(:,t), V(:,:,t)); +end +% for t=1:T-1 +% dom = (1:(2*n)); +% mu = [x(:,t); x(:,t)]; +% Sigma = [V(:,:,t) VV(:,:,t+1)'; +% VV(:,:,t+1) V(:,:,t+1)]; +% engine.two_slice_marginal{t} = mpot(dom+(t-1)*ss, ns(dom), 1, mu, Sigma); +% end +for t=2:T + %dom = (1:(2*n)); + current_slice = hnodes; + next_slice = hnodes + ss; + dom = [current_slice next_slice]; + mu = [x(:,t-1); x(:,t)]; + Sigma = [V(:,:,t-1) VV(:,:,t)'; + VV(:,:,t) V(:,:,t)]; + engine.two_slice_marginal{t-1} = mpot(dom+(t-2)*ss, ns(dom), 1, mu, Sigma); +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/kalman_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/kalman_inf_engine.m new file mode 100644 index 00000000..df03a56f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/kalman_inf_engine.m @@ -0,0 +1,23 @@ +function engine = kalman_inf_engine(bnet) +% KALMAN_INF_ENGINE Inference engine for Linear-Gaussian state-space models. +% engine = kalman_inf_engine(bnet) +% +% 'onodes' specifies which nodes are observed; these must be leaves. +% The remaining nodes are all hidden. All nodes must have linear-Gaussian CPDs. +% The hidden nodes must be persistent, i.e., they must have children in +% the next time slice. In addition, they may not have any children within the current slice, +% except to the observed leaves. In other words, the topology must be isomorphic to a standard LDS. +% +% There are many derivations of the filtering and smoothing equations for Linear Dynamical +% Systems in the literature. I particularly like the following +% - "From HMMs to LDSs", T. Minka, MIT Tech Report, (no date), available from +% ftp://vismod.www.media.mit.edu/pub/tpminka/papers/minka-lds-tut.ps.gz + +[engine.trans_mat, engine.trans_cov, engine.obs_mat, engine.obs_cov, engine.init_state, engine.init_cov] = ... + dbn_to_lds(bnet); + +% This is where we will store the results between enter_evidence and marginal_nodes +engine.one_slice_marginal = []; +engine.two_slice_marginal = []; + +engine = class(engine, 'kalman_inf_engine', inf_engine(bnet)); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..738c30dc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/marginal_nodes.m @@ -0,0 +1,25 @@ +function marginal = marginal_nodes(engine, nodes, t) +% MARGINAL_NODES Compute the marginal on the specified query nodes (kalman) +% marginal = marginal_nodes(engine, nodes, t) +% +% 't' specifies the time slice of the earliest node in 'nodes'. +% 'nodes' cannot span more than 2 time slices. +% +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3, +% i.e., nodes 3 and 5 in the unrolled network, + +if nargin < 3, t = 1; end + +bnet = bnet_from_engine(engine); +ss = length(bnet.intra); +if all(nodes <= ss) + bigpot = engine.one_slice_marginal{t}; +else + bigpot = engine.two_slice_marginal{t}; +end + +nodes = nodes + (t-1)*ss; +pot = marginalize_pot(bigpot, nodes); +marginal = pot_to_marginal(pot); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/CVS/Entries new file mode 100644 index 00000000..9a351a87 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/CVS/Entries @@ -0,0 +1,3 @@ +/dbn_to_lds.m/1.1.1.1/Wed May 29 15:59:56 2002// +/extract_params_from_gbn.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/CVS/Repository new file mode 100644 index 00000000..3f67aee0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@kalman_inf_engine/private diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/dbn_to_lds.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/dbn_to_lds.m new file mode 100644 index 00000000..6249ac0d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/dbn_to_lds.m @@ -0,0 +1,26 @@ +function [trans_mat, trans_cov, obs_mat, obs_cov, init_state, init_cov] = dbn_to_lds(bnet) +% DBN_TO_LDS Compute the Linear Dynamical System parameters from the Gaussian DBN. +% [trans_mat, trans_cov, obs_mat, obs_cov, init_state, init_cov] = dbn_to_lds(bnet) + +onodes = bnet.observed; +ss = length(bnet.intra); +num_nodes = ss*2; +assert(isequal(bnet.cnodes_slice, 1:ss)); +[W,D,mu] = extract_params_from_gbn(bnet); + +hnodes = mysetdiff(1:ss, onodes); +bs = bnet.node_sizes(:); % block sizes + +obs_mat = W(block(hnodes,bs), block(onodes,bs))'; +u = block(onodes,bs); +obs_cov = D(u,u); + +trans_mat = W(block(hnodes,bs), block(hnodes + ss, bs))'; +u = block(hnodes + ss, bs); +trans_cov = D(u,u); + +u = block(hnodes,bs); +init_cov = D(u,u); +init_state = mu(u); + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/extract_params_from_gbn.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/extract_params_from_gbn.m new file mode 100644 index 00000000..86345830 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/private/extract_params_from_gbn.m @@ -0,0 +1,38 @@ +function [B,D,mu] = extract_params_from_gbn(bnet) +% Extract all the local parameters of each Gaussian node, and collect them into global matrices. +% [B,D,mu] = extract_params_from_gbn(bnet) +% +% B(i,j) is a block matrix that contains the transposed weight matrix from node i to node j. +% D(i,i) is a block matrix that contains the noise covariance matrix for node i. +% mu(i) is a block vector that contains the shifted noise mean for node i. + +% In Shachter's model, the mean of each node in the global gaussian is +% the same as the node's local unconditional mean. +% In Alag's model (which we use), the global mean gets shifted. + + +num_nodes = length(bnet.dag); +bs = bnet.node_sizes(:); % bs = block sizes +N = sum(bs); % num scalar nodes + +B = zeros(N,N); +D = zeros(N,N); +mu = zeros(N,1); + +for i=1:num_nodes % in topological order + ps = parents(bnet.dag, i); + e = bnet.equiv_class(i); + %[m, Sigma, weights] = extract_params_from_CPD(bnet.CPD{e}); + s = struct(bnet.CPD{e}); % violate privacy of object + m = s.mean; Sigma = s.cov; weights = s.weights; + if length(ps) == 0 + mu(block(i,bs)) = m; + else + mu(block(i,bs)) = m + weights * mu(block(ps,bs)); + end + B(block(ps,bs), block(i,bs)) = weights'; + D(block(i,bs), block(i,bs)) = Sigma; +end + + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/update_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/update_engine.m new file mode 100644 index 00000000..d89605e7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@kalman_inf_engine/update_engine.m @@ -0,0 +1,9 @@ +function engine = update_engine(engine, newCPDs) +% UPDATE_ENGINE Update the engine to take into account the new parameters (kalman) +% engine = update_engine(engine, newCPDs) + +engine.inf_engine = update_engine(engine.inf_engine, newCPDs); +[engine.trans_mat, engine.trans_cov, engine.obs_mat, engine.obs_cov, engine.init_state, engine.init_cov] = ... + dbn_to_lds(bnet_from_engine(engine)); + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/CVS/Entries new file mode 100644 index 00000000..84a6daa1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/CVS/Entries @@ -0,0 +1,6 @@ +/enter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_soft_ev.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +/pearl_dbn_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +D/Old//// +D/private//// diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/CVS/Repository new file mode 100644 index 00000000..b7a44128 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@pearl_dbn_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/CVS/Entries new file mode 100644 index 00000000..d729c48f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/CVS/Entries @@ -0,0 +1,8 @@ +/correct_smooth.m/1.1.1.1/Wed May 29 15:59:56 2002// +/enter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/filter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/filter_evidence_obj_oriented.m/1.1.1.1/Wed May 29 15:59:56 2002// +/smooth_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/smooth_evidence_fast.m/1.1.1.1/Wed May 29 15:59:56 2002// +/wrong_smooth.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/CVS/Repository new file mode 100644 index 00000000..db9771c6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/correct_smooth.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/correct_smooth.m new file mode 100644 index 00000000..275afd41 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/correct_smooth.m @@ -0,0 +1,244 @@ +function [marginal, msg, loglik] = smooth_evidence(engine, evidence) +% [marginal, msg, loglik] = smooth_evidence(engine, evidence) (pearl_dbn) + +disp('warning: broken'); + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +bnet2 = dbn_to_bnet(bnet, T); +ns = bnet2.node_sizes; +hnodes = mysetdiff(1:ss, engine.onodes); +hnodes = hnodes(:)'; + +onodes2 = unroll_set(engine.onodes(:), ss, T); +onodes2 = onodes2(:)'; + +hnodes2 = unroll_set(hnodes(:), ss, T); +hnodes2 = hnodes2(:)'; + +[engine.parent_index, engine.child_index] = mk_pearl_msg_indices(bnet2); + +msg = init_msgs(bnet2.dag, ns, evidence, bnet2.equiv_class, bnet2.CPD); + +verbose = 0; + +niter = 1; +for iter=1:niter + % FORWARD + for t=1:T + if verbose, fprintf('t=%d\n', t); end + % observed leaves send lambda to parents + for i=engine.onodes(:)' + n = i + (t-1)*ss; + ps = parents(bnet2.dag, n); + for p=ps(:)' + j = engine.child_index{p}(n); % n is p's j'th child + if t > 1 + e = bnet.equiv_class(i, 2); + else + e = bnet.equiv_class(i, 1); + end + lam_msg = normalise(compute_lambda_msg(bnet.CPD{e}, n, ps, msg, p)); + msg{p}.lambda_from_child{j} = lam_msg; + if verbose, fprintf('%d sends lambda to %d\n', n, p); disp(lam_msg); end + end + end + + % update pi + for i=hnodes + n = i + (t-1)*ss; + ps = parents(bnet2.dag, n); + if t==1 + e = bnet.equiv_class(i,1); + else + e = bnet.equiv_class(i,2); + end + msg{n}.pi = compute_pi(bnet.CPD{e}, n, ps, msg); + if verbose, fprintf('%d computes pi\n', n); disp(msg{n}.pi); end + end + + % send pi msg to children + for i=hnodes + n = i + (t-1)*ss; + %cs = myintersect(children(bnet2.dag, n), hnodes2); + cs = children(bnet2.dag, n); + for c=cs(:)' + j = engine.parent_index{c}(n); % n is c's j'th parent + pi_msg = normalise(compute_pi_msg(n, cs, msg, c, ns)); + msg{c}.pi_from_parent{j} = pi_msg; + if verbose, fprintf('%d sends pi to %d\n', n, c); disp(pi_msg); end + end + end + end + + % BACKWARD + for t=T:-1:1 + if verbose, fprintf('t = %d\n', t); end + % update lambda + for i=hnodes + n = i + (t-1)*ss; + cs = children(bnet2.dag, n); + msg{n}.lambda = compute_lambda(n, cs, msg, ns); + if verbose, fprintf('%d computes lambda\n', n); disp(msg{n}.lambda); end + end + % send lambda msgs to parents + for i=hnodes + n = i + (t-1)*ss; + %ps = myintersect(parents(bnet2.dag, n), hnodes2); + ps = parents(bnet2.dag, n); + for p=ps(:)' + j = engine.child_index{p}(n); % n is p's j'th child + if t > 1 + e = bnet.equiv_class(i, 2); + else + e = bnet.equiv_class(i, 1); + end + lam_msg = normalise(compute_lambda_msg(bnet.CPD{e}, n, ps, msg, p)); + msg{p}.lambda_from_child{j} = lam_msg; + if verbose, fprintf('%d sends lambda to %d\n', n, p); disp(lam_msg); end + end + end + end + +end + + +marginal = cell(ss,T); +lik = zeros(1,ss*T); +for t=1:T + for i=1:ss + n = i + (t-1)*ss; + [bel, lik(n)] = normalise(msg{n}.pi .* msg{n}.lambda); + marginal{i,t} = bel; + end +end + +loglik = sum(log(lik)); + + + +%%%%%%% + +function lambda = compute_lambda(n, cs, msg, ns) +% Pearl p183 eq 4.50 +lambda = prod_lambda_msgs(n, cs, msg, ns); + +%%%%%%% + +function pi_msg = compute_pi_msg(n, cs, msg, c, ns) +% Pearl p183 eq 4.53 and 4.51 +pi_msg = msg{n}.pi .* prod_lambda_msgs(n, cs, msg, ns, c); + +%%%%%%%%% + +function lam = prod_lambda_msgs(n, cs, msg, ns, except) + +if nargin < 5, except = -1; end + +lam = msg{n}.lambda_from_self(:); +lam = ones(ns(n), 1); +for i=1:length(cs) + c = cs(i); + if c ~= except + lam = lam .* msg{n}.lambda_from_child{i}; + end +end + + +%%%%%%%%% + +function msg = init_msgs(dag, ns, evidence, eclass, CPD) +% INIT_MSGS Initialize the lambda/pi message and state vectors (pearl_dbn) +% msg = init_msgs(dag, ns, evidence) + +N = length(dag); +msg = cell(1,N); +observed = ~isemptycell(evidence(:)); + +for n=1:N + ps = parents(dag, n); + msg{n}.pi_from_parent = cell(1, length(ps)); + for i=1:length(ps) + p = ps(i); + msg{n}.pi_from_parent{i} = ones(ns(p), 1); + end + + cs = children(dag, n); + msg{n}.lambda_from_child = cell(1, length(cs)); + for i=1:length(cs) + c = cs(i); + msg{n}.lambda_from_child{i} = ones(ns(n), 1); + end + + msg{n}.lambda = ones(ns(n), 1); + msg{n}.lambda_from_self = ones(ns(n), 1); + msg{n}.pi = ones(ns(n), 1); + + % Initialize the lambdas with any evidence + if observed(n) + v = evidence{n}; + %msg{n}.lambda_from_self = zeros(ns(n), 1); + %msg{n}.lambda_from_self(v) = 1; % delta function + msg{n}.lambda = zeros(ns(n), 1); + msg{n}.lambda(v) = 1; % delta function + end + +end + + +%%%%%%%% + +function msg = init_ev_msgs(engine, evidence, msg) + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +pot_type = 'd'; +t = 1; +hnodes = mysetdiff(1:ss, engine.onodes); +for i=engine.onodes(:)' + fam = family(bnet.dag, i); + e = bnet.equiv_class(i, 1); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,1)); + temp = pot_to_marginal(CPDpot); + msg{i}.lambda_from_self = temp.T; +end +for t=2:T + for i=engine.onodes(:)' + fam = family(bnet.dag, i, 2); % extract from slice t + e = bnet.equiv_class(i, 2); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,t-1:t)); + temp = pot_to_marginal(CPDpot); + n = i + (t-1)*ss; + msg{n}.lambda_from_self = temp.T; + end +end + + +%%%%%%%%%%% + +function msg = init_ev_msgs2(engine, evidence, msg) + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +pot_type = 'd'; +t = 1; +hnodes = mysetdiff(1:ss, engine.onodes); +for i=engine.onodes(:)' + fam = family(bnet.dag, i); + e = bnet.equiv_class(i, 1); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,1)); + temp = pot_to_marginal(CPDpot); + msg{i}.lambda_from_self = temp.T; +end +for t=2:T + for i=engine.onodes(:)' + fam = family(bnet.dag, i, 2); % extract from slice t + e = bnet.equiv_class(i, 2); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,t-1:t)); + temp = pot_to_marginal(CPDpot); + n = i + (t-1)*ss; + msg{n}.lambda_from_self = temp.T; + end +end + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/enter_evidence.m new file mode 100644 index 00000000..18e7519b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/enter_evidence.m @@ -0,0 +1,123 @@ +function [engine, loglik] = enter_evidence(engine, evidence, filter) +% ENTER_EVIDENCE Add the specified evidence to the network (pearl_dbn) +% [engine, loglik] = enter_evidence(engine, evidence, filter) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% If filter = 1, we do filtering, otherwise smoothing (default). + +if nargin < 3, filter = 0; end + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +bnet2 = dbn_to_bnet(bnet, T); +ns = bnet2.node_sizes; +hnodes = mysetdiff(1:ss, engine.onodes); +hnodes = hnodes(:)'; + +[engine.parent_index, engine.child_index] = mk_pearl_msg_indices(bnet2); + +msg = init_msgs(bnet2.dag, ns, evidence); +msg = init_ev_msgs(engine, evidence, msg); + +niter = 1; +for iter=1:niter + % FORWARD + for t=1:T + % update pi + for i=1:ss %hnodes + n = i + (t-1)*ss; + ps = parents(bnet2.dag, n); + if t==1 + e = bnet.equiv_class(i,1); + else + e = bnet.equiv_class(i,2); + end + msg{n}.pi = compute_pi(bnet.CPD{e}, n, ps, msg); + %msg{n}.pi = normalise(msg{n}.pi(:) .* msg{n}.lambda_from_self(:)); + end + % send pi msg to children + for i=1:ss % hnodes + n = i + (t-1)*ss; + cs = children(bnet2.dag, n); + for c=cs(:)' + j = engine.parent_index{c}(n); % n is c's j'th parent + msg{c}.pi_from_parent{j} = normalise(compute_pi_msg(n, cs, msg, c, ns)); + end + end + end + + if filter + disp('skipping smoothing'); + break; + end + + % BACKWARD + for t=T:-1:1 + % update lambda + for i=1:ss % hnodes + n = i + (t-1)*ss; + cs = children(bnet2.dag, n); + msg{n}.lambda = compute_lambda(n, cs, msg, ns); + end + % send lambda msgs to parents + for i=1:ss % hnodes + n = i + (t-1)*ss; + ps = parents(bnet2.dag, n); + for p=ps(:)' + j = engine.child_index{p}(n); % n is p's j'th child + if t > 1 + e = bnet.equiv_class(i, 2); + else + e = bnet.equiv_class(i, 1); + end + msg{p}.lambda_from_child{j} = normalise(compute_lambda_msg(bnet.CPD{e}, n, ps, msg, p)); + end + end + end + +end + + +engine.marginal = cell(ss,T); +lik = zeros(1,ss*T); +for t=1:T + for i=1:ss + n = i + (t-1)*ss; + [bel, lik(n)] = normalise(msg{n}.pi .* msg{n}.lambda); + engine.marginal{i,t} = bel; + end +end + +engine.evidence = evidence; % needed by marginal_nodes and marginal_family +engine.msg = msg; % needed by marginal_family +loglik = sum(log(lik)); + + + +%%%%%%% + +function lambda = compute_lambda(n, cs, msg, ns) +% Pearl p183 eq 4.50 +lambda = prod_lambda_msgs(n, cs, msg, ns); + +%%%%%%% + +function pi_msg = compute_pi_msg(n, cs, msg, c, ns) +% Pearl p183 eq 4.53 and 4.51 +pi_msg = msg{n}.pi .* prod_lambda_msgs(n, cs, msg, ns, c); + +%%%%%%%%% + +function lam = prod_lambda_msgs(n, cs, msg, ns, except) + +if nargin < 5, except = -1; end + +lam = msg{n}.lambda_from_self(:); +%lam = ones(ns(n), 1); +for i=1:length(cs) + c = cs(i); + if c ~= except + lam = lam .* msg{n}.lambda_from_child{i}; + end +end + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/filter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/filter_evidence.m new file mode 100644 index 00000000..a3462437 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/filter_evidence.m @@ -0,0 +1,146 @@ +function [marginal, msg, loglik] = filter_evidence(engine, evidence) + +error('broken'); + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +onodes = engine.onodes; +hnodes = mysetdiff(1:ss, onodes); +hnodes = hnodes(:)'; + +ns = bnet.node_sizes(:); +onodes2 = [onodes(:); onodes(:)+ss]; +ns(onodes2) = 1; + +verbose = 1; +if verbose, fprintf('\nnew filtering\n'); end + +pot_type = 'd'; +niter = engine.max_iter; + +% msg(i1,t1,i2,j2) (i1,t1) -> (i2,t2) +%lambda_msg = cell(ss,T,ss,T); +%pi_msg = cell(ss,T,ss,T); + +% intra_lambda_msg(i,j,t) (i,t) -> (j,t), i is child +% inter_lambda_msg(i,j,t) (i,t+1) -> (j,t), i is child +% inter_pi_msg(i,j,t) (i,t-1) -> (j,t), i is parent +intra_lambda_msg = cell(ss,ss,T); +inter_lambda_msg = cell(ss,ss,T); +inter_pi_msg = cell(ss,ss,T); + +lambda = cell(ss,T); +pi = cell(ss,T); + +for t=1:T + for i=1:ss + lambda{i,t} = ones(ns(i), 1); + pi{i,t} = ones(ns(i), 1); + + cs = children(bnet.intra, i); + for c=cs(:)' + intra_lambda_msg{c,i,t} = ones(ns(i),1); + end + + cs = children(bnet.inter, i); + for c=cs(:)' + inter_lambda_msg{c,i,t} = ones(ns(i),1); + end + + ps = parents(bnet.inter, i); + for p=ps(:)' + inter_pi_msg{p,i,t} = ones(ns(i), 1); % not used for t==1 + end + end +end + +% each hidden node absorbs lambda from its observed child (if any) +for t=1:T + for i=hnodes + c = engine.obschild(i); + if c > 0 + if t==1 + fam = family(bnet.dag, c); + e = bnet.equiv_class(c, 1); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,1)); + else + fam = family(bnet.dag, c, 2); % within 2 slice network + e = bnet.equiv_class(c, 2); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,t-1:t)); + end + temp = pot_to_marginal(CPDpot); + lam_msg = normalise(temp.T); + %if verbose, fprintf('(%d,%d) sends lambda to (%d,%d)\n', c,t, i,t); disp(lam_msg); end + intra_lambda_msg{c,i,t} = lam_msg; + end + end +end + +% FORWARD +for t=1:T + % update pi + for i=hnodes + if t==1 + e = bnet.equiv_class(i,1); + temp = struct(bnet.CPD{e}); + pi{i,t} = temp.CPT; + else + e = bnet.equiv_class(i,2); + temp = struct(bnet.CPD{e}); + ps = parents(bnet.inter, i); + dom = [ps i+ss]; + pot = dpot(dom, ns(dom), temp.CPT); + for p=ps(:)' + temp = dpot(p, ns(p), inter_pi_msg{p,i,t}); + pot = multiply_by_pot(pot, temp); + end + pot = marginalize_pot(pot, i+ss); + temp = pot_to_marginal(pot); + pi{i,t} = temp.T; + %if verbose, fprintf('(%d,%d) computes pi\n', i,t); disp(pi{i,t}); end + end + + c = engine.obschild(i); + if c > 0 + pi{i,t} = normalise(pi{i,t} .* intra_lambda_msg{c,i,t}); + end + %if verbose, fprintf('(%d,%d) recomputes pi\n', i,t); disp(pi{i,t}); end + if verbose, fprintf('%d recomputes pi\n', i+(t-1)*ss); disp(pi{i,t}); end + end + + % send pi msg to children + for i=hnodes + cs = children(bnet.inter, i); + for c=cs(:)' + pot = pi{i,t}; + for k=cs(:)' + if k ~= c + pot = pot .* inter_lambda_msg{k,i,t}; + end + end + cs2 = children(bnet.intra, i); + for k=cs2(:)' + pot = pot .* intra_lambda_msg{k,i,t}; + end + pot = normalise(pot); + %if verbose, fprintf('(%d,%d) sends pi to (%d,%d)\n', i,t, c,t+1); disp(pot); end + if verbose, fprintf('%d sends pi to %d\n', i+(t-1)*ss, c+t*ss); disp(pot); end + inter_pi_msg{i,c,t+1} = pot; + end + end +end + + +marginal = cell(ss,T); +for t=1:T + for i=hnodes + %marginal{i,t} = normalise(pi{i,t} .* lambda{i,t}); + marginal{i,t} = normalise(pi{i,t}); + end +end + +loglik = 0; + +msg.inter_pi_msg = inter_pi_msg; +msg.inter_lambda_msg = inter_lambda_msg; +msg.intra_lambda_msg = intra_lambda_msg; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/filter_evidence_obj_oriented.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/filter_evidence_obj_oriented.m new file mode 100644 index 00000000..fec80b11 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/filter_evidence_obj_oriented.m @@ -0,0 +1,158 @@ +function [marginal, msg, loglik] = filter_evidence_old(engine, evidence) +% [marginal, msg, loglik] = filter_evidence(engine, evidence) (pearl_dbn) + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +bnet2 = dbn_to_bnet(bnet, T); +ns = bnet2.node_sizes; +hnodes = mysetdiff(1:ss, engine.onodes); +hnodes = hnodes(:)'; + +[engine.parent_index, engine.child_index] = mk_pearl_msg_indices(bnet2); + +msg = init_msgs(bnet2.dag, ns, evidence); +msg = init_ev_msgs(engine, evidence, msg); + +verbose = 1; +if verbose, fprintf('\nold filtering\n'); end + +for t=1:T + % update pi + for i=hnodes + n = i + (t-1)*ss; + ps = parents(bnet2.dag, n); + if t==1 + e = bnet.equiv_class(i,1); + else + e = bnet.equiv_class(i,2); + end + msg{n}.pi = compute_pi(bnet.CPD{e}, n, ps, msg); + %if verbose, fprintf('%d computes pi\n', n); disp(msg{n}.pi); end + msg{n}.pi = normalise(msg{n}.pi(:) .* msg{n}.lambda_from_self(:)); + if verbose, fprintf('%d recomputes pi\n', n); disp(msg{n}.pi); end + end + % send pi msg to children + for i=hnodes + n = i + (t-1)*ss; + cs = children(bnet2.dag, n); + for c=cs(:)' + j = engine.parent_index{c}(n); % n is c's j'th parent + pi_msg = normalise(compute_pi_msg(n, cs, msg, c, ns)); + msg{c}.pi_from_parent{j} = pi_msg; + if verbose, fprintf('%d sends pi to %d\n', n,c); disp(pi_msg); end + end + end +end + + +marginal = cell(ss,T); +lik = zeros(1,ss*T); +for t=1:T + for i=1:ss + n = i + (t-1)*ss; + %[bel, lik(n)] = normalise(msg{n}.pi .* msg{n}.lambda); + [bel, lik(n)] = normalise(msg{n}.pi); + marginal{i,t} = bel; + end +end + +loglik = sum(log(lik)); + + + +%%%%%%% + +function lambda = compute_lambda(n, cs, msg, ns) +% Pearl p183 eq 4.50 +lambda = prod_lambda_msgs(n, cs, msg, ns); + +%%%%%%% + +function pi_msg = compute_pi_msg(n, cs, msg, c, ns) +% Pearl p183 eq 4.53 and 4.51 +pi_msg = msg{n}.pi .* prod_lambda_msgs(n, cs, msg, ns, c); + +%%%%%%%%% + +function lam = prod_lambda_msgs(n, cs, msg, ns, except) + +if nargin < 5, except = -1; end + +%lam = msg{n}.lambda_from_self(:); +lam = ones(ns(n), 1); +for i=1:length(cs) + c = cs(i); + if c ~= except + lam = lam .* msg{n}.lambda_from_child{i}; + end +end + + +%%%%%%%%%%% + +function msg = init_msgs(dag, ns, evidence) +% INIT_MSGS Initialize the lambda/pi message and state vectors (pearl_dbn) +% msg = init_msgs(dag, ns, evidence) +% +% We assume all the hidden nodes are discrete. + +N = length(dag); +msg = cell(1,N); +observed = ~isemptycell(evidence(:)); + +for n=1:N + ps = parents(dag, n); + msg{n}.pi_from_parent = cell(1, length(ps)); + for i=1:length(ps) + p = ps(i); + msg{n}.pi_from_parent{i} = ones(ns(p), 1); + end + + cs = children(dag, n); + msg{n}.lambda_from_child = cell(1, length(cs)); + for i=1:length(cs) + c = cs(i); + msg{n}.lambda_from_child{i} = ones(ns(n), 1); + end + + msg{n}.lambda = ones(ns(n), 1); + msg{n}.pi = ones(ns(n), 1); + + msg{n}.lambda_from_self = ones(ns(n), 1); +end + + +%%%%%%%%% + +function msg = init_ev_msgs(engine, evidence, msg) +% Initialize the lambdas with any evidence + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +pot_type = 'd'; +t = 1; +hnodes = mysetdiff(1:ss, engine.onodes); +for i=hnodes(:)' + c = engine.obschild(i); + if c > 0 + fam = family(bnet.dag, c); + e = bnet.equiv_class(c, 1); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,1)); + temp = pot_to_marginal(CPDpot); + n = i; + msg{n}.lambda_from_self = temp.T; + end +end +for t=2:T + for i=hnodes(:)' + c = engine.obschild(i); + if c > 0 + fam = family(bnet.dag, c, 2); + e = bnet.equiv_class(c, 2); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,t-1:t)); + temp = pot_to_marginal(CPDpot); + n = i + (t-1)*ss; + msg{n}.lambda_from_self = temp.T; + end + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/smooth_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/smooth_evidence.m new file mode 100644 index 00000000..554b579f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/smooth_evidence.m @@ -0,0 +1,181 @@ +function [marginal, msg, loglik] = smooth_evidence(engine, evidence) + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +onodes = engine.onodes; +hnodes = mysetdiff(1:ss, onodes); +hnodes = hnodes(:)'; + +ns = bnet.node_sizes(:); +onodes2 = [onodes(:); onodes(:)+ss]; +ns(onodes2) = 1; + +verbose = 0; +pot_type = 'd'; +niter = engine.max_iter; + +if verbose, fprintf('new smooth\n'); end + +% msg(i1,t1,i2,j2) (i1,t1) -> (i2,t2) +%lambda_msg = cell(ss,T,ss,T); +%pi_msg = cell(ss,T,ss,T); + +% intra_lambda_msg(i,j,t) (i,t) -> (j,t), i is child +% inter_lambda_msg(i,j,t) (i,t+1) -> (j,t), i is child +% inter_pi_msg(i,j,t) (i,t-1) -> (j,t), i is parent +intra_lambda_msg = cell(ss,ss,T); +inter_lambda_msg = cell(ss,ss,T); +inter_pi_msg = cell(ss,ss,T); + +lambda = cell(ss,T); +pi = cell(ss,T); + +for t=1:T + for i=1:ss + lambda{i,t} = ones(ns(i), 1); + pi{i,t} = ones(ns(i), 1); + + cs = children(bnet.intra, i); + for c=cs(:)' + intra_lambda_msg{c,i,t} = ones(ns(i),1); + end + + cs = children(bnet.inter, i); + for c=cs(:)' + inter_lambda_msg{c,i,t} = ones(ns(i),1); + end + + ps = parents(bnet.inter, i); + for p=ps(:)' + inter_pi_msg{p,i,t} = ones(ns(i), 1); % not used for t==1 + end + end +end + + +% each hidden node absorbs lambda from its observed child (if any) +for t=1:T + for i=hnodes + c = engine.obschild(i); + if c > 0 + if t==1 + fam = family(bnet.dag, c); + e = bnet.equiv_class(c, 1); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,1)); + else + fam = family(bnet.dag, c, 2); % within 2 slice network + e = bnet.equiv_class(c, 2); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,t-1:t)); + end + temp = pot_to_marginal(CPDpot); + lam_msg = normalise(temp.T); + intra_lambda_msg{c,i,t} = lam_msg; + end + end +end + +for iter=1:engine.max_iter + % FORWARD + for t=1:T + % update pi + for i=hnodes + if t==1 + e = bnet.equiv_class(i,1); + CPD = struct(bnet.CPD{e}); + pi{i,t} = CPD.CPT; + else + e = bnet.equiv_class(i,2); + CPD = struct(bnet.CPD{e}); + ps = parents(bnet.inter, i); + dom = [ps i+ss]; + pot = dpot(dom, ns(dom), CPD.CPT); + for p=ps(:)' + temp = dpot(p, ns(p), inter_pi_msg{p,i,t}); + pot = multiply_by_pot(pot, temp); + end + pot = marginalize_pot(pot, i+ss); + temp = pot_to_marginal(pot); + pi{i,t} = temp.T; + end + if verbose, fprintf('%d updates pi\n', i+(t-1)*ss); disp(pi{i,t}); end + end + + % send pi msg to children + for i=hnodes + cs = children(bnet.inter, i); + for c=cs(:)' + pot = pi{i,t}; + for k=cs(:)' + if k ~= c + pot = pot .* inter_lambda_msg{k,i,t}; + end + end + cs2 = children(bnet.intra, i); + for k=cs2(:)' + pot = pot .* intra_lambda_msg{k,i,t}; + end + inter_pi_msg{i,c,t+1} = normalise(pot); + if verbose, fprintf('%d sends pi to %d\n', i+(t-1)*ss, c+t*ss); disp(inter_pi_msg{i,c,t+1}); end + end + end + end + + if verbose, fprintf('backwards\n'); end + % BACKWARD + for t=T:-1:1 + % update lambda + for i=hnodes + pot = ones(ns(i), 1); + cs = children(bnet.inter, i); + for c=cs(:)' + pot = pot .* inter_lambda_msg{c,i,t}; + end + cs = children(bnet.intra, i); + for c=cs(:)' + pot = pot .* intra_lambda_msg{c,i,t}; + end + lambda{i,t} = normalise(pot); + if verbose, fprintf('%d computes lambda\n', i+(t-1)*ss); disp(lambda{i,t}); end + end + + % send lambda msgs to hidden parents in prev slcie + for i=hnodes + ps = parents(bnet.inter, i); + if t > 1 + e = bnet.equiv_class(i, 2); + CPD = struct(bnet.CPD{e}); + fam = [ps i+ss]; + for p=ps(:)' + pot = dpot(fam, ns(fam), CPD.CPT); + temp = dpot(i+ss, ns(i), lambda{i,t}); + pot = multiply_by_pot(pot, temp); + for k=ps(:)' + if k ~= p + temp = dpot(k, ns(k), inter_pi_msg{k,i,t}); + pot = multiply_by_pot(pot, temp); + end + end + pot = marginalize_pot(pot, p); + temp = pot_to_marginal(pot); + inter_lambda_msg{i,p,t-1} = normalise(temp.T); + if verbose, fprintf('%d sends lambda to %d\n', i+(t-1)*ss, p+(t-2)*ss); disp(inter_lambda_msg{i,p,t-1}); end + end + end + end + end +end + + + +marginal = cell(ss,T); +for t=1:T + for i=hnodes + marginal{i,t} = normalise(pi{i,t} .* lambda{i,t}); + end +end + +loglik = 0; + +msg.inter_pi_msg = inter_pi_msg; +msg.inter_lambda_msg = inter_lambda_msg; +msg.intra_lambda_msg = intra_lambda_msg; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/smooth_evidence_fast.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/smooth_evidence_fast.m new file mode 100644 index 00000000..8f4ebd2f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/smooth_evidence_fast.m @@ -0,0 +1,179 @@ +function [marginal, msg, loglik] = smooth_evidence_fast(engine, evidence) + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +onodes = engine.onodes; +hnodes = mysetdiff(1:ss, onodes); +hnodes = hnodes(:)'; + +ns = bnet.node_sizes(:); +onodes2 = [onodes(:); onodes(:)+ss]; +ns(onodes2) = 1; + +verbose = 0; +pot_type = 'd'; +niter = engine.max_iter; + +if verbose, fprintf('new smooth\n'); end + +% msg(i1,t1,i2,j2) (i1,t1) -> (i2,t2) +%lambda_msg = cell(ss,T,ss,T); +%pi_msg = cell(ss,T,ss,T); + +% intra_lambda_msg(i,j,t) (i,t) -> (j,t), i is child +% inter_lambda_msg(i,j,t) (i,t+1) -> (j,t), i is child +% inter_pi_msg(i,j,t) (i,t-1) -> (j,t), i is parent +intra_lambda_msg = cell(ss,ss,T); +inter_lambda_msg = cell(ss,ss,T); +inter_pi_msg = cell(ss,ss,T); + +lambda = cell(ss,T); +pi = cell(ss,T); + +for t=1:T + for i=1:ss + lambda{i,t} = ones(ns(i), 1); + pi{i,t} = ones(ns(i), 1); + + cs = children(bnet.intra, i); + for c=cs(:)' + intra_lambda_msg{c,i,t} = ones(ns(i),1); + end + + cs = children(bnet.inter, i); + for c=cs(:)' + inter_lambda_msg{c,i,t} = ones(ns(i),1); + end + + ps = parents(bnet.inter, i); + for p=ps(:)' + inter_pi_msg{p,i,t} = ones(ns(i), 1); % not used for t==1 + end + end +end + + +% each hidden node absorbs lambda from its observed child (if any) +for t=1:T + for i=hnodes + c = engine.obschild(i); + if c > 0 + if t==1 + fam = family(bnet.dag, c); + e = bnet.equiv_class(c, 1); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,1)); + else + fam = family(bnet.dag, c, 2); % within 2 slice network + e = bnet.equiv_class(c, 2); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,t-1:t)); + end + temp = pot_to_marginal(CPDpot); + lam_msg = normalise(temp.T); + intra_lambda_msg{c,i,t} = lam_msg; + end + end +end + +for iter=1:engine.max_iter + % FORWARD + for t=1:T + % update pi + for i=hnodes + if t==1 + e = bnet.equiv_class(i,1); + temp = struct(bnet.CPD{e}); + pi{i,t} = temp.CPT; + else + e = bnet.equiv_class(i,2); + CPD = struct(bnet.CPD{e}); + ps = parents(bnet.inter, i); + temp = CPD.CPT; + for p=ps(:)' + temp(:) = temp(:) .* inter_pi_msg{p,i,t}(engine.mult_parent_ndx{i,p}); + end + dom = [ps i+ss]; + pot = dpot(dom, ns(dom), temp); + pot = marginalize_pot(pot, i+ss); + temp = pot_to_marginal(pot); + pi{i,t} = temp.T; + end + if verbose, fprintf('%d updates pi\n', i+(t-1)*ss); disp(pi{i,t}); end + end + + % send pi msg to children + for i=hnodes + cs = children(bnet.inter, i); + for c=cs(:)' + pot = pi{i,t}; + for k=cs(:)' + if k ~= c + pot = pot .* inter_lambda_msg{k,i,t}; + end + end + cs2 = children(bnet.intra, i); + for k=cs2(:)' + pot = pot .* intra_lambda_msg{k,i,t}; + end + inter_pi_msg{i,c,t+1} = normalise(pot); + if verbose, fprintf('%d sends pi to %d\n', i+(t-1)*ss, c+t*ss); disp(inter_pi_msg{i,c,t+1}); end + end + end + end + + if verbose, fprintf('backwards\n'); end + % BACKWARD + for t=T:-1:1 + % update lambda + for i=hnodes + pot = ones(ns(i), 1); + cs = children(bnet.inter, i); + for c=cs(:)' + pot = pot .* inter_lambda_msg{c,i,t}; + end + cs = children(bnet.intra, i); + for c=cs(:)' + pot = pot .* intra_lambda_msg{c,i,t}; + end + lambda{i,t} = normalise(pot); + if verbose, fprintf('%d computes lambda\n', i+(t-1)*ss); disp(lambda{i,t}); end + end + + % send lambda msgs to hidden parents in prev slcie + for i=hnodes + ps = parents(bnet.inter, i); + if t > 1 + e = bnet.equiv_class(i, 2); + CPD = struct(bnet.CPD{e}); + for p=ps(:)' + temp = CPD.CPT(:) .* lambda{i,t}(engine.mult_self_ndx{i}); + for k=ps(:)' + if k ~= p + temp(:) = temp(:) .* inter_pi_msg{k,i,t}(engine.mult_parent_ndx{i,k}); + end + end + fam = [ps i+ss]; + pot = dpot(fam, ns(fam), temp); + pot = marginalize_pot(pot, p); + temp = pot_to_marginal(pot); + inter_lambda_msg{i,p,t-1} = normalise(temp.T); + if verbose, fprintf('%d sends lambda to %d\n', i+(t-1)*ss, p+(t-2)*ss); disp(inter_lambda_msg{i,p,t-1}); end + end + end + end + end +end + + + +marginal = cell(ss,T); +for t=1:T + for i=hnodes + marginal{i,t} = normalise(pi{i,t} .* lambda{i,t}); + end +end + +loglik = 0; + +msg.inter_pi_msg = inter_pi_msg; +msg.inter_lambda_msg = inter_lambda_msg; +msg.intra_lambda_msg = intra_lambda_msg; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/wrong_smooth.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/wrong_smooth.m new file mode 100644 index 00000000..d66d61ad --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/Old/wrong_smooth.m @@ -0,0 +1,210 @@ +function [marginal, msg, loglik] = smooth_evidence(engine, evidence) +% [marginal, msg, loglik] = smooth_evidence(engine, evidence) (pearl_dbn) + +disp('warning: pearl_dbn smoothing is broken'); + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +bnet2 = dbn_to_bnet(bnet, T); +ns = bnet2.node_sizes; +hnodes = mysetdiff(1:ss, engine.onodes); +hnodes = hnodes(:)'; + +onodes2 = unroll_set(engine.onodes(:), ss, T); +onodes2 = onodes2(:)'; + +hnodes2 = unroll_set(hnodes(:), ss, T); +hnodes2 = hnodes2(:)'; + +[engine.parent_index, engine.child_index] = mk_pearl_msg_indices(bnet2); + +msg = init_msgs(bnet2.dag, ns, evidence, bnet2.equiv_class, bnet2.CPD); + +verbose = 0; +pot_type = 'd'; +niter = 1; +for iter=1:niter + % FORWARD + for t=1:T + if verbose, fprintf('t=%d\n', t); end + + % each hidden node absorbs lambda from its observed child (if any) + for i=hnodes + c = engine.obschild(i); + if c > 0 + if t==1 + fam = family(bnet.dag, c); + e = bnet.equiv_class(c, 1); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,1)); + else + fam = family(bnet.dag, 2); % within 2 slice network + e = bnet.equiv_class(c, 2); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,t-1:t)); + end + temp = pot_to_marginal(CPDpot); + n = i + (t-1)*ss; + lam_msg = normalise(temp.T); + j = engine.child_index{n}(c+(t-1)*ss); + assert(j==1); + msg{n}.lambda_from_child{j} = lam_msg; + if verbose, fprintf('%d sends lambda to %d\n', c + (t-1)*ss, n); disp(lam_msg); end + end + end + + % update pi + for i=hnodes + n = i + (t-1)*ss; + ps = parents(bnet2.dag, n); + if t==1 + e = bnet.equiv_class(i,1); + else + e = bnet.equiv_class(i,2); + end + msg{n}.pi = compute_pi(bnet.CPD{e}, n, ps, msg); + if verbose, fprintf('%d computes pi\n', n); disp(msg{n}.pi); end + end + + % send pi msg to children in next slice + for i=hnodes + n = i + (t-1)*ss; + %cs = myintersect(children(bnet2.dag, n), hnodes2); + cs = children(bnet2.dag, n); + for c=cs(:)' + j = engine.parent_index{c}(n); % n is c's j'th parent + pi_msg = normalise(compute_pi_msg(n, cs, msg, c, ns)); + msg{c}.pi_from_parent{j} = pi_msg; + if verbose, fprintf('%d sends pi to %d\n', n, c); disp(pi_msg); end + end + end + end + + % BACKWARD + for t=T:-1:1 + if verbose, fprintf('t = %d\n', t); end + + % update lambda + for i=hnodes + n = i + (t-1)*ss; + cs = children(bnet2.dag, n); + msg{n}.lambda = compute_lambda(n, cs, msg, ns); + if verbose, fprintf('%d computes lambda\n', n); disp(msg{n}.lambda); end + end + + % send lambda msgs to hidden parents in prev slcie + for i=hnodes + n = i + (t-1)*ss; + %ps = myintersect(parents(bnet2.dag, n), hnodes2); + ps = parents(bnet2.dag, n); + for p=ps(:)' + j = engine.child_index{p}(n); % n is p's j'th child + if t > 1 + e = bnet.equiv_class(i, 2); + else + e = bnet.equiv_class(i, 1); + end + lam_msg = normalise(compute_lambda_msg(bnet.CPD{e}, n, ps, msg, p)); + msg{p}.lambda_from_child{j} = lam_msg; + if verbose, fprintf('%d sends lambda to %d\n', n, p); disp(lam_msg); end + end + end + + % send pi msg to observed children + if 0 + for i=hnodes + n = i + (t-1)*ss; + cs = myintersect(children(bnet2.dag, n), onodes2); + %cs = children(bnet2.dag, n); + for c=cs(:)' + j = engine.parent_index{c}(n); % n is c's j'th parent + pi_msg = normalise(compute_pi_msg(n, cs, msg, c, ns)); + msg{c}.pi_from_parent{j} = pi_msg; + if verbose, fprintf('%d sends pi to %d\n', n, c); disp(pi_msg); end + end + end + end + + end +end + + +marginal = cell(ss,T); +lik = zeros(1,ss*T); +for t=1:T + for i=hnodes + n = i + (t-1)*ss; + [bel, lik(n)] = normalise(msg{n}.pi .* msg{n}.lambda); + marginal{i,t} = bel; + end +end + +loglik = 0; +%loglik = sum(log(lik)); + + + +%%%%%%% + +function lambda = compute_lambda(n, cs, msg, ns) +% Pearl p183 eq 4.50 +lambda = prod_lambda_msgs(n, cs, msg, ns); + +%%%%%%% + +function pi_msg = compute_pi_msg(n, cs, msg, c, ns) +% Pearl p183 eq 4.53 and 4.51 +pi_msg = msg{n}.pi .* prod_lambda_msgs(n, cs, msg, ns, c); + +%%%%%%%%% + +function lam = prod_lambda_msgs(n, cs, msg, ns, except) + +if nargin < 5, except = -1; end + +%lam = msg{n}.lambda_from_self(:); +lam = ones(ns(n), 1); +for i=1:length(cs) + c = cs(i); + if c ~= except + lam = lam .* msg{n}.lambda_from_child{i}; + end +end + + +%%%%%%%%% + +function msg = init_msgs(dag, ns, evidence, eclass, CPD) +% INIT_MSGS Initialize the lambda/pi message and state vectors (pearl_dbn) +% msg = init_msgs(dag, ns, evidence) + +N = length(dag); +msg = cell(1,N); +observed = ~isemptycell(evidence(:)); + +for n=1:N + ps = parents(dag, n); + msg{n}.pi_from_parent = cell(1, length(ps)); + for i=1:length(ps) + p = ps(i); + msg{n}.pi_from_parent{i} = ones(ns(p), 1); + end + + cs = children(dag, n); + msg{n}.lambda_from_child = cell(1, length(cs)); + for i=1:length(cs) + c = cs(i); + msg{n}.lambda_from_child{i} = ones(ns(n), 1); + end + + msg{n}.lambda = ones(ns(n), 1); + msg{n}.pi = ones(ns(n), 1); + + % Initialize the lambdas with any evidence + if observed(n) + v = evidence{n}; + msg{n}.lambda = zeros(ns(n), 1); + msg{n}.lambda(v) = 1; % delta function + msg{n}.lambda = []; + end + +end + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/enter_evidence.m new file mode 100644 index 00000000..624cac96 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/enter_evidence.m @@ -0,0 +1,35 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (loopy_dbn) +% [engine, loglik] = enter_evidence(engine, evidence, ....) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product (not yet supported), else sum-product [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +assert(~maximize); +assert(~filter); + +[engine.marginal, engine.msg, loglik] = enter_soft_ev(engine, evidence); +engine.evidence = evidence; % needed by marginal_nodes and marginal_family diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/enter_soft_ev.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/enter_soft_ev.m new file mode 100644 index 00000000..5c88f53f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/enter_soft_ev.m @@ -0,0 +1,137 @@ +function [marginal, msg, loglik] = enter_soft_ev(engine, evidence) +% [marginal, msg, loglik] = smooth_evidence(engine, evidence) (pearl_dbn) + + +[ss T] = size(evidence); +bnet = bnet_from_engine(engine); +bnet2 = dbn_to_bnet(bnet, T); +ns = bnet2.node_sizes; +hnodes = mysetdiff(1:ss, engine.onodes); +hnodes = hnodes(:)'; + +onodes2 = unroll_set(engine.onodes(:), ss, T); +onodes2 = onodes2(:)'; + +hnodes2 = unroll_set(hnodes(:), ss, T); +hnodes2 = hnodes2(:)'; + +[engine.parent_index, engine.child_index] = mk_pearl_msg_indices(bnet2); + +rand_init = 0; +use_ev = 0; +msg = init_pearl_msgs(bnet2.dag, ns, evidence, rand_init, use_ev); +msg = init_pearl_dbn_ev_msgs(bnet, evidence, engine); + +verbose = 0; +pot_type = 'd'; +niter = engine.max_iter; + +if verbose, fprintf('old smooth\n'); end + +for iter=1:niter + % FORWARD + for t=1:T + if verbose, fprintf('t=%d\n', t); end + + % update pi + for i=hnodes + n = i + (t-1)*ss; + ps = parents(bnet2.dag, n); + if t==1 + e = bnet.equiv_class(i,1); + else + e = bnet.equiv_class(i,2); + end + msg{n}.pi = compute_pi(bnet.CPD{e}, n, ps, msg); + if verbose, fprintf('%d computes pi\n', n); disp(msg{n}.pi); end + end + + % send pi msg to children + for i=hnodes + n = i + (t-1)*ss; + %cs = myintersect(children(bnet2.dag, n), hnodes2); + cs = children(bnet2.dag, n); % must use all children to get index right + for c=cs(:)' + j = engine.parent_index{c}(n); % n is c's j'th parent + pi_msg = normalise(compute_pi_msg(n, cs, msg, c, ns)); + msg{c}.pi_from_parent{j} = pi_msg; + if verbose, fprintf('%d sends pi to %d\n', n, c); disp(pi_msg); end + end + end + end + + % BACKWARD + for t=T:-1:1 + if verbose, fprintf('t = %d\n', t); end + + % update lambda + for i=hnodes + n = i + (t-1)*ss; + cs = children(bnet2.dag, n); + msg{n}.lambda = compute_lambda(n, cs, msg, ns); + if verbose, fprintf('%d computes lambda\n', n); disp(msg{n}.lambda); end + end + + % send lambda msgs to hidden parents in prev slcie + for i=hnodes + n = i + (t-1)*ss; + ps = parents(bnet2.dag, n); + for p=ps(:)' + j = engine.child_index{p}(n); % n is p's j'th child + if t > 1 + e = bnet.equiv_class(i, 2); + else + e = bnet.equiv_class(i, 1); + end + lam_msg = normalise(compute_lambda_msg(bnet.CPD{e}, n, ps, msg, p)); + msg{p}.lambda_from_child{j} = lam_msg; + if verbose, fprintf('%d sends lambda to %d\n', n, p); disp(lam_msg); end + end + end + + end +end + + +marginal = cell(ss,T); +lik = zeros(1,ss*T); +for t=1:T + for i=hnodes + n = i + (t-1)*ss; + [bel, lik(n)] = normalise(msg{n}.pi .* msg{n}.lambda); + marginal{i,t} = bel; + end +end + +loglik = 0; +%loglik = sum(log(lik)); + + + +%%%%%%% + +function lambda = compute_lambda(n, cs, msg, ns) +% Pearl p183 eq 4.50 +lambda = prod_lambda_msgs(n, cs, msg, ns); + +%%%%%%% + +function pi_msg = compute_pi_msg(n, cs, msg, c, ns) +% Pearl p183 eq 4.53 and 4.51 +pi_msg = msg{n}.pi .* prod_lambda_msgs(n, cs, msg, ns, c); + +%%%%%%%%% + +function lam = prod_lambda_msgs(n, cs, msg, ns, except) + +if nargin < 5, except = -1; end + +%lam = msg{n}.lambda_from_self(:); +lam = ones(ns(n), 1); +for i=1:length(cs) + c = cs(i); + if c ~= except + lam = lam .* msg{n}.lambda_from_child{i}; + end +end + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..9440459a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/marginal_nodes.m @@ -0,0 +1,18 @@ +function marginal = marginal_nodes(engine, nodes, t) +% MARGINAL_NODES Compute the marginal on the specified query nodes (pearl_dbn) +% marginal = marginal_nodes(engine, i, t) +% returns Pr(X(i,t) | Y(1:T)), where X(i,t) is the i'th node in the t'th slice. +% If enter_evidence used filtering instead of smoothing, this will return Pr(X(i,t) | Y(1:t)). + +if nargin < 3, t = 1; end +assert(length(nodes)==1); +i = nodes(end); +if ~myismember(i, engine.onodes) + marginal.T = engine.marginal{i,t}; +else + marginal.T = 1; % observed +end + +% we convert the domain to the unrolled numbering system +% so that update_ess extracts the right evidence. +marginal.domain = nodes+(t-1)*engine.ss; diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/pearl_dbn_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/pearl_dbn_inf_engine.m new file mode 100644 index 00000000..2e0509fd --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/pearl_dbn_inf_engine.m @@ -0,0 +1,65 @@ +function engine = pearl_dbn_inf_engine(bnet, varargin) +% LOOPY_DBN_INF_ENGINE Loopy Pearl version of forwards-backwards +% engine = loopy_dbn_inf_engine(bnet, ...) +% +% Optional arguments +% 'max_iter' - specifies the max num. forward-backward passes to perform [1] +% 'tol' - as in loopy_pearl [1e-3] +% 'momentum' - as in loopy_pearl [0] + +error('pearl_dbn does not work yet') + +max_iter = 1; +tol = 1e-3; +momentum = 0; + +if nargin >= 2 + args = varargin; + nargs = length(args); + for i=1:2:nargs + switch args{i}, + case 'max_iter', max_iter = args{i+1}; + case 'tol', tol = args{i+1}; + case 'momentum', momentum = args{i+1}; + end + end +end + + +engine.max_iter = max_iter; +engine.tol = tol; +engine.momentum = momentum; +engine.pearl_engine = []; +engine.T = []; +engine.ss = length(bnet.intra); + +engine.marginal = []; +engine.evidence = []; +engine.msg = []; +engine.parent_index = []; +engine.child_index = []; +%[engine.parent_index, engine.child_index] = mk_pearl_msg_indices(bnet); % need to unroll first + +ss = length(bnet.intra); +engines.ss = ss; +onodes = bnet.observed; +hnodes = mysetdiff(1:ss, onodes); +obschild = zeros(1,ss); +for i=hnodes(:)' + %ocs = myintersect(children(bnet.dag, i), onodes); + ocs = children(bnet.intra, i); + assert(length(ocs) <= 1); + if length(ocs)==1 + obschild(i) = ocs(1); + end +end +engine.obschild = obschild; + +engine.mult_self_ndx = []; +engine.mult_parent_ndx = []; +engine.marg_self_ndx = []; +engine.marg_parent_ndx = []; + + +engine = class(engine, 'loopy_dbn_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/CVS/Entries new file mode 100644 index 00000000..e35b6662 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/CVS/Entries @@ -0,0 +1,2 @@ +/init_pearl_dbn_ev_msgs.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/CVS/Repository new file mode 100644 index 00000000..2cb0fa7a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@pearl_dbn_inf_engine/private diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/init_pearl_dbn_ev_msgs.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/init_pearl_dbn_ev_msgs.m new file mode 100644 index 00000000..893af2ae --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_dbn_inf_engine/private/init_pearl_dbn_ev_msgs.m @@ -0,0 +1,28 @@ +function msg = init_pearl_dbn_ev_msgs(bnet, evidence, engine) + +[ss T] = size(evidence); +pot_type = 'd'; + +% each hidden node absorbs lambda from its observed child (if any) +for t=1:T + for i=hnodes + c = engine.obschild(i); + if c > 0 + if t==1 + fam = family(bnet.dag, c); + e = bnet.equiv_class(c, 1); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,1)); + else + fam = family(bnet.dag, c, 2); % within 2 slice network + e = bnet.equiv_class(c, 2); + CPDpot = CPD_to_pot(pot_type, bnet.CPD{e}, fam, bnet.node_sizes(:), bnet.cnodes(:), evidence(:,t-1:t)); + end + temp = pot_to_marginal(CPDpot); + n = i + (t-1)*ss; + lam_msg = normalise(temp.T); + j = engine.child_index{n}(c+(t-1)*ss); + assert(j==1); + msg{n}.lambda_from_child{j} = lam_msg; + end + end +end diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/CVS/Entries new file mode 100644 index 00000000..d2a809f0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/CVS/Entries @@ -0,0 +1,6 @@ +/enter_evidence.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_family.m/1.1.1.1/Wed May 29 15:59:56 2002// +/marginal_nodes.m/1.1.1.1/Wed May 29 15:59:56 2002// +/pearl_unrolled_dbn_inf_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +/update_engine.m/1.1.1.1/Wed May 29 15:59:56 2002// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/CVS/Repository new file mode 100644 index 00000000..5c0fed50 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/enter_evidence.m new file mode 100644 index 00000000..a9731ffd --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/enter_evidence.m @@ -0,0 +1,41 @@ +function [engine, loglik, niter] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (loopy_unrolled_dbn) +% [engine, loglik, niter] = enter_evidence(engine, evidence, ....) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product (not yet supported), else sum-product [0] +% filename - as in loopy_pearl +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filename = engine.filename; + +if nargin >= 2 + args = varargin; + nargs = length(args); + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filename', filename = args{i+1}; + end + end +end + + +[ss T] = size(evidence); +if T ~= engine.T + bnetT = dbn_to_bnet(bnet_from_engine(engine), T); + engine.unrolled_engine = pearl_inf_engine(bnetT, 'protocol', engine.protocol, ... + 'max_iter', engine.max_iter_per_slice * T, ... + 'tol', engine.tol, 'momentum', engine.momentum); + engine.T = T; +end +[engine.unrolled_engine, loglik, niter] = enter_evidence(engine.unrolled_engine, evidence(:), ... + 'maximize', maximize, 'filename', filename); + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/marginal_family.m new file mode 100644 index 00000000..a40f2974 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/marginal_family.m @@ -0,0 +1,11 @@ +function marginal = marginal_family(engine, i, t, add_ev) +% MARGINAL_FAMILY Compute the marginal on the specified family (jtree_unrolled_dbn) +% marginal = marginal_family(engine, i, t) + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end +assert(~add_ev); + +%marginal = marginal_family(engine.unrolled_engine, i + (t-1)*engine.ss, add_ev); +marginal = marginal_family(engine.unrolled_engine, i + (t-1)*engine.ss); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..0fb095e5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/marginal_nodes.m @@ -0,0 +1,16 @@ +function marginal = marginal_nodes(engine, nodes, t, add_ev) +% MARGINAL_NODES Compute the marginal on the specified query nodes (loopy_unrolled_dbn) +% marginal = marginal_nodes(engine, nodes, t) +% +% 't' specifies the time slice of the earliest node in 'nodes'. +% 'nodes' must occur in some clique. +% +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3, +% i.e., nodes 3 and 5 in the unrolled network, + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end + +marginal = marginal_nodes(engine.unrolled_engine, nodes + (t-1)*engine.ss, add_ev); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/pearl_unrolled_dbn_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/pearl_unrolled_dbn_inf_engine.m new file mode 100644 index 00000000..637d29c8 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/pearl_unrolled_dbn_inf_engine.m @@ -0,0 +1,39 @@ +function engine = pearl_unrolled_dbn_inf_engine(bnet, varargin) +% LOOPY_DBN_INF_ENGINE Loopy Pearl version of forwards-backwards +% engine = loopy_unrolld_dbn_inf_engine(bnet, ...) +% +% Optional arguments +% 'max_iter' - specifies the max num. forward-backward passes to perform PER SLICE [2] +% 'tol' - as in loopy_pearl [1e-3] +% 'momentum' - as in loopy_pearl [0] +% protocol - tree or parallel [parallel] +% filename - as in pearl [ '' ] + +max_iter_per_slice = 2; +tol = 1e-3; +momentum = 0; +protocol = 'parallel'; +filename = ''; + +args = varargin; +for i=1:2:length(args) + switch args{i}, + case 'max_iter', max_iter_per_slice = args{i+1}; + case 'tol', tol = args{i+1}; + case 'momentum', momentum = args{i+1}; + case 'protocol', protocol = args{i+1}; + case 'filename', filename = args{i+1}; + end +end + +engine.filename = filename; +engine.max_iter_per_slice = max_iter_per_slice; +engine.tol = tol; +engine.momentum = momentum; +engine.unrolled_engine = []; +engine.T = -1; +engine.ss = length(bnet.intra); +engine.protocol = protocol; + +engine = class(engine, 'pearl_unrolled_dbn_inf_engine', inf_engine(bnet)); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/update_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/update_engine.m new file mode 100644 index 00000000..e8613a43 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@pearl_unrolled_dbn_inf_engine/update_engine.m @@ -0,0 +1,6 @@ +function engine = update_engine(engine, newCPDs) +% UPDATE_ENGINE Update the engine to take into account the new parameters (pearl_unrolled_dbn) +% engine = update_engine(engine, newCPDs) + +engine.inf_engine = update_engine(engine.inf_engine, newCPDs); +engine.unrolled_engine = update_engine(engine.unrolled_engine, newCPDs); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/CVS/Entries new file mode 100644 index 00000000..cb266b88 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/CVS/Entries @@ -0,0 +1,7 @@ +/enter_evidence.m/1.1.1.1/Wed Feb 19 09:52:12 2003// +/marginal_family.m/1.1.1.1/Wed Feb 19 09:52:12 2003// +/marginal_nodes.m/1.1.1.1/Wed Feb 19 09:52:12 2003// +/stable_ho_inf_engine.m/1.1.1.1/Fri Mar 14 09:45:34 2003// +/test_ho_inf_enginge.m/1.1.1.1/Wed Feb 19 09:52:12 2003// +/update_engine.m/1.1.1.1/Wed Feb 19 09:52:12 2003// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/CVS/Repository new file mode 100644 index 00000000..0769f5f6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic/@stable_ho_inf_engine diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/enter_evidence.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/enter_evidence.m new file mode 100644 index 00000000..48b230c9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/enter_evidence.m @@ -0,0 +1,43 @@ +function [engine, loglik] = enter_evidence(engine, evidence, varargin) +% ENTER_EVIDENCE Add the specified evidence to the network (jtree_unrolled_dbn) +% [engine, loglik] = enter_evidence(engine, evidence, ...) +% +% evidence{i,t} = [] if if X(i,t) is hidden, and otherwise contains its observed value (scalar or column vector) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% maximize - if 1, does max-product instead of sum-product [0] +% filter - if 1, does filtering (not supported), else smoothing [0] +% +% e.g., engine = enter_evidence(engine, ev, 'maximize', 1) + +maximize = 0; +filter = 0; + +% parse optional params +args = varargin; +nargs = length(args); +if nargs > 0 + for i=1:2:nargs + switch args{i}, + case 'maximize', maximize = args{i+1}; + case 'filter', filter = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end +end + +if filter + error('jtree_unrolled_dbn does not support filtering') +end + +if size(evidence,2) ~= engine.nslices + error(['engine was created assuming there are ' num2str(engine.nslices) ... + ' slices, but evidence has ' num2str(size(evidence,2))]) +end + +[engine.unrolled_engine, loglik] = enter_evidence(engine.unrolled_engine, evidence, 'maximize', maximize); + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/marginal_family.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/marginal_family.m new file mode 100644 index 00000000..a40f2974 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/marginal_family.m @@ -0,0 +1,11 @@ +function marginal = marginal_family(engine, i, t, add_ev) +% MARGINAL_FAMILY Compute the marginal on the specified family (jtree_unrolled_dbn) +% marginal = marginal_family(engine, i, t) + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end +assert(~add_ev); + +%marginal = marginal_family(engine.unrolled_engine, i + (t-1)*engine.ss, add_ev); +marginal = marginal_family(engine.unrolled_engine, i + (t-1)*engine.ss); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/marginal_nodes.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/marginal_nodes.m new file mode 100644 index 00000000..0fb095e5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/marginal_nodes.m @@ -0,0 +1,16 @@ +function marginal = marginal_nodes(engine, nodes, t, add_ev) +% MARGINAL_NODES Compute the marginal on the specified query nodes (loopy_unrolled_dbn) +% marginal = marginal_nodes(engine, nodes, t) +% +% 't' specifies the time slice of the earliest node in 'nodes'. +% 'nodes' must occur in some clique. +% +% Example: +% Consider a DBN with 2 nodes per slice. +% Then t=2, nodes=[1 3] refers to node 1 in slice 2 and node 1 in slice 3, +% i.e., nodes 3 and 5 in the unrolled network, + +if nargin < 3, t = 1; end +if nargin < 4, add_ev = 0; end + +marginal = marginal_nodes(engine.unrolled_engine, nodes + (t-1)*engine.ss, add_ev); diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/stable_ho_inf_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/stable_ho_inf_engine.m new file mode 100644 index 00000000..523a2fbb --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/stable_ho_inf_engine.m @@ -0,0 +1,67 @@ +function engine = dv_unrolled_dbn_inf_engine(bnet, T, varargin) +% JTREE_UNROLLED_DBN_INF_ENGINE Unroll the DBN for T time-slices and apply jtree to the resulting static net +% engine = jtree_unrolled_dbn_inf_engine(bnet, T, ...) +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% +% useC - 1 means use jtree_C_inf_engine instead of jtree_inf_engine [0] +% constrained - 1 means we constrain ourselves to eliminate slice t before t+1 [1] +% +% e.g., engine = jtree_unrolled_inf_engine(bnet, 'useC', 1); + +% set default params +N = length(bnet.intra); +useC = 0; +constrained = 1; + +if nargin >= 3 + args = varargin; + nargs = length(args); + if isstr(args{1}) + for i=1:2:nargs + switch args{i}, + case 'useC', useC = args{i+1}; + case 'constrained', constrained = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end + end + else + error(['invalid argument name ' args{1}]); + end +end + +bnet2 = hodbn_to_bnet(bnet, T); +ss = length(bnet.intra); +engine.ss = ss; + +% If constrained_order = 1 we constrain ourselves to eliminate slice t before t+1. +% This prevents cliques containing nodes from far-apart time-slices. +if constrained + stages = num2cell(unroll_set(1:ss, ss, T), 1); +else + stages = { 1:length(bnet2.dag) }; +end +if useC + %jengine = jtree_C_inf_engine(bnet2, 'stages', stages); + %function is not implemented + assert(0) +else + jengine = stab_cond_gauss_inf_engine(bnet2); +end + +engine.unrolled_engine = jengine; +% we don't inherit from jtree_inf_engine, because that would only store bnet2, +% and we would lose access to the DBN-specific fields like intra/inter + +engine.nslices = T; +engine = class(engine, 'stable_ho_inf_engine', inf_engine(bnet)); + + + + + + + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/test_ho_inf_enginge.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/test_ho_inf_enginge.m new file mode 100644 index 00000000..6165f500 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/test_ho_inf_enginge.m @@ -0,0 +1,87 @@ +function [engine,engine2] = test_ho_inf_enginge(order,T) + +assert(order >= 1) +% Model a SISO system, i. e. all node are one-dimensional +% The nodes are numbered as follows +% u(t) = 1 input +% y(t) = 2 model output +% z(t) = 3 noise +% q(t) = 4 observed output = noise + model output + +ns = [1 1 1 1]; + +% Model a linear system, i.e. there are no discrete nodes +dn = []; + +% Modeling of connections within a time slice +intra = zeros(4); +intra(2,4) = 1; % Connection y(t) -> q(t) +intra(3,4) = 1; % Connection z(t) -> q(t) + +% Connections to the next time slice +inter = zeros(4,4,order); +inter(1,2,1) = 1; % u(t) -> y(t+1); +inter(2,2,1) = 1; %y(t) -> y(t+1); +inter(3,3,1) = 1; %z(t) -> z(t+1); + +if order >= 2 + inter(1,2,2) = 1; % u(t) -> y(t+2); + inter(2,2,2) = 1; % y(t) -> y(t+2); +end + +for i = 3: order + inter(:,:,i) = inter(:,:,i-1); %u(t) -> y(t+i) y(t) -> y(t) +i +end; + + +% Compution of a higer order Markov Model +bnet = mk_higher_order_dbn(intra,inter,ns,'discrete',dn); +bnet2 = mk_dbn(intra,inter(:,:,1),ns,'discrete',dn) + + +%Calculation of the number of nodes with different parameters +%There is one input and one output nodes 2 +%There are two different disturbance node 2 +%There are order +1 nodes for y 1 + order +numOfNodes = 5 + order; + +% First input node +bnet.CPD{1} = gaussian_CPD(bnet,1,'mean',0); +bnet2.CPD{1} = gaussian_CPD(bnet,1,'mean',0); +% Modeled output +bnet.CPD{2} = gaussian_CPD(bnet,2,'mean',0); +bnet2.CPD{2} = gaussian_CPD(bnet,2,'mean',0); +%Disturbance +bnet.CPD{3} = gaussian_CPD(bnet,3,'mean',0); +bnet2.CPD{3} = gaussian_CPD(bnet,3,'mean',0); + +%Qutput +bnet.CPD{4} = gaussian_CPD(bnet,4,'mean',0); +bnet2.CPD{4} = gaussian_CPD(bnet,4,'mean',0); + + +%Output node in the second time-slice +%Remember that node number 6 is an example for +%the fifth equivalence class +bnet.CPD{5} = gaussian_CPD(bnet,6,'mean',0); +bnet2.CPD{5} = gaussian_CPD(bnet,6,'mean',0); + +%Disturbance node in the second time slice +bnet.CPD{6} = gaussian_CPD(bnet,7,'mean',0); +bnet2.CPD{6} = gaussian_CPD(bnet,7,'mean',0); + +% Modeling of the remaining nodes for y +for i = 7:numOfNodes + bnet.CPD{i} = gaussian_CPD(bnet,(i - 6)*4 + 7,'mean',0); +end + +% Generation of the inference engine +engine = dv_unrolled_dbn_inf_engine(bnet,T); +engine2 = jtree_unrolled_dbn_inf_engine(bnet,T); + + + + + + + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/update_engine.m b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/update_engine.m new file mode 100644 index 00000000..5c42d4f6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/@stable_ho_inf_engine/update_engine.m @@ -0,0 +1,7 @@ +function engine = update_engine(engine, newCPDs) +% UPDATE_ENGINE Update the engine to take into account the new parameters (jtree_unrolled_dbn) +% engine = update_engine(engine, newCPDs) + +engine.inf_engine = update_engine(engine.inf_engine, newCPDs); +engine.unrolled_engine = update_engine(engine.unrolled_engine, newCPDs); + diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Entries b/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Entries new file mode 100644 index 00000000..0593a1e2 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Entries @@ -0,0 +1,2 @@ +/dummy/1.1.1.1/Sat Jan 18 22:22:28 2003// +D diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Entries.Log new file mode 100644 index 00000000..ab4d0aa0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Entries.Log @@ -0,0 +1,12 @@ +A D/@bk_ff_hmm_inf_engine//// +A D/@bk_inf_engine//// +A D/@cbk_inf_engine//// +A D/@ff_inf_engine//// +A D/@frontier_inf_engine//// +A D/@hmm_inf_engine//// +A D/@jtree_dbn_inf_engine//// +A D/@jtree_unrolled_dbn_inf_engine//// +A D/@kalman_inf_engine//// +A D/@pearl_dbn_inf_engine//// +A D/@pearl_unrolled_dbn_inf_engine//// +A D/@stable_ho_inf_engine//// diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Repository b/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Repository new file mode 100644 index 00000000..cc4cac18 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/inference/dynamic diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Root b/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/inference/dynamic/dummy b/sourcecodes/bnt-master/BNT/inference/dynamic/dummy new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/inference/dynamic/dummy |
