function bnet = mk_bnet(adj_mat, node_sizes, discrete_nodes, algo, equiv_class, obs_nodes, uedges, cliques)
% MK_BNET Make a belief network.
%
% BNET = MK_BNET(ADJ_MAT, NODE_SIZES) makes a belief net with an arc from i to j iff adj_mat(i,j) = 1.
% NODE_SIZES(i) is the number of values node i can take on (its arity).
%
% BNET = MK_BNET(ADJ_MAT, NODE_SIZES, DISCRETE_NODES) makes the specified nodes discrete-valued; the
% rest are assumed continuous-valued vectors (default: all nodes are discrete). NODE_SIZES(i) is
% the arity of node i (if discrete) or its dimensionality/length (if cts).
%
% BNET = MK_BNET(ADJ_MAT, NODE_SIZES, DISCRETE_NODES, ALGO) uses the specified inference
% algorithm. The options are 'jtree' for jtree (default), 'pearl' for Pearl, 'lw' for likelihood
% weighting, 'ls' for logic sampling, 'cutset' for cutset conditioning.
% Jtree can handle all node types. Pearl can handle tabular, noisy-or and multiplexer.
% The other algorithms can currently only handle tabular nodes.
%
% BNET = MK_BNET(ADJ_MAT, NODE_SIZES, DISCRETE_NODES, ALGO, EQUIV_CLASS) puts different nodes
% into the same equivalence class. EQUIV_CLASS(I) = J means node I gets its parameters from BNODES{J}.
% The default is that each node is its own equivalence class (i.e., no parameter tieing).
%
% BNET = MK_BNET(ADJ_MAT, NODE_SIZES, DISCRETE_NODES, ALGO, EQUIV_CLASS, OBS_NODES) declares the
% specified nodes are always being observed. Default: OBS_NODES = [], which means the
% observed nodes must be specified when we call enter_evidence.
%
% BNET = MK_BNET(ADJ_MAT, NODE_SIZES, DISCRETE_NODES, ALGO, EQUIV_CLASS, OBS_NODES, UGRAPH, CLIQUES)
% specifies that there is an undirected edge between i and j iff UGRAPH(i,j)=1 or UGRAPH(j,i)=1.
% CLIQUES specify the maximal cliques between the nodes in the undirected component.
% This is a bare bones implementation of chain graphs.
% Currently, only roots can be part of an undirected component. They must be created with mk_input_node.
% You can specify the clique potential using bnet.clpot{i} = mk_table(members, sizes)
% and bnet.clpot{i}.T = ... (if discrete). They must be hidden.
%
% After using MK_BNET, call BNET = MK_XXX_NODE(BNET, I) for each equivalence class I,
% and then call BNET = COMPILE_PARAMS(BNET).

num_nodes = length(adj_mat);
if ~exist('discrete_nodes'), discrete_nodes = 1:num_nodes; end
if ~exist('algo'), algo = 'jtree'; end
if ~exist('equiv_class'), equiv_class = 1:num_nodes; end
if ~exist('obs_nodes'), obs_nodes = []; end
if ~exist('uedges'), uedges = []; end
if ~exist('cliques'), cliques = []; end

assert(acyclic(adj_mat,1));

bnet.algo = algo;
bnet.adj_mat = sparse(adj_mat);

bnet.is_dbn = 0;
bnet.slice_size = 0;
bnet.name = [];
bnet.num_slices = 0;
bnet.useC = 0;

bnet.uedges = mk_symmetric(uedges);
bnet.cliques = cliques;

bnet.node_sizes = node_sizes;
bnet.discrete_nodes = discrete_nodes;
bnet.cts_nodes = setdiff(1:num_nodes, discrete_nodes);
bnet.observed_nodes = obs_nodes;
bnet.equiv_class = equiv_class;
num_equiv_classes = max(equiv_class);
bnet.bnodes = cell(1, num_equiv_classes);
bnet.members_of_equiv_class = inverse_relation(num2cell(bnet.equiv_class), num_equiv_classes);
bnet.blocks = node_sizes;
bnet.blocks(discrete_nodes) = 1;
bnet.values = blockmatrix(1, [], 1, bnet.blocks);
%%%bnet.values = blockmatrix(1, bnet.blocks);
bnet.desired_marginal = [];
bnet.maximize = 0;
bnet.wait_till_evidence = 0; % see enter_evidence for an explanation

switch algo
  case 'jtree',
    if ~bnet.wait_till_evidence
      bnet.jtree = mk_jtree(bnet);
    end
  case 'pearl',
    bnet.pearl = initialize_pearl(bnet);
  case 'cutset',
    %bnet = initialize_cutset(bnet);
    bnet.cutset.initialized = 0;
    %since we don't yet know the cutset
  case {'lw', 'ls',}
    bnet.sample.num_samples = 50;
    bnet.sample.desired_marginals = sparse(1, length(bnet.adj_mat));
    bnet.sample.desired_families = sparse(1, length(bnet.adj_mat));
  otherwise,
    error(['unrecognized inference algorithm ' algo]);
end
