wolffd@0: function engine = jtree_limid_inf_engine(bnet) wolffd@0: % JTREE_LIMID_INF_ENGINE Make a junction tree engine for use by solve_limid wolffd@0: % engine = jtree_limid_inf_engine(bnet) wolffd@0: % wolffd@0: % This engine is designed to compute marginals on decision nodes wolffd@0: wolffd@0: wolffd@0: MG = moralize(bnet.dag); wolffd@0: % We do not remove the utility nodes, because that complicates the book-keeping. wolffd@0: % Leaving them in will not introduce any un-necessary triangulation arcs, because they are always leaves. wolffd@0: % Also, since utility nodes have size 1, they do not increase the size of the potentials. wolffd@0: wolffd@0: ns = bnet.node_sizes; wolffd@0: elim_order = best_first_elim_order(MG, ns); wolffd@0: [MTG, engine.cliques] = triangulate(MG, elim_order); wolffd@0: [engine.jtree, root, B, w] = cliques_to_jtree(engine.cliques, ns); wolffd@0: wolffd@0: % A node can be a member of many cliques, but is assigned to exactly one, to avoid wolffd@0: % double-counting its CPD. We assign node i to clique c if c is the "lightest" clique that wolffd@0: % contains i's family, so it can accomodate its CPD. wolffd@0: N = length(bnet.dag); wolffd@0: engine.clq_ass_to_node = zeros(1, N); wolffd@0: for i=1:N wolffd@0: clqs_containing_family = find(all(B(:,family(bnet.dag, i)), 2)); % all selected columns must be 1 wolffd@0: c = clqs_containing_family(argmin(w(clqs_containing_family))); wolffd@0: engine.clq_ass_to_node(i) = c; wolffd@0: end wolffd@0: wolffd@0: wolffd@0: % Compute the separators between connected cliques. wolffd@0: [is,js] = find(engine.jtree > 0); wolffd@0: num_cliques = length(engine.cliques); wolffd@0: engine.separator = cell(num_cliques, num_cliques); wolffd@0: for k=1:length(is) wolffd@0: i = is(k); j = js(k); wolffd@0: engine.separator{i,j} = find(B(i,:) & B(j,:)); % intersect(cliques{i}, cliques{j}); wolffd@0: end wolffd@0: wolffd@0: wolffd@0: % create |D| different rooted jtree's wolffd@0: engine.rooted_jtree = cell(1, N); wolffd@0: engine.preorder = cell(1, N); wolffd@0: engine.postorder = cell(1, N); wolffd@0: for d=bnet.decision_nodes(:)' wolffd@0: root = engine.clq_ass_to_node(d); wolffd@0: [engine.rooted_jtree{d}, engine.preorder{d}, engine.postorder{d}] = mk_rooted_tree(engine.jtree, root); wolffd@0: end wolffd@0: wolffd@0: engine.exclude = []; wolffd@0: engine.evidence = []; wolffd@0: wolffd@0: engine = class(engine, 'jtree_limid_inf_engine', inf_engine(bnet));