function bnode = mk_noisyfn_node(bnet, self, F, symmetric, ps)
% MK_NOISYFN_NODE Make a noisy-function node.
% bnode = mk_noisyfn_node(bnet, self, F, symmetric, parents)
%
% A noisy-function node is a generalization of a noisy-or node.
% See the README file for details.
% The parameters are stored in the bnode.pfail.
%
% bnode.adjustable_mask(i,j) = 0 means pfail(i,j) is clamped.
% Default: everything is adjustable.
%
% symmetric = 1 if each noisy channel is symmetric. Default = 0.
%
% The optional argument 'parents' is used by modify_parents_noisyfn 
% to override the ones specified in bnet.
%
% The following fields control the behavior of the M step (maximize_params_noisyfn).
% (See learn_params for details.)
% bnode.max_iter is the maximum number of steps to take (default: 10)
% bnode.thresh is the threshold for stopping  (default: 1e-4).
% bnode.verbose controls whether to print (default: 0).


if ~exist('symmetric'), symmetric = 0; end
if ~exist('ps'), ps = parents(bnet.adj_mat, self); end
bnode.max_iter = 10;
bnode.thresh = 1e-4;
bnode.verbose = 0;
bnode.options = options;
ns = bnet.node_sizes;
bnode.self = self;
bnode.parents = ps;
fam = [bnode.self bnode.parents];

bnode.fn = F;
bnode.symmetric = symmetric;
bnode.prior = 0;

np = length(bnode.parents);
psize = ns(bnode.parents(1));
assert(isequal(ns(bnode.parents), psize*ones(1,np)));
nsc = ns(bnode.self);

bnode.type = 'noisyfn';
bnode.adjustable = 1;
bnode.self_size = ns(bnode.self);
bnode.parent_sizes = ns(bnode.parents);
bnode.parent_size = psize;
if bnode.symmetric
  assert(psize == 2);
  bnode.nparams = np; % prob. each channel gets corrupted
  bnode.adjustable_mask = ones(np, 1);
else
  bnode.nparams = np * psize;
  bnode.adjustable_mask = ones(np, psize);
end
bnode = randomize_params_noisyfn(bnode);
bnode = reset_ess_noisyfn(bnode);


