function bnode = mk_noisyor_node(bnet, self)
% MK_NOISYOR_NODE Make a noisy-or node.
% bnode = mk_noisyor_node(bnet, self)
%
% A noisyor node turns on if any of its parents are on, provided they are not inhibited.
% The prob. that the i'th parent gets inhibited (flipped from 1 to 0) is bnode.inhibit(i+1).
% bnode.inhibit(1) is the prob. that the leak node gets inhibited.
% (A leak node is a dummy parent that is always on.)
% Think of this as deterministic OR gate through a noisy channel.
%
% bnode.adjustable_mask(i) = 0 means inhibit(i) is clamped (i.e., it will not be changed).
% (Default = 1, which means unclamped.)
% e.g., set inhibit(1) = 1.0 and adjustable_mask(1) = 0 to permanently turn off the leak node.
%
% The following fields control the behavior of the M step (maximize_params_noisyor).
% (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).


% For simple generalizations of the noisy-OR model, see e.g.,
% - Srinivas, "A generalization of the noisy-OR model", UAI 93
% - Meek and Heckerman, "Learning Causal interaction models", UAI 97.

bnode.max_iter = 10;
bnode.thresh = 1e-4;
bnode.verbose = 0;
ns = bnet.node_sizes;
bnode.self = self;
bnode.parents = parents(bnet.adj_mat, self);
fam = [bnode.self bnode.parents];
np = length(bnode.parents);

if ~isequal(ns(fam), twos(1,np+1))
  error('noisyor nodes and their parents must be binary');
end
if ~subset(fam, bnet.discrete_nodes)
  error('noisyor nodes and their parents must be discrete');
end

bnode.type = 'noisyor';
bnode.adjustable = 1;
bnode.adjustable_mask = ones(1, np+1);
bnode.self_size = ns(self);
bnode.parent_sizes = ns(bnode.parents);
bnode.nparams = length(bnode.parents)+1;

bnode = randomize_params_noisyor(bnode);
bnode = reset_ess_noisyor(bnode);

