diff options
| author | ziejd2 | 2017-09-28 15:04:40 -0500 |
|---|---|---|
| committer | ziejd2 | 2017-09-28 15:04:40 -0500 |
| commit | 8070dc963753142bb86c4ed698d91fd623ed28e7 (patch) | |
| tree | d0f6dd8fc46a49b819aa55c1a90faa14d8448883 /sourcecodes/bnt-master/BNT/CPDs | |
| parent | 7cc31810d53176e805532b2789955f4eedbce6bb (diff) | |
| download | BNW-8070dc963753142bb86c4ed698d91fd623ed28e7.tar.gz | |
BNW using Octave instead of Matlab.
This version of BNW should perform the same as the original version. The only difference is that it uses Octave instead of Matlab when running BayesNet Toolbox during parameter learning. I am calling this BNW_1.02. It can be accessed at: compbio.uthsc.edu/BNW_1.02
Diffstat (limited to 'sourcecodes/bnt-master/BNT/CPDs')
302 files changed, 8611 insertions, 0 deletions
diff --git a/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/CVS/Entries new file mode 100644 index 00000000..06c13c68 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/CVS/Entries @@ -0,0 +1,2 @@ +/boolean_CPD.m/1.1.1.1/Wed May 29 15:59:52 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/CVS/Repository new file mode 100644 index 00000000..d57d477d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@boolean_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/boolean_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/boolean_CPD.m new file mode 100644 index 00000000..3b35788f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@boolean_CPD/boolean_CPD.m @@ -0,0 +1,179 @@ +function CPD = boolean_CPD(bnet, self, ftype, fname, pfail) +% BOOLEAN_CPD Make a tabular CPD representing a (noisy) boolean function +% +% CPD = boolean_cpd(bnet, self, 'inline', f) uses the inline function f +% to specify the CPT. +% e.g., suppose X4 = X2 AND (NOT X3). Then we can write +% bnet.CPD{4} = boolean_CPD(bnet, 4, 'inline', inline('(x(1) & ~x(2)')); +% Note that x(1) refers pvals(1) = X2, and x(2) refers to pvals(2)=X3. +% +% CPD = boolean_cpd(bnet, self, 'named', f) assumes f is a function name. +% f can be built-in to matlab, or a file. +% e.g., If X4 = X2 AND X3, we can write +% bnet.CPD{4} = boolean_CPD(bnet, 4, 'named', 'and'); +% e.g., If X4 = X2 OR X3, we can write +% bnet.CPD{4} = boolean_CPD(bnet, 4, 'named', 'any'); +% +% CPD = boolean_cpd(bnet, self, 'rnd') makes a random non-redundant bool fn. +% +% CPD = boolean_CPD(bnet, self, 'inline'/'named', f, pfail) +% will put probability mass 1-pfail on f(parents), and put pfail on the other value. +% This is useful for simulating noisy boolean functions. +% If pfail is omitted, it is set to 0. +% (Note that adding noise to a random (non-redundant) boolean function just creates a different +% (potentially redundant) random boolean function.) +% +% Note: This cannot be used to simulate a noisy-OR gate. +% Example: suppose C has parents A and B, and the +% link of A->C fails with prob pA and the link B->C fails with pB. +% Then the noisy-OR gate defines the following distribution +% +% A B P(C=0) +% 0 0 1.0 +% 1 0 pA +% 0 1 pB +% 1 1 pA * PB +% +% By contrast, boolean_CPD(bnet, C, 'any', p) would define +% +% A B P(C=0) +% 0 0 1-p +% 1 0 p +% 0 1 p +% 1 1 p + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = tabular_CPD(bnet, self); + return; +elseif isa(bnet, 'boolean_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end + +if nargin < 5, pfail = 0; end + +ps = parents(bnet.dag, self); +ns = bnet.node_sizes; +psizes = ns(ps); +self_size = ns(self); + +psucc = 1-pfail; + +k = length(ps); +switch ftype + case 'inline', f = eval_bool_fn(fname, k); + case 'named', f = eval_bool_fn(fname, k); + case 'rnd', f = mk_rnd_bool_fn(k); + otherwise, error(['unknown function type ' ftype]); +end + +CPT = zeros(prod(psizes), self_size); +ndx = find(f==0); +CPT(ndx, 1) = psucc; +CPT(ndx, 2) = pfail; +ndx = find(f==1); +CPT(ndx, 2) = psucc; +CPT(ndx, 1) = pfail; +if k > 0 + CPT = reshape(CPT, [psizes self_size]); +end + +clamp = 1; +CPD = tabular_CPD(bnet, self, CPT, [], clamp); + + + +%%%%%%%%%%%% + +function f = eval_bool_fn(fname, n) +% EVAL_BOOL_FN Evaluate a boolean function on all bit vectors of length n +% f = eval_bool_fn(fname, n) +% +% e.g. f = eval_bool_fn(inline('x(1) & x(3)'), 3) +% returns 0 0 0 0 0 1 0 1 + +ns = 2*ones(1, n); +f = zeros(1, 2^n); +bits = ind2subv(ns, 1:2^n); +for i=1:2^n + f(i) = feval(fname, bits(i,:)-1); +end + +%%%%%%%%%%%%%%% + +function f = mk_rnd_bool_fn(n) +% MK_RND_BOOL_FN Make a random bit vector of length n that encodes a non-redundant boolean function +% f = mk_rnd_bool_fn(n) + +red = 1; +while red + f = sample_discrete([0.5 0.5], 2^n, 1)-1; + red = redundant_bool_fn(f); +end + +%%%%%%%% + + +function red = redundant_bool_fn(f) +% REDUNDANT_BOOL_FN Does a boolean function depend on all its input values? +% r = redundant_bool_fn(f) +% +% f is a vector of length 2^n, representing the output for each bit vector. +% An input is redundant if there is no assignment to the other bits +% which changes the output e.g., input 1 is redundant if u(2:n) s.t., +% f([0 u(2:n)]) <> f([1 u(2:n)]). +% A function is redundant it it has any redundant inputs. + +n = log2(length(f)); +ns = 2*ones(1,n); +red = 0; +for i=1:n + ens = ns; + ens(i) = 1; + U = ind2subv(ens, 1:2^(n-1)); + U(:,i) = 1; + f1 = f(subv2ind(ns, U)); + U(:,i) = 2; + f2 = f(subv2ind(ns, U)); + if isequal(f1, f2) + red = 1; + return; + end +end + + +%%%%%%%%%% + +function [b, iter] = rnd_truth_table(N) +% RND_TRUTH_TABLE Construct the output of a random truth table s.t. each input is non-redundant +% b = rnd_truth_table(N) +% +% N is the number of inputs. +% b is a random bit string of length N, representing the output of the truth table. +% Non-redundant means that, for each input position k, +% there are at least two bit patterns, u and v, that differ only in the k'th position, +% s.t., f(u) ~= f(v), where f is the function represented by b. +% We use rejection sampling to ensure non-redundancy. +% +% Example: b = [0 0 0 1 0 0 0 1] is indep of 3rd input (AND of inputs 1 and 2) + +bits = ind2subv(2*ones(1,N), 1:2^N)-1; +redundant = 1; +iter = 0; +while redundant && (iter < 4) + iter = iter + 1; + b = sample_discrete([0.5 0.5], 1, 2^N)-1; + redundant = 0; + for i=1:N + on = find(bits(:,i)==1); + off = find(bits(:,i)==0); + if isequal(b(on), b(off)) + redundant = 1; + break; + end + end +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/CVS/Entries new file mode 100644 index 00000000..eb6be4f0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/CVS/Entries @@ -0,0 +1,2 @@ +/deterministic_CPD.m/1.1.1.1/Mon Oct 7 13:26:36 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/CVS/Repository new file mode 100644 index 00000000..fe1e84b5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@deterministic_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/deterministic_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/deterministic_CPD.m new file mode 100644 index 00000000..f44b6545 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@deterministic_CPD/deterministic_CPD.m @@ -0,0 +1,59 @@ +function CPD = deterministic_CPD(bnet, self, fname, pfail) +% DETERMINISTIC_CPD Make a tabular CPD representing a (noisy) deterministic function +% +% CPD = deterministic_CPD(bnet, self, fname) +% This calls feval(fname, pvals) for each possible vector of parent values. +% e.g., suppose there are 2 ternary parents, then pvals = +% [1 1], [2 1], [3 1], [1 2], [2 2], [3 2], [1 3], [2 3], [3 3] +% If v = feval(fname, pvals(i)), then +% CPD(x | parents=pvals(i)) = 1 if x==v, and = 0 if x<>v +% e.g., suppose X4 = X2 AND (NOT X3). Then +% bnet.CPD{4} = deterministic_CPD(bnet, 4, inline('((x(1)-1) & ~(x(2)-1)) + 1')); +% Note that x(1) refers pvals(1) = X2, and x(2) refers to pvals(2)=X3 +% See also boolean_CPD. +% +% CPD = deterministic_CPD(bnet, self, fname, pfail) +% will put probability mass 1-pfail on f(parents), and distribute pfail over the other values. +% This is useful for simulating noisy deterministic functions. +% If pfail is omitted, it is set to 0. +% + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = tabular_CPD(bnet, self); + return; +elseif isa(bnet, 'deterministic_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end + +if nargin < 4, pfail = 0; end + +ps = parents(bnet.dag, self); +ns = bnet.node_sizes; +psizes = ns(ps); +self_size = ns(self); + +psucc = 1-pfail; + +CPT = zeros(prod(psizes), self_size); +pvals = zeros(1, length(ps)); +for i=1:prod(psizes) + pvals = ind2subv(psizes, i); + x = feval(fname, pvals); + %fprintf('%d ', [pvals x]); fprintf('\n'); + if psucc == 1 + CPT(i, x) = 1; + else + CPT(i, x) = psucc; + rest = mysetdiff(1:self_size, x); + CPT(i, rest) = pfail/length(rest); + end +end +CPT = reshape(CPT, [psizes self_size]); + +CPD = tabular_CPD(bnet, self, 'CPT',CPT, 'clamped',1); + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CPD_to_lambda_msg.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CPD_to_lambda_msg.m new file mode 100644 index 00000000..d53e9e0f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CPD_to_lambda_msg.m @@ -0,0 +1,16 @@ +function lam_msg = CPD_to_lambda_msg(CPD, msg_type, n, ps, msg, p, evidence) +% CPD_TO_LAMBDA_MSG Compute lambda message (discrete) +% lam_msg = compute_lambda_msg(CPD, msg_type, n, ps, msg, p, evidence) +% Pearl p183 eq 4.52 + +switch msg_type + case 'd', + T = prod_CPT_and_pi_msgs(CPD, n, ps, msg, p); + mysize = length(msg{n}.lambda); + lambda = dpot(n, mysize, msg{n}.lambda); + T = multiply_by_pot(T, lambda); + lam_msg = pot_to_marginal(marginalize_pot(T, p)); + lam_msg = lam_msg.T; + case 'g', + error('discrete_CPD can''t create Gaussian msgs') +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CPD_to_pi.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CPD_to_pi.m new file mode 100644 index 00000000..5962c92e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CPD_to_pi.m @@ -0,0 +1,13 @@ +function pi = CPD_to_pi(CPD, msg_type, n, ps, msg, evidence) +% COMPUTE_PI Compute pi vector (discrete) +% pi = compute_pi(CPD, msg_type, n, ps, msg, evidence) +% Pearl p183 eq 4.51 + +switch msg_type + case 'd', + T = prod_CPT_and_pi_msgs(CPD, n, ps, msg); + pi = pot_to_marginal(marginalize_pot(T, n)); + pi = pi.T(:); + case 'g', + error('can only convert discrete CPD to Gaussian pi if observed') +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CPD_to_scgpot.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CPD_to_scgpot.m new file mode 100644 index 00000000..3d611536 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CPD_to_scgpot.m @@ -0,0 +1,25 @@ +function pot = CPD_to_scgpot(CPD, domain, ns, cnodes, evidence) +% CPD_TO_SCGPOT Convert a CPD to a CG potential, incorporating any evidence (discrete) +% pot = CPD_to_scgpot(CPD, domain, ns, cnodes, evidence) +% +% domain is the domain of CPD. +% node_sizes(i) is the size of node i. +% cnodes +% evidence{i} is the evidence on the i'th node. + +%odom = domain(~isemptycell(evidence(domain))); + +%vals = cat(1, evidence{odom}); +%map = find_equiv_posns(odom, domain); +%index = mk_multi_index(length(domain), map, vals); +CPT = CPD_to_CPT(CPD); +%CPT = CPT(index{:}); +CPT = CPT(:); +%ns(odom) = 1; +potarray = cell(1, length(CPT)); +for i=1:length(CPT) + %p = CPT(i); + potarray{i} = scgcpot(0, 0, CPT(i)); + %scpot{i} = scpot(0, 0); +end +pot = scgpot(domain, [], [], ns, potarray); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Entries new file mode 100644 index 00000000..57599441 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Entries @@ -0,0 +1,15 @@ +/CPD_to_lambda_msg.m/1.1.1.1/Wed May 29 15:59:52 2002// +/CPD_to_pi.m/1.1.1.1/Wed May 29 15:59:52 2002// +/CPD_to_scgpot.m/1.1.1.1/Wed May 29 15:59:52 2002// +/README/1.1.1.1/Wed May 29 15:59:52 2002// +/convert_CPD_to_table_hidden_ps.m/1.1.1.1/Wed May 29 15:59:52 2002// +/convert_obs_CPD_to_table.m/1.1.1.1/Wed May 29 15:59:52 2002// +/convert_to_pot.m/1.1.1.1/Fri Feb 20 22:00:38 2004// +/convert_to_sparse_table.c/1.1.1.1/Wed May 29 15:59:52 2002// +/convert_to_table.m/1.1.1.1/Wed May 29 15:59:52 2002// +/discrete_CPD.m/1.1.1.1/Wed May 29 15:59:52 2002// +/dom_sizes.m/1.1.1.1/Wed May 29 15:59:52 2002// +/log_prob_node.m/1.1.1.1/Wed May 29 15:59:52 2002// +/prob_node.m/1.1.1.1/Wed May 29 15:59:52 2002// +/sample_node.m/1.1.1.1/Wed May 29 15:59:52 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Entries.Log new file mode 100644 index 00000000..9c6f22e4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Old//// +A D/private//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Repository new file mode 100644 index 00000000..f3418ec7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@discrete_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/CVS/Entries new file mode 100644 index 00000000..15bb91c3 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/CVS/Entries @@ -0,0 +1,5 @@ +/convert_to_pot.m/1.1.1.1/Wed May 29 15:59:52 2002// +/convert_to_table.m/1.1.1.1/Wed May 29 15:59:52 2002// +/prob_CPD.m/1.1.1.1/Wed May 29 15:59:52 2002// +/prob_node.m/1.1.1.1/Wed May 29 15:59:52 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/CVS/Repository new file mode 100644 index 00000000..df41b4fd --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@discrete_CPD/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/convert_to_pot.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/convert_to_pot.m new file mode 100644 index 00000000..3f178e1c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/convert_to_pot.m @@ -0,0 +1,44 @@ +function pot = convert_to_pot(CPD, pot_type, domain, evidence) +% CONVERT_TO_POT Convert a tabular CPD to one or more potentials +% pots = convert_to_pot(CPD, pot_type, domain, evidence) +% +% pots{i} = CPD evaluated using evidence(domain(:,i)) +% If 'domains' is a single row vector, pots will be an object, not a cell array. + +ncases = size(domain,2); +assert(ncases==1); % not yet vectorized + +sz = dom_sizes(CPD); +ns = zeros(1, max(domain)); +ns(domain) = sz; + +local_ev = evidence(domain); +obs_bitv = ~isemptycell(local_ev); +odom = domain(obs_bitv); +T = convert_to_table(CPD, domain, local_ev, obs_bitv); + +switch pot_type + case 'u', + pot = upot(domain, sz, T, 0*myones(sz)); + case 'd', + ns(odom) = 1; + pot = dpot(domain, ns(domain), T); + case {'c','g'}, + % Since we want the output to be a Gaussian, the whole family must be observed. + % In other words, the potential is really just a constant. + p = T; + %p = prob_node(CPD, evidence(domain(end)), evidence(domain(1:end-1))); + ns(domain) = 0; + pot = cpot(domain, ns(domain), log(p)); + case 'cg', + T = T(:); + ns(odom) = 1; + can = cell(1, length(T)); + for i=1:length(T) + can{i} = cpot([], [], log(T(i))); + end + pot = cgpot(domain, [], ns, can); + otherwise, + error(['unrecognized pot type ' pot_type]) +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/convert_to_table.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/convert_to_table.m new file mode 100644 index 00000000..65121122 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/convert_to_table.m @@ -0,0 +1,23 @@ +function T = convert_to_table(CPD, domain, local_ev, obs_bitv) +% CONVERT_TO_TABLE Convert a discrete CPD to a table +% function T = convert_to_table(CPD, domain, local_ev, obs_bitv) +% +% We convert the CPD to a CPT, and then lookup the evidence on the discrete parents. +% The resulting table can easily be converted to a potential. + + +CPT = CPD_to_CPT(CPD); +obs_child_only = ~any(obs_bitv(1:end-1)) & obs_bitv(end); + +if obs_child_only + sz = size(CPT); + CPT = reshape(CPT, prod(sz(1:end-1)), sz(end)); + o = local_ev{end}; + T = CPT(:, o); +else + odom = domain(obs_bitv); + vals = cat(1, local_ev{find(obs_bitv)}); % undo cell array + map = find_equiv_posns(odom, domain); + index = mk_multi_index(length(domain), map, vals); + T = CPT(index{:}); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/prob_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/prob_CPD.m new file mode 100644 index 00000000..c0a79bda --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/prob_CPD.m @@ -0,0 +1,25 @@ +function p = prob_CPD(CPD, domain, ns, cnodes, evidence) +% PROB_CPD Compute prob of a node given evidence on the parents (discrete) +% p = prob_CPD(CPD, domain, ns, cnodes, evidence) +% +% domain is the domain of CPD. +% node_sizes(i) is the size of node i. +% cnodes = all the cts nodes +% evidence{i} is the evidence on the i'th node. + +ps = domain(1:end-1); +self = domain(end); +CPT = CPD_to_CPT(CPD); + +if isempty(ps) + T = CPT; +else + assert(~any(isemptycell(evidence(ps)))); + pvals = cat(1, evidence{ps}); + i = subv2ind(ns(ps), pvals(:)'); + T = reshape(CPT, [prod(ns(ps)) ns(self)]); + T = T(i,:); +end +p = T(evidence{self}); + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/prob_node.m new file mode 100644 index 00000000..1a39fc79 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/Old/prob_node.m @@ -0,0 +1,51 @@ +function [P, p] = prob_node(CPD, self_ev, pev) +% PROB_NODE Compute prod_m P(x(i,m)| x(pi_i,m), theta_i) for node i (discrete) +% [P, p] = prob_node(CPD, self_ev, pev) +% +% self_ev(m) is the evidence on this node in case m. +% pev(i,m) is the evidence on the i'th parent in case m (if there are any parents). +% (These may also be cell arrays.) +% +% p(m) = P(x(i,m)| x(pi_i,m), theta_i) +% P = prod p(m) + +if iscell(self_ev), usecell = 1; else usecell = 0; end + +ncases = length(self_ev); +sz = dom_sizes(CPD); + +nparents = length(sz)-1; +if nparents == 0 + assert(isempty(pev)); +else + assert(isequal(size(pev), [nparents ncases])); +end + +n = length(sz); +dom = 1:n; +p = zeros(1, ncases); +if nparents == 0 + for m=1:ncases + if usecell + evidence = {self_ev{m}}; + else + evidence = num2cell(self_ev(m)); + end + T = convert_to_table(CPD, dom, evidence); + p(m) = T; + end +else + for m=1:ncases + if usecell + evidence = cell(1,n); + evidence(1:n-1) = pev(:,m); + evidence(n) = self_ev(m); + else + evidence = num2cell([pev(:,m)', self_ev(m)]); + end + T = convert_to_table(CPD, dom, evidence); + p(m) = T; + end +end +P = prod(p); + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/README b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/README new file mode 100644 index 00000000..c0c5a3b3 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/README @@ -0,0 +1,5 @@ +Any CPD on a discrete child with discrete parents +can be represented as a table (although this might be quite big). +discrete_CPD uses this tabular representation to implement various +functions. Subtypes are free to implement more efficient versions. + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_CPD_to_table_hidden_ps.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_CPD_to_table_hidden_ps.m new file mode 100644 index 00000000..c8f44f7e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_CPD_to_table_hidden_ps.m @@ -0,0 +1,20 @@ +function T = convert_CPD_to_table_hidden_ps(CPD, child_obs) +% CONVERT_CPD_TO_TABLE_HIDDEN_PS Convert a discrete CPD to a table +% T = convert_CPD_to_table_hidden_ps(CPD, child_obs) +% +% This is like convert_to_table, except that we are guaranteed that +% none of the parents have evidence on them. +% child_obs may be an integer (1,2,...) or []. + +CPT = CPD_to_CPT(CPD); +if isempty(child_obs) + T = CPT(:); +else + sz = dom_sizes(CPD); + if length(sz)==1 % no parents + T = CPT(child_obs); + else + CPT = reshape(CPT, prod(sz(1:end-1)), sz(end)); + T = CPT(:, child_obs); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_obs_CPD_to_table.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_obs_CPD_to_table.m new file mode 100644 index 00000000..04004088 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_obs_CPD_to_table.m @@ -0,0 +1,13 @@ +function T = convert_to_table(CPD, domain, evidence) +% CONVERT_TO_TABLE Convert a discrete CPD to a table +% T = convert_to_table(CPD, domain, evidence) +% +% We convert the CPD to a CPT, and then lookup the evidence on the discrete parents. +% The resulting table can easily be converted to a potential. + +CPT = CPD_to_CPT(CPD); +odom = domain(~isemptycell(evidence(domain))); +vals = cat(1, evidence{odom}); +map = find_equiv_posns(odom, domain); +index = mk_multi_index(length(domain), map, vals); +T = CPT(index{:}); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_to_pot.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_to_pot.m new file mode 100644 index 00000000..ecc57d49 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_to_pot.m @@ -0,0 +1,62 @@ +function pot = convert_to_pot(CPD, pot_type, domain, evidence) +% CONVERT_TO_POT Convert a discrete CPD to a potential +% pot = convert_to_pot(CPD, pot_type, domain, evidence) +% +% pots = CPD evaluated using evidence(domain) + +ncases = size(domain,2); +assert(ncases==1); % not yet vectorized + +sz = dom_sizes(CPD); +ns = zeros(1, max(domain)); +ns(domain) = sz; + +CPT1 = CPD_to_CPT(CPD); +spar = issparse(CPT1); +odom = domain(~isemptycell(evidence(domain))); +if spar + T = convert_to_sparse_table(CPD, domain, evidence); +else + T = convert_to_table(CPD, domain, evidence); +end + +switch pot_type + case 'u', + pot = upot(domain, sz, T, 0*myones(sz)); + case 'd', + ns(odom) = 1; + pot = dpot(domain, ns(domain), T); + case {'c','g'}, + % Since we want the output to be a Gaussian, the whole family must be observed. + % In other words, the potential is really just a constant. + p = T; + %p = prob_node(CPD, evidence(domain(end)), evidence(domain(1:end-1))); + ns(domain) = 0; + pot = cpot(domain, ns(domain), log(p)); + + case 'cg', + T = T(:); + ns(odom) = 1; + can = cell(1, length(T)); + for i=1:length(T) + if T(i) == 0 + can{i} = cpot([], [], -Inf); % bug fix by Bob Welch 20/2/04 + else + can{i} = cpot([], [], log(T(i))); + end; + end + pot = cgpot(domain, [], ns, can); + + case 'scg' + T = T(:); + ns(odom) = 1; + pot_array = cell(1, length(T)); + for i=1:length(T) + pot_array{i} = scgcpot([], [], T(i)); + end + pot = scgpot(domain, [], [], ns, pot_array); + + otherwise, + error(['unrecognized pot type ' pot_type]) +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_to_sparse_table.c b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_to_sparse_table.c new file mode 100644 index 00000000..369f5b7e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_to_sparse_table.c @@ -0,0 +1,154 @@ +/* convert_to_sparse_table.c convert a sparse discrete CPD with evidence into sparse table */ +/* convert_to_pot.m located in ../CPDs/discrete_CPD call it */ +/* 3 input */ +/* CPD prhs[0] with 1D sparse CPT */ +/* domain prhs[1] */ +/* evidence prhs[2] */ +/* 1 output */ +/* T plhs[0] sparse table */ + +#include <math.h> +#include "mex.h" + +void ind_subv(int index, const int *cumprod, const int n, int *bsubv){ + int i; + + for (i = n-1; i >= 0; i--) { + bsubv[i] = ((int)floor(index / cumprod[i])); + index = index % cumprod[i]; + } +} + +int subv_ind(const int n, const int *cumprod, const int *subv){ + int i, index=0; + + for(i=0; i<n; i++){ + index += subv[i] * cumprod[i]; + } + return index; +} + +void reset_nzmax(mxArray *spArray, const int old_nzmax, const int new_nzmax){ + double *ptr; + void *newptr; + int *ir, *jc; + int nbytes; + + if(new_nzmax == old_nzmax) return; + nbytes = new_nzmax * sizeof(*ptr); + ptr = mxGetPr(spArray); + newptr = mxRealloc(ptr, nbytes); + mxSetPr(spArray, newptr); + nbytes = new_nzmax * sizeof(*ir); + ir = mxGetIr(spArray); + newptr = mxRealloc(ir, nbytes); + mxSetIr(spArray, newptr); + jc = mxGetJc(spArray); + jc[0] = 0; + jc[1] = new_nzmax; + mxSetNzmax(spArray, new_nzmax); +} + + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ + int i, j, NS, NZB, count, bdim, match, domain, bindex, sindex, nzCounts=0; + int *observed, *bsubv, *ssubv, *bir, *sir, *bjc, *sjc, *mask, *ssize, *bcumprod, *scumprod; + double *pDomain, *pSize, *bpr, *spr; + mxArray *pTemp; + + pTemp = mxGetField(prhs[0], 0, "CPT"); + bpr = mxGetPr(pTemp); + bir = mxGetIr(pTemp); + bjc = mxGetJc(pTemp); + NZB = bjc[1]; + pTemp = mxGetField(prhs[0], 0, "sizes"); + pSize = mxGetPr(pTemp); + + pDomain = mxGetPr(prhs[1]); + bdim = mxGetNumberOfElements(prhs[1]); + + mask = malloc(bdim * sizeof(int)); + ssize = malloc(bdim * sizeof(int)); + observed = malloc(bdim * sizeof(int)); + + for(i=0; i<bdim; i++){ + ssize[i] = (int)pSize[i]; + } + + count = 0; + for(i=0; i<bdim; i++){ + domain = (int)pDomain[i] - 1; + pTemp = mxGetCell(prhs[2], domain); + if(pTemp){ + mask[count] = i; + ssize[i] = 1; + observed[count] = (int)mxGetScalar(pTemp) - 1; + count++; + } + } + + if(count == 0){ + pTemp = mxGetField(prhs[0], 0, "CPT"); + plhs[0] = mxDuplicateArray(pTemp); + free(mask); + free(ssize); + free(observed); + return; + } + + bsubv = malloc(bdim * sizeof(int)); + ssubv = malloc(count * sizeof(int)); + bcumprod = malloc(bdim * sizeof(int)); + scumprod = malloc(bdim * sizeof(int)); + + NS = 1; + for(i=0; i<bdim; i++){ + NS *= ssize[i]; + } + + plhs[0] = mxCreateSparse(NS, 1, NS, mxREAL); + spr = mxGetPr(plhs[0]); + sir = mxGetIr(plhs[0]); + sjc = mxGetJc(plhs[0]); + sjc[0] = 0; + sjc[1] = NS; + + bcumprod[0] = 1; + scumprod[0] = 1; + for(i=0; i<bdim-1; i++){ + bcumprod[i+1] = bcumprod[i] * (int)pSize[i]; + scumprod[i+1] = scumprod[i] * ssize[i]; + } + + nzCounts = 0; + for(i=0; i<NZB; i++){ + bindex = bir[i]; + ind_subv(bindex, bcumprod, bdim, bsubv); + for(j=0; j<count; j++){ + ssubv[j] = bsubv[mask[j]]; + } + match = 1; + for(j=0; j<count; j++){ + if((ssubv[j]) != observed[j]){ + match = 0; + break; + } + } + if(match){ + spr[nzCounts] = bpr[i]; + sindex = subv_ind(bdim, scumprod, bsubv); + sir[nzCounts] = sindex; + nzCounts++; + } + } + + reset_nzmax(plhs[0], NS, nzCounts); + free(mask); + free(ssize); + free(observed); + free(bsubv); + free(ssubv); + free(bcumprod); + free(scumprod); +} + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_to_table.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_to_table.m new file mode 100644 index 00000000..dc5bcd40 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/convert_to_table.m @@ -0,0 +1,15 @@ +function T = convert_to_table(CPD, domain, evidence) +% CONVERT_TO_TABLE Convert a discrete CPD to a table +% T = convert_to_table(CPD, domain, evidence) +% +% We convert the CPD to a CPT, and then lookup the evidence on the discrete parents. +% The resulting table can easily be converted to a potential. + +domain = domain(:); +CPT = CPD_to_CPT(CPD); +odom = domain(~isemptycell(evidence(domain))); +vals = cat(1, evidence{odom}); +map = find_equiv_posns(odom, domain); +index = mk_multi_index(length(domain), map, vals); +T = CPT(index{:}); +T = T(:); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/discrete_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/discrete_CPD.m new file mode 100644 index 00000000..b4250831 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/discrete_CPD.m @@ -0,0 +1,6 @@ +function CPD = discrete_CPD(clamped, dom_sizes) +% DISCRETE_CPD Virtual constructor for generic discrete CPD +% CPD = discrete_CPD(clamped, dom_sizes) + +CPD.dom_sizes = dom_sizes; +CPD = class(CPD, 'discrete_CPD', generic_CPD(clamped)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/dom_sizes.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/dom_sizes.m new file mode 100644 index 00000000..2ee750de --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/dom_sizes.m @@ -0,0 +1,5 @@ +function sz = dom_sizes(CPD) +% DOM_SIZES Return the size of each node in the domain +% sz = dom_sizes(CPD) + +sz = CPD.dom_sizes; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/log_prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/log_prob_node.m new file mode 100644 index 00000000..315464a9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/log_prob_node.m @@ -0,0 +1,12 @@ +function L = log_prob_node(CPD, self_ev, pev) +% LOG_PROB_NODE Compute sum_m log P(x(i,m)| x(pi_i,m), theta_i) for node i (discrete) +% L = log_prob_node(CPD, self_ev, pev) +% +% self_ev(m) is the evidence on this node in case m. +% pev(i,m) is the evidence on the i'th parent in case m (if there are any parents). +% (These may also be cell arrays.) + +[P, p] = prob_node(CPD, self_ev, pev); % P may underflow, so we use p +tiny = exp(-700); +p = p + (p==0)*tiny; % replace 0s by tiny +L = sum(log(p)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/CVS/Entries new file mode 100644 index 00000000..da678df7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/CVS/Entries @@ -0,0 +1,2 @@ +/prod_CPT_and_pi_msgs.m/1.1.1.1/Wed May 29 15:59:52 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/CVS/Repository new file mode 100644 index 00000000..2b3c1c9d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@discrete_CPD/private diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/prod_CPT_and_pi_msgs.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/prod_CPT_and_pi_msgs.m new file mode 100644 index 00000000..fe8f6a20 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/private/prod_CPT_and_pi_msgs.m @@ -0,0 +1,18 @@ +function T = prod_CPT_and_pi_msgs(CPD, n, ps, msgs, except) +% PROD_CPT_AND_PI_MSGS Multiply the CPD and all the pi messages from parents, perhaps excepting one +% T = prod_CPY_and_pi_msgs(CPD, n, ps, msgs, except) + +if nargin < 5, except = -1; end + +dom = [ps n]; +%ns = sparse(1, max(dom)); +ns = zeros(1, max(dom)); +CPT = CPD_to_CPT(CPD); +ns(dom) = mysize(CPT); +T = dpot(dom, ns(dom), CPT); +for i=1:length(ps) + p = ps(i); + if p ~= except + T = multiply_by_pot(T, dpot(p, ns(p), msgs{n}.pi_from_parent{i})); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/prob_node.m new file mode 100644 index 00000000..275870c8 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/prob_node.m @@ -0,0 +1,81 @@ +function [P, p] = prob_node(CPD, self_ev, pev) +% PROB_NODE Compute prod_m P(x(i,m)| x(pi_i,m), theta_i) for node i (discrete) +% [P, p] = prob_node(CPD, self_ev, pev) +% +% self_ev(m) is the evidence on this node in case m. +% pev(i,m) is the evidence on the i'th parent in case m (if there are any parents). +% (These may also be cell arrays.) +% +% p(m) = P(x(i,m)| x(pi_i,m), theta_i) +% P = prod p(m) + +if iscell(self_ev), usecell = 1; else usecell = 0; end + +ncases = length(self_ev); +sz = dom_sizes(CPD); + +nparents = length(sz)-1; +if nparents == 0 + assert(isempty(pev)); +else + assert(isequal(size(pev), [nparents ncases])); +end + +n = length(sz); +dom = 1:n; +p = zeros(1, ncases); +if isa(CPD, 'tabular_CPD') + % speed up by looking up CPT using index Zhang Yimin 2001-12-31 + if usecell + if nparents == 0 + data = [cell2num(self_ev)]; + else + data = [cell2num(pev); cell2num(self_ev)]; + end + else + if nparents == 0 + data = [self_ev]; + else + data = [pev; self_ev]; + end + end + + indices = subv2ind(sz, data'); % each row of data' is a case + + CPT=CPD_to_CPT(CPD); + p = CPT(indices); + + %get the prob list + %cpt_size = prod(sz); + %prob_list=reshape(CPT, cpt_size, 1); + %for m=1:ncases %here we assume we get evidence for node and all its parents + % idx=indices(m); + % p(m)=prob_list(idx); + %end + +else % eg. softmax + + for m=1:ncases + if usecell + if nparents == 0 + evidence = {self_ev{m}}; + else + evidence = cell(1,n); + evidence(1:n-1) = pev(:,m); + evidence(n) = self_ev(m); + end + else + if nparents == 0 + evidence = num2cell(self_ev(m)); + else + evidence = num2cell([pev(:,m)', self_ev(m)]); + end + end + T = convert_to_table(CPD, dom, evidence); + p(m) = T; + end +end + +P = prod(p); + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/sample_node.m b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/sample_node.m new file mode 100644 index 00000000..9e0ed994 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@discrete_CPD/sample_node.m @@ -0,0 +1,34 @@ +function y = sample_node(CPD, pvals) +% SAMPLE_NODE Draw a random sample from P(Xi | x(pi_i), theta_i) (discrete) +% y = sample_node(CPD, parent_evidence) +% +% parent_evidence{i} is the value of the i'th parent + +if 0 +n = length(pvals)+1; +dom = 1:n; +evidence = cell(1,n); +evidence(1:n-1) = pvals; +T = convert_to_table(CPD, dom, evidence); +y = sample_discrete(T); +end + + +CPT = CPD_to_CPT(CPD); +sz = mysize(CPT); +nparents = length(sz)-1; +switch nparents + case 0, T = CPT; + case 1, T = CPT(pvals{1}, :); + case 2, T = CPT(pvals{1}, pvals{2}, :); + case 3, T = CPT(pvals{1}, pvals{2}, pvals{3}, :); + case 4, T = CPT(pvals{1}, pvals{2}, pvals{3}, pvals{4}, :); + otherwise, + pvals = cat(1, pvals{:}); + psz = sz(1:end-1); + ssz = sz(end); + i = subv2ind(psz, pvals(:)'); + T = reshape(CPT, [prod(psz) ssz]); + T = T(i,:); +end +y = sample_discrete(T); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CPD_to_lambda_msg.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CPD_to_lambda_msg.m new file mode 100644 index 00000000..340ebe5c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CPD_to_lambda_msg.m @@ -0,0 +1,59 @@ +function lam_msg = CPD_to_lambda_msg(CPD, msg_type, n, ps, msg, p, evidence) +% CPD_TO_LAMBDA_MSG Compute lambda message (gaussian) +% lam_msg = compute_lambda_msg(CPD, msg_type, n, ps, msg, p, evidence) +% Pearl p183 eq 4.52 + +switch msg_type + case 'd', + error('gaussian_CPD can''t create discrete msgs') + case 'g', + cps = ps(CPD.cps); + cpsizes = CPD.sizes(CPD.cps); + self_size = CPD.sizes(end); + i = find_equiv_posns(p, cps); % p is n's i'th cts parent + psz = cpsizes(i); + if all(msg{n}.lambda.precision == 0) % no info to send on + lam_msg.precision = zeros(psz, psz); + lam_msg.info_state = zeros(psz, 1); + return; + end + [m, Q, W] = gaussian_CPD_params_given_dps(CPD, [ps n], evidence); + Bmu = m; + BSigma = Q; + for k=1:length(cps) % only get pi msgs from cts parents + pk = cps(k); + if pk ~= p + %bk = block(k, cpsizes); + bk = CPD.cps_block_ndx{k}; + Bk = W(:, bk); + m = msg{n}.pi_from_parent{k}; + BSigma = BSigma + Bk * m.Sigma * Bk'; + Bmu = Bmu + Bk * m.mu; + end + end + % BSigma = Q + sum_{k \neq i} B_k Sigma_k B_k' + %bi = block(i, cpsizes); + bi = CPD.cps_block_ndx{i}; + Bi = W(:,bi); + P = msg{n}.lambda.precision; + if (rcond(P) > 1e-3) || isinf(P) + if isinf(P) % Y is observed + Sigma_lambda = zeros(self_size, self_size); % infinite precision => 0 variance + mu_lambda = msg{n}.lambda.mu; % observed_value; + else + Sigma_lambda = inv(P); + mu_lambda = Sigma_lambda * msg{n}.lambda.info_state; + end + C = inv(Sigma_lambda + BSigma); + lam_msg.precision = Bi' * C * Bi; + lam_msg.info_state = Bi' * C * (mu_lambda - Bmu); + else + % method that uses matrix inversion lemma to avoid inverting P + A = inv(P + inv(BSigma)); + C = P - P*A*P; + lam_msg.precision = Bi' * C * Bi; + D = eye(self_size) - P*A; + z = msg{n}.lambda.info_state; + lam_msg.info_state = Bi' * (D*z - D*P*Bmu); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CPD_to_pi.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CPD_to_pi.m new file mode 100644 index 00000000..910973e7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CPD_to_pi.m @@ -0,0 +1,22 @@ +function pi = CPD_to_pi(CPD, msg_type, n, ps, msg, evidence) +% CPD_TO_PI Compute the pi vector (gaussian) +% function pi = CPD_to_pi(CPD, msg_type, n, ps, msg, evidence) + +switch msg_type + case 'd', + error('gaussian_CPD can''t create discrete msgs') + case 'g', + [m, Q, W] = gaussian_CPD_params_given_dps(CPD, [ps n], evidence); + cps = ps(CPD.cps); + cpsizes = CPD.sizes(CPD.cps); + pi.mu = m; + pi.Sigma = Q; + for k=1:length(cps) % only get pi msgs from cts parents + %bk = block(k, cpsizes); + bk = CPD.cps_block_ndx{k}; + Bk = W(:, bk); + m = msg{n}.pi_from_parent{k}; + pi.Sigma = pi.Sigma + Bk * m.Sigma * Bk'; + pi.mu = pi.mu + Bk * m.mu; % m.mu = u(k) + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CPD_to_scgpot.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CPD_to_scgpot.m new file mode 100644 index 00000000..90e7cc80 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CPD_to_scgpot.m @@ -0,0 +1,58 @@ +function pot = CPD_to_scgpot(CPD, domain, ns, cnodes, evidence) +% CPD_TO_CGPOT Convert a Gaussian CPD to a CG potential, incorporating any evidence +% pot = CPD_to_cgpot(CPD, domain, ns, cnodes, evidence) + +self = CPD.self; +dnodes = mysetdiff(1:length(ns), cnodes); +odom = domain(~isemptycell(evidence(domain))); +cdom = myintersect(cnodes, domain); +cheaddom = myintersect(self, domain); +ctaildom = mysetdiff(cdom,cheaddom); +ddom = myintersect(dnodes, domain); +cobs = myintersect(cdom, odom); +dobs = myintersect(ddom, odom); +ens = ns; % effective node size +ens(cobs) = 0; +ens(dobs) = 1; + +% Extract the params compatible with the observations (if any) on the discrete parents (if any) +% parents are all but the last domain element +ps = domain(1:end-1); +dps = myintersect(ps, ddom); +dops = myintersect(dps, odom); + +map = find_equiv_posns(dops, dps); +dpvals = cat(1, evidence{dops}); +index = mk_multi_index(length(dps), map, dpvals); + +dpsize = prod(ens(dps)); +cpsize = size(CPD.weights(:,:,1), 2); % cts parents size +ss = size(CPD.mean, 1); % self size +% the reshape acts like a squeeze +m = reshape(CPD.mean(:, index{:}), [ss dpsize]); +C = reshape(CPD.cov(:, :, index{:}), [ss ss dpsize]); +W = reshape(CPD.weights(:, :, index{:}), [ss cpsize dpsize]); + + +% Convert each conditional Gaussian to a canonical potential +pot = cell(1, dpsize); +for i=1:dpsize + %pot{i} = linear_gaussian_to_scgcpot(m(:,i), C(:,:,i), W(:,:,i), cdom, ns, cnodes, evidence); + pot{i} = scgcpot(ss, cpsize, 1, m(:,i), W(:,:,i), C(:,:,i)); +end + +pot = scgpot(ddom, cheaddom, ctaildom, ens, pot); + + +function pot = linear_gaussian_to_scgcpot(mu, Sigma, W, domain, ns, cnodes, evidence) +% LINEAR_GAUSSIAN_TO_CPOT Convert a linear Gaussian CPD to a stable conditional potential element. +% pot = linear_gaussian_to_cpot(mu, Sigma, W, domain, ns, cnodes, evidence) + +p = 1; +A = mu; +B = W; +C = Sigma; +ns(odom) = 0; +%pot = scgcpot(, ns(domain), p, A, B, C); + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Entries new file mode 100644 index 00000000..a6bd3e14 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Entries @@ -0,0 +1,20 @@ +/CPD_to_lambda_msg.m/1.1.1.1/Wed May 29 15:59:52 2002// +/CPD_to_pi.m/1.1.1.1/Wed May 29 15:59:52 2002// +/CPD_to_scgpot.m/1.1.1.1/Wed May 29 15:59:52 2002// +/adjustable_CPD.m/1.1.1.1/Wed May 29 15:59:52 2002// +/convert_CPD_to_table_hidden_ps.m/1.1.1.1/Wed May 29 15:59:52 2002// +/convert_to_pot.m/1.1.1.1/Sun Mar 9 23:03:16 2003// +/convert_to_table.m/1.1.1.1/Sun May 11 23:31:54 2003// +/display.m/1.1.1.1/Wed May 29 15:59:52 2002// +/gaussian_CPD.m/1.1.1.1/Wed Jun 15 21:13:06 2005// +/gaussian_CPD_params_given_dps.m/1.1.1.1/Sun May 11 23:13:40 2003// +/get_field.m/1.1.1.1/Wed May 29 15:59:52 2002// +/learn_params.m/1.1.1.1/Thu Jun 10 01:28:10 2004// +/log_prob_node.m/1.1.1.1/Tue Sep 10 17:44:00 2002// +/maximize_params.m/1.1.1.1/Tue May 20 14:10:06 2003// +/maximize_params_debug.m/1.1.1.1/Fri Jan 31 00:13:10 2003// +/reset_ess.m/1.1.1.1/Wed May 29 15:59:52 2002// +/sample_node.m/1.1.1.1/Wed May 29 15:59:52 2002// +/set_fields.m/1.1.1.1/Wed May 29 15:59:52 2002// +/update_ess.m/1.1.1.1/Tue Jul 22 22:55:46 2003// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Entries.Log new file mode 100644 index 00000000..9c6f22e4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Old//// +A D/private//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Repository new file mode 100644 index 00000000..98ebf3cb --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@gaussian_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CPD_to_lambda_msg.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CPD_to_lambda_msg.m new file mode 100644 index 00000000..5a6d398a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CPD_to_lambda_msg.m @@ -0,0 +1,64 @@ +function lam_msg = CPD_to_lambda_msg(CPD, msg_type, n, ps, msg, p) +% CPD_TO_LAMBDA_MSG Compute lambda message (gaussian) +% lam_msg = compute_lambda_msg(CPD, msg_type, n, ps, msg, p) +% Pearl p183 eq 4.52 + +switch msg_type + case 'd', + error('gaussian_CPD can''t create discrete msgs') + case 'g', + self_size = CPD.sizes(end); + if all(msg{n}.lambda.precision == 0) % no info to send on + lam_msg.precision = zeros(self_size); + lam_msg.info_state = zeros(self_size, 1); + return; + end + cpsizes = CPD.sizes(CPD.cps); + dpval = 1; + Q = CPD.cov(:,:,dpval); + Sigmai = Q; + wmu = zeros(self_size, 1); + for k=1:length(ps) + pk = ps(k); + if pk ~= p + bk = block(k, cpsizes); + Bk = CPD.weights(:, bk, dpval); + m = msg{n}.pi_from_parent{k}; + Sigmai = Sigmai + Bk * m.Sigma * Bk'; + wmu = wmu + Bk * m.mu; % m.mu = u(k) + end + end + % Sigmai = Q + sum_{k \neq i} B_k Sigma_k B_k' + i = find_equiv_posns(p, ps); + bi = block(i, cpsizes); + Bi = CPD.weights(:,bi, dpval); + + if 0 + P = msg{n}.lambda.precision; + if isinf(P) % inv(P)=Sigma_lambda=0 + precision_temp = inv(Sigmai); + lam_msg.precision = Bi' * precision_temp * Bi; + lam_msg.info_state = precision_temp * (msg{n}.lambda.mu - wmu); + else + A = inv(P + inv(Sigmai)); + precision_temp = P + P*A*P; + lam_msg.precision = Bi' * precision_temp * Bi; + self_size = length(P); + C = eye(self_size) + P*A; + z = msg{n}.lambda.info_state; + lam_msg.info_state = C*z - C*P*wmu; + end + end + + if isinf(msg{n}.lambda.precision) + Sigma_lambda = zeros(self_size, self_size); % infinite precision => 0 variance + mu_lambda = msg{n}.lambda.mu; % observed_value; + else + Sigma_lambda = inv(msg{n}.lambda.precision); + mu_lambda = Sigma_lambda * msg{n}.lambda.info_state; + end + precision_temp = inv(Sigma_lambda + Sigmai); + lam_msg.precision = Bi' * precision_temp * Bi; + lam_msg.info_state = Bi' * precision_temp * (mu_lambda - wmu); +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CVS/Entries new file mode 100644 index 00000000..ea2f5a4c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CVS/Entries @@ -0,0 +1,7 @@ +/CPD_to_lambda_msg.m/1.1.1.1/Wed May 29 15:59:52 2002// +/gaussian_CPD.m/1.1.1.1/Wed May 29 15:59:52 2002// +/log_prob_node.m/1.1.1.1/Wed May 29 15:59:52 2002// +/maximize_params.m/1.1.1.1/Thu Jan 30 22:38:16 2003// +/update_ess.m/1.1.1.1/Wed May 29 15:59:52 2002// +/update_tied_ess.m/1.1.1.1/Wed May 29 15:59:52 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CVS/Repository new file mode 100644 index 00000000..c89b5b86 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@gaussian_CPD/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/gaussian_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/gaussian_CPD.m new file mode 100644 index 00000000..6f7138fc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/gaussian_CPD.m @@ -0,0 +1,184 @@ +function CPD = gaussian_CPD(varargin) +% GAUSSIAN_CPD Make a conditional linear Gaussian distrib. +% +% To define this CPD precisely, call the continuous (cts) parents (if any) X, +% the discrete parents (if any) Q, and this node Y. Then the distribution on Y is: +% - no parents: Y ~ N(mu, Sigma) +% - cts parents : Y|X=x ~ N(mu + W x, Sigma) +% - discrete parents: Y|Q=i ~ N(mu(i), Sigma(i)) +% - cts and discrete parents: Y|X=x,Q=i ~ N(mu(i) + W(i) x, Sigma(i)) +% +% CPD = gaussian_CPD(bnet, node, ...) will create a CPD with random parameters, +% where node is the number of a node in this equivalence class. +% +% The list below gives optional arguments [default value in brackets]. +% (Let ns(i) be the size of node i, X = ns(X), Y = ns(Y) and Q = prod(ns(Q)).) +% +% mean - mu(:,i) is the mean given Q=i [ randn(Y,Q) ] +% cov - Sigma(:,:,i) is the covariance given Q=i [ repmat(eye(Y,Y), [1 1 Q]) ] +% weights - W(:,:,i) is the regression matrix given Q=i [ randn(Y,X,Q) ] +% cov_type - if 'diag', Sigma(:,:,i) is diagonal [ 'full' ] +% tied_cov - if 1, we constrain Sigma(:,:,i) to be the same for all i [0] +% clamp_mean - if 1, we do not adjust mu(:,i) during learning [0] +% clamp_cov - if 1, we do not adjust Sigma(:,:,i) during learning [0] +% clamp_weights - if 1, we do not adjust W(:,:,i) during learning [0] +% cov_prior_weight - weight given to I prior for estimating Sigma [0.01] +% +% e.g., CPD = gaussian_CPD(bnet, i, 'mean', [0; 0], 'clamp_mean', 'yes') +% +% For backwards compatibility with BNT2, you can also specify the parameters in the following order +% CPD = gaussian_CPD(bnet, self, mu, Sigma, W, cov_type, tied_cov, clamp_mean, clamp_cov, clamp_weight) +% +% Sometimes it is useful to create an "isolated" CPD, without needing to pass in a bnet. +% In this case, you must specify the discrete and cts parents (dps, cps) and the family sizes, followed +% by the optional arguments above: +% CPD = gaussian_CPD('self', i, 'dps', dps, 'cps', cps, 'sz', fam_size, ...) + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + clamp = 0; + CPD = class(CPD, 'gaussian_CPD', generic_CPD(clamp)); + return; +elseif isa(varargin{1}, 'gaussian_CPD') + % This might occur if we are copying an object. + CPD = varargin{1}; + return; +end +CPD = init_fields; + +CPD = class(CPD, 'gaussian_CPD', generic_CPD(0)); + + +% parse mandatory arguments +if ~isstr(varargin{1}) % pass in bnet + bnet = varargin{1}; + self = varargin{2}; + args = varargin(3:end); + ns = bnet.node_sizes; + ps = parents(bnet.dag, self); + dps = myintersect(ps, bnet.dnodes); + cps = myintersect(ps, bnet.cnodes); + fam_sz = ns([ps self]); +else + disp('parsing new style') + for i=1:2:length(varargin) + switch varargin{i}, + case 'self', self = varargin{i+1}; + case 'dps', dps = varargin{i+1}; + case 'cps', cps = varargin{i+1}; + case 'sz', fam_sz = varargin{i+1}; + end + end + ps = myunion(dps, cps); + args = varargin; +end + +CPD.self = self; +CPD.sizes = fam_sz; + +% Figure out which (if any) of the parents are discrete, and which cts, and how big they are +% dps = discrete parents, cps = cts parents +CPD.cps = find_equiv_posns(cps, ps); % cts parent index +CPD.dps = find_equiv_posns(dps, ps); +ss = fam_sz(end); +psz = fam_sz(1:end-1); +dpsz = prod(psz(CPD.dps)); +cpsz = sum(psz(CPD.cps)); + +% set default params +CPD.mean = randn(ss, dpsz); +CPD.cov = 100*repmat(eye(ss), [1 1 dpsz]); +CPD.weights = randn(ss, cpsz, dpsz); +CPD.cov_type = 'full'; +CPD.tied_cov = 0; +CPD.clamped_mean = 0; +CPD.clamped_cov = 0; +CPD.clamped_weights = 0; +CPD.cov_prior_weight = 0.01; + +nargs = length(args); +if nargs > 0 + if ~isstr(args{1}) + % gaussian_CPD(bnet, self, mu, Sigma, W, cov_type, tied_cov, clamp_mean, clamp_cov, clamp_weights) + if nargs >= 1 & ~isempty(args{1}), CPD.mean = args{1}; end + if nargs >= 2 & ~isempty(args{2}), CPD.cov = args{2}; end + if nargs >= 3 & ~isempty(args{3}), CPD.weights = args{3}; end + if nargs >= 4 & ~isempty(args{4}), CPD.cov_type = args{4}; end + if nargs >= 5 & ~isempty(args{5}) & strcmp(args{5}, 'tied'), CPD.tied_cov = 1; end + if nargs >= 6 & ~isempty(args{6}), CPD.clamped_mean = 1; end + if nargs >= 7 & ~isempty(args{7}), CPD.clamped_cov = 1; end + if nargs >= 8 & ~isempty(args{8}), CPD.clamped_weights = 1; end + else + CPD = set_fields(CPD, args{:}); + end +end + +% Make sure the matrices have 1 dimension per discrete parent. +% Bug fix due to Xuejing Sun 3/6/01 +CPD.mean = myreshape(CPD.mean, [ss ns(dps)]); +CPD.cov = myreshape(CPD.cov, [ss ss ns(dps)]); +CPD.weights = myreshape(CPD.weights, [ss cpsz ns(dps)]); + +CPD.init_cov = CPD.cov; % we reset to this if things go wrong during learning + +% expected sufficient statistics +CPD.Wsum = zeros(dpsz,1); +CPD.WYsum = zeros(ss, dpsz); +CPD.WXsum = zeros(cpsz, dpsz); +CPD.WYYsum = zeros(ss, ss, dpsz); +CPD.WXXsum = zeros(cpsz, cpsz, dpsz); +CPD.WXYsum = zeros(cpsz, ss, dpsz); + +% For BIC +CPD.nsamples = 0; +switch CPD.cov_type + case 'full', + ncov_params = ss*(ss-1)/2; % since symmetric (and positive definite) + case 'diag', + ncov_params = ss; + otherwise + error(['unrecognized cov_type ' cov_type]); +end +% params = weights + mean + cov +if CPD.tied_cov + CPD.nparams = ss*cpsz*dpsz + ss*dpsz + ncov_params; +else + CPD.nparams = ss*cpsz*dpsz + ss*dpsz + dpsz*ncov_params; +end + + + +clamped = CPD.clamped_mean & CPD.clamped_cov & CPD.clamped_weights; +CPD = set_clamped(CPD, clamped); + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.self = []; +CPD.sizes = []; +CPD.cps = []; +CPD.dps = []; +CPD.mean = []; +CPD.cov = []; +CPD.weights = []; +CPD.clamped_mean = []; +CPD.clamped_cov = []; +CPD.clamped_weights = []; +CPD.init_cov = []; +CPD.cov_type = []; +CPD.tied_cov = []; +CPD.Wsum = []; +CPD.WYsum = []; +CPD.WXsum = []; +CPD.WYYsum = []; +CPD.WXXsum = []; +CPD.WXYsum = []; +CPD.nsamples = []; +CPD.nparams = []; +CPD.cov_prior_weight = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/log_prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/log_prob_node.m new file mode 100644 index 00000000..3fa398c8 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/log_prob_node.m @@ -0,0 +1,59 @@ +function L = log_prob_node(CPD, self_ev, pev) +% LOG_PROB_NODE Compute prod_m log P(x(i,m)| x(pi_i,m), theta_i) for node i (gaussian) +% L = log_prob_node(CPD, self_ev, pev) +% +% self_ev(m) is the evidence on this node in case m. +% pev(i,m) is the evidence on the i'th parent in case m (if there are any parents). +% (These may also be cell arrays.) + +if iscell(self_ev), usecell = 1; else usecell = 0; end + +use_log = 1; +ncases = length(self_ev); +nparents = length(CPD.sizes)-1; +assert(ncases == size(pev, 2)); + +if ncases == 0 + L = 0; + return; +end + +if length(CPD.dps)==0 % no discrete parents, so we can vectorize + i = 1; + if usecell + Y = cell2num(self_ev); + else + Y = self_ev; + end + if length(CPD.cps) == 0 + L = gaussian_prob(Y, CPD.mean(:,i), CPD.cov(:,:,i), use_log); + else + if usecell + X = cell2num(pev); + else + X = pev; + end + L = gaussian_prob(Y, CPD.mean(:,i) + CPD.weights(:,:,i)*X, CPD.cov(:,:,i), use_log); + end +else % each case uses a (potentially) different set of parameters + L = 0; + for m=1:ncases + if usecell + dpvals = cat(1, pev{CPD.dps, m}); + else + dpvals = pev(CPD.dps, m); + end + i = subv2ind(CPD.sizes(CPD.dps), dpvals(:)'); + y = self_ev{m}; + if length(CPD.cps) == 0 + L = L + gaussian_prob(y, CPD.mean(:,i), CPD.cov(:,:,i), use_log); + else + if usecell + x = cat(1, pev{CPD.cps, m}); + else + x = pev(CPD.cps, m); + end + L = L + gaussian_prob(y, CPD.mean(:,i) + CPD.weights(:,:,i)*x, CPD.cov(:,:,i), use_log); + end + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/maximize_params.m new file mode 100644 index 00000000..48447358 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/maximize_params.m @@ -0,0 +1,147 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a CPD to their ML values (Gaussian) +% CPD = maximize_params(CPD, temperature) +% +% Temperature is currently only used for entropic prior on Sigma + +% For details, see "Fitting a Conditional Gaussian Distribution", Kevin Murphy, tech. report, +% 1998, available at www.cs.berkeley.edu/~murphyk/papers.html +% Refering to table 2, we use equations 1/2 to estimate the covariance matrix in the untied/tied case, +% and equation 9 to estimate the weight matrix and mean. +% We do not implement spherical Gaussians - the code is already pretty complicated! + +if ~adjustable_CPD(CPD), return; end + +%assert(approxeq(CPD.nsamples, sum(CPD.Wsum))); +assert(~any(isnan(CPD.WXXsum))) +assert(~any(isnan(CPD.WXYsum))) +assert(~any(isnan(CPD.WYYsum))) + +[self_size cpsize dpsize] = size(CPD.weights); + +% Append 1s to the parents, and derive the corresponding cross products. +% This is used when estimate the means and weights simultaneosuly, +% and when estimatting Sigma. +% Let x2 = [x 1]' +XY = zeros(cpsize+1, self_size, dpsize); % XY(:,:,i) = sum_l w(l,i) x2(l) y(l)' +XX = zeros(cpsize+1, cpsize+1, dpsize); % XX(:,:,i) = sum_l w(l,i) x2(l) x2(l)' +YY = zeros(self_size, self_size, dpsize); % YY(:,:,i) = sum_l w(l,i) y(l) y(l)' +for i=1:dpsize + XY(:,:,i) = [CPD.WXYsum(:,:,i) % X*Y + CPD.WYsum(:,i)']; % 1*Y + % [x * [x' 1] = [xx' x + % 1] x' 1] + XX(:,:,i) = [CPD.WXXsum(:,:,i) CPD.WXsum(:,i); + CPD.WXsum(:,i)' CPD.Wsum(i)]; + YY(:,:,i) = CPD.WYYsum(:,:,i); +end + +w = CPD.Wsum(:); +% Set any zeros to one before dividing +% This is valid because w(i)=0 => WYsum(:,i)=0, etc +w = w + (w==0); + +if CPD.clamped_mean + % Estimating B2 and then setting the last column (the mean) to the clamped mean is *not* equivalent + % to estimating B and then adding the clamped_mean to the last column. + if ~CPD.clamped_weights + B = zeros(self_size, cpsize, dpsize); + for i=1:dpsize + if det(CPD.WXXsum(:,:,i))==0 + B(:,:,i) = 0; + else + % Eqn 9 in table 2 of TR + %B(:,:,i) = CPD.WXYsum(:,:,i)' * inv(CPD.WXXsum(:,:,i)); + B(:,:,i) = (CPD.WXXsum(:,:,i) \ CPD.WXYsum(:,:,i))'; + end + end + %CPD.weights = reshape(B, [self_size cpsize dpsize]); + CPD.weights = B; + end +elseif CPD.clamped_weights % KPM 1/25/02 + if ~CPD.clamped_mean % ML estimate is just sample mean of the residuals + for i=1:dpsize + CPD.mean(:,i) = (CPD.WYsum(:,i) - CPD.weights(:,:,i) * CPD.WXsum(:,i)) / w(i); + end + end +else % nothing is clamped, so estimate mean and weights simultaneously + B2 = zeros(self_size, cpsize+1, dpsize); + for i=1:dpsize + if det(XX(:,:,i))==0 % fix by U. Sondhauss 6/27/99 + B2(:,:,i)=0; + else + % Eqn 9 in table 2 of TR + %B2(:,:,i) = XY(:,:,i)' * inv(XX(:,:,i)); + B2(:,:,i) = (XX(:,:,i) \ XY(:,:,i))'; + end + CPD.mean(:,i) = B2(:,cpsize+1,i); + CPD.weights(:,:,i) = B2(:,1:cpsize,i); + end +end + +% Let B2 = [W mu] +if cpsize>0 + B2(:,1:cpsize,:) = reshape(CPD.weights, [self_size cpsize dpsize]); +end +B2(:,cpsize+1,:) = reshape(CPD.mean, [self_size dpsize]); + +% To avoid singular covariance matrices, +% we use the regularization method suggested in "A Quasi-Bayesian approach to estimating +% parameters for mixtures of normal distributions", Hamilton 91. +% If the ML estimate is Sigma = M/N, the MAP estimate is (M+gamma*I) / (N+gamma), +% where gamma >=0 is a smoothing parameter (equivalent sample size of I prior) + +gamma = CPD.cov_prior_weight; + +if ~CPD.clamped_cov + if CPD.cov_prior_entropic % eqn 12 of Brand AI/Stat 99 + Z = 1-temp; + % When temp > 1, Z is negative, so we are dividing by a smaller + % number, ie. increasing the variance. + else + Z = 0; + end + if CPD.tied_cov + S = zeros(self_size, self_size); + % Eqn 2 from table 2 in TR + for i=1:dpsize + S = S + (YY(:,:,i) - B2(:,:,i)*XY(:,:,i)); + end + %denom = max(1, CPD.nsamples + gamma + Z); + denom = CPD.nsamples + gamma + Z; + S = (S + gamma*eye(self_size)) / denom; + if strcmp(CPD.cov_type, 'diag') + S = diag(diag(S)); + end + CPD.cov = repmat(S, [1 1 dpsize]); + else + for i=1:dpsize + % Eqn 1 from table 2 in TR + S = YY(:,:,i) - B2(:,:,i)*XY(:,:,i); + %denom = max(1, w(i) + gamma + Z); % gives wrong answers on mhmm1 + denom = w(i) + gamma + Z; + S = (S + gamma*eye(self_size)) / denom; + CPD.cov(:,:,i) = S; + end + if strcmp(CPD.cov_type, 'diag') + for i=1:dpsize + CPD.cov(:,:,i) = diag(diag(CPD.cov(:,:,i))); + end + end + end +end + + +check_covars = 0; +min_covar = 1e-5; +if check_covars % prevent collapsing to a point + for i=1:dpsize + if min(svd(CPD.cov(:,:,i))) < min_covar + disp(['resetting singular covariance for node ' num2str(CPD.self)]); + CPD.cov(:,:,i) = CPD.init_cov(:,:,i); + end + end +end + + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/update_ess.m new file mode 100644 index 00000000..988012e2 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/update_ess.m @@ -0,0 +1,85 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a Gaussian node +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) + +%if nargin < 6 +% hidden_bitv = zeros(1, max(fmarginal.domain)); +% hidden_bitv(find(isempty(evidence)))=1; +%end + +dom = fmarginal.domain; +self = dom(end); +ps = dom(1:end-1); +hidden_self = hidden_bitv(self); +cps = myintersect(ps, cnodes); +dps = mysetdiff(ps, cps); +hidden_cps = all(hidden_bitv(cps)); +hidden_dps = all(hidden_bitv(dps)); + +CPD.nsamples = CPD.nsamples + 1; +[ss cpsz dpsz] = size(CPD.weights); % ss = self size + +% Let X be the cts parent (if any), Y be the cts child (self). + +if ~hidden_self & (isempty(cps) | ~hidden_cps) & hidden_dps % all cts nodes are observed, all discrete nodes are hidden + % Since X and Y are observed, SYY = 0, SXX = 0, SXY = 0 + % Since discrete parents are hidden, we do not need to add evidence to w. + w = fmarginal.T(:); + CPD.Wsum = CPD.Wsum + w; + y = evidence{self}; + Cyy = y*y'; + if ~CPD.useC + W = repmat(w(:)',ss,1); % W(y,i) = w(i) + W2 = repmat(reshape(W, [ss 1 dpsz]), [1 ss 1]); % W2(x,y,i) = w(i) + CPD.WYsum = CPD.WYsum + W .* repmat(y(:), 1, dpsz); + CPD.WYYsum = CPD.WYYsum + W2 .* repmat(reshape(Cyy, [ss ss 1]), [1 1 dpsz]); + else + W = w(:)'; + W2 = reshape(W, [1 1 dpsz]); + CPD.WYsum = CPD.WYsum + rep_mult(W, y(:), size(CPD.WYsum)); + CPD.WYYsum = CPD.WYYsum + rep_mult(W2, Cyy, size(CPD.WYYsum)); + end + if cpsz > 0 % X exists + x = cat(1, evidence{cps}); x = x(:); + Cxx = x*x'; + Cxy = x*y'; + if ~CPD.useC + CPD.WXsum = CPD.WXsum + W .* repmat(x(:), 1, dpsz); + CPD.WXXsum = CPD.WXXsum + W2 .* repmat(reshape(Cxx, [cpsz cpsz 1]), [1 1 dpsz]); + CPD.WXYsum = CPD.WXYsum + W2 .* repmat(reshape(Cxy, [cpsz ss 1]), [1 1 dpsz]); + else + CPD.WXsum = CPD.WXsum + rep_mult(W, x(:), size(CPD.WXsum)); + CPD.WXXsum = CPD.WXXsum + rep_mult(W2, Cxx, size(CPD.WXXsum)); + CPD.WXYsum = CPD.WXYsum + rep_mult(W2, Cxy, size(CPD.WXYsum)); + end + end + return; +end + +% general (non-vectorized) case +fullm = add_evidence_to_gmarginal(fmarginal, evidence, ns, cnodes); % slow! + +if dpsz == 1 % no discrete parents + w = 1; +else + w = fullm.T(:); +end + +CPD.Wsum = CPD.Wsum + w; +xi = 1:cpsz; +yi = (cpsz+1):(cpsz+ss); +for i=1:dpsz + muY = fullm.mu(yi, i); + SYY = fullm.Sigma(yi, yi, i); + CPD.WYsum(:,i) = CPD.WYsum(:,i) + w(i)*muY; + CPD.WYYsum(:,:,i) = CPD.WYYsum(:,:,i) + w(i)*(SYY + muY*muY'); % E[X Y] = Cov[X,Y] + E[X] E[Y] + if cpsz > 0 + muX = fullm.mu(xi, i); + SXX = fullm.Sigma(xi, xi, i); + SXY = fullm.Sigma(xi, yi, i); + CPD.WXsum(:,i) = CPD.WXsum(:,i) + w(i)*muX; + CPD.WXXsum(:,:,i) = CPD.WXXsum(:,:,i) + w(i)*(SXX + muX*muX'); + CPD.WXYsum(:,:,i) = CPD.WXYsum(:,:,i) + w(i)*(SXY + muX*muY'); + end +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/update_tied_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/update_tied_ess.m new file mode 100644 index 00000000..798c795c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/Old/update_tied_ess.m @@ -0,0 +1,118 @@ +function CPD = update_tied_ess(CPD, domain, engine, evidence, ns, cnodes) + +if ~adjustable_CPD(CPD), return; end +nCPDs = size(domain, 2); +fmarginal = cell(1, nCPDs); +for l=1:nCPDs + fmarginal{l} = marginal_family(engine, nodes(l)); +end + +[ss cpsz dpsz] = size(CPD.weights); +if const_evidence_pattern(engine) + dom = domain(:,1); + dnodes = mysetdiff(1:length(ns), cnodes); + ddom = myintersect(dom, dnodes); + cdom = myintersect(dom, cnodes); + odom = dom(~isemptycell(evidence(dom))); + hdom = dom(isemptycell(evidence(dom))); + % If all hidden nodes are discrete and all cts nodes are observed + % (e.g., HMM with Gaussian output) + % we can add the observed evidence in parallel + if mysubset(ddom, hdom) & mysubset(cdom, odom) + [mu, Sigma, T] = add_cts_ev_to_marginals(fmarginal, evidence, ns, cnodes); + else + mu = zeros(ss, dpsz, nCPDs); + Sigma = zeros(ss, ss, dpsz, nCPDs); + T = zeros(dpsz, nCPDs); + for l=1:nCPDs + [mu(:,:,l), Sigma(:,:,:,l), T(:,l)] = add_ev_to_marginals(fmarginal{l}, evidence, ns, cnodes); + end + end +end +CPD.nsamples = CPD.nsamples + nCPDs; + + +if dpsz == 1 % no discrete parents + w = 1; +else + w = fullm.T(:); +end +CPD.Wsum = CPD.Wsum + w; +% Let X be the cts parent (if any), Y be the cts child (self). +xi = 1:cpsz; +yi = (cpsz+1):(cpsz+ss); +for i=1:dpsz + muY = fullm.mu(yi, i); + SYY = fullm.Sigma(yi, yi, i); + CPD.WYsum(:,i) = CPD.WYsum(:,i) + w(i)*muY; + CPD.WYYsum(:,:,i) = CPD.WYYsum(:,:,i) + w(i)*(SYY + muY*muY'); % E[X Y] = Cov[X,Y] + E[X] E[Y] + if cpsz > 0 + muX = fullm.mu(xi, i); + SXX = fullm.Sigma(xi, xi, i); + SXY = fullm.Sigma(xi, yi, i); + CPD.WXsum(:,i) = CPD.WXsum(:,i) + w(i)*muX; + CPD.WXYsum(:,:,i) = CPD.WXYsum(:,:,i) + w(i)*(SXY + muX*muY'); + CPD.WXXsum(:,:,i) = CPD.WXXsum(:,:,i) + w(i)*(SXX + muX*muX'); + end +end + + +%%%%%%%%%%%%% + +function fullm = add_evidence_to_marginal(fmarginal, evidence, ns, cnodes) + + +dom = fmarginal.domain; + +% Find out which values of the discrete parents (if any) are compatible with +% the discrete evidence (if any). +dnodes = mysetdiff(1:length(ns), cnodes); +ddom = myintersect(dom, dnodes); +cdom = myintersect(dom, cnodes); +odom = dom(~isemptycell(evidence(dom))); +hdom = dom(isemptycell(evidence(dom))); + +dobs = myintersect(ddom, odom); +dvals = cat(1, evidence{dobs}); +ens = ns; % effective node sizes +ens(dobs) = 1; +S = prod(ens(ddom)); +subs = ind2subv(ens(ddom), 1:S); +mask = find_equiv_posns(dobs, ddom); +subs(mask) = dvals; +supportedQs = subv2ind(ns(ddom), subs); + +if isempty(ddom) + Qarity = 1; +else + Qarity = prod(ns(ddom)); +end +fullm.T = zeros(Qarity, 1); +fullm.T(supportedQs) = fmarginal.T(:); + +% Now put the hidden cts parts into their right blocks, +% leaving the observed cts parts as 0. +cobs = myintersect(cdom, odom); +chid = myintersect(cdom, hdom); +cvals = cat(1, evidence{cobs}); +n = sum(ns(cdom)); +fullm.mu = zeros(n,Qarity); +fullm.Sigma = zeros(n,n,Qarity); + +if ~isempty(chid) + chid_blocks = block(find_equiv_posns(chid, cdom), ns(cdom)); +end +if ~isempty(cobs) + cobs_blocks = block(find_equiv_posns(cobs, cdom), ns(cdom)); +end + +for i=1:length(supportedQs) + Q = supportedQs(i); + if ~isempty(chid) + fullm.mu(chid_blocks, Q) = fmarginal.mu(:, i); + fullm.Sigma(chid_blocks, chid_blocks, Q) = fmarginal.Sigma(:,:,i); + end + if ~isempty(cobs) + fullm.mu(cobs_blocks, Q) = cvals(:); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/adjustable_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/adjustable_CPD.m new file mode 100644 index 00000000..ea5190c3 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/adjustable_CPD.m @@ -0,0 +1,5 @@ +function p = adjustable_CPD(CPD) +% ADJUSTABLE_CPD Does this CPD have any adjustable params? (gaussian) +% p = adjustable_CPD(CPD) + +p = ~CPD.clamped_mean || ~CPD.clamped_cov || ~CPD.clamped_weights; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/convert_CPD_to_table_hidden_ps.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/convert_CPD_to_table_hidden_ps.m new file mode 100644 index 00000000..acb2c7d2 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/convert_CPD_to_table_hidden_ps.m @@ -0,0 +1,20 @@ +function T = convert_CPD_to_table_hidden_ps(CPD, self_val) +% CONVERT_CPD_TO_TABLE_HIDDEN_PS Convert a Gaussian CPD to a table +% function T = convert_CPD_to_table_hidden_ps(CPD, self_val) +% +% self_val must be a non-empty vector. +% All the parents are hidden. +% +% This is used by misc/convert_dbn_CPDs_to_tables + +m = CPD.mean; +C = CPD.cov; +W = CPD.weights; + +[ssz dpsize] = size(m); + +T = zeros(dpsize, 1); +for i=1:dpsize + T(i) = gaussian_prob(self_val, m(:,i), C(:,:,i)); +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/convert_to_pot.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/convert_to_pot.m new file mode 100644 index 00000000..6afe8d1d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/convert_to_pot.m @@ -0,0 +1,71 @@ +function pot = convert_to_pot(CPD, pot_type, domain, evidence) +% CONVERT_TO_POT Convert a Gaussian CPD to one or more potentials +% pot = convert_to_pot(CPD, pot_type, domain, evidence) + +sz = CPD.sizes; +ns = zeros(1, max(domain)); +ns(domain) = sz; + +odom = domain(~isemptycell(evidence(domain))); +ps = domain(1:end-1); +cps = ps(CPD.cps); +dps = ps(CPD.dps); +self = domain(end); +cdom = [cps(:)' self]; +ddom = dps; +cnodes = cdom; + +switch pot_type + case 'u', + error('gaussian utility potentials not yet supported'); + + case 'd', + T = convert_to_table(CPD, domain, evidence); + ns(odom) = 1; + pot = dpot(domain, ns(domain), T); + + case {'c','g'}, + [m, C, W] = gaussian_CPD_params_given_dps(CPD, domain, evidence); + pot = linear_gaussian_to_cpot(m, C, W, domain, ns, cnodes, evidence); + + case 'cg', + [m, C, W] = gaussian_CPD_params_given_dps(CPD, domain, evidence); + % Convert each conditional Gaussian to a canonical potential + cobs = myintersect(cdom, odom); + dobs = myintersect(ddom, odom); + ens = ns; % effective node size + ens(cobs) = 0; + ens(dobs) = 1; + dpsize = prod(ens(dps)); + can = cell(1, dpsize); + for i=1:dpsize + if isempty(W) + can{i} = linear_gaussian_to_cpot(m(:,i), C(:,:,i), [], cdom, ns, cnodes, evidence); + else + can{i} = linear_gaussian_to_cpot(m(:,i), C(:,:,i), W(:,:,i), cdom, ns, cnodes, evidence); + end + end + pot = cgpot(ddom, cdom, ens, can); + + case 'scg', + [m, C, W] = gaussian_CPD_params_given_dps(CPD, domain, evidence); + cobs = myintersect(cdom, odom); + dobs = myintersect(ddom, odom); + ens = ns; % effective node size + ens(cobs) = 0; + ens(dobs) = 1; + dpsize = prod(ens(dps)); + cpsize = size(W, 2); % cts parents size + ss = size(m, 1); % self size + cheaddom = self; + ctaildom = cps(:)'; + pot_array = cell(1, dpsize); + for i=1:dpsize + pot_array{i} = scgcpot(ss, cpsize, 1, m(:,i), W(:,:,i), C(:,:,i)); + end + pot = scgpot(ddom, cheaddom, ctaildom, ens, pot_array); + + otherwise, + error(['unrecognized pot_type' pot_type]) +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/convert_to_table.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/convert_to_table.m new file mode 100644 index 00000000..4a8d5904 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/convert_to_table.m @@ -0,0 +1,38 @@ +function T = convert_to_table(CPD, domain, evidence) +% CONVERT_TO_TABLE Convert a Gaussian CPD to a table +% T = convert_to_table(CPD, domain, evidence) + + +sz = CPD.sizes; +ns = zeros(1, max(domain)); +ns(domain) = sz; + +odom = domain(~isemptycell(evidence(domain))); +ps = domain(1:end-1); +cps = ps(CPD.cps); +dps = ps(CPD.dps); +self = domain(end); +cdom = [cps(:)' self]; +ddom = dps; +cnodes = cdom; + +[m, C, W] = gaussian_CPD_params_given_dps(CPD, domain, evidence); + + +ns(odom) = 1; +dpsize = prod(ns(dps)); +self = domain(end); +assert(myismember(self, odom)); +self_val = evidence{self}; +T = zeros(dpsize, 1); +if length(cps) > 0 + assert(~any(isemptycell(evidence(cps)))); + cps_vals = cat(1, evidence{cps}); + for i=1:dpsize + T(i) = gaussian_prob(self_val, m(:,i) + W(:,:,i)*cps_vals, C(:,:,i)); + end +else + for i=1:dpsize + T(i) = gaussian_prob(self_val, m(:,i), C(:,:,i)); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/display.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/display.m new file mode 100644 index 00000000..a3d73c83 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/display.m @@ -0,0 +1,4 @@ +function display(CPD) + +disp('gaussian_CPD object'); +disp(struct(CPD)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/gaussian_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/gaussian_CPD.m new file mode 100644 index 00000000..de519218 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/gaussian_CPD.m @@ -0,0 +1,161 @@ +function CPD = gaussian_CPD(bnet, self, varargin) +% GAUSSIAN_CPD Make a conditional linear Gaussian distrib. +% +% CPD = gaussian_CPD(bnet, node, ...) will create a CPD with random parameters, +% where node is the number of a node in this equivalence class. + +% To define this CPD precisely, call the continuous (cts) parents (if any) X, +% the discrete parents (if any) Q, and this node Y. Then the distribution on Y is: +% - no parents: Y ~ N(mu, Sigma) +% - cts parents : Y|X=x ~ N(mu + W x, Sigma) +% - discrete parents: Y|Q=i ~ N(mu(i), Sigma(i)) +% - cts and discrete parents: Y|X=x,Q=i ~ N(mu(i) + W(i) x, Sigma(i)) +% +% The list below gives optional arguments [default value in brackets]. +% (Let ns(i) be the size of node i, X = ns(X), Y = ns(Y) and Q = prod(ns(Q)).) +% Parameters will be reshaped to the right size if necessary. +% +% mean - mu(:,i) is the mean given Q=i [ randn(Y,Q) ] +% cov - Sigma(:,:,i) is the covariance given Q=i [ repmat(100*eye(Y,Y), [1 1 Q]) ] +% weights - W(:,:,i) is the regression matrix given Q=i [ randn(Y,X,Q) ] +% cov_type - if 'diag', Sigma(:,:,i) is diagonal [ 'full' ] +% tied_cov - if 1, we constrain Sigma(:,:,i) to be the same for all i [0] +% clamp_mean - if 1, we do not adjust mu(:,i) during learning [0] +% clamp_cov - if 1, we do not adjust Sigma(:,:,i) during learning [0] +% clamp_weights - if 1, we do not adjust W(:,:,i) during learning [0] +% cov_prior_weight - weight given to I prior for estimating Sigma [0.01] +% cov_prior_entropic - if 1, we also use an entropic prior for Sigma [0] +% +% e.g., CPD = gaussian_CPD(bnet, i, 'mean', [0; 0], 'clamp_mean', 1) + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + clamp = 0; + CPD = class(CPD, 'gaussian_CPD', generic_CPD(clamp)); + return; +elseif isa(bnet, 'gaussian_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + +CPD = class(CPD, 'gaussian_CPD', generic_CPD(0)); + +args = varargin; +ns = bnet.node_sizes; +ps = parents(bnet.dag, self); +dps = myintersect(ps, bnet.dnodes); +cps = myintersect(ps, bnet.cnodes); +fam_sz = ns([ps self]); + +CPD.self = self; +CPD.sizes = fam_sz; + +% Figure out which (if any) of the parents are discrete, and which cts, and how big they are +% dps = discrete parents, cps = cts parents +CPD.cps = find_equiv_posns(cps, ps); % cts parent index +CPD.dps = find_equiv_posns(dps, ps); +ss = fam_sz(end); +psz = fam_sz(1:end-1); +dpsz = prod(psz(CPD.dps)); +cpsz = sum(psz(CPD.cps)); + +% set default params +CPD.mean = randn(ss, dpsz); +CPD.cov = 100*repmat(eye(ss), [1 1 dpsz]); +CPD.weights = randn(ss, cpsz, dpsz); +CPD.cov_type = 'full'; +CPD.tied_cov = 0; +CPD.clamped_mean = 0; +CPD.clamped_cov = 0; +CPD.clamped_weights = 0; +CPD.cov_prior_weight = 0.01; +CPD.cov_prior_entropic = 0; +nargs = length(args); +if nargs > 0 + CPD = set_fields(CPD, args{:}); +end + +% Make sure the matrices have 1 dimension per discrete parent. +% Bug fix due to Xuejing Sun 3/6/01 +CPD.mean = myreshape(CPD.mean, [ss ns(dps)]); +CPD.cov = myreshape(CPD.cov, [ss ss ns(dps)]); +CPD.weights = myreshape(CPD.weights, [ss cpsz ns(dps)]); + +% Precompute indices into block structured matrices +% to speed up CPD_to_lambda_msg and CPD_to_pi +cpsizes = CPD.sizes(CPD.cps); +CPD.cps_block_ndx = cell(1, length(cps)); +for i=1:length(cps) + CPD.cps_block_ndx{i} = block(i, cpsizes); +end + +%%%%%%%%%%% +% Learning stuff + +% expected sufficient statistics +CPD.Wsum = zeros(dpsz,1); +CPD.WYsum = zeros(ss, dpsz); +CPD.WXsum = zeros(cpsz, dpsz); +CPD.WYYsum = zeros(ss, ss, dpsz); +CPD.WXXsum = zeros(cpsz, cpsz, dpsz); +CPD.WXYsum = zeros(cpsz, ss, dpsz); + +% For BIC +CPD.nsamples = 0; +switch CPD.cov_type + case 'full', + % since symmetric + %ncov_params = ss*(ss-1)/2; + ncov_params = ss*(ss+1)/2; + case 'diag', + ncov_params = ss; + otherwise + error(['unrecognized cov_type ' cov_type]); +end +% params = weights + mean + cov +if CPD.tied_cov + CPD.nparams = ss*cpsz*dpsz + ss*dpsz + ncov_params; +else + CPD.nparams = ss*cpsz*dpsz + ss*dpsz + dpsz*ncov_params; +end + +% for speeding up maximize_params +CPD.useC = exist('rep_mult'); + +clamped = CPD.clamped_mean && CPD.clamped_cov && CPD.clamped_weights; +CPD = set_clamped(CPD, clamped); + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.self = []; +CPD.sizes = []; +CPD.cps = []; +CPD.dps = []; +CPD.mean = []; +CPD.cov = []; +CPD.weights = []; +CPD.clamped_mean = []; +CPD.clamped_cov = []; +CPD.clamped_weights = []; +CPD.cov_type = []; +CPD.tied_cov = []; +CPD.Wsum = []; +CPD.WYsum = []; +CPD.WXsum = []; +CPD.WYYsum = []; +CPD.WXXsum = []; +CPD.WXYsum = []; +CPD.nsamples = []; +CPD.nparams = []; +CPD.cov_prior_weight = []; +CPD.cov_prior_entropic = []; +CPD.useC = []; +CPD.cps_block_ndx = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/gaussian_CPD_params_given_dps.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/gaussian_CPD_params_given_dps.m new file mode 100644 index 00000000..72231a76 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/gaussian_CPD_params_given_dps.m @@ -0,0 +1,28 @@ +function [m, C, W] = gaussian_CPD_params_given_dps(CPD, domain, evidence) +% GAUSSIAN_CPD_PARAMS_GIVEN_EV_ON_DPS Extract parameters given evidence on all discrete parents +% function [m, C, W] = gaussian_CPD_params_given_ev_on_dps(CPD, domain, evidence) + +ps = domain(1:end-1); +dps = ps(CPD.dps); +if isempty(dps) + m = CPD.mean; + C = CPD.cov; + W = CPD.weights; +else + odom = domain(~isemptycell(evidence(domain))); + dops = myintersect(dps, odom); + dpvals = cat(1, evidence{dops}); + if length(dops) == length(dps) + dpsizes = CPD.sizes(CPD.dps); + dpval = subv2ind(dpsizes, dpvals(:)'); + m = CPD.mean(:, dpval); + C = CPD.cov(:, :, dpval); + W = CPD.weights(:, :, dpval); + else + map = find_equiv_posns(dops, dps); + index = mk_multi_index(length(dps), map, dpvals); + m = CPD.mean(:, index{:}); + C = CPD.cov(:, :, index{:}); + W = CPD.weights(:, :, index{:}); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/get_field.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/get_field.m new file mode 100644 index 00000000..2a50e1ac --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/get_field.m @@ -0,0 +1,19 @@ +function val = get_params(CPD, name) +% GET_PARAMS Get the parameters (fields) for a gaussian_CPD object +% val = get_params(CPD, name) +% +% The following fields can be accessed +% +% mean - mu(:,i) is the mean given Q=i +% cov - Sigma(:,:,i) is the covariance given Q=i +% weights - W(:,:,i) is the regression matrix given Q=i +% +% e.g., mean = get_params(CPD, 'mean') + +switch name + case 'mean', val = CPD.mean; + case 'cov', val = CPD.cov; + case 'weights', val = CPD.weights; + otherwise, + error(['invalid argument name ' name]); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/learn_params.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/learn_params.m new file mode 100644 index 00000000..7ae5cb52 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/learn_params.m @@ -0,0 +1,31 @@ +function CPD = learn_params(CPD, fam, data, ns, cnodes) +%function CPD = learn_params(CPD, fam, data, ns, cnodes) +% LEARN_PARAMS Compute the maximum likelihood estimate of the params of a gaussian CPD given complete data +% CPD = learn_params(CPD, fam, data, ns, cnodes) +% +% data(i,m) is the value of node i in case m (can be cell array). +% We assume this node has a maximize_params method. + +ncases = size(data, 2); +CPD = reset_ess(CPD); +% make a fully observed joint distribution over the family +fmarginal.domain = fam; +fmarginal.T = 1; +fmarginal.mu = []; +fmarginal.Sigma = []; +if ~iscell(data) + cases = num2cell(data); +else + cases = data; +end +hidden_bitv = zeros(1, max(fam)); +for m=1:ncases + % specify (as a bit vector) which elements in the family domain are hidden + hidden_bitv = zeros(1, max(fmarginal.domain)); + ev = cases(:,m); + hidden_bitv(find(isempty(ev)))=1; + CPD = update_ess(CPD, fmarginal, ev, ns, cnodes, hidden_bitv); +end +CPD = maximize_params(CPD); + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/log_prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/log_prob_node.m new file mode 100644 index 00000000..ac10f8a3 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/log_prob_node.m @@ -0,0 +1,49 @@ +function L = log_prob_node(CPD, self_ev, pev) +% LOG_PROB_NODE Compute prod_m log P(x(i,m)| x(pi_i,m), theta_i) for node i (gaussian) +% L = log_prob_node(CPD, self_ev, pev) +% +% self_ev(m) is the evidence on this node in case m. +% pev(i,m) is the evidence on the i'th parent in case m (if there are any parents). +% (These may also be cell arrays.) + +if iscell(self_ev), usecell = 1; else usecell = 0; end + +use_log = 1; +ncases = length(self_ev); +nparents = length(CPD.sizes)-1; +assert(ncases == size(pev, 2)); + +if ncases == 0 + L = 0; + return; +end + +L = 0; +for m=1:ncases + if isempty(CPD.dps) + i = 1; + else + if usecell + dpvals = cat(1, pev{CPD.dps, m}); + else + dpvals = pev(CPD.dps, m); + end + i = subv2ind(CPD.sizes(CPD.dps), dpvals(:)'); + end + if usecell + y = self_ev{m}; + else + y = self_ev(m); + end + if length(CPD.cps) == 0 + L = L + gaussian_prob(y, CPD.mean(:,i), CPD.cov(:,:,i), use_log); + else + if usecell + x = cat(1, pev{CPD.cps, m}); + else + x = pev(CPD.cps, m); + end + L = L + gaussian_prob(y, CPD.mean(:,i) + CPD.weights(:,:,i)*x, CPD.cov(:,:,i), use_log); + end +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/maximize_params.m new file mode 100644 index 00000000..1624cbf2 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/maximize_params.m @@ -0,0 +1,68 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a CPD to their ML values (Gaussian) +% CPD = maximize_params(CPD, temperature) +% +% Temperature is currently ignored. + +if ~adjustable_CPD(CPD), return; end + + +if CPD.clamped_mean + cl_mean = CPD.mean; +else + cl_mean = []; +end + +if CPD.clamped_cov + cl_cov = CPD.cov; +else + cl_cov = []; +end + +if CPD.clamped_weights + cl_weights = CPD.weights; +else + cl_weights = []; +end + +[ssz psz Q] = size(CPD.weights); + +[ss cpsz dpsz] = size(CPD.weights); % ss = self size = ssz +if cpsz > CPD.nsamples + fprintf('gaussian_CPD/maximize_params: warning: input dimension (%d) > nsamples (%d)\n', ... + cpsz, CPD.nsamples); +end + +prior = repmat(CPD.cov_prior_weight*eye(ssz,ssz), [1 1 Q]); + + +[CPD.mean, CPD.cov, CPD.weights] = ... + clg_Mstep(CPD.Wsum, CPD.WYsum, CPD.WYYsum, [], CPD.WXsum, CPD.WXXsum, CPD.WXYsum, ... + 'cov_type', CPD.cov_type, 'clamped_mean', cl_mean, ... + 'clamped_cov', cl_cov, 'clamped_weights', cl_weights, ... + 'tied_cov', CPD.tied_cov, ... + 'cov_prior', prior); + +if 0 +CPD.mean = reshape(CPD.mean, [ss dpsz]); +CPD.cov = reshape(CPD.cov, [ss ss dpsz]); +CPD.weights = reshape(CPD.weights, [ss cpsz dpsz]); +end + +% Bug fix 11 May 2003 KPM +% clg_Mstep collapses all discrete parents into one mega-node +% but convert_to_CPT needs access to each parent separately +sz = CPD.sizes; +ss = sz(end); + +% Bug fix KPM 20 May 2003: +cpsz = sum(sz(CPD.cps)); +%if isempty(CPD.cps) +% cpsz = 0; +%else +% cpsz = sz(CPD.cps); +%end +dpsz = sz(CPD.dps); +CPD.mean = myreshape(CPD.mean, [ss dpsz]); +CPD.cov = myreshape(CPD.cov, [ss ss dpsz]); +CPD.weights = myreshape(CPD.weights, [ss cpsz dpsz]); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/maximize_params_debug.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/maximize_params_debug.m new file mode 100644 index 00000000..a588756d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/maximize_params_debug.m @@ -0,0 +1,189 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a CPD to their ML values (Gaussian) +% CPD = maximize_params(CPD, temperature) +% +% Temperature is currently ignored. + +if ~adjustable_CPD(CPD), return; end + +CPD1 = struct(new_maximize_params(CPD)); +CPD2 = struct(old_maximize_params(CPD)); +assert(approxeq(CPD1.mean, CPD2.mean)) +assert(approxeq(CPD1.cov, CPD2.cov)) +assert(approxeq(CPD1.weights, CPD2.weights)) + +CPD = new_maximize_params(CPD); + +%%%%%%% +function CPD = new_maximize_params(CPD) + +if CPD.clamped_mean + cl_mean = CPD.mean; +else + cl_mean = []; +end + +if CPD.clamped_cov + cl_cov = CPD.cov; +else + cl_cov = []; +end + +if CPD.clamped_weights + cl_weights = CPD.weights; +else + cl_weights = []; +end + +[ssz psz Q] = size(CPD.weights); + +prior = repmat(CPD.cov_prior_weight*eye(ssz,ssz), [1 1 Q]); +[CPD.mean, CPD.cov, CPD.weights] = ... + Mstep_clg('w', CPD.Wsum, 'YY', CPD.WYYsum, 'Y', CPD.WYsum, 'YTY', [], ... + 'XX', CPD.WXXsum, 'XY', CPD.WXYsum, 'X', CPD.WXsum, ... + 'cov_type', CPD.cov_type, 'clamped_mean', cl_mean, ... + 'clamped_cov', cl_cov, 'clamped_weights', cl_weights, ... + 'tied_cov', CPD.tied_cov, ... + 'cov_prior', prior); + + +%%%%%%%%%%% + +function CPD = old_maximize_params(CPD) + + +if ~adjustable_CPD(CPD), return; end + +%assert(approxeq(CPD.nsamples, sum(CPD.Wsum))); +assert(~any(isnan(CPD.WXXsum))) +assert(~any(isnan(CPD.WXYsum))) +assert(~any(isnan(CPD.WYYsum))) + +[self_size cpsize dpsize] = size(CPD.weights); + +% Append 1s to the parents, and derive the corresponding cross products. +% This is used when estimate the means and weights simultaneosuly, +% and when estimatting Sigma. +% Let x2 = [x 1]' +XY = zeros(cpsize+1, self_size, dpsize); % XY(:,:,i) = sum_l w(l,i) x2(l) y(l)' +XX = zeros(cpsize+1, cpsize+1, dpsize); % XX(:,:,i) = sum_l w(l,i) x2(l) x2(l)' +YY = zeros(self_size, self_size, dpsize); % YY(:,:,i) = sum_l w(l,i) y(l) y(l)' +for i=1:dpsize + XY(:,:,i) = [CPD.WXYsum(:,:,i) % X*Y + CPD.WYsum(:,i)']; % 1*Y + % [x * [x' 1] = [xx' x + % 1] x' 1] + XX(:,:,i) = [CPD.WXXsum(:,:,i) CPD.WXsum(:,i); + CPD.WXsum(:,i)' CPD.Wsum(i)]; + YY(:,:,i) = CPD.WYYsum(:,:,i); +end + +w = CPD.Wsum(:); +% Set any zeros to one before dividing +% This is valid because w(i)=0 => WYsum(:,i)=0, etc +w = w + (w==0); + +if CPD.clamped_mean + % Estimating B2 and then setting the last column (the mean) to the clamped mean is *not* equivalent + % to estimating B and then adding the clamped_mean to the last column. + if ~CPD.clamped_weights + B = zeros(self_size, cpsize, dpsize); + for i=1:dpsize + if det(CPD.WXXsum(:,:,i))==0 + B(:,:,i) = 0; + else + % Eqn 9 in table 2 of TR + %B(:,:,i) = CPD.WXYsum(:,:,i)' * inv(CPD.WXXsum(:,:,i)); + B(:,:,i) = (CPD.WXXsum(:,:,i) \ CPD.WXYsum(:,:,i))'; + end + end + %CPD.weights = reshape(B, [self_size cpsize dpsize]); + CPD.weights = B; + end +elseif CPD.clamped_weights % KPM 1/25/02 + if ~CPD.clamped_mean % ML estimate is just sample mean of the residuals + for i=1:dpsize + CPD.mean(:,i) = (CPD.WYsum(:,i) - CPD.weights(:,:,i) * CPD.WXsum(:,i)) / w(i); + end + end +else % nothing is clamped, so estimate mean and weights simultaneously + B2 = zeros(self_size, cpsize+1, dpsize); + for i=1:dpsize + if det(XX(:,:,i))==0 % fix by U. Sondhauss 6/27/99 + B2(:,:,i)=0; + else + % Eqn 9 in table 2 of TR + %B2(:,:,i) = XY(:,:,i)' * inv(XX(:,:,i)); + B2(:,:,i) = (XX(:,:,i) \ XY(:,:,i))'; + end + CPD.mean(:,i) = B2(:,cpsize+1,i); + CPD.weights(:,:,i) = B2(:,1:cpsize,i); + end +end + +% Let B2 = [W mu] +if cpsize>0 + B2(:,1:cpsize,:) = reshape(CPD.weights, [self_size cpsize dpsize]); +end +B2(:,cpsize+1,:) = reshape(CPD.mean, [self_size dpsize]); + +% To avoid singular covariance matrices, +% we use the regularization method suggested in "A Quasi-Bayesian approach to estimating +% parameters for mixtures of normal distributions", Hamilton 91. +% If the ML estimate is Sigma = M/N, the MAP estimate is (M+gamma*I) / (N+gamma), +% where gamma >=0 is a smoothing parameter (equivalent sample size of I prior) + +gamma = CPD.cov_prior_weight; + +if ~CPD.clamped_cov + if CPD.cov_prior_entropic % eqn 12 of Brand AI/Stat 99 + Z = 1-temp; + % When temp > 1, Z is negative, so we are dividing by a smaller + % number, ie. increasing the variance. + else + Z = 0; + end + if CPD.tied_cov + S = zeros(self_size, self_size); + % Eqn 2 from table 2 in TR + for i=1:dpsize + S = S + (YY(:,:,i) - B2(:,:,i)*XY(:,:,i)); + end + %denom = CPD.nsamples + gamma + Z; + denom = CPD.nsamples + Z; + S = (S + gamma*eye(self_size)) / denom; + if strcmp(CPD.cov_type, 'diag') + S = diag(diag(S)); + end + CPD.cov = repmat(S, [1 1 dpsize]); + else + for i=1:dpsize + % Eqn 1 from table 2 in TR + S = YY(:,:,i) - B2(:,:,i)*XY(:,:,i); + %denom = w(i) + gamma + Z; + denom = w(i) + Z; + S = (S + gamma*eye(self_size)) / denom; + CPD.cov(:,:,i) = S; + end + if strcmp(CPD.cov_type, 'diag') + for i=1:dpsize + CPD.cov(:,:,i) = diag(diag(CPD.cov(:,:,i))); + end + end + end +end + + +check_covars = 0; +min_covar = 1e-5; +if check_covars % prevent collapsing to a point + for i=1:dpsize + if min(svd(CPD.cov(:,:,i))) < min_covar + disp(['resetting singular covariance for node ' num2str(CPD.self)]); + CPD.cov(:,:,i) = CPD.init_cov(:,:,i); + end + end +end + + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CPD_to_linear_gaussian.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CPD_to_linear_gaussian.m new file mode 100644 index 00000000..dfc0cccc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CPD_to_linear_gaussian.m @@ -0,0 +1,19 @@ +function [mu, Sigma, W] = CPD_to_linear_gaussian(CPD, domain, ns, cnodes, evidence) + +ps = domain(1:end-1); +dnodes = mysetdiff(1:length(ns), cnodes); +dps = myintersect(ps, dnodes); % discrete parents + +if isempty(dps) + Q = 1; +else + assert(~any(isemptycell(evidence(dps)))); + dpvals = cat(1, evidence{dps}); + Q = subv2ind(ns(dps), dpvals(:)'); +end + +mu = CPD.mean(:,Q); +Sigma = CPD.cov(:,:,Q); +W = CPD.weights(:,:,Q); + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CVS/Entries new file mode 100644 index 00000000..afb40930 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CVS/Entries @@ -0,0 +1,2 @@ +/CPD_to_linear_gaussian.m/1.1.1.1/Wed May 29 15:59:52 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CVS/Repository new file mode 100644 index 00000000..8aa921a1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@gaussian_CPD/private diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/private/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/reset_ess.m new file mode 100644 index 00000000..d27105f0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/reset_ess.m @@ -0,0 +1,11 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics for a Gaussian CPD. +% CPD = reset_ess(CPD) + +CPD.nsamples = 0; +CPD.Wsum = zeros(size(CPD.Wsum)); +CPD.WYsum = zeros(size(CPD.WYsum)); +CPD.WYYsum = zeros(size(CPD.WYYsum)); +CPD.WXsum = zeros(size(CPD.WXsum)); +CPD.WXXsum = zeros(size(CPD.WXXsum)); +CPD.WXYsum = zeros(size(CPD.WXYsum)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/sample_node.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/sample_node.m new file mode 100644 index 00000000..74875eeb --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/sample_node.m @@ -0,0 +1,22 @@ +function y = sample_node(CPD, pev) +% SAMPLE_NODE Draw a random sample from P(Xi | x(pi_i), theta_i) (gaussian) +% y = sample_node(CPD, parent_evidence) +% +% pev{i} is the value of the i'th parent (if there are any parents) +% y is the sampled value (a scalar or vector) + +if length(CPD.dps)==0 + i = 1; +else + dpvals = cat(1, pev{CPD.dps}); + i = subv2ind(CPD.sizes(CPD.dps), dpvals(:)'); +end + +if length(CPD.cps) == 0 + y = gsamp(CPD.mean(:,i), CPD.cov(:,:,i), 1); +else + pev = pev(:); + x = cat(1, pev{CPD.cps}); + y = gsamp(CPD.mean(:,i) + CPD.weights(:,:,i)*x(:), CPD.cov(:,:,i), 1); +end +y = y(:); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/set_fields.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/set_fields.m new file mode 100644 index 00000000..4c1aef22 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/set_fields.m @@ -0,0 +1,43 @@ +function CPD = set_fields(CPD, varargin) +% SET_PARAMS Set the parameters (fields) for a gaussian_CPD object +% CPD = set_params(CPD, name/value pairs) +% +% The following optional arguments can be specified in the form of name/value pairs: +% +% mean - mu(:,i) is the mean given Q=i +% cov - Sigma(:,:,i) is the covariance given Q=i +% weights - W(:,:,i) is the regression matrix given Q=i +% cov_type - if 'diag', Sigma(:,:,i) is diagonal +% tied_cov - if 1, we constrain Sigma(:,:,i) to be the same for all i +% clamp_mean - if 1, we do not adjust mu(:,i) during learning +% clamp_cov - if 1, we do not adjust Sigma(:,:,i) during learning +% clamp_weights - if 1, we do not adjust W(:,:,i) during learning +% clamp - if 1, we do not adjust any params +% cov_prior_weight - weight given to I prior for estimating Sigma +% cov_prior_entropic - if 1, we also use an entropic prior for Sigma [0] +% +% e.g., CPD = set_params(CPD, 'mean', [0;0]) + +args = varargin; +nargs = length(args); +for i=1:2:nargs + switch args{i}, + case 'mean', CPD.mean = args{i+1}; + case 'cov', CPD.cov = args{i+1}; + case 'weights', CPD.weights = args{i+1}; + case 'cov_type', CPD.cov_type = args{i+1}; + %case 'tied_cov', CPD.tied_cov = strcmp(args{i+1}, 'yes'); + case 'tied_cov', CPD.tied_cov = args{i+1}; + case 'clamp_mean', CPD.clamped_mean = args{i+1}; + case 'clamp_cov', CPD.clamped_cov = args{i+1}; + case 'clamp_weights', CPD.clamped_weights = args{i+1}; + case 'clamp', clamp = args{i+1}; + CPD.clamped_mean = clamp; + CPD.clamped_cov = clamp; + CPD.clamped_weights = clamp; + case 'cov_prior_weight', CPD.cov_prior_weight = args{i+1}; + case 'cov_prior_entropic', CPD.cov_prior_entropic = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/update_ess.m new file mode 100644 index 00000000..3b58c02e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gaussian_CPD/update_ess.m @@ -0,0 +1,88 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a Gaussian node +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) + +%if nargin < 6 +% hidden_bitv = zeros(1, max(fmarginal.domain)); +% hidden_bitv(find(isempty(evidence)))=1; +%end + +dom = fmarginal.domain; +self = dom(end); +ps = dom(1:end-1); +cps = myintersect(ps, cnodes); +dps = mysetdiff(ps, cps); + +CPD.nsamples = CPD.nsamples + 1; +[ss cpsz dpsz] = size(CPD.weights); % ss = self size +[ss dpsz] = size(CPD.mean); + +% Let X be the cts parent (if any), Y be the cts child (self). + +if ~hidden_bitv(self) && ~any(hidden_bitv(cps)) && all(hidden_bitv(dps)) + % Speedup for the common case that all cts nodes are observed, all discrete nodes are hidden + % Since X and Y are observed, SYY = 0, SXX = 0, SXY = 0 + % Since discrete parents are hidden, we do not need to add evidence to w. + w = fmarginal.T(:); + CPD.Wsum = CPD.Wsum + w; + y = evidence{self}; + Cyy = y*y'; + if ~CPD.useC + WY = repmat(w(:)',ss,1); % WY(y,i) = w(i) + WYY = repmat(reshape(WY, [ss 1 dpsz]), [1 ss 1]); % WYY(y,y',i) = w(i) + %CPD.WYsum = CPD.WYsum + WY .* repmat(y(:), 1, dpsz); + CPD.WYsum = CPD.WYsum + y(:) * w(:)'; + CPD.WYYsum = CPD.WYYsum + WYY .* repmat(reshape(Cyy, [ss ss 1]), [1 1 dpsz]); + else + W = w(:)'; + W2 = reshape(W, [1 1 dpsz]); + CPD.WYsum = CPD.WYsum + rep_mult(W, y(:), size(CPD.WYsum)); + CPD.WYYsum = CPD.WYYsum + rep_mult(W2, Cyy, size(CPD.WYYsum)); + end + if cpsz > 0 % X exists + x = cat(1, evidence{cps}); x = x(:); + Cxx = x*x'; + Cxy = x*y'; + WX = repmat(w(:)',cpsz,1); % WX(x,i) = w(i) + WXX = repmat(reshape(WX, [cpsz 1 dpsz]), [1 cpsz 1]); % WXX(x,x',i) = w(i) + WXY = repmat(reshape(WX, [cpsz 1 dpsz]), [1 ss 1]); % WXY(x,y,i) = w(i) + if ~CPD.useC + CPD.WXsum = CPD.WXsum + WX .* repmat(x(:), 1, dpsz); + CPD.WXXsum = CPD.WXXsum + WXX .* repmat(reshape(Cxx, [cpsz cpsz 1]), [1 1 dpsz]); + CPD.WXYsum = CPD.WXYsum + WXY .* repmat(reshape(Cxy, [cpsz ss 1]), [1 1 dpsz]); + else + CPD.WXsum = CPD.WXsum + rep_mult(W, x(:), size(CPD.WXsum)); + CPD.WXXsum = CPD.WXXsum + rep_mult(W2, Cxx, size(CPD.WXXsum)); + CPD.WXYsum = CPD.WXYsum + rep_mult(W2, Cxy, size(CPD.WXYsum)); + end + end + return; +end + +% general (non-vectorized) case +fullm = add_evidence_to_gmarginal(fmarginal, evidence, ns, cnodes); % slow! + +if dpsz == 1 % no discrete parents + w = 1; +else + w = fullm.T(:); +end + +CPD.Wsum = CPD.Wsum + w; +xi = 1:cpsz; +yi = (cpsz+1):(cpsz+ss); +for i=1:dpsz + muY = fullm.mu(yi, i); + SYY = fullm.Sigma(yi, yi, i); + CPD.WYsum(:,i) = CPD.WYsum(:,i) + w(i)*muY; + CPD.WYYsum(:,:,i) = CPD.WYYsum(:,:,i) + w(i)*(SYY + muY*muY'); % E[X Y] = Cov[X,Y] + E[X] E[Y] + if cpsz > 0 + muX = fullm.mu(xi, i); + SXX = fullm.Sigma(xi, xi, i); + SXY = fullm.Sigma(xi, yi, i); + CPD.WXsum(:,i) = CPD.WXsum(:,i) + w(i)*muX; + CPD.WXXsum(:,:,i) = CPD.WXXsum(:,:,i) + w(i)*(SXX + muX*muX'); + CPD.WXYsum(:,:,i) = CPD.WXYsum(:,:,i) + w(i)*(SXY + muX*muY'); + end +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Entries new file mode 100644 index 00000000..47f0e262 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Entries @@ -0,0 +1,8 @@ +/README/1.1.1.1/Wed May 29 15:59:52 2002// +/adjustable_CPD.m/1.1.1.1/Wed May 29 15:59:52 2002// +/display.m/1.1.1.1/Wed May 29 15:59:52 2002// +/generic_CPD.m/1.1.1.1/Wed May 29 15:59:52 2002// +/learn_params.m/1.1.1.1/Thu Jun 10 01:53:20 2004// +/log_prior.m/1.1.1.1/Wed May 29 15:59:52 2002// +/set_clamped.m/1.1.1.1/Wed May 29 15:59:52 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Entries.Log new file mode 100644 index 00000000..24f16336 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Entries.Log @@ -0,0 +1 @@ +A D/Old//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Repository new file mode 100644 index 00000000..19ab61e0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@generic_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/BIC_score_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/BIC_score_CPD.m new file mode 100644 index 00000000..a73d073b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/BIC_score_CPD.m @@ -0,0 +1,26 @@ +function score = BIC_score_CPD(CPD, fam, data, ns, cnodes) +% BIC_score_CPD Compute the BIC score of a generic CPD +% score = BIC_score_CPD(CPD, fam, data, ns, cnodes) +% +% We assume this node has a maximize_params method + +ncases = size(data, 2); +CPD = reset_ess(CPD); +% make a fully observed joint distribution over the family +fmarginal.domain = fam; +fmarginal.T = 1; +fmarginal.mu = []; +fmarginal.Sigma = []; +if ~iscell(data) + cases = num2cell(data); +else + cases = data; +end +for m=1:ncases + CPD = update_ess(CPD, fmarginal, cases(:,m), ns, cnodes); +end +CPD = maximize_params(CPD); +self = fam(end); +ps = fam(1:end-1); +L = log_prob_node(CPD, cases(self,:), cases(ps,:)); +score = L - 0.5*CPD.nparams*log(ncases); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CPD_to_dpots.m b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CPD_to_dpots.m new file mode 100644 index 00000000..47daac88 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CPD_to_dpots.m @@ -0,0 +1,16 @@ +function pots = CPD_to_dpots(CPD, domain, ns, cnodes, evidence) +% CPD_TO_DPOTS Convert the CPD to several discrete potentials, for different instantiations (generic) +% pots = CPD_to_dpots(CPD, domain, ns, cnodes, evidence) +% +% domain(:,i) is the domain of the i'th instantiation of CPD. +% node_sizes(i) is the size of node i. +% cnodes = all the cts nodes +% evidence{i} is the evidence on the i'th node. +% +% This just calls CPD_to_dpot for each domain. + +nCPDs = size(domain,2); +pots = cell(1,nCPDs); +for i=1:nCPDs + pots{i} = CPD_to_dpot(CPD, domain(:,i), ns, cnodes, evidence); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CVS/Entries new file mode 100644 index 00000000..505b09aa --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CVS/Entries @@ -0,0 +1,3 @@ +/BIC_score_CPD.m/1.1.1.1/Wed May 29 15:59:52 2002// +/CPD_to_dpots.m/1.1.1.1/Wed May 29 15:59:52 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CVS/Repository new file mode 100644 index 00000000..96b94fc8 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@generic_CPD/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/README b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/README new file mode 100644 index 00000000..7a9b164b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/README @@ -0,0 +1,2 @@ +A generic CPD implements general purpose functions like 'display', +that subtypes can inherit. diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/adjustable_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/adjustable_CPD.m new file mode 100644 index 00000000..78feea55 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/adjustable_CPD.m @@ -0,0 +1,5 @@ +function p = adjustable_CPD(CPD) +% ADJUSTABLE_CPD Does this CPD have any adjustable params? (generic) +% p = adjustable_CPD(CPD) + +p = ~CPD.clamped; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/display.m b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/display.m new file mode 100644 index 00000000..001ab2c9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/display.m @@ -0,0 +1,3 @@ +function display(CPD) + +disp(struct(CPD)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/generic_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/generic_CPD.m new file mode 100644 index 00000000..66a85e6a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/generic_CPD.m @@ -0,0 +1,8 @@ +function CPD = generic_CPD(clamped) +% GENERIC_CPD Virtual constructor for generic CPD +% CPD = discrete_CPD(clamped) + +if nargin < 1, clamped = 0; end + +CPD.clamped = clamped; +CPD = class(CPD, 'generic_CPD'); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/learn_params.m b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/learn_params.m new file mode 100644 index 00000000..c36eb004 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/learn_params.m @@ -0,0 +1,32 @@ +function CPD = learn_params(CPD, fam, data, ns, cnodes) +% LEARN_PARAMS Compute the maximum likelihood estimate of the params of a generic CPD given complete data +% CPD = learn_params(CPD, fam, data, ns, cnodes) +% +% data(i,m) is the value of node i in case m (can be cell array). +% We assume this node has a maximize_params method. + +%error('no longer supported') % KPM 1 Feb 03 + +if 1 +ncases = size(data, 2); +CPD = reset_ess(CPD); +% make a fully observed joint distribution over the family +fmarginal.domain = fam; +fmarginal.T = 1; +fmarginal.mu = []; +fmarginal.Sigma = []; +if ~iscell(data) + cases = num2cell(data); +else + cases = data; +end +hidden_bitv = zeros(1, max(fam)); +for m=1:ncases + % specify (as a bit vector) which elements in the family domain are hidden + hidden_bitv = zeros(1, max(fmarginal.domain)); + ev = cases(:,m); + hidden_bitv(find(isempty(evidence)))=1; + CPD = update_ess(CPD, fmarginal, ev, ns, cnodes, hidden_bitv); +end +CPD = maximize_params(CPD); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/log_prior.m b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/log_prior.m new file mode 100644 index 00000000..a73dcde0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/log_prior.m @@ -0,0 +1,5 @@ +function L = log_prior(CPD) +% LOG_PRIOR Return log P(theta) for a generic CPD - we return 0 +% L = log_prior(CPD) + +L = 0; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/set_clamped.m b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/set_clamped.m new file mode 100644 index 00000000..5ad68037 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@generic_CPD/set_clamped.m @@ -0,0 +1,3 @@ +function CPD = set_clamped(CPD, bit) + +CPD.clamped = bit; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CPD_to_lambda_msg.m b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CPD_to_lambda_msg.m new file mode 100644 index 00000000..c323e8e5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CPD_to_lambda_msg.m @@ -0,0 +1,62 @@ +function lam_msg = CPD_to_lambda_msg(CPD, msg_type, n, ps, msg, p, evidence) +% CPD_TO_LAMBDA_MSG Compute lambda message (gmux) +% lam_msg = compute_lambda_msg(CPD, msg_type, n, ps, msg, p, evidence) +% Pearl p183 eq 4.52 + +% Let Y be this node, X1..Xn be the cts parents and M the discrete switch node. +% e.g., for n=3, M=1 +% +% X1 X2 X3 M +% \ +% \ +% Y +% +% So the only case in which we send an informative message is if p=1=M. +% To the other cts parents, we send the "know nothing" message. + +switch msg_type + case 'd', + error('gaussian_CPD can''t create discrete msgs') + case 'g', + cps = ps(CPD.cps); + cpsizes = CPD.sizes(CPD.cps); + self_size = CPD.sizes(end); + i = find_equiv_posns(p, cps); % p is n's i'th cts parent + psz = cpsizes(i); + dps = ps(CPD.dps); + M = evidence{dps}; + if isempty(M) + error('gmux node must have observed discrete parent') + end + P = msg{n}.lambda.precision; + if all(P == 0) | (cps(M) ~= p) % if we know nothing, or are sending to a disconnected parent + lam_msg.precision = zeros(psz, psz); + lam_msg.info_state = zeros(psz, 1); + return; + end + % We are sending a message to the only effectively connected parent. + % There are no other incoming pi messages. + Bmu = CPD.mean(:,M); + BSigma = CPD.cov(:,:,M); + Bi = CPD.weights(:,:,M); + if (det(P) > 0) | isinf(P) + if isinf(P) % Y is observed + Sigma_lambda = zeros(self_size, self_size); % infinite precision => 0 variance + mu_lambda = msg{n}.lambda.mu; % observed_value; + else + Sigma_lambda = inv(P); + mu_lambda = Sigma_lambda * msg{n}.lambda.info_state; + end + C = inv(Sigma_lambda + BSigma); + lam_msg.precision = Bi' * C * Bi; + lam_msg.info_state = Bi' * C * (mu_lambda - Bmu); + else + % method that uses matrix inversion lemma + A = inv(P + inv(BSigma)); + C = P - P*A*P; + lam_msg.precision = Bi' * C * Bi; + D = eye(self_size) - P*A; + z = msg{n}.lambda.info_state; + lam_msg.info_state = Bi' * (D*z - D*P*Bmu); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CPD_to_pi.m b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CPD_to_pi.m new file mode 100644 index 00000000..63b5726b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CPD_to_pi.m @@ -0,0 +1,18 @@ +function pi = CPD_to_pi(CPD, msg_type, n, ps, msg, evidence) +% CPD_TO_PI Compute the pi vector (gaussian) +% function pi = CPD_to_pi(CPD, msg_type, n, ps, msg, evidence) + +switch msg_type + case 'd', + error('gaussian_CPD can''t create discrete msgs') + case 'g', + dps = ps(CPD.dps); + k = evidence{dps}; + if isempty(k) + error('gmux node must have observed discrete parent') + end + m = msg{n}.pi_from_parent{k}; + B = CPD.weights(:,:,k); + pi.mu = CPD.mean(:,k) + B * m.mu; + pi.Sigma = CPD.cov(:,:,k) + B * m.Sigma * B'; +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Entries new file mode 100644 index 00000000..2a911068 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Entries @@ -0,0 +1,7 @@ +/CPD_to_lambda_msg.m/1.1.1.1/Wed May 29 15:59:52 2002// +/CPD_to_pi.m/1.1.1.1/Wed May 29 15:59:54 2002// +/convert_to_pot.m/1.1.1.1/Wed May 29 15:59:52 2002// +/display.m/1.1.1.1/Wed May 29 15:59:54 2002// +/gmux_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +/sample_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Entries.Log new file mode 100644 index 00000000..24f16336 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Entries.Log @@ -0,0 +1 @@ +A D/Old//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Repository new file mode 100644 index 00000000..8d764710 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@gmux_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/CVS/Entries new file mode 100644 index 00000000..f5a137ab --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/CVS/Entries @@ -0,0 +1,2 @@ +/gmux_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/CVS/Repository new file mode 100644 index 00000000..20395ac5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@gmux_CPD/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/gmux_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/gmux_CPD.m new file mode 100644 index 00000000..5c9507cf --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/Old/gmux_CPD.m @@ -0,0 +1,92 @@ +function CPD = gmux_CPD(bnet, self, varargin) +% GMUX_CPD Make a Gaussian multiplexer node +% +% CPD = gmux_CPD(bnet, node, ...) is used similarly to gaussian_CPD, +% except we assume there is exactly one discrete parent (call it M) +% which is used to select which cts parent to pass through to the output. +% i.e., we define P(Y=y|M=m, X1, ..., XK) = N(y | W*x(m) + mu, Sigma) +% where Y represents this node, and the Xi's are the cts parents. +% All the Xi must have the same size, and the num values for M must be K. +% +% Currently the params for this kind of CPD cannot be learned. +% +% Optional arguments [ default in brackets ] +% +% mean - mu [zeros(Y,1)] +% cov - Sigma [eye(Y,Y)] +% weights - W [ randn(Y,X) ] + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + clamp = 0; + CPD = class(CPD, 'gmux_CPD', generic_CPD(clamp)); + return; +elseif isa(bnet, 'gmux_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + +CPD = class(CPD, 'gmux_CPD', generic_CPD(1)); + +ns = bnet.node_sizes; +ps = parents(bnet.dag, self); +dps = myintersect(ps, bnet.dnodes); +cps = myintersect(ps, bnet.cnodes); +fam_sz = ns([ps self]); + +CPD.self = self; +CPD.sizes = fam_sz; + +% Figure out which (if any) of the parents are discrete, and which cts, and how big they are +% dps = discrete parents, cps = cts parents +CPD.cps = find_equiv_posns(cps, ps); % cts parent index +CPD.dps = find_equiv_posns(dps, ps); +if length(CPD.dps) ~= 1 + error('gmux must have exactly 1 discrete parent') +end +ss = fam_sz(end); +cpsz = fam_sz(CPD.cps(1)); % in gaussian_CPD, cpsz = sum(fam_sz(CPD.cps)) +if ~all(fam_sz(CPD.cps) == cpsz) + error('all cts parents must have same size') +end +dpsz = fam_sz(CPD.dps); +if dpsz ~= length(cps) + error(['the arity of the mux node is ' num2str(dpsz) ... + ' but there are ' num2str(length(cps)) ' cts parents']); +end + +% set default params +CPD.mean = zeros(ss, 1); +CPD.cov = eye(ss); +CPD.weights = randn(ss, cpsz); + +args = varargin; +nargs = length(args); +for i=1:2:nargs + switch args{i}, + case 'mean', CPD.mean = args{i+1}; + case 'cov', CPD.cov = args{i+1}; + case 'weights', CPD.weights = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end +end + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.self = []; +CPD.sizes = []; +CPD.cps = []; +CPD.dps = []; +CPD.mean = []; +CPD.cov = []; +CPD.weights = []; + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/convert_to_pot.m b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/convert_to_pot.m new file mode 100644 index 00000000..bf8c29c4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/convert_to_pot.m @@ -0,0 +1,37 @@ +function pot = convert_to_pot(CPD, pot_type, domain, evidence) +% CONVERT_TO_POT Convert a gmux CPD to a Gaussian potential +% pot = convert_to_pot(CPD, pot_type, domain, evidence) + +switch pot_type + case {'d', 'u', 'cg', 'scg'}, + error(['can''t convert gmux to potential of type ' pot_type]) + + case {'c','g'}, + % We create a large weight matrix with zeros in all blocks corresponding + % to the non-chosen parents, since they are effectively disconnected. + % The chosen parent is determined by the value, m, of the discrete parent. + % Thus the potential is as large as the whole family. + ps = domain(1:end-1); + dps = ps(CPD.dps); % CPD.dps is an index, not a node number (because of param tying) + cps = ps(CPD.cps); + m = evidence{dps}; + if isempty(m) + error('gmux node must have observed discrete parent') + end + bs = CPD.sizes(CPD.cps); + b = block(m, bs); + sum_cpsz = sum(CPD.sizes(CPD.cps)); + selfsz = CPD.sizes(end); + W = zeros(selfsz, sum_cpsz); + W(:,b) = CPD.weights(:,:,m); + + ns = zeros(1, max(domain)); + ns(domain) = CPD.sizes; + self = domain(end); + cdom = [cps(:)' self]; + pot = linear_gaussian_to_cpot(CPD.mean(:,m), CPD.cov(:,:,m), W, domain, ns, cdom, evidence); + + otherwise, + error(['unrecognized pot_type' pot_type]) +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/display.m b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/display.m new file mode 100644 index 00000000..4b04168c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/display.m @@ -0,0 +1,4 @@ +function display(CPD) + +disp('gmux_CPD object'); +disp(struct(CPD)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/gmux_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/gmux_CPD.m new file mode 100644 index 00000000..4cef195c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/gmux_CPD.m @@ -0,0 +1,95 @@ +function CPD = gmux_CPD(bnet, self, varargin) +% GMUX_CPD Make a Gaussian multiplexer node +% +% CPD = gmux_CPD(bnet, node, ...) is used similarly to gaussian_CPD, +% except we assume there is exactly one discrete parent (call it M) +% which is used to select which cts parent to pass through to the output. +% i.e., we define P(Y=y|M=m, X1, ..., XK) = N(y | W(m)*x(m) + mu(m), Sigma(m)) +% where Y represents this node, and the Xi's are the cts parents. +% All the Xi must have the same size, and the num values for M must be K. +% +% Currently the params for this kind of CPD cannot be learned. +% +% Optional arguments [ default in brackets ] +% +% mean - mu(:,i) is the mean given M=i [ zeros(Y,K) ] +% cov - Sigma(:,:,i) is the covariance given M=i [ repmat(1*eye(Y,Y), [1 1 K]) ] +% weights - W(:,:,i) is the regression matrix given M=i [ randn(Y,X,K) ] + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + clamp = 0; + CPD = class(CPD, 'gmux_CPD', generic_CPD(clamp)); + return; +elseif isa(bnet, 'gmux_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + +CPD = class(CPD, 'gmux_CPD', generic_CPD(1)); + +ns = bnet.node_sizes; +ps = parents(bnet.dag, self); +dps = myintersect(ps, bnet.dnodes); +cps = myintersect(ps, bnet.cnodes); +fam_sz = ns([ps self]); + +CPD.self = self; +CPD.sizes = fam_sz; + +% Figure out which (if any) of the parents are discrete, and which cts, and how big they are +% dps = discrete parents, cps = cts parents +CPD.cps = find_equiv_posns(cps, ps); % cts parent index +CPD.dps = find_equiv_posns(dps, ps); +if length(CPD.dps) ~= 1 + error('gmux must have exactly 1 discrete parent') +end +ss = fam_sz(end); +cpsz = fam_sz(CPD.cps(1)); % in gaussian_CPD, cpsz = sum(fam_sz(CPD.cps)) +if ~all(fam_sz(CPD.cps) == cpsz) + error('all cts parents must have same size') +end +dpsz = fam_sz(CPD.dps); +if dpsz ~= length(cps) + error(['the arity of the mux node is ' num2str(dpsz) ... + ' but there are ' num2str(length(cps)) ' cts parents']); +end + +% set default params +%CPD.mean = zeros(ss, 1); +%CPD.cov = eye(ss); +%CPD.weights = randn(ss, cpsz); +CPD.mean = zeros(ss, dpsz); +CPD.cov = 1*repmat(eye(ss), [1 1 dpsz]); +CPD.weights = randn(ss, cpsz, dpsz); + +args = varargin; +nargs = length(args); +for i=1:2:nargs + switch args{i}, + case 'mean', CPD.mean = args{i+1}; + case 'cov', CPD.cov = args{i+1}; + case 'weights', CPD.weights = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end +end + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.self = []; +CPD.sizes = []; +CPD.cps = []; +CPD.dps = []; +CPD.mean = []; +CPD.cov = []; +CPD.weights = []; + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/sample_node.m b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/sample_node.m new file mode 100644 index 00000000..53842a5d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@gmux_CPD/sample_node.m @@ -0,0 +1,10 @@ +function y = sample_node(CPD, pev) +% SAMPLE_NODE Draw a random sample from P(Xi | x(pi_i), theta_i) (gmux) +% y = sample_node(CPD, parent_evidence) +% +% parent_ev{i} is the value of the i'th parent + +dpval = pev{CPD.dps}; +x = pev{CPD.cps(dpval)}; +y = gsamp(CPD.mean(:,dpval) + CPD.weights(:,:,dpval)*x(:), CPD.cov(:,:,dpval), 1); +y = y(:); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CPD_to_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CPD_to_CPT.m new file mode 100644 index 00000000..1942f60f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CPD_to_CPT.m @@ -0,0 +1,35 @@ +function CPT = CPD_to_CPT(CPD) +% Compute the big CPT for an HHMM Q node (including F parents) +% by combining internal transprob and startprob +% function CPT = CPD_to_CPT(CPD) + +Qsz = CPD.Qsz; + +if ~isempty(CPD.Fbelow_ndx) + if ~isempty(CPD.Fself_ndx) % general case + error('not implemented') + else % no F from self, hence no startprob (top level) + nps = length(CPD.dom_sz)-1; % num parents + CPT = 0*myones(CPD.dom_sz); + % when Fself=1, the CPT(i,j) = delta(i,j) for all k + for k=1:prod(CPD.Qpsizes) + Qps_vals = ind2subv(CPD.Qpsizes, k); + ndx = mk_multi_index(nps+1, [CPD.Fbelow_ndx CPD.Qps_ndx], [1 Qps_vals]); + CPT(ndx{:}) = eye(Qsz); % CPT(:,2,k,:) or CPT(:,k,2,:) etc + end + ndx = mk_multi_index(nps+1, CPD.Fbelow_ndx, 2); + CPT(ndx{:}) = CPD.transprob; % we assume transprob is in topo order + end +else % no F signal from below + if ~isempty(CPD.Fself_ndx) % bottom level + nps = length(CPD.dom_sz)-1; % num parents + CPT = 0*myones(CPD.dom_sz); + ndx = mk_multi_index(nps+1, CPD.Fself_ndx, 1); + CPT(ndx{:}) = CPD.transprob; + ndx = mk_multi_index(nps+1, CPD.Fself_ndx, 2); + CPT(ndx{:}) = CPD.startprob; + else % no F from self + error('An hhmmQ node without any F parents is just a tabular_CPD') + end +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CVS/Entries new file mode 100644 index 00000000..5e60dca6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CVS/Entries @@ -0,0 +1,6 @@ +/CPD_to_CPT.m/1.1.1.1/Tue Sep 24 12:46:46 2002// +/hhmm2Q_CPD.m/1.1.1.1/Tue Sep 24 22:34:40 2002// +/maximize_params.m/1.1.1.1/Tue Sep 24 22:44:36 2002// +/reset_ess.m/1.1.1.1/Tue Sep 24 22:36:16 2002// +/update_ess.m/1.1.1.1/Tue Sep 24 22:43:30 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CVS/Repository new file mode 100644 index 00000000..f66442c4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@hhmm2Q_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/hhmm2Q_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/hhmm2Q_CPD.m new file mode 100644 index 00000000..c1a0cc20 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/hhmm2Q_CPD.m @@ -0,0 +1,65 @@ +function CPD = hhmm2Q_CPD(bnet, self, varargin) +% HHMMQ_CPD Make the CPD for a Q node in a 2 level hierarchical HMM +% CPD = hhmmQ_CPD(bnet, self, ...) +% +% Fself(t-1) Qps +% \ | +% \ v +% Qold(t-1) -> Q(t) +% / +% / +% Fbelow(t-1) +% +% +% optional args [defaults] +% +% Fself - node number <= ss +% Fbelow - node number <= ss +% Qps - node numbers (all <= 2*ss) - uses 2TBN indexing +% transprob - CPT for when Fbelow=2 and Fself=1 +% startprob - CPT for when Fbelow=2 and Fself=2 +% If Fbelow=1, we cannot change state. + +ss = bnet.nnodes_per_slice; +ns = bnet.node_sizes(:); + +% set default arguments +Fself = []; +Fbelow = []; +Qps = []; +startprob = []; +transprob = []; + +for i=1:2:length(varargin) + switch varargin{i}, + case 'Fself', Fself = varargin{i+1}; + case 'Fbelow', Fbelow = varargin{i+1}; + case 'Qps', Qps = varargin{i+1}; + case 'transprob', transprob = varargin{i+1}; + case 'startprob', startprob = varargin{i+1}; + end +end + +ps = parents(bnet.dag, self); +old_self = self-ss; +ndsz = ns(:)'; +CPD.dom_sz = [ndsz(ps) ns(self)]; +CPD.Fself_ndx = find_equiv_posns(Fself, ps); +CPD.Fbelow_ndx = find_equiv_posns(Fbelow, ps); +Qps = mysetdiff(ps, [Fself Fbelow old_self]); +CPD.Qps_ndx = find_equiv_posns(Qps, ps); +CPD.old_self_ndx = find_equiv_posns(old_self, ps); + +Qps = ps(CPD.Qps_ndx); +CPD.Qsz = ns(self); +CPD.Qpsizes = ns(Qps); + +CPD.transprob = transprob; +CPD.startprob = startprob; +CPD.start_counts = []; +CPD.trans_counts = []; + +CPD = class(CPD, 'hhmm2Q_CPD', discrete_CPD(0, CPD.dom_sz)); + + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/maximize_params.m new file mode 100644 index 00000000..9fe4d0ac --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/maximize_params.m @@ -0,0 +1,10 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a hhmmQ node to their ML/MAP values. +% CPD = maximize_params(CPD, temperature) + +if sum(CPD.start_counts(:)) > 0 + CPD.startprob = mk_stochastic(CPD.start_counts); +end +if sum(CPD.trans_counts(:)) > 0 + CPD.transprob = mk_stochastic(CPD.trans_counts); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/reset_ess.m new file mode 100644 index 00000000..8204c167 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/reset_ess.m @@ -0,0 +1,12 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics of a hhmm2 Q node. +% CPD = reset_ess(CPD) + +domsz = CPD.dom_sz; +domsz(CPD.Fself_ndx) = 1; +domsz(CPD.Fbelow_ndx) = 1; +Qdom_sz = domsz; +Qdom_sz(Qdom_sz==1)=[]; % get rid of dimensions of size 1 + +CPD.start_counts = zeros(Qdom_sz); +CPD.trans_counts = zeros(Qdom_sz); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/update_ess.m new file mode 100644 index 00000000..1a15d26c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmm2Q_CPD/update_ess.m @@ -0,0 +1,26 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) + +marg = add_ev_to_dmarginal(fmarginal, evidence, ns); + +nps = length(CPD.dom_sz)-1; % num parents + +if ~isempty(CPD.Fbelow_ndx) + if ~isempty(CPD.Fself_ndx) % general case + ndx = mk_multi_index(nps+1, [CPD.Fbelow_ndx CPD.Fself_ndx], [2 1]); + CPD.trans_counts = CPD.trans_counts + squeeze(marg.T(ndx{:})); + ndx = mk_multi_index(nps+1, [CPD.Fbelow_ndx CPD.Fself_ndx], [2 2]); + CPD.start_counts = CPD.start_counts + squeeze(marg.T(ndx{:})); + else % no F from self, hence no startprob (top level) + ndx = mk_multi_index(nps+1, CPD.Fbelow_ndx, 2); + CPD.trans_counts = CPD.trans_counts + squeeze(marg.T(ndx{:})); + end +else % no F signal from below + if ~isempty(CPD.Fself_ndx) % self F (bottom level) + ndx = mk_multi_index(nps+1, CPD.Fself_ndx, 1); + CPD.trans_counts = CPD.trans_counts + squeeze(marg.T(ndx{:})); + ndx = mk_multi_index(nps+1, CPD.Fself_ndx, 2); + CPD.start_counts = CPD.start_counts + squeeze(marg.T(ndx{:})); + else % no F from self or below + error('no F signal') + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/CVS/Entries new file mode 100644 index 00000000..3e0cc360 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/CVS/Entries @@ -0,0 +1,7 @@ +/hhmmF_CPD.m/1.1.1.1/Mon Jun 24 23:38:24 2002// +/log_prior.m/1.1.1.1/Wed May 29 15:59:54 2002// +/maximize_params.m/1.1.1.1/Wed May 29 15:59:54 2002// +/reset_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +/update_CPT.m/1.1.1.1/Mon Jun 24 22:45:04 2002// +/update_ess.m/1.1.1.1/Mon Jun 24 23:54:30 2002// +D/Old//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/CVS/Repository new file mode 100644 index 00000000..7c96bc38 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@hhmmF_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/CVS/Entries new file mode 100644 index 00000000..3ab747df --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/CVS/Entries @@ -0,0 +1,7 @@ +/hhmmF_CPD.m/1.1.1.1/Mon Jun 24 22:35:06 2002// +/log_prior.m/1.1.1.1/Mon Jun 24 22:35:06 2002// +/maximize_params.m/1.1.1.1/Mon Jun 24 22:35:06 2002// +/reset_ess.m/1.1.1.1/Mon Jun 24 22:35:06 2002// +/update_CPT.m/1.1.1.1/Mon Jun 24 22:35:06 2002// +/update_ess.m/1.1.1.1/Mon Jun 24 22:35:06 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/CVS/Repository new file mode 100644 index 00000000..8981a516 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@hhmmF_CPD/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/hhmmF_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/hhmmF_CPD.m new file mode 100644 index 00000000..4fdd9bc9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/hhmmF_CPD.m @@ -0,0 +1,76 @@ +function CPD = hhmmF_CPD(bnet, self, Qnodes, d, D, varargin) +% HHMMF_CPD Make the CPD for an F node at depth D of a D-level hierarchical HMM +% CPD = hhmmF_CPD(bnet, self, Qnodes, d, D, ...) +% +% Q(d-1) +% \ +% \ +% F(d) +% / | +% / | +% Q(d) F(d+1) +% +% We assume nodes are ordered (numbered) as follows: +% Q(1), ... Q(d), F(d+1), F(d) +% +% F(d)=2 means level d has finished. The prob this happens depends on Q(d) +% and optionally on Q(d-1), Q(d=1), ..., Q(1). +% Also, level d can only finish if the level below has finished +% (hence the F(d+1) -> F(d) arc). +% +% If d=D, there is no F(d+1), so F(d) is just a regular tabular_CPD. +% If all models always finish in the same state (e.g., their last), +% we don't need to condition on the state of parent models (Q(d-1), ...) +% +% optional args [defaults] +% +% termprob - termprob(k,i,2) = prob finishing given Q(d)=i and Q(1:d-1)=k [ finish in last state ] +% +% hhmmF_CPD is a subclass of tabular_CPD so we inherit inference methods like CPD_to_pot, etc. +% +% We create an isolated tabular_CPD with no F parent to learn termprob +% so we can avail of e.g., entropic or Dirichlet priors. +% +% For details, see "Linear-time inference in hierarchical HMMs", Murphy and Paskin, NIPS'01. + + +ps = parents(bnet.dag, self); +Qps = myintersect(ps, Qnodes); +F = mysetdiff(ps, Qps); +CPD.Q = Qps(end); % Q(d) +assert(CPD.Q == Qnodes(d)); +CPD.Qps = Qps(1:end-1); % all Q parents except Q(d), i.e., calling context + +ns = bnet.node_sizes(:); +CPD.Qsizes = ns(Qnodes); +CPD.d = d; +CPD.D = D; + +Qsz = ns(CPD.Q); +Qpsz = prod(ns(CPD.Qps)); + +% set default arguments +p = 0.9; +%termprob(k,i,t) Might terminate if i=Qsz; will not terminate if i<Qsz +termprob = zeros(Qpsz, Qsz, 2); +termprob(:, Qsz, 2) = p; +termprob(:, Qsz, 1) = 1-p; +termprob(:, 1:(Qsz-1), 1) = 1; + +for i=1:2:length(varargin) + switch varargin{i}, + case 'termprob', termprob = varargin{i+1}; + otherwise, error(['unrecognized argument ' varargin{i}]) + end +end + +ps = [CPD.Qps CPD.Q]; +% ns(self) = 2 since this is an F node +CPD.sub_CPD_term = mk_isolated_tabular_CPD(ps, ns([ps self]), {'CPT', termprob}); +S = struct(CPD.sub_CPD_term); +CPD.termprob = S.CPT; + +CPD = class(CPD, 'hhmmF_CPD', tabular_CPD(bnet, self)); + +CPD = update_CPT(CPD); + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/log_prior.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/log_prior.m new file mode 100644 index 00000000..7561205d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/log_prior.m @@ -0,0 +1,5 @@ +function L = log_prior(CPD) +% LOG_PRIOR Return log P(theta) for a hhmm F CPD +% L = log_prior(CPD) + +L = log_prior(CPD.sub_CPD_term); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/maximize_params.m new file mode 100644 index 00000000..16e51ddc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/maximize_params.m @@ -0,0 +1,9 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a hhmmF node to their ML/MAP values. +% CPD = maximize_params(CPD, temperature) + +CPD.sub_CPD_term = maximize_params(CPD.sub_CPD_term, temp); +S = struct(CPD.sub_CPD_term); +CPD.termprob = S.CPT; + +CPD = update_CPT(CPD); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/reset_ess.m new file mode 100644 index 00000000..f4428937 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/reset_ess.m @@ -0,0 +1,5 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics of a hhmm F node. +% CPD = reset_ess(CPD) + +CPD.sub_CPD_term = reset_ess(CPD.sub_CPD_term); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/update_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/update_CPT.m new file mode 100644 index 00000000..4ce14d9f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/update_CPT.m @@ -0,0 +1,13 @@ +function CPD = update_CPT(CPD) +% Compute the big CPT for an HHMM F node given internal termprob +% function CPD = update_CPT(CPD) + +Qsz = CPD.Qsizes(CPD.Q); +Qpsz = prod(CPD.Qsizes(CPD.Qps)); + +% P(Q(1:d-1), Q(d), F(d+1), F(d)) +CPT = zeros(Qpsz, Qsz, 2, 2); +CPT(:,:,1,1) = 1; % if F(d+1)=1, then F(d)=1 +CPT(:,:,2,:) = CPD.termprob; + +CPD = set_fields(CPD, 'CPT', CPT); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/update_ess.m new file mode 100644 index 00000000..18f7057e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/Old/update_ess.m @@ -0,0 +1,61 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a hhmmF node. +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) + +% Figure out the node numbers associated with each parent +% so we extract evidence from the right place +dom = fmarginal.domain; % Q(1) .. Q(d) F(d+1) F(d) +Qps = fmarginal.domain(1:end-2); +Q = Qps(end); +Qps = Qps(1:end-1); + +Qsz = CPD.Qsizes(CPD.Q); +Qpsz = prod(CPD.Qsizes(CPD.Qps)); % may be 1 + +% We assume the F node are always hidden, but allow some of the Q nodes +% to be observed. We do case analysis for speed. +%We only extract prob from fmarginal.T when F(d+1)=2 i.e., model below has finished. +% wrong -> % We sum over the possibilities that F(d+1) = 1 or 2 + +obs_self = ~hidden_bitv(Q); +if obs_self + self_val = evidence{Q}; +end + +if isempty(Qps) % independent of parent context + counts = zeros(Qsz, 2); + %fmarginal.T(Q(d), F(d+1), F(d)) + if obs_self + marg = myreshape(fmarginal.T, [1 2 2]); + counts(self_val,:) = marg(1,2,:); + %counts(self_val,:) = marg(1,1,:) + marg(1,2,:); + else + marg = myreshape(fmarginal.T, [Qsz 2 2]); + counts = squeeze(marg(:,2,:)); + %counts = squeeze(marg(:,2,:)) + squeeze(marg(:,1,:)); + end +else + counts = zeros(Qpsz, Qsz, 2); + %fmarginal.T(Q(1:d-1), Q(d), F(d+1), F(d)) + obs_Qps = ~any(hidden_bitv(Qps)); % we assume that all or none of the Q parents are observed + if obs_Qps + Qps_val = subv2ind(Qpsz, cat(1, evidence{Qps})); + end + if obs_self & obs_Qps + marg = myreshape(fmarginal.T, [1 1 2 2]); + counts(Qps_val, self_val, :) = squeeze(marg(1,1,2,:)); + %counts(Qps_val, self_val, :) = squeeze(marg(1,1,2,:)) + squeeze(marg(1,1,1,:)); + elseif ~obs_self & obs_Qps + marg = myreshape(fmarginal.T, [1 Qsz 2 2]); + counts(Qps_val, :, :) = squeeze(marg(1,:,2,:)); + %counts(Qps_val, :, :) = squeeze(marg(1,:,2,:)) + squeeze(marg(1,:,1,:)); + elseif obs_self & ~obs_Qps + error('not yet implemented') + else + marg = myreshape(fmarginal.T, [Qpsz Qsz 2 2]); + counts(:, :, :) = squeeze(marg(:,:,2,:)); + %counts(:, :, :) = squeeze(marg(:,:,2,:)) + squeeze(marg(:,:,1,:)); + end +end + +CPD.sub_CPD_term = update_ess_simple(CPD.sub_CPD_term, counts); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/hhmmF_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/hhmmF_CPD.m new file mode 100644 index 00000000..0c15580e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/hhmmF_CPD.m @@ -0,0 +1,73 @@ +function CPD = hhmmF_CPD(bnet, self, Qself, Fbelow, varargin) +% HHMMF_CPD Make the CPD for an F node in a hierarchical HMM +% CPD = hhmmF_CPD(bnet, self, Qself, Fbelow, ...) +% +% Qps +% \ +% \ +% Fself +% / | +% / | +% Qself Fbelow +% +% We assume nodes are ordered (numbered) as follows: Qps, Q, Fbelow, F +% All nodes numbers should be from slice 1. +% +% If Fbelow if missing, this becomes a regular tabular_CPD. +% Qps may be omitted. +% +% optional args [defaults] +% +% Qps - node numbers. +% termprob - termprob(k,i,2) = prob finishing given Q(d)=i and Q(1:d-1)=k [ finish in last state wp 0.9] +% +% hhmmF_CPD is a subclass of tabular_CPD so we inherit inference methods like CPD_to_pot, etc. +% +% We create an isolated tabular_CPD with no F parent to learn termprob +% so we can avail of e.g., entropic or Dirichlet priors. +% +% For details, see "Linear-time inference in hierarchical HMMs", Murphy and Paskin, NIPS'01. + + + +Qps = []; +% get parents +for i=1:2:length(varargin) + switch varargin{i}, + case 'Qps', Qps = varargin{i+1}; + end +end + +ns = bnet.node_sizes(:); +Qsz = ns(Qself); +Qpsz = prod(ns(Qps)); +CPD.Qsz = Qsz; +CPD.Qpsz = Qpsz; + +ps = parents(bnet.dag, self); +CPD.Fbelow_ndx = find_equiv_posns(Fbelow, ps); +CPD.Qps_ndx = find_equiv_posns(Qps, ps); +CPD.Qself_ndx = find_equiv_posns(Qself, ps); + +% set default arguments +p = 0.9; +%termprob(k,i,t) Might terminate if i=Qsz; will not terminate if i<Qsz +termprob = zeros(Qpsz, Qsz, 2); +termprob(:, Qsz, 2) = p; +termprob(:, Qsz, 1) = 1-p; +termprob(:, 1:(Qsz-1), 1) = 1; + +for i=1:2:length(varargin) + switch varargin{i}, + case 'termprob', termprob = varargin{i+1}; + end +end + +CPD.sub_CPD_term = mk_isolated_tabular_CPD([Qpsz Qsz 2], {'CPT', termprob}); +S = struct(CPD.sub_CPD_term); +CPD.termprob = S.CPT; + +CPD = class(CPD, 'hhmmF_CPD', tabular_CPD(bnet, self)); + +CPD = update_CPT(CPD); + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/log_prior.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/log_prior.m new file mode 100644 index 00000000..7561205d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/log_prior.m @@ -0,0 +1,5 @@ +function L = log_prior(CPD) +% LOG_PRIOR Return log P(theta) for a hhmm F CPD +% L = log_prior(CPD) + +L = log_prior(CPD.sub_CPD_term); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/maximize_params.m new file mode 100644 index 00000000..16e51ddc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/maximize_params.m @@ -0,0 +1,9 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a hhmmF node to their ML/MAP values. +% CPD = maximize_params(CPD, temperature) + +CPD.sub_CPD_term = maximize_params(CPD.sub_CPD_term, temp); +S = struct(CPD.sub_CPD_term); +CPD.termprob = S.CPT; + +CPD = update_CPT(CPD); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/reset_ess.m new file mode 100644 index 00000000..f4428937 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/reset_ess.m @@ -0,0 +1,5 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics of a hhmm F node. +% CPD = reset_ess(CPD) + +CPD.sub_CPD_term = reset_ess(CPD.sub_CPD_term); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/update_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/update_CPT.m new file mode 100644 index 00000000..1250457e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/update_CPT.m @@ -0,0 +1,13 @@ +function CPD = update_CPT(CPD) +% Compute the big CPT for an HHMM F node given internal termprob +% function CPD = update_CPT(CPD) + +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + +% CPT(Qpsz, Q, Fbelow, Fself) +CPT = zeros(Qpsz, Qsz, 2, 2); +CPT(:,:,1,1) = 1; % if Fbelow=1 (off), then Fself=1 (off) +CPT(:,:,2,:) = CPD.termprob; + +CPD = set_fields(CPD, 'CPT', CPT); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/update_ess.m new file mode 100644 index 00000000..cc636520 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmF_CPD/update_ess.m @@ -0,0 +1,40 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a hhmmF node. +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% +% We assume the F nodes are always hidden + +% Figure out the node numbers associated with each parent +dom = fmarginal.domain; +%Fself = dom(end); +%Fbelow = dom(CPD.Fbelow_ndx); +Qself = dom(CPD.Qself_ndx); +Qps = dom(CPD.Qps_ndx); + +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + +if all(hidden_bitv(Qps)) % we assume all are hidden or all are observed + k_ndx = 1:Qpsz; + eff_Qpsz = Qpsz; +else + k_ndx = subv2ind(Qpsz, cat(1, evidence{Qps})); + eff_Qpsz = 1; +end + +if hidden_bitv(Qself) + j_ndx = 1:Qsz; + eff_Qsz = Qsz; +else + j_ndx = evidence{Qself}; + eff_Qsz = 1; +end + +% Fmarginal(Qps, Q, Fbelow, F) +fmarg = myreshape(fmarginal.T, [eff_Qpsz eff_Qsz 2 2]); + +counts = zeros(Qpsz, Qsz, 2); +%counts(k_ndx, j_ndx, :) = sum(fmarginal.T(:, :, :, :), 3); % sum over Fbelow +counts(k_ndx, j_ndx, :) = fmarg(:, :, 2, :); % Fbelow = 2 + +CPD.sub_CPD_term = update_ess_simple(CPD.sub_CPD_term, counts); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/CVS/Entries new file mode 100644 index 00000000..0afc5821 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/CVS/Entries @@ -0,0 +1,7 @@ +/hhmmQ_CPD.m/1.1.1.1/Tue Sep 24 04:19:26 2002// +/log_prior.m/1.1.1.1/Wed May 29 15:59:54 2002// +/maximize_params.m/1.1.1.1/Tue Sep 24 13:10:18 2002// +/reset_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +/update_CPT.m/1.1.1.1/Tue Sep 24 02:58:18 2002// +/update_ess.m/1.1.1.1/Thu Jul 24 13:41:34 2003// +D/Old//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/CVS/Repository new file mode 100644 index 00000000..f226b7fc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@hhmmQ_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/CVS/Entries new file mode 100644 index 00000000..06bd5c8c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/CVS/Entries @@ -0,0 +1,10 @@ +/hhmmQ_CPD.m/1.1.1.1/Mon Jun 24 18:19:00 2002// +/log_prior.m/1.1.1.1/Mon Jun 24 18:19:00 2002// +/maximize_params.m/1.1.1.1/Mon Jun 24 18:19:00 2002// +/reset_ess.m/1.1.1.1/Mon Jun 24 18:19:00 2002// +/update_CPT.m/1.1.1.1/Tue Sep 24 02:30:32 2002// +/update_ess.m/1.1.1.1/Mon Jun 24 18:19:00 2002// +/update_ess2.m/1.1.1.1/Mon Jun 24 21:20:52 2002// +/update_ess3.m/1.1.1.1/Mon Jun 24 22:08:08 2002// +/update_ess4.m/1.1.1.1/Mon Jun 24 22:23:32 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/CVS/Repository new file mode 100644 index 00000000..8e7c978a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@hhmmQ_CPD/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/hhmmQ_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/hhmmQ_CPD.m new file mode 100644 index 00000000..24ef464b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/hhmmQ_CPD.m @@ -0,0 +1,126 @@ +function CPD = hhmmQ_CPD(bnet, self, Qnodes, d, D, varargin) +% HHMMQ_CPD Make the CPD for a Q node at depth D of a D-level hierarchical HMM +% CPD = hhmmQ_CPD(bnet, self, Qnodes, d, D, ...) +% +% Fd(t-1) \ Q1:d-1(t) +% \ | +% \ v +% Qd(t-1) -> Qd(t) +% / +% / +% Fd+1(t-1) +% +% We assume parents are ordered (numbered) as follows: +% Qd(t-1), Fd+1(t-1), Fd(t-1), Q1(t), ..., Qd(t) +% +% The parents of Qd(t) can either be just Qd-1(t) or the whole stack Q1:d-1(t) (allQ) +% In either case, we will call them Qps. +% If d=1, Qps does not exist. Also, the F1(t-1) -> Q1(t) arc is optional. +% If the arc is missing, startprob does not need to be specified, +% since the toplevel is assumed to never reset (F1 does not exist). +% If d=D, Fd+1(t-1) does not exist (there is no signal from below). +% +% optional args [defaults] +% +% transprob - transprob(i,k,j) = prob transition from i to j given Qps = k ['leftright'] +% selfprob - prob of a transition from i to i given Qps=k [0.1] +% startprob - startprob(k,j) = prob start in j given Qps = k ['leftstart'] +% startargs - other args to be passed to the sub tabular_CPD for learning startprob +% transargs - other args will be passed to the sub tabular_CPD for learning transprob +% allQ - 1 means use all Q nodes above d as parents, 0 means just level d-1 [0] +% F1toQ1 - 1 means add F1(t-1) -> Q1(t) arc, 0 means level 1 never resets [0] +% +% For d=1, startprob(1,j) is only needed if F1toQ1=1 +% Also, transprob(i,j) can be used instead of transprob(i,1,j). +% +% hhmmQ_CPD is a subclass of tabular_CPD so we inherit inference methods like CPD_to_pot, etc. +% +% We create isolated tabular_CPDs with no F parents to learn transprob/startprob +% so we can avail of e.g., entropic or Dirichlet priors. +% In the future, we will be able to represent the transprob using a tree_CPD. +% +% For details, see "Linear-time inference in hierarchical HMMs", Murphy and Paskin, NIPS'01. + + +ss = bnet.nnodes_per_slice; +%assert(self == Qnodes(d)+ss); +ns = bnet.node_sizes(:); +CPD.Qsizes = ns(Qnodes); +CPD.d = d; +CPD.D = D; +allQ = 0; + +% find out which parents to use, to get right size +for i=1:2:length(varargin) + switch varargin{i}, + case 'allQ', allQ = varargin{i+1}; + end +end + +if d==1 + CPD.Qps = []; +else + if allQ + CPD.Qps = Qnodes(1:d-1); + else + CPD.Qps = Qnodes(d-1); + end +end + +Qsz = ns(self); +Qpsz = prod(ns(CPD.Qps)); + +% set default arguments +startprob = 'leftstart'; +transprob = 'leftright'; +startargs = {}; +transargs = {}; +CPD.F1toQ1 = 0; +selfprob = 0.1; + +for i=1:2:length(varargin) + switch varargin{i}, + case 'transprob', transprob = varargin{i+1}; + case 'selfprob', selfprob = varargin{i+1}; + case 'startprob', startprob = varargin{i+1}; + case 'startargs', startargs = varargin{i+1}; + case 'transargs', transargs = varargin{i+1}; + case 'F1toQ1', CPD.F1toQ1 = varargin{i+1}; + end +end + +Qps = CPD.Qps + ss; +old_self = self-ss; + +if strcmp(transprob, 'leftright') + LR = mk_leftright_transmat(Qsz, selfprob); + transprob = repmat(reshape(LR, [1 Qsz Qsz]), [Qpsz 1 1]); % transprob(k,i,j) + transprob = permute(transprob, [2 1 3]); % now transprob(i,k,j) +end +transargs{end+1} = 'CPT'; +transargs{end+1} = transprob; +CPD.sub_CPD_trans = mk_isolated_tabular_CPD([old_self Qps], ns([old_self Qps self]), transargs); +S = struct(CPD.sub_CPD_trans); +CPD.transprob = myreshape(S.CPT, [Qsz Qpsz Qsz]); + + +if strcmp(startprob, 'leftstart') + startprob = zeros(Qpsz, Qsz); + startprob(:,1) = 1; +end + +if (d==1) & ~CPD.F1toQ1 + CPD.sub_CPD_start = []; + CPD.startprob = []; +else + startargs{end+1} = 'CPT'; + startargs{end+1} = startprob; + CPD.sub_CPD_start = mk_isolated_tabular_CPD(Qps, ns([Qps self]), startargs); + S = struct(CPD.sub_CPD_start); + CPD.startprob = myreshape(S.CPT, [Qpsz Qsz]); +end + +CPD = class(CPD, 'hhmmQ_CPD', tabular_CPD(bnet, self)); + +CPD = update_CPT(CPD); + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/log_prior.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/log_prior.m new file mode 100644 index 00000000..d44bec5e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/log_prior.m @@ -0,0 +1,8 @@ +function L = log_prior(CPD) +% LOG_PRIOR Return log P(theta) for a hhmm CPD +% L = log_prior(CPD) + +L = log_prior(CPD.sub_CPD_trans); +if ~isempty(CPD.sub_CPD_start) + L = L + log_prior(CPD.sub_CPD_start); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/maximize_params.m new file mode 100644 index 00000000..0e4632aa --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/maximize_params.m @@ -0,0 +1,40 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a hhmmQ node to their ML/MAP values. +% CPD = maximize_params(CPD, temperature) + +Qsz = CPD.Qsizes(CPD.d); +Qpsz = prod(CPD.Qsizes(CPD.Qps)); + +if ~isempty(CPD.sub_CPD_start) + CPD.sub_CPD_start = maximize_params(CPD.sub_CPD_start, temp); + S = struct(CPD.sub_CPD_start); + CPD.startprob = myreshape(S.CPT, [Qpsz Qsz]); + %CPD.startprob = S.CPT; +end + +if 1 + % If we are in a state that can only go the end state, + % we will never see a transition to another (non-end) state, + % so counts(i,k,j)=0 (and termprob(k,i)=1). + % We set counts(i,k,i)=1 in this case. + % This will cause remove_hhmm_end_state to return a + % stochastic matrix, but otherwise has no effect on EM. + counts = get_field(CPD.sub_CPD_trans, 'counts'); + counts = reshape(counts, [Qsz Qpsz Qsz]); + for k=1:Qpsz + for i=1:Qsz + if sum(counts(i,k,:))==0 % never witnessed a transition out of i + counts(i,k,i)=1; % add self loop + %fprintf('CPDQ d=%d i=%d k=%d\n', CPD.d, i, k); + end + end + end + CPD.sub_CPD_trans = set_fields(CPD.sub_CPD_trans, 'counts', counts(:)); +end + +CPD.sub_CPD_trans = maximize_params(CPD.sub_CPD_trans, temp); +S = struct(CPD.sub_CPD_trans); +%CPD.transprob = S.CPT; +CPD.transprob = myreshape(S.CPT, [Qsz Qpsz Qsz]); + +CPD = update_CPT(CPD); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/reset_ess.m new file mode 100644 index 00000000..45a70ad7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/reset_ess.m @@ -0,0 +1,8 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics of a hhmm Q node. +% CPD = reset_ess(CPD) + +if ~isempty(CPD.sub_CPD_start) + CPD.sub_CPD_start = reset_ess(CPD.sub_CPD_start); +end +CPD.sub_CPD_trans = reset_ess(CPD.sub_CPD_trans); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_CPT.m new file mode 100644 index 00000000..503c225b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_CPT.m @@ -0,0 +1,74 @@ +function CPD = update_CPT(CPD) +% Compute the big CPT for an HHMM Q node (including F parents) given internal transprob and startprob +% function CPD = update_CPT(CPD) + +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + +if ~isempty(CPD.Fbelow_ndx) + if ~isempty(CPD.Fself_ndx) % general case + % Fb(t-1) Fself(t-1) P(Q(t)=j| Q(t-1)=i, Qps(t)=k) + % ------------------------------------------------------ + % 1 1 delta(i,j) + % 2 1 transprob(i,k,j) + % 1 2 impossible + % 2 2 startprob(k,j) + CPT = zeros(Qsz, 2, 2, Qpsz, Qsz); + I = repmat(eye(Qsz), [1 1 Qpsz]); % i,j,k + I = permute(I, [1 3 2]); % i,k,j + CPT(:, 1, 1, :, :) = I; + CPT(:, 2, 1, :, :) = CPD.transprob; + CPT(:, 1, 2, :, :) = I; + CPT(:, 2, 2, :, :) = repmat(reshape(CPD.startprob, [1 Qpsz Qsz]), [Qsz 1 1]); % replicate over i + else % no F from self, hence no startprob + % Fb(t-1) P(Q(t)=j| Q(t-1)=i, Qps(t)=k) + % ------------------------------------------------------ + % 1 delta(i,j) + % 2 transprob(i,k,j) + + nps = length(CPD.dom_sz)-1; % num parents + CPT = 0*myones(CPD.dom_sz); + %CPT = zeros(Qsz, 2, Qpsz, Qsz); % assumes CPT(Q(t-1), F(t-1), Qps, Q(t)) + % but a member of Qps may preceed Q(t-1) or F(t-1) in the ordering + + I = repmat(eye(Qsz), [1 1 Qpsz]); % i,j,k + I = permute(I, [1 3 2]); % i,k,j + + % the following fails if there is a member of Qps with a lower + % number than F + %CPT(:, 1, :, :) = I; + %CPT(:, 2, :, :) = CPD.transprob; + + ndx = mk_multi_index(nps+1, CPD.Fbelow_ndx, 1); + CPT(ndx{:}) = I; + ndx = mk_multi_index(nps+1, CPD.Fbelow_ndx, 2); + CPT(ndx{:}) = CPD.transprob; + keyboard + end +else % no F signal from below + if ~isempty(CPD.Fself_ndx) + % Q(t-1), Fself(t-1), Qps, Q(t) + + % if condition start on previous concrete state (as in map learning), + % CPT(:, 1, :, :, :) = CPD.transprob(Q(t-1), Qps, Q(t)) + % CPT(:, 2, :, :, :) = CPD.startprob(Q(t-1), Qps, Q(t)) + + % Fself(t-1) P(Q(t-1)=i, Qps(t)=k -> Q(t)=j) + % ------------------------------------------------------ + % 1 transprob(i,k,j) + % 2 startprob(k,j) + CPT = zeros(Qsz, 2, Qpsz, Qsz); + I = repmat(eye(Qsz), [1 1 Qpsz]); % i,j,k + I = permute(I, [1 3 2]); % i,k,j + CPT(:, 1, :, :) = CPD.transprob; + if CPD.fullstartprob + CPT(:, 2, :, :) = CPD.startprob; + else + CPT(:, 2, :, :) = repmat(reshape(CPD.startprob, [1 Qpsz Qsz]), [Qsz 1 1]); % replicate over i + end + else % no F from self + error('An hhmmQ node without any F parents is just a tabular_CPD') + end +end + +CPD = set_fields(CPD, 'CPT', CPT); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess.m new file mode 100644 index 00000000..51c2bd1f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess.m @@ -0,0 +1,141 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a hhmm Q node. +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, idden_bitv) + +% Figure out the node numbers associated with each parent +% e.g., D=4, d=3, Qps = all Qs above, so dom = [Q3(t-1) F4(t-1) F3(t-1) Q1(t) Q2(t) Q3(t)]. +% so self = Q3(t), old_self = Q3(t-1), CPD.Qps = [1 2], Qps = [Q1(t) Q2(t)] +dom = fmarginal.domain; +self = dom(end); +old_self = dom(1); +Qps = dom(length(dom)-length(CPD.Qps):end-1); + +Qsz = CPD.Qsizes(CPD.d); +Qpsz = prod(CPD.Qsizes(CPD.Qps)); + +% If some of the Q nodes are observed (which happens during supervised training) +% the counts will only be non-zero in positions +% consistent with the evidence. We put the computed marginal responsibilities +% into the appropriate slots of the big counts array. +% (Recall that observed discrete nodes only have a single effective value.) +% (A more general, but much slower, way is to call add_evidence_to_dmarginal.) +% We assume the F nodes are never observed. + +obs_self = ~hidden_bitv(self); +obs_Qps = (~isempty(Qps)) & (~any(hidden_bitv(Qps))); % we assume that all or none of the Q parents are observed + +if obs_self + self_val = evidence{self}; + oldself_val = evidence{old_self}; +end + +if obs_Qps + Qps_val = subv2ind(Qpsz, cat(1, evidence{Qps})); + if Qps_val == 0 + keyboard + end +end + +if CPD.d==1 % no Qps from above + if ~CPD.F1toQ1 % no F from self + % marg(Q1(t-1), F2(t-1), Q1(t)) + % F2(t-1) P(Q1(t)=j | Q1(t-1)=i) + % 1 delta(i,j) + % 2 transprob(i,j) + if obs_self + hor_counts = zeros(Qsz, Qsz); + hor_counts(oldself_val, self_val) = fmarginal.T(2); + else + marg = reshape(fmarginal.T, [Qsz 2 Qsz]); + hor_counts = squeeze(marg(:,2,:)); + end + else + % marg(Q1(t-1), F2(t-1), F1(t-1), Q1(t)) + % F2(t-1) F1(t-1) P(Qd(t)=j| Qd(t-1)=i) + % ------------------------------------------------------ + % 1 1 delta(i,j) + % 2 1 transprob(i,j) + % 1 2 impossible + % 2 2 startprob(j) + if obs_self + marg = myreshape(fmarginal.T, [1 2 2 1]); + hor_counts = zeros(Qsz, Qsz); + hor_counts(oldself_val, self_val) = marg(1,2,1,1); + ver_counts = zeros(Qsz, 1); + %ver_counts(self_val) = marg(1,2,2,1); + ver_counts(self_val) = marg(1,2,2,1) + marg(1,1,2,1); + else + marg = reshape(fmarginal.T, [Qsz 2 2 Qsz]); + hor_counts = squeeze(marg(:,2,1,:)); + %ver_counts = squeeze(sum(marg(:,2,2,:),1)); % sum over i + ver_counts = squeeze(sum(marg(:,2,2,:),1)) + squeeze(sum(marg(:,1,2,:),1)); % sum i,b + end + end % F1toQ1 +else % d ~= 1 + if CPD.d < CPD.D % general case + % marg(Qd(t-1), Fd+1(t-1), Fd(t-1), Qps(t), Qd(t)) + % Fd+1(t-1) Fd(t-1) P(Qd(t)=j| Qd(t-1)=i, Qps(t)=k) + % ------------------------------------------------------ + % 1 1 delta(i,j) + % 2 1 transprob(i,k,j) + % 1 2 impossible + % 2 2 startprob(k,j) + if obs_Qps & obs_self + marg = myreshape(fmarginal.T, [1 2 2 1 1]); + k = 1; + hor_counts = zeros(Qsz, Qpsz, Qsz); + hor_counts(oldself_val, Qps_val, self_val) = marg(1, 2,1, k,1); + ver_counts = zeros(Qpsz, Qsz); + %ver_counts(Qps_val, self_val) = marg(1, 2,2, k,1); + ver_counts(Qps_val, self_val) = marg(1, 2,2, k,1) + marg(1, 1,2, k,1); + elseif obs_Qps & ~obs_self + marg = myreshape(fmarginal.T, [Qsz 2 2 1 Qsz]); + k = 1; + hor_counts = zeros(Qsz, Qpsz, Qsz); + hor_counts(:, Qps_val, :) = marg(:, 2,1, k,:); + ver_counts = zeros(Qpsz, Qsz); + %ver_counts(Qps_val, :) = sum(marg(:, 2,2, k,:), 1); + ver_counts(Qps_val, :) = sum(marg(:, 2,2, k,:), 1) + sum(marg(:, 1,2, k,:), 1); + elseif ~obs_Qps & obs_self + error('not yet implemented') + else % everything is hidden + marg = reshape(fmarginal.T, [Qsz 2 2 Qpsz Qsz]); + hor_counts = squeeze(marg(:,2,1,:,:)); % i,k,j + %ver_counts = squeeze(sum(marg(:,2,2,:,:),1)); % sum over i + ver_counts = squeeze(sum(marg(:,2,2,:,:),1)) + squeeze(sum(marg(:,1,2,:,:),1)); % sum over i,b + end + else % d == D, so no F from below + % marg(QD(t-1), FD(t-1), Qps(t), QD(t)) + % FD(t-1) P(QD(t)=j | QD(t-1)=i, Qps(t)=k) + % 1 transprob(i,k,j) + % 2 startprob(k,j) + if obs_Qps & obs_self + marg = myreshape(fmarginal.T, [1 2 1 1]); + k = 1; + hor_counts = zeros(Qsz, Qpsz, Qsz); + hor_counts(oldself_val, Qps_val, self_val) = marg(1, 1, k,1); + ver_counts = zeros(Qpsz, Qsz); + ver_counts(Qps_val, self_val) = marg(1, 2, k,1); + elseif obs_Qps & ~obs_self + marg = myreshape(fmarginal.T, [Qsz 2 1 Qsz]); + k = 1; + hor_counts = zeros(Qsz, Qpsz, Qsz); + hor_counts(:, Qps_val, :) = marg(:, 1, k,:); + ver_counts = zeros(Qpsz, Qsz); + ver_counts(Qps_val, :) = sum(marg(:, 2, k, :), 1); + elseif ~obs_Qps & obs_self + error('not yet implemented') + else % everything is hidden + marg = reshape(fmarginal.T, [Qsz 2 Qpsz Qsz]); + hor_counts = squeeze(marg(:,1,:,:)); + ver_counts = squeeze(sum(marg(:,2,:,:),1)); % sum over i + end + end +end + +CPD.sub_CPD_trans = update_ess_simple(CPD.sub_CPD_trans, hor_counts); + +if ~isempty(CPD.sub_CPD_start) + CPD.sub_CPD_start = update_ess_simple(CPD.sub_CPD_start, ver_counts); +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess2.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess2.m new file mode 100644 index 00000000..41fc7380 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess2.m @@ -0,0 +1,178 @@ +function CPD = update_ess2(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a hhmm Q node. +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, idden_bitv) + +% Figure out the node numbers associated with each parent +dom = fmarginal.domain; +self = dom(end); % by assumption +old_self = dom(CPD.old_self_ndx); +Fself = dom(CPD.Fself_ndx); +Fbelow = dom(CPD.Fbelow_ndx); +Qps = dom(CPD.Qps_ndx); + +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + + +fmarg = add_ev_to_dmarginal(fmarginal, evidence, ns); + + + +% hor_counts(old_self, Qps, self), +% fmarginal(old_self, Fbelow, Fself, Qps, self) +% hor_counts(i,k,j) = fmarginal(i,2,1,k,j) % below has finished, self has not +% ver_counts(i,k,j) = fmarginal(i,2,2,k,j) % below has finished, and so has self (reset) +% Since any of i,j,k may be observed, we write +% hor_counts(counts_ndx{:}) = fmarginal(fmarg_ndx{:}) +% where e.g., counts_ndx = {1, ':', 2} if Qps is hidden but we observe old_self=1, self=2. +% To create this counts_ndx, we write counts_ndx = mk_multi_ndx(3, obs_dim, obs_val) +% where counts_obs_dim = [1 3], counts_obs_val = [1 2] specifies the values of dimensions 1 and 3. + +counts_obs_dim = []; +fmarg_obs_dim = []; +obs_val = []; +if hidden_bitv(self) + effQsz = Qsz; +else + effQsz = 1; + counts_obs_dim = [counts_obs_dim 3]; + fmarg_obs_dim = [fmarg_obs_dim 5]; + obs_val = [obs_val evidence{self}]; +end + +% e.g., D=4, d=3, Qps = all Qs above, so dom = [Q3(t-1) F4(t-1) F3(t-1) Q1(t) Q2(t) Q3(t)]. +% so self = Q3(t), old_self = Q3(t-1), CPD.Qps = [1 2], Qps = [Q1(t) Q2(t)] +dom = fmarginal.domain; +self = dom(end); +old_self = dom(1); +Qps = dom(length(dom)-length(CPD.Qps):end-1); + +Qsz = CPD.Qsizes(CPD.d); +Qpsz = prod(CPD.Qsizes(CPD.Qps)); + +% If some of the Q nodes are observed (which happens during supervised training) +% the counts will only be non-zero in positions +% consistent with the evidence. We put the computed marginal responsibilities +% into the appropriate slots of the big counts array. +% (Recall that observed discrete nodes only have a single effective value.) +% (A more general, but much slower, way is to call add_evidence_to_dmarginal.) +% We assume the F nodes are never observed. + +obs_self = ~hidden_bitv(self); +obs_Qps = (~isempty(Qps)) & (~any(hidden_bitv(Qps))); % we assume that all or none of the Q parents are observed + +if obs_self + self_val = evidence{self}; + oldself_val = evidence{old_self}; +end + +if obs_Qps + Qps_val = subv2ind(Qpsz, cat(1, evidence{Qps})); + if Qps_val == 0 + keyboard + end +end + +if CPD.d==1 % no Qps from above + if ~CPD.F1toQ1 % no F from self + % marg(Q1(t-1), F2(t-1), Q1(t)) + % F2(t-1) P(Q1(t)=j | Q1(t-1)=i) + % 1 delta(i,j) + % 2 transprob(i,j) + if obs_self + hor_counts = zeros(Qsz, Qsz); + hor_counts(oldself_val, self_val) = fmarginal.T(2); + else + marg = reshape(fmarginal.T, [Qsz 2 Qsz]); + hor_counts = squeeze(marg(:,2,:)); + end + else + % marg(Q1(t-1), F2(t-1), F1(t-1), Q1(t)) + % F2(t-1) F1(t-1) P(Qd(t)=j| Qd(t-1)=i) + % ------------------------------------------------------ + % 1 1 delta(i,j) + % 2 1 transprob(i,j) + % 1 2 impossible + % 2 2 startprob(j) + if obs_self + marg = myreshape(fmarginal.T, [1 2 2 1]); + hor_counts = zeros(Qsz, Qsz); + hor_counts(oldself_val, self_val) = marg(1,2,1,1); + ver_counts = zeros(Qsz, 1); + %ver_counts(self_val) = marg(1,2,2,1); + ver_counts(self_val) = marg(1,2,2,1) + marg(1,1,2,1); + else + marg = reshape(fmarginal.T, [Qsz 2 2 Qsz]); + hor_counts = squeeze(marg(:,2,1,:)); + %ver_counts = squeeze(sum(marg(:,2,2,:),1)); % sum over i + ver_counts = squeeze(sum(marg(:,2,2,:),1)) + squeeze(sum(marg(:,1,2,:),1)); % sum i,b + end + end % F1toQ1 +else % d ~= 1 + if CPD.d < CPD.D % general case + % marg(Qd(t-1), Fd+1(t-1), Fd(t-1), Qps(t), Qd(t)) + % Fd+1(t-1) Fd(t-1) P(Qd(t)=j| Qd(t-1)=i, Qps(t)=k) + % ------------------------------------------------------ + % 1 1 delta(i,j) + % 2 1 transprob(i,k,j) + % 1 2 impossible + % 2 2 startprob(k,j) + if obs_Qps & obs_self + marg = myreshape(fmarginal.T, [1 2 2 1 1]); + k = 1; + hor_counts = zeros(Qsz, Qpsz, Qsz); + hor_counts(oldself_val, Qps_val, self_val) = marg(1, 2,1, k,1); + ver_counts = zeros(Qpsz, Qsz); + %ver_counts(Qps_val, self_val) = marg(1, 2,2, k,1); + ver_counts(Qps_val, self_val) = marg(1, 2,2, k,1) + marg(1, 1,2, k,1); + elseif obs_Qps & ~obs_self + marg = myreshape(fmarginal.T, [Qsz 2 2 1 Qsz]); + k = 1; + hor_counts = zeros(Qsz, Qpsz, Qsz); + hor_counts(:, Qps_val, :) = marg(:, 2,1, k,:); + ver_counts = zeros(Qpsz, Qsz); + %ver_counts(Qps_val, :) = sum(marg(:, 2,2, k,:), 1); + ver_counts(Qps_val, :) = sum(marg(:, 2,2, k,:), 1) + sum(marg(:, 1,2, k,:), 1); + elseif ~obs_Qps & obs_self + error('not yet implemented') + else % everything is hidden + marg = reshape(fmarginal.T, [Qsz 2 2 Qpsz Qsz]); + hor_counts = squeeze(marg(:,2,1,:,:)); % i,k,j + %ver_counts = squeeze(sum(marg(:,2,2,:,:),1)); % sum over i + ver_counts = squeeze(sum(marg(:,2,2,:,:),1)) + squeeze(sum(marg(:,1,2,:,:),1)); % sum over i,b + end + else % d == D, so no F from below + % marg(QD(t-1), FD(t-1), Qps(t), QD(t)) + % FD(t-1) P(QD(t)=j | QD(t-1)=i, Qps(t)=k) + % 1 transprob(i,k,j) + % 2 startprob(k,j) + if obs_Qps & obs_self + marg = myreshape(fmarginal.T, [1 2 1 1]); + k = 1; + hor_counts = zeros(Qsz, Qpsz, Qsz); + hor_counts(oldself_val, Qps_val, self_val) = marg(1, 1, k,1); + ver_counts = zeros(Qpsz, Qsz); + ver_counts(Qps_val, self_val) = marg(1, 2, k,1); + elseif obs_Qps & ~obs_self + marg = myreshape(fmarginal.T, [Qsz 2 1 Qsz]); + k = 1; + hor_counts = zeros(Qsz, Qpsz, Qsz); + hor_counts(:, Qps_val, :) = marg(:, 1, k,:); + ver_counts = zeros(Qpsz, Qsz); + ver_counts(Qps_val, :) = sum(marg(:, 2, k, :), 1); + elseif ~obs_Qps & obs_self + error('not yet implemented') + else % everything is hidden + marg = reshape(fmarginal.T, [Qsz 2 Qpsz Qsz]); + hor_counts = squeeze(marg(:,1,:,:)); + ver_counts = squeeze(sum(marg(:,2,:,:),1)); % sum over i + end + end +end + +CPD.sub_CPD_trans = update_ess_simple(CPD.sub_CPD_trans, hor_counts); + +if ~isempty(CPD.sub_CPD_start) + CPD.sub_CPD_start = update_ess_simple(CPD.sub_CPD_start, ver_counts); +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess3.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess3.m new file mode 100644 index 00000000..da7ab6bd --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess3.m @@ -0,0 +1,80 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a hhmm Q node. +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, idden_bitv) +% +% we assume if one of the Qps is observed, all of them are +% We assume the F nodes are already hidden + +% Figure out the node numbers associated with each parent +dom = fmarginal.domain; +self = dom(CPD.self_ndx); +old_self = dom(CPD.old_self_ndx); +%Fself = dom(CPD.Fself_ndx); +%Fbelow = dom(CPD.Fbelow_ndx); +Qps = dom(CPD.Qps_ndx); + +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + + +% hor_counts(old_self, Qps, self), +% fmarginal(old_self, Fbelow, Fself, Qps, self) +% hor_counts(i,k,j) = fmarginal(i,2,1,k,j) % below has finished, self has not +% ver_counts(i,k,j) = fmarginal(i,2,2,k,j) % below has finished, and so has self (reset) +% Since any of i,j,k may be observed, we write +% hor_counts(ndx{:}) = fmarginal(...) +% where e.g., ndx = {1, ':', 2} if Qps is hidden but we observe old_self=1, self=2. + +% ndx{i,k,j} +if hidden_bitv(old_self) + ndx{1} = ':'; +else + ndx{1} = evidence{old_self}; +end +if hidden_bitv(Qps) + ndx{2} = ':'; +else + ndx{2} = subv2ind(Qpsz, cat(1, evidence{Qps})); +end +if hidden_bitv(self) + ndx{3} = ':'; +else + ndx{3} = evidence{self}; +end + +fmarg = add_ev_to_dmarginal(fmarginal, evidence, ns); +% marg(Qold(t-1), Fbelow(t-1), Fself(t-1), Qps(t), Qself(t)) +hor_counts = zeros(Qsz, Qpsz, Qsz); +ver_counts = zeros(Qpsz, Qsz); + +if ~isempty(CPD.Fbelow_ndx) + if ~isempty(CPD.Fself_ndx) % general case + fmarg.T = myreshape(fmarg.T, [Qsz 2 2 Qpsz Qsz]); + marg_ndx = {ndx{1}, 2, 1, ndx{2}, ndx{3}}; + hor_counts(ndx{:}) = fmarg.T(marg_ndx{:}); + ver_counts(ndx{2:3}) = ... % sum over Fbelow and Qold=i + sum(fmarg.T({ndx{1}, 1, 2, ndx{2}, ndx{3}}),1) + .. + sum(fmarg.T({ndx{1}, 2, 2, ndx{2}, ndx{3}}),1); + else % no F from self, hence no startprob + fmarg.T = myreshape(fmarg.T, [Qsz 2 Qpsz Qsz]); + hor_counts(ndx{:}) = fmarg.T({ndx{1}, 2, ndx{2}, ndx{3}}); + end +else % no F signal from below + if ~isempty(CPD.Fself_ndx) % self F + fmarg.T = myreshape(fmarg.T, [Qsz 2 Qpsz Qsz]); + hor_counts(ndx{:}) = fmarg.T({ndx{1}, 1, ndx{2}, ndx{3}}); + ver_counts(ndx{2:3}) = ... % sum over Qold=i + sum(fmarg.T({ndx{1}, 2, ndx{2}, ndx{3}}),1); + else % no F from self + error('An hhmmQ node without any F parents is just a tabular_CPD') + end +end + + +CPD.sub_CPD_trans = update_ess_simple(CPD.sub_CPD_trans, hor_counts); + +if ~isempty(CPD.sub_CPD_start) + CPD.sub_CPD_start = update_ess_simple(CPD.sub_CPD_start, ver_counts); +end + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess4.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess4.m new file mode 100644 index 00000000..c826da1c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/Old/update_ess4.m @@ -0,0 +1,95 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a hhmm Q node. +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, idden_bitv) +% +% we assume if one of the Qps is observed, all of them are +% We assume the F nodes are already hidden + +% Figure out the node numbers associated with each parent +dom = fmarginal.domain; +self = dom(CPD.self_ndx); +old_self = dom(CPD.old_self_ndx); +%Fself = dom(CPD.Fself_ndx); +%Fbelow = dom(CPD.Fbelow_ndx); +Qps = dom(CPD.Qps_ndx); + +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + + +% hor_counts(old_self, Qps, self), +% fmarginal(old_self, Fbelow, Fself, Qps, self) +% hor_counts(i,k,j) = fmarginal(i,2,1,k,j) % below has finished, self has not +% ver_counts(i,k,j) = fmarginal(i,2,2,k,j) % below has finished, and so has self (reset) +% Since any of i,j,k may be observed, we write +% hor_counts(i_counts_ndx, kndx, jndx) = fmarginal(i_fmarg_ndx...) +% where i_fmarg_ndx = 1 and i_counts_ndx = i if old_self is observed to have value i, +% i_fmarg_ndx = 1:Qsz and i_counts_ndx = 1:Qsz if old_self is hidden, etc. + + +if hidden_bitv(old_self) + i_counts_ndx = 1:Qsz; + i_fmarg_ndx = 1:Qsz; + eff_oldQsz = Qsz; +else + i_counts_ndx = evidence{old_self}; + i_fmarg_ndx = 1; + eff_oldQsz = 1; +end + +if all(hidden_bitv(Qps)) % we assume all are hidden or all are observed + k_counts_ndx = 1:Qpsz; + k_fmarg_ndx = 1:Qpsz; + eff_Qpsz = Qpsz; +else + k_counts_ndx = subv2ind(Qpsz, cat(1, evidence{Qps})); + k_fmarg_ndx = 1; + eff_Qpsz = 1; +end + +if hidden_bitv(self) + j_counts_ndx = 1:Qsz; + j_fmarg_ndx = 1:Qsz; + eff_Qsz = Qsz; +else + j_counts_ndx = evidence{self}; + j_fmarg_ndx = 1; + eff_Qsz = 1; +end + +hor_counts = zeros(Qsz, Qpsz, Qsz); +ver_counts = zeros(Qpsz, Qsz); + +if ~isempty(CPD.Fbelow_ndx) + if ~isempty(CPD.Fself_ndx) % general case + fmarg.T = myreshape(fmarg.T, [eff_oldQsz 2 2 eff_Qpsz eff_Qsz]); + hor_counts(i_counts_ndx, k_counts_ndx, j_counts_ndx) = ... + fmarg.T(:, i_fmarg_ndx, 2, 1, k_fmarg_ndx, j_fmarg_ndx); + ver_counts(k_counts_ndx, j_counts_ndx) = ... % sum over Fbelow and Qold + sum(fmarg.T(:, 1, 2, k_fmarg_ndx, j_fmarg_ndx), 1) + ... + sum(fmarg.T(:, 2, 2, k_fmarg_ndx, j_fmarg_ndx), 1); + else % no F from self, hence no startprob + fmarg.T = myreshape(fmarg.T, [eff_oldQsz 2 eff_Qpsz eff_Qsz]); + hor_counts(i_counts_ndx, k_counts_ndx, j_counts_ndx) = ... + fmarg.T(i_fmarg_ndx, 2, k_fmarg_ndx, j_fmarg_ndx); + end +else % no F signal from below + if ~isempty(CPD.Fself_ndx) % self F + fmarg.T = myreshape(fmarg.T, [eff_oldQsz 2 eff_Qpsz eff_Qsz]); + hor_counts(i_counts_ndx, k_counts_ndx, j_counts_ndx) = ... + fmarg.T(i_fmarg_ndx, 1, k_fmarg_ndx, j_fmarg_ndx); + ver_counts(k_counts_ndx, j_counts_ndx) = ... % sum over Qold + sum(fmarg.T(:, 2, k_fmarg_ndx, j_fmarg_ndx), 1); + else % no F from self + error('An hhmmQ node without any F parents is just a tabular_CPD') + end +end + + +CPD.sub_CPD_trans = update_ess_simple(CPD.sub_CPD_trans, hor_counts); + +if ~isempty(CPD.sub_CPD_start) + CPD.sub_CPD_start = update_ess_simple(CPD.sub_CPD_start, ver_counts); +end + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/hhmmQ_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/hhmmQ_CPD.m new file mode 100644 index 00000000..6f289602 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/hhmmQ_CPD.m @@ -0,0 +1,132 @@ +function CPD = hhmmQ_CPD(bnet, self, varargin) +% HHMMQ_CPD Make the CPD for a Q node in a hierarchical HMM +% CPD = hhmmQ_CPD(bnet, self, ...) +% +% Fself(t-1) Qps(t) +% \ | +% \ v +% Qold(t-1) -> Q(t) +% / +% / +% Fbelow(t-1) +% +% Let ss = slice size = num. nodes per slice. +% This node is Q(t), and has mandatory parents Qold(t-1) (assumed to be numbered Q(t)-ss) +% and optional parents Fbelow, Fself, Qps. +% We require parents to be ordered (numbered) as follows: +% Qold, Fbelow, Fself, Qps, Q. +% +% If Fself=2, we use the transition matrix, else we use the prior matrix. +% If Fself node is omitted (eg. top level), we always use the transition matrix. +% If Fbelow=2, we may change state, otherwise we must stay in the same state. +% If Fbelow node is omitted (eg., bottom level), we may change state at every step. +% If Qps (Q parents) are specified, all parameters are conditioned on their joint value. +% We may choose any subset of nodes to condition on, as long as they as numbered lower than self. +% +% optional args [defaults] +% +% Fself - node number <= ss +% Fbelow - node number <= ss +% Qps - node numbers (all <= 2*ss) - uses 2TBN indexing +% transprob - transprob(i,k,j) = prob transition from i to j given Qps = k ['leftright'] +% selfprob - prob of a transition from i to i given Qps=k [0.1] +% startprob - startprob(k,j) = prob start in j given Qps = k ['leftstart'] +% startargs - other args to be passed to the sub tabular_CPD for learning startprob +% transargs - other args will be passed to the sub tabular_CPD for learning transprob +% fullstartprob - 1 means startprob depends on Q(t-1) [0] +% hhmmQ_CPD is a subclass of tabular_CPD so we inherit inference methods like CPD_to_pot, etc. +% +% We create isolated tabular_CPDs with no F parents to learn transprob/startprob +% so we can avail of e.g., entropic or Dirichlet priors. +% In the future, we will be able to represent the transprob using a tree_CPD. +% +% For details, see "Linear-time inference in hierarchical HMMs", Murphy and Paskin, NIPS'01. + + +ss = bnet.nnodes_per_slice; +ns = bnet.node_sizes(:); + +% set default arguments +Fself = []; +Fbelow = []; +Qps = []; +startprob = 'leftstart'; +transprob = 'leftright'; +startargs = {}; +transargs = {}; +selfprob = 0.1; +fullstartprob = 0; + +for i=1:2:length(varargin) + switch varargin{i}, + case 'Fself', Fself = varargin{i+1}; + case 'Fbelow', Fbelow = varargin{i+1}; + case 'Qps', Qps = varargin{i+1}; + case 'transprob', transprob = varargin{i+1}; + case 'selfprob', selfprob = varargin{i+1}; + case 'startprob', startprob = varargin{i+1}; + case 'startargs', startargs = varargin{i+1}; + case 'transargs', transargs = varargin{i+1}; + case 'fullstartprob', fullstartprob = varargin{i+1}; + end +end + +CPD.fullstartprob = fullstartprob; + +ps = parents(bnet.dag, self); +ndsz = ns(:)'; +CPD.dom_sz = [ndsz(ps) ns(self)]; +CPD.Fself_ndx = find_equiv_posns(Fself, ps); +CPD.Fbelow_ndx = find_equiv_posns(Fbelow, ps); +%CPD.Qps_ndx = find_equiv_posns(Qps+ss, ps); +CPD.Qps_ndx = find_equiv_posns(Qps, ps); +old_self = self-ss; +CPD.old_self_ndx = find_equiv_posns(old_self, ps); + +Qps = ps(CPD.Qps_ndx); +CPD.Qsz = ns(self); +CPD.Qpsz = prod(ns(Qps)); +CPD.Qpsizes = ns(Qps); +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + +if strcmp(transprob, 'leftright') + LR = mk_leftright_transmat(Qsz, selfprob); + transprob = repmat(reshape(LR, [1 Qsz Qsz]), [Qpsz 1 1]); % transprob(k,i,j) + transprob = permute(transprob, [2 1 3]); % now transprob(i,k,j) +end +transargs{end+1} = 'CPT'; +transargs{end+1} = transprob; +CPD.sub_CPD_trans = mk_isolated_tabular_CPD(ns([old_self Qps self]), transargs); +S = struct(CPD.sub_CPD_trans); +%CPD.transprob = myreshape(S.CPT, [Qsz Qpsz Qsz]); +CPD.transprob = S.CPT; + + +if strcmp(startprob, 'leftstart') + startprob = zeros(Qpsz, Qsz); + startprob(:,1) = 1; +end +if isempty(CPD.Fself_ndx) + CPD.sub_CPD_start = []; + CPD.startprob = []; +else + startargs{end+1} = 'CPT'; + startargs{end+1} = startprob; + if CPD.fullstartprob + CPD.sub_CPD_start = mk_isolated_tabular_CPD(ns([self Qps self]), startargs); + S = struct(CPD.sub_CPD_start); + %CPD.startprob = myreshape(S.CPT, [Qsz Qpsz Qsz]); + CPD.startprob = S.CPT; + else + CPD.sub_CPD_start = mk_isolated_tabular_CPD(ns([Qps self]), startargs); + S = struct(CPD.sub_CPD_start); + %CPD.startprob = myreshape(S.CPT, [CPD.Qpsizes Qsz]); + CPD.startprob = S.CPT; + end +end + +CPD = class(CPD, 'hhmmQ_CPD', tabular_CPD(bnet, self)); + +CPD = update_CPT(CPD); + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/log_prior.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/log_prior.m new file mode 100644 index 00000000..d44bec5e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/log_prior.m @@ -0,0 +1,8 @@ +function L = log_prior(CPD) +% LOG_PRIOR Return log P(theta) for a hhmm CPD +% L = log_prior(CPD) + +L = log_prior(CPD.sub_CPD_trans); +if ~isempty(CPD.sub_CPD_start) + L = L + log_prior(CPD.sub_CPD_start); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/maximize_params.m new file mode 100644 index 00000000..541a50be --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/maximize_params.m @@ -0,0 +1,40 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a hhmmQ node to their ML/MAP values. +% CPD = maximize_params(CPD, temperature) + +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + +if ~isempty(CPD.sub_CPD_start) + CPD.sub_CPD_start = maximize_params(CPD.sub_CPD_start, temp); + S = struct(CPD.sub_CPD_start); + CPD.startprob = myreshape(S.CPT, [Qpsz Qsz]); + %CPD.startprob = S.CPT; +end + +if 1 + % If we are in a state that can only go the end state, + % we will never see a transition to another (non-end) state, + % so counts(i,k,j)=0 (and termprob(k,i)=1). + % We set counts(i,k,i)=1 in this case. + % This will cause remove_hhmm_end_state to return a + % stochastic matrix, but otherwise has no effect on EM. + counts = get_field(CPD.sub_CPD_trans, 'counts'); + counts = reshape(counts, [Qsz Qpsz Qsz]); + for k=1:Qpsz + for i=1:Qsz + if sum(counts(i,k,:))==0 % never witnessed a transition out of i + counts(i,k,i)=1; % add self loop + %fprintf('CPDQ d=%d i=%d k=%d\n', CPD.d, i, k); + end + end + end + CPD.sub_CPD_trans = set_fields(CPD.sub_CPD_trans, 'counts', counts(:)); +end + +CPD.sub_CPD_trans = maximize_params(CPD.sub_CPD_trans, temp); +S = struct(CPD.sub_CPD_trans); +%CPD.transprob = S.CPT; +CPD.transprob = myreshape(S.CPT, [Qsz Qpsz Qsz]); + +CPD = update_CPT(CPD); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/reset_ess.m new file mode 100644 index 00000000..45a70ad7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/reset_ess.m @@ -0,0 +1,8 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics of a hhmm Q node. +% CPD = reset_ess(CPD) + +if ~isempty(CPD.sub_CPD_start) + CPD.sub_CPD_start = reset_ess(CPD.sub_CPD_start); +end +CPD.sub_CPD_trans = reset_ess(CPD.sub_CPD_trans); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/update_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/update_CPT.m new file mode 100644 index 00000000..9ed1a352 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/update_CPT.m @@ -0,0 +1,70 @@ +function CPD = update_CPT(CPD) +% Compute the big CPT for an HHMM Q node (including F parents) given internal transprob and startprob +% function CPD = update_CPT(CPD) + +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + +if ~isempty(CPD.Fbelow_ndx) + if ~isempty(CPD.Fself_ndx) % general case + % Fb(t-1) Fself(t-1) P(Q(t)=j| Q(t-1)=i, Qps(t)=k) + % ------------------------------------------------------ + % 1 1 delta(i,j) + % 2 1 transprob(i,k,j) + % 1 2 impossible + % 2 2 startprob(k,j) + CPT = zeros(Qsz, 2, 2, Qpsz, Qsz); + I = repmat(eye(Qsz), [1 1 Qpsz]); % i,j,k + I = permute(I, [1 3 2]); % i,k,j + CPT(:, 1, 1, :, :) = I; + CPT(:, 2, 1, :, :) = CPD.transprob; + CPT(:, 1, 2, :, :) = I; + CPT(:, 2, 2, :, :) = repmat(reshape(CPD.startprob, [1 Qpsz Qsz]), ... + [Qsz 1 1]); % replicate over i + else % no F from self, hence no startprob + % Fb(t-1) P(Q(t)=j| Q(t-1)=i, Qps(t)=k) + % ------------------------------------------------------ + % 1 delta(i,j) + % 2 transprob(i,k,j) + + nps = length(CPD.dom_sz)-1; % num parents + CPT = 0*myones(CPD.dom_sz); + %CPT = zeros(Qsz, 2, Qpsz, Qsz); % assumes CPT(Q(t-1), F(t-1), Qps, Q(t)) + % but a member of Qps may preceed Q(t-1) or F(t-1) in the ordering + + for k=1:CPD.Qpsz + Qps_vals = ind2subv(CPD.Qpsizes, k); + ndx = mk_multi_index(nps+1, [CPD.Fbelow_ndx CPD.Qps_ndx], [1 Qps_vals]); + CPT(ndx{:}) = eye(Qsz); % CPT(:,2,k,:) or CPT(:,k,2,:) etc + end + ndx = mk_multi_index(nps+1, CPD.Fbelow_ndx, 2); + CPT(ndx{:}) = CPD.transprob; % we assume transprob is in topo order + end +else % no F signal from below + if ~isempty(CPD.Fself_ndx) + % Q(t-1), Fself(t-1), Qps, Q(t) + + % Fself(t-1) P(Q(t-1)=i, Qps(t)=k -> Q(t)=j) + % ------------------------------------------------------ + % 1 transprob(i,k,j) + % 2 startprob(k,j) + + nps = length(CPD.dom_sz)-1; % num parents + CPT = 0*myones(CPD.dom_sz); + ndx = mk_multi_index(nps+1, CPD.Fself_ndx, 1); + CPT(ndx{:}) = CPD.transprob; + if CPD.fullstartprob + ndx = mk_multi_index(nps+1, CPD.Fself_ndx, 2); + CPT(ndx{:}) = CPD.startprob; + else + for i=1:CPD.Qsz + ndx = mk_multi_index(nps+1, [CPD.Fself_ndx CPD.old_self_ndx], [2 i]); + CPT(ndx{:}) = CPD.startprob; + end + end + else % no F from self + error('An hhmmQ node without any F parents is just a tabular_CPD') + end +end + +CPD = set_fields(CPD, 'CPT', CPT); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/update_ess.m new file mode 100644 index 00000000..07dfc72e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@hhmmQ_CPD/update_ess.m @@ -0,0 +1,86 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a hhmm Q node. +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, idden_bitv) +% +% we assume if one of the Qps is observed, all of them are +% We assume the F nodes are already hidden + +% Figure out the node numbers associated with each parent +dom = fmarginal.domain; +self = dom(end); +old_self = dom(CPD.old_self_ndx); +%Fself = dom(CPD.Fself_ndx); +%Fbelow = dom(CPD.Fbelow_ndx); +Qps = dom(CPD.Qps_ndx); + +Qsz = CPD.Qsz; +Qpsz = CPD.Qpsz; + + +% hor_counts(old_self, Qps, self), +% fmarginal(old_self, Fbelow, Fself, Qps, self) +% hor_counts(i,k,j) = fmarginal(i,2,1,k,j) % below has finished, self has not +% ver_counts(i,k,j) = fmarginal(i,2,2,k,j) % below has finished, and so has self (reset) +% Since any of i,j,k may be observed, we write +% hor_counts(i_counts_ndx, kndx, jndx) = fmarginal(i_fmarg_ndx...) +% where i_fmarg_ndx = 1 and i_counts_ndx = i if old_self is observed to have value i, +% i_fmarg_ndx = 1:Qsz and i_counts_ndx = 1:Qsz if old_self is hidden, etc. + + +if hidden_bitv(old_self) + i_counts_ndx = 1:Qsz; + eff_oldQsz = Qsz; +else + i_counts_ndx = evidence{old_self}; + eff_oldQsz = 1; +end + +if all(hidden_bitv(Qps)) % we assume all are hidden or all are observed + k_counts_ndx = 1:Qpsz; + eff_Qpsz = Qpsz; +else + k_counts_ndx = subv2ind(Qpsz, cat(1, evidence{Qps})); + eff_Qpsz = 1; +end + +if hidden_bitv(self) + j_counts_ndx = 1:Qsz; + eff_Qsz = Qsz; +else + j_counts_ndx = evidence{self}; + eff_Qsz = 1; +end + +hor_counts = zeros(Qsz, Qpsz, Qsz); +ver_counts = zeros(Qpsz, Qsz); + +if ~isempty(CPD.Fbelow_ndx) + if ~isempty(CPD.Fself_ndx) % general case + fmarg = myreshape(fmarginal.T, [eff_oldQsz 2 2 eff_Qpsz eff_Qsz]); + hor_counts(i_counts_ndx, k_counts_ndx, j_counts_ndx) = fmarg(:, 2, 1, :, :); + ver_counts(k_counts_ndx, j_counts_ndx) = ... % sum over Fbelow and Qold + sumv(fmarg(:, :, 2, :, :), [1 2]); % require Fself=2 + else % no F from self, hence no startprob + fmarg = myreshape(fmarginal.T, [eff_oldQsz 2 eff_Qpsz eff_Qsz]); + hor_counts(i_counts_ndx, k_counts_ndx, j_counts_ndx) = ... + fmarg(:, 2, :, :); % require Fbelow = 2 + end +else % no F signal from below + if ~isempty(CPD.Fself_ndx) % self F + fmarg = myreshape(fmarginal.T, [eff_oldQsz 2 eff_Qpsz eff_Qsz]); + hor_counts(i_counts_ndx, k_counts_ndx, j_counts_ndx) = fmarg(:, 1, :, :); + ver_counts(k_counts_ndx, j_counts_ndx) = ... % sum over Qold + squeeze(sum(fmarg(:, 2, :, :), 1)); % Fself=2 + else % no F from self + error('An hhmmQ node without any F parents is just a tabular_CPD') + end +end + + +CPD.sub_CPD_trans = update_ess_simple(CPD.sub_CPD_trans, hor_counts); + +if ~isempty(CPD.sub_CPD_start) + CPD.sub_CPD_start = update_ess_simple(CPD.sub_CPD_start, ver_counts); +end + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Entries new file mode 100644 index 00000000..1cef220a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Entries @@ -0,0 +1,6 @@ +/convert_to_table.m/1.1.1.1/Wed May 29 15:59:54 2002// +/maximize_params.m/1.1.1.1/Wed May 29 15:59:54 2002// +/mlp_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +/reset_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +/update_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Repository new file mode 100644 index 00000000..9fadd7fe --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@mlp_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/convert_to_table.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/convert_to_table.m new file mode 100644 index 00000000..7e25d072 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/convert_to_table.m @@ -0,0 +1,80 @@ +function T = convert_to_table(CPD, domain, evidence) +% CONVERT_TO_TABLE Convert a mlp CPD to a table, incorporating any evidence +% T = convert_to_table(CPD, domain, evidence) + +self = domain(end); +ps = domain(1:end-1); % self' parents +%cps = myintersect(ps, cnodes); % self' continous parents +cnodes = domain(CPD.cpndx); +cps = myintersect(ps, cnodes); +odom = domain(~isemptycell(evidence(domain))); % obs nodes in the net +assert(myismember(cps, odom)); % !ALL the CTS parents must be observed! +ns(cps)=1; +dps = mysetdiff(ps, cps); % self' discrete parents +dobs = myintersect(dps, odom); % discrete obs parents + +% Extract the params compatible with the observations (if any) on the discrete parents (if any) + +if ~isempty(dobs), + dvals = cat(1, evidence{dobs}); + ns_eff= CPD.sizes; % effective node sizes + ens=ns_eff; + ens(dobs) = 1; + S=prod(ens(dps)); + subs = ind2subv(ens(dps), 1:S); + mask = find_equiv_posns(dobs, dps); + for i=1:length(mask), + subs(:,mask(i)) = dvals(i); + end + support = subv2ind(ns_eff(dps), subs)'; +else + ns_eff= CPD.sizes; + support=[1:prod(ns_eff(dps))]; +end + +W1=[]; b1=[]; W2=[]; b2=[]; + +W1 = CPD.W1(:,:,support); +b1= CPD.b1(support,:); +W2 = CPD.W2(:,:,support); +b2= CPD.b2(support,:); +ns(odom) = 1; +dpsize = prod(ns(dps)); % overall size of the self' discrete parents + +x = cat(1, evidence{cps}); +ndata=size(x,2); + +if ~isempty(evidence{self}) % + app=struct(CPD); % + ns(self)=app.mlp{1}.nout; % pump up self to the original dimension if observed + clear app; % +end % + +T =zeros(dpsize, ns(self)); % +for i=1:dpsize % + W1app = W1(:,:,i); % + b1app = b1(i,:); % + W2app = W2(:,:,i); % + b2app = b2(i,:); % for each of the dpsize combinations of self'parents values + z = tanh(x(:)'*W1app + ones(ndata, 1)*b1app); % we tabulate the corrisponding glm model + a = z*W2app + ones(ndata, 1)*b2app; % (element of the cell array CPD.glim) + appoggio = normalise(exp(a)); % + T(i,:)=appoggio; % + W1app=[]; W2app=[]; b1app=[]; b2app=[]; % + z=[]; a=[]; appoggio=[]; % +end % + +if ~isempty(evidence{self}) + appoggio=[]; % + appoggio=zeros(1,ns(self)); % + r = evidence{self}; %...if self is observed => in output there's only the probability of the 'true' class + for i=1:dpsize % + appoggio(i)=T(i,r); % + end + T=zeros(dpsize,1); + for i=1:dpsize + T(i,1)=appoggio(i); + end + clear appoggio; + ns(self) = 1; +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/maximize_params.m new file mode 100644 index 00000000..19d0a1be --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/maximize_params.m @@ -0,0 +1,34 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Find ML params of an MLP using Scaled Conjugated Gradient (SCG) +% CPD = maximize_params(CPD, temperature) +% temperature parameter is ignored + +if ~adjustable_CPD(CPD), return; end +options = foptions; + +% options(1) >= 0 means print an annoying message when the max. num. iter. is reached +if CPD.verbose + options(1) = 1; +else + options(1) = -1; +end +%options(1) = CPD.verbose; + +options(2) = CPD.wthresh; +options(3) = CPD.llthresh; +options(14) = CPD.max_iter; + +dpsz=length(CPD.mlp); + +for i=1:dpsz + mask=[]; + mask=find(CPD.eso_weights(:,:,i)>0); % for adapting the parameters we use only positive weighted example + if ~isempty(mask), + CPD.mlp{i} = netopt_weighted(CPD.mlp{i}, options, CPD.parent_vals(mask',:), CPD.self_vals(mask',:,i), CPD.eso_weights(mask',:,i), 'scg'); + + CPD.W1(:,:,i)=CPD.mlp{i}.w1; % update the parameters matrix + CPD.b1(i,:)=CPD.mlp{i}.b1; % + CPD.W2(:,:,i)=CPD.mlp{i}.w2; % update the parameters matrix + CPD.b2(i,:)=CPD.mlp{i}.b2; % + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/mlp_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/mlp_CPD.m new file mode 100644 index 00000000..7e9d3f6b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/mlp_CPD.m @@ -0,0 +1,139 @@ +function CPD = mlp_CPD(bnet, self, nhidden, w1, b1, w2, b2, clamped, max_iter, verbose, wthresh, llthresh) +% MLP_CPD Make a CPD from a Multi Layer Perceptron (i.e., feedforward neural network) +% +% We use a different MLP for each discrete parent combination (if there are any discrete parents). +% We currently assume this node (the child) is discrete. +% +% CPD = mlp_CPD(bnet, self, nhidden) +% will create a CPD with random parameters, where self is the number of this node and nhidden the number of the hidden nodes. +% The params are drawn from N(0, s*I), where s = 1/sqrt(n+1), n = length(X). +% +% CPD = mlp_CPD(bnet, self, nhidden, w1, b1, w2, b2) allows you to specify the params, where +% w1 = first-layer weight matrix +% b1 = first-layer bias vector +% w2 = second-layer weight matrix +% b2 = second-layer bias vector +% These are assumed to be the same for each discrete parent combination. +% If any of these are [], random values will be created. +% +% CPD = mlp_CPD(bnet, self, nhidden, w1, b1, w2, b2, clamped) allows you to prevent the params from being +% updated during learning (if clamped = 1). Default: clamped = 0. +% +% CPD = mlp_CPD(bnet, self, nhidden, w1, b1, w2, b2, clamped, max_iter, verbose, wthresh, llthresh) +% alllows you to specify params that control the M step: +% max_iter - the maximum number of steps to take (default: 10) +% verbose - controls whether to print (default: 0 means silent). +% wthresh - a measure of the precision required for the value of +% the weights W at the solution. Default: 1e-2. +% llthresh - a measure of the precision required of the objective +% function (log-likelihood) at the solution. Both this and the previous condition must +% be satisfied for termination. Default: 1e-2. +% +% For learning, we use a weighted version of scaled conjugated gradient in the M step. + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'mlp_CPD', discrete_CPD(0,[])); + return; +elseif isa(bnet, 'mlp_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + +assert(myismember(self, bnet.dnodes)); +ns = bnet.node_sizes; + +ps = parents(bnet.dag, self); +dnodes = mysetdiff(1:length(bnet.dag), bnet.cnodes); +dps = myintersect(ps, dnodes); +cps = myintersect(ps, bnet.cnodes); +dpsz = prod(ns(dps)); +cpsz = sum(ns(cps)); +self_size = ns(self); + +% discrete/cts parent index - which ones of my parents are discrete/cts? +CPD.dpndx = find_equiv_posns(dps, ps); +CPD.cpndx = find_equiv_posns(cps, ps); + +CPD.mlp = cell(1,dpsz); +for i=1:dpsz + CPD.mlp{i} = mlp(cpsz, nhidden, self_size, 'softmax'); + if nargin >=4 & ~isempty(w1) + CPD.mlp{i}.w1 = w1; + end + if nargin >=5 & ~isempty(b1) + CPD.mlp{i}.b1 = b1; + end + if nargin >=6 & ~isempty(w2) + CPD.mlp{i}.w2 = w2; + end + if nargin >=7 & ~isempty(b2) + CPD.mlp{i}.b2 = b2; + end + W1app(:,:,i)=CPD.mlp{i}.w1; + W2app(:,:,i)=CPD.mlp{i}.w2; + b1app(i,:)=CPD.mlp{i}.b1; + b2app(i,:)=CPD.mlp{i}.b2; +end +if nargin < 8, clamped = 0; end +if nargin < 9, max_iter = 10; end +if nargin < 10, verbose = 0; end +if nargin < 11, wthresh = 1e-2; end +if nargin < 12, llthresh = 1e-2; end + +CPD.self = self; +CPD.max_iter = max_iter; +CPD.verbose = verbose; +CPD.wthresh = wthresh; +CPD.llthresh = llthresh; + +% sufficient statistics +% Since MLP is not in the exponential family, we must store all the raw data. +% +CPD.W1=W1app; % Extract all the parameters of the node for handling discrete obs parents +CPD.W2=W2app; % +nparaW=[size(W1app) size(W2app)]; % +CPD.b1=b1app; % +CPD.b2=b2app; % +nparab=[size(b1app) size(b2app)]; % + +CPD.sizes=bnet.node_sizes(:); % used in CPD_to_table to pump up the node sizes + +CPD.parent_vals = []; % X(l,:) = value of cts parents in l'th example + +CPD.eso_weights=[]; % weights used by the SCG algorithm + +CPD.self_vals = []; % Y(l,:) = value of self in l'th example + +% For BIC +CPD.nsamples = 0; +CPD.nparams=prod(nparaW)+prod(nparab); +CPD = class(CPD, 'mlp_CPD', discrete_CPD(clamped, ns([ps self]))); + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.mlp = {}; +CPD.self = []; +CPD.max_iter = []; +CPD.verbose = []; +CPD.wthresh = []; +CPD.llthresh = []; +CPD.approx_hess = []; +CPD.W1 = []; +CPD.W2 = []; +CPD.b1 = []; +CPD.b2 = []; +CPD.sizes = []; +CPD.parent_vals = []; +CPD.eso_weights=[]; +CPD.self_vals = []; +CPD.nsamples = []; +CPD.nparams = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/reset_ess.m new file mode 100644 index 00000000..ba7a7101 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/reset_ess.m @@ -0,0 +1,12 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics for a CPD (mlp) +% CPD = reset_ess(CPD) + +CPD.W1 = []; +CPD.W2 = []; +CPD.b1 = []; +CPD.b2 = []; +CPD.parent_vals = []; +CPD.eso_weights=[]; +CPD.self_vals = []; +CPD.nsamples = 0; \ No newline at end of file diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/update_ess.m new file mode 100644 index 00000000..353a0b5c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/update_ess.m @@ -0,0 +1,131 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a CPD (MLP) +% CPD = update_ess(CPD, family_marginal, evidence, node_sizes, cnodes, hidden_bitv) +% +% fmarginal = overall posterior distribution of self and its parents +% fmarginal(i1,i2...,ik,s)=prob(Pa1=i1,...,Pak=ik, self=s| X) +% +% => 1) prob(self|Pa1,...,Pak)=fmarginal/prob(Pa1,...,Pak) with prob(Pa1,...,Pak)=sum{s,fmarginal} +% [self estimation -> CPD.self_vals] +% 2) prob(Pa1,...,Pak) [SCG weights -> CPD.eso_weights] +% +% Hidden_bitv is ignored + +% Written by Pierpaolo Brutti + +if ~adjustable_CPD(CPD), return; end + +dom = fmarginal.domain; +cdom = myintersect(dom, cnodes); +assert(~any(isemptycell(evidence(cdom)))); +ns(cdom)=1; + +self = dom(end); +ps=dom(1:end-1); +dpdom=mysetdiff(ps,cdom); + +dnodes = mysetdiff(1:length(ns), cnodes); + +ddom = myintersect(ps, dnodes); % +if isempty(evidence{self}), % if self is hidden in what follow we must + ddom = myintersect(dom, dnodes); % consider its dimension +end % + +odom = dom(~isemptycell(evidence(dom))); +hdom = dom(isemptycell(evidence(dom))); % hidden parents in domain + +dobs = myintersect(ddom, odom); +dvals = cat(1, evidence{dobs}); +ens = ns; % effective node sizes +ens(dobs) = 1; + +dpsz=prod(ns(dpdom)); +S=prod(ens(ddom)); +subs = ind2subv(ens(ddom), 1:S); +mask = find_equiv_posns(dobs, ddom); +for i=1:length(mask), + subs(:,mask(i)) = dvals(i); +end +supportedQs = subv2ind(ns(ddom), subs); + +Qarity = prod(ns(ddom)); +if isempty(ddom), + Qarity = 1; +end +fullm.T = zeros(Qarity, 1); +fullm.T(supportedQs) = fmarginal.T(:); + +% For dynamic (recurrent) net------------------------------------------------------------- +% ---------------------------------------------------------------------------------------- +high=size(evidence,1); % slice height +ss_ns=ns(1:high); % single slice nodes sizes +pos=self; % +slice_num=0; % +while pos>high, % + slice_num=slice_num+1; % find active slice + pos=pos-high; % pos=self posistion into a single slice +end % + +last_dim=pos-1; % +if isempty(evidence{self}), % + last_dim=pos; % +end % last_dim=last reshaping dimension +reg=dom-slice_num*high; +dex=myintersect(reg(find(reg>=0)), [1:last_dim]); % +rs_dim=ss_ns(dex); % reshaping dimensions + +if slice_num>0, + act_slice=[]; past_ancest=[]; % + act_slice=slice_num*high+[1:high]; % recover the active slice nodes + % past_ancest=mysetdiff(ddom, act_slice); + past_ancest=mysetdiff(ps, act_slice); % recover ancestors contained into past slices + app=ns(past_ancest); + rs_dim=[app(:)' rs_dim(:)']; % +end % +if length(rs_dim)==1, rs_dim=[1 rs_dim]; end % +if size(rs_dim,1)~=1, rs_dim=rs_dim'; end % + +fullm.T=reshape(fullm.T, rs_dim); % reshaping the marginal + +% ---------------------------------------------------------------------------------------- +% ---------------------------------------------------------------------------------------- + +% X = cts parent, R = discrete self + +% 1) observations vector -> CPD.parents_vals ------------------------------------------------- +x = cat(1, evidence{cdom}); + +% 2) weights vector -> CPD.eso_weights ------------------------------------------------------- +if isempty(evidence{self}) % R is hidden + sum_over=length(rs_dim); + app=sum(fullm.T, sum_over); + pesi=reshape(app,[dpsz,1]); + clear app; +else + pesi=reshape(fullm.T,[dpsz,1]); +end + +assert(approxeq(sum(pesi),1)); + +% 3) estimate (if R is hidden) or recover (if R is obs) self'value---------------------------- +if isempty(evidence{self}) % R is hidden + app=mk_stochastic(fullm.T); % P(self|Pa1,...,Pak)=fmarginal/prob(Pa1,...,Pak) + app=reshape(app,[dpsz ns(self)]); % matrix size: prod{j,ns(Paj)} x ns(self) + r=app; + clear app; +else + r = zeros(dpsz,ns(self)); + for i=1:dpsz + if pesi(i)~=0, r(i,evidence{self}) = 1; end + end +end +for i=1:dpsz + if pesi(i) ~=0, assert(approxeq(sum(r(i,:)),1)); end +end + +CPD.nsamples = CPD.nsamples + 1; +CPD.parent_vals(CPD.nsamples,:) = x(:)'; +for i=1:dpsz + CPD.eso_weights(CPD.nsamples,:,i)=pesi(i); + CPD.self_vals(CPD.nsamples,:,i) = r(i,:); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_CPT.m new file mode 100644 index 00000000..93a6dcef --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_CPT.m @@ -0,0 +1,34 @@ +function CPT = CPD_to_CPT(CPD) +% CPD_TO_CPT Convert the discrete CPD to tabular form (noisyor) +% CPT = CPD_to_CPT(CPD) +% +% CPT(U1,...,Un, X) = Pr(X|U1,...,Un) where the Us are the parents (excluding leak). + +if ~isempty(CPD.CPT) + CPT = CPD.CPT; % remember to flush cache if params change (e.g., during learning) + return; +end + +q = [CPD.leak_inhibit CPD.inhibit(:)']; +% q(i) is the prob. that the i'th parent will be inhibited (flipped from 1 to 0). +% q(1) is the leak inhibition probability, and length(q) = n + 1. + +if length(q)==1 + CPT = [q 1-q]; + return; +end + +n = length(q); +Bn = ind2subv(2*ones(1,n), 1:(2^n))-1; % all n bit vectors, with the left most column toggling fastest (LSB) +CPT = zeros(2^n, 2); +% Pr(X=0 | U_1 .. U_n) = prod_{i: U_i = on} q_i = prod_i q_i ^ U_i = exp(u' * log(q_i)) +% This method is problematic when q contains zeros + +Q = repmat(q(:)', 2^n, 1); +Q(logical(~Bn)) = 1; +CPT(:,1) = prod(Q,2); +CPT(:,2) = 1-CPT(:,1); + +CPT = reshape(CPT(2:2:end), 2*ones(1,n)); % skip cases in which the leak is off + +CPD.CPT = CPT; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_CPT.m~ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_CPT.m~ new file mode 100644 index 00000000..6f4cacd4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_CPT.m~ @@ -0,0 +1,70 @@ +function CPT = CPD_to_CPT(CPD) +% CPD_TO_CPT Convert the discrete CPD to tabular form (noisyor) +% CPT = CPD_to_CPT(CPD) +% +% CPT(U1,...,Un, X) = Pr(X|U1,...,Un) where the Us are the parents (excluding leak). + +if ~isempty(CPD.CPT) + CPT = CPD.CPT; % remember to flush cache if params change (e.g., during learning) + return; +end + +q = [CPD.leak_inhibit CPD.inhibit(:)']; +% q(i) is the prob. that the i'th parent will be inhibited (flipped from 1 to 0). +% q(1) is the leak inhibition probability, and length(q) = n + 1. + +if length(q)==1 + CPT = [q 1-q]; + return; +end + +n = length(q); +Bn = ind2subv(2*ones(1,n), 1:(2^n))-1; % all n bit vectors, with the left most column toggling fastest (LSB) +CPT = zeros(2^n, 2); +% Pr(X=0 | U_1 .. U_n) = prod_{i: U_i = on} q_i = prod_i q_i ^ U_i = exp(u' * log(q_i)) +% This method is problematic when q contains zeros + +Q = repmat(q(:)', 2^n, 1); +Q(logical(~Bn)) = 1; +CPT(:,1) = prod(Q,2); +CPT(:,2) = 1-CPT(:,1); + +CPT = reshape(CPT(2:2:end), 2*ones(1,n)); % skip cases in which the leak is off + +CPD.CPT = CPT; + +function CPT = CPD_to_CPT(CPD) +% CPD_TO_CPT Convert the discrete CPD to tabular form (noisyor) +% CPT = CPD_to_CPT(CPD) +% +% CPT(U1,...,Un, X) = Pr(X|U1,...,Un) where the Us are the parents (excluding leak). + +if ~isempty(CPD.CPT) + CPT = CPD.CPT; % remember to flush cache if params change (e.g., during learning) + return; +end + +q = [CPD.leak_inhibit CPD.inhibit(:)']; +% q(i) is the prob. that the i'th parent will be inhibited (flipped from 1 to 0). +% q(1) is the leak inhibition probability, and length(q) = n + 1. + +if length(q)==1 + CPT = [q 1-q]; + return; +end + +n = length(q); +Bn = ind2subv(2*ones(1,n), 1:(2^n))-1; % all n bit vectors, with the left most column toggling fastest (LSB) +CPT = zeros(2^n, 2); +% Pr(X=0 | U_1 .. U_n) = prod_{i: U_i = on} q_i = prod_i q_i ^ U_i = exp(u' * log(q_i)) +% This method is problematic when q contains zeros + +Q = repmat(q(:)', 2^n, 1); +Q(logical(~Bn)) = 1; +CPT(:,1) = prod(Q,2); +CPT(:,2) = 1-CPT(:,1); + +CPT = reshape(CPT(2:2:end), 2*ones(1,n)); % skip cases in which the leak is off + +CPD.CPT = CPT; + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_lambda_msg.m b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_lambda_msg.m new file mode 100644 index 00000000..8046c860 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_lambda_msg.m @@ -0,0 +1,19 @@ +function lam_msg = CPD_to_lambda_msg(CPD, msg_type, n, ps, msg, p, evidence) +% CPD_TO_LAMBDA_MSG Compute lambda message (noisyor) +% lam_msg = CPD_to_lambda_msg(CPD, msg_type, n, ps, msg, p) +% Pearl p190 top eqn + +switch msg_type + case 'd', + l0 = msg{n}.lambda(1); + l1 = msg{n}.lambda(2); + Pi = sum_prod_CPD_and_pi_msgs(CPD, n, ps, msg, p); + i = find(p==ps); % p is n's i'th parent + q = CPD.inhibit(i); + lam_msg = zeros(2,1); + for u=0:1 + lam_msg(u+1) = l1 - (q^u)*(l1 - l0)*Pi; + end + case 'g', + error('noisyor_CPD can''t create Gaussian msgs') +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_pi.m b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_pi.m new file mode 100644 index 00000000..6955f115 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CPD_to_pi.m @@ -0,0 +1,12 @@ +function pi = CPD_to_pi(CPD, msg_type, n, ps, msg, evidence) +% CPD_TO_PI Compute pi vector (noisyor) +% pi = CPD_to_pi(CPD, msg_type, n, ps, msg) +% Pearl p188 eqn 4.57 + +switch msg_type + case 'd', + pi = sum_prod_CPD_and_pi_msgs(CPD, n, ps, msg); + pi = [pi 1-pi]'; + case 'g', + error('can''t convert noisy-or CPD to Gaussian pi') +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Entries new file mode 100644 index 00000000..4cfae75b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Entries @@ -0,0 +1,5 @@ +/CPD_to_CPT.m/1.1.1.1/Mon Aug 2 22:23:32 2004// +/CPD_to_lambda_msg.m/1.1.1.1/Wed May 29 15:59:54 2002// +/CPD_to_pi.m/1.1.1.1/Wed May 29 15:59:54 2002// +/noisyor_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Entries.Log new file mode 100644 index 00000000..b2cd71e0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Entries.Log @@ -0,0 +1 @@ +A D/private//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Repository new file mode 100644 index 00000000..a3f1a38a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@noisyor_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/noisyor_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/noisyor_CPD.m new file mode 100644 index 00000000..aecf9b6d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/noisyor_CPD.m @@ -0,0 +1,79 @@ +function CPD = noisyor_CPD(bnet, self, leak_inhibit, inhibit) +% NOISYOR_CPD Make a noisy-or CPD +% CPD = NOISYOR_CPD(BNET, NODE_NUM, LEAK_INHIBIT, INHIBIT) +% +% A noisy-or 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 inhibit(i). +% The prob that the leak node (a dummy parent that is always on) gets inhibit is leak_inhibit. +% These params default to random values if omitted. +% +% Example: suppose C has parents A and B, and the +% link of A->C fails with prob pA and the link B->C fails with pB. +% Then the noisy-OR gate defines the following distribution +% +% A B P(C=0) +% 0 0 1.0 +% 1 0 pA +% 0 1 pB +% 1 1 pA * PB +% +% Currently, learning is not supported for noisy-or nodes +% (since the M step is somewhat complicated). +% +% 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. + + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'noisyor_CPD', discrete_CPD(1, [])); + return; +elseif isa(bnet, 'noisyor_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + + +ps = parents(bnet.dag, self); +fam = [ps self]; +ns = bnet.node_sizes; +assert(all(ns(fam)==2)); +assert(isempty(myintersect(fam, bnet.cnodes))); + +if nargin < 3, leak_inhibit = rand(1, 1); end +if nargin < 4, inhibit = rand(1, length(ps)); end + +CPD.self = self; +CPD.inhibit = inhibit; +CPD.leak_inhibit = leak_inhibit; + + +% For BIC +CPD.nparams = 0; +CPD.nsamples = 0; + +CPD.CPT = []; % cached copy, to speed up CPD_to_CPT + +clamped = 1; +CPD = class(CPD, 'noisyor_CPD', discrete_CPD(clamped, ns([ps self]))); + + + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.self = []; +CPD.inhibit = []; +CPD.leak_inhibit = []; +CPD.nparams = []; +CPD.nsamples = []; +CPD.CPT = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/CVS/Entries new file mode 100644 index 00000000..b56c53ed --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/CVS/Entries @@ -0,0 +1,2 @@ +/sum_prod_CPD_and_pi_msgs.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/CVS/Repository new file mode 100644 index 00000000..716dfddd --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@noisyor_CPD/private diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/sum_prod_CPD_and_pi_msgs.m b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/sum_prod_CPD_and_pi_msgs.m new file mode 100644 index 00000000..9ca31d3c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@noisyor_CPD/private/sum_prod_CPD_and_pi_msgs.m @@ -0,0 +1,25 @@ +function pi = sum_prod_CPD_and_pi_msgs(CPD, n, ps, msg, except) +% SUM_PROD_CPD_AND_PI_MSGS Compute pi = sum_{u\p} P(n|u) prod_{ui in ps\p} pi_msg(ui->n) +% pi = sum_prod_CPD_and_pi_msgs(CPD, n, ps, msg, p) +% +% pi = prod_i (qi pi_msg(ui->n) + 1 - pi_msg(ui->n)) = prod_i (1 - ci pi_msg(ui->n)) +% is the product of the endorsement withheld (Pearl p188 eqn 4.56) +% We skip p from this product, if specified. + +if nargin < 5, except = -1; end +pi = 1; +for i=1:length(ps) + p = ps(i); + if p ~= except + pi_from_parent = msg{n}.pi_from_parent{i}; + q = CPD.inhibit(i); + c = 1-q; + pi = pi * (1 - c*pi_from_parent(2)); + end +end +% The pi msg that a leak node sends to its child is [0 1] +% since its own pi is [0 1] and its lambda to self is [0 1]. +q = CPD.leak_inhibit; +% 1 - c*pi_from_parent = 1-c*1 = q +pi = pi * q; + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CPD_to_pi.m b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CPD_to_pi.m new file mode 100644 index 00000000..65f4eb5e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CPD_to_pi.m @@ -0,0 +1,12 @@ +function pi = CPD_to_pi(CPD, msg_type, n, ps, msg, evidence) +% CPD_TO_PI Compute the pi vector (root) +% function pi = CPD_to_pi(CPD, msg_type, n, ps, msg, evidence) + +self_ev = evidence{n}; +switch msg_type + case 'd', + error('root_CPD can''t create discrete msgs') + case 'g', + pi.mu = self_ev; + pi.Sigma = zeros(size(self_ev)); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Entries new file mode 100644 index 00000000..215e86ce --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Entries @@ -0,0 +1,7 @@ +/CPD_to_pi.m/1.1.1.1/Wed May 29 15:59:54 2002// +/convert_to_pot.m/1.1.1.1/Wed May 29 15:59:54 2002// +/log_marg_prob_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +/log_prob_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +/root_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +/sample_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Entries.Log new file mode 100644 index 00000000..24f16336 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Entries.Log @@ -0,0 +1 @@ +A D/Old//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Repository new file mode 100644 index 00000000..0f9893ad --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@root_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CPD_to_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CPD_to_CPT.m new file mode 100644 index 00000000..ffc23a75 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CPD_to_CPT.m @@ -0,0 +1,5 @@ +function CPT = CPD_to_CPT(CPD) +% CPD_TO_CPT Convert the CPD to tabular form (root) +% CPT = CPD_to_CPT(CPD) + +CPT = 1; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CVS/Entries new file mode 100644 index 00000000..7c0869aa --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CVS/Entries @@ -0,0 +1,2 @@ +/CPD_to_CPT.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CVS/Repository new file mode 100644 index 00000000..ac53f91a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@root_CPD/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/convert_to_pot.m b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/convert_to_pot.m new file mode 100644 index 00000000..6a25f1aa --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/convert_to_pot.m @@ -0,0 +1,28 @@ +function pot = convert_to_pot(CPD, pot_type, domain, evidence) +% CONVERT_TO_POT Convert a root CPD to one or more potentials +% pots = convert_to_pot(CPD, pot_type, domain, evidence) + +assert(length(domain)==1); +assert(~isempty(evidence(domain))); +T = 1; + +sz = CPD.sizes; +ns = zeros(1, max(domain)); +ns(domain) = sz; + +switch pot_type + case 'u', + pot = upot(domain, 1, T, 0); + case 'd', + ns(domain) = 1; + pot = dpot(domain, ns(domain), T); + case {'c','g'}, + ns(domain) = 0; + pot = cpot(domain, ns(domain), 0); + case 'cg', + ddom = []; + cdom = domain; % we assume the root node is cts + %pot = cgpot(ddom, cdom, ns, {cpot([],[],0)}); + pot = cgpot(ddom, cdom, ns); +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/log_marg_prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/log_marg_prob_node.m new file mode 100644 index 00000000..f45d3f4c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/log_marg_prob_node.m @@ -0,0 +1,9 @@ +function L = log_marg_prob_node(CPD, self_ev, pev) +% LOG_MARG_PROB_NODE Compute prod_m log int_{theta_i} P(x(i,m)| x(pi_i,m), theta_i) for node i (root) +% L = log_marg_prob_node(CPD, self_ev, pev) +% +% self_ev{m} is the evidence on this node in case m +% pev{i,m} is the evidence on the i'th parent in case m (ignored) +% We always return L = 0. + +L = 0; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/log_prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/log_prob_node.m new file mode 100644 index 00000000..8d549631 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/log_prob_node.m @@ -0,0 +1,9 @@ +function L = log_prob_node(CPD, self_ev, pev) +% LOG_PROB_NODE Compute prod_m log P(x(i,m)| x(pi_i,m), theta_i) for node i (root) +% L = log_prob_node(CPD, self_ev, pev) +% +% self_ev{m} is the evidence on this node in case m +% pev{i,m} is the evidence on the i'th parent in case m (ignored) +% We always return L = 0. + +L = 0; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/root_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/root_CPD.m new file mode 100644 index 00000000..b07df1e5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/root_CPD.m @@ -0,0 +1,48 @@ +function CPD = root_CPD(bnet, self, val) +% ROOT_CPD Make a conditional prob. distrib. which has no parameters. +% CPD = ROOT_CPD(BNET, NODE_NUM, VAL) +% +% The node must not have any parents and is assumed to always be observed. +% It is a way of modelling exogenous inputs to a model. +% VAL is the value to which the root is clamped (default: []) + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'root_CPD', generic_CPD(1)); + return; +elseif isa(bnet, 'root_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + + +if nargin < 3, val = []; end + +ns = bnet.node_sizes; +ps = parents(bnet.dag, self); +if ~isempty(ps) + error('root CPDs should have no parents') +end + +CPD.self = self; +CPD.val = val; +CPD.sizes = ns(self); + +clamped = 1; +CPD = class(CPD, 'root_CPD', generic_CPD(clamped)); + + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.self = []; +CPD.val = []; +CPD.sizes = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/sample_node.m b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/sample_node.m new file mode 100644 index 00000000..5ced75f4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@root_CPD/sample_node.m @@ -0,0 +1,9 @@ +function y = sample_node(CPD, pev) +% SAMPLE_NODE Draw a random sample from P(Y|pa(y), theta) (root) +% Y = SAMPLE_NODE(CPD, PEV) +% +% pev{i} is the evidence on the i'th parent. +% Since a root has no parents, we ignore pev, +% and return the value the root was clamped to when it was created. + +y = CPD.val; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Entries new file mode 100644 index 00000000..1e0984dc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Entries @@ -0,0 +1,11 @@ +/convert_to_pot.m/1.1.1.1/Wed May 29 15:59:54 2002// +/convert_to_table.m/1.1.1.1/Tue Mar 30 17:19:22 2004// +/display.m/1.1.1.1/Wed May 29 15:59:54 2002// +/get_field.m/1.1.1.1/Wed May 29 15:59:54 2002// +/maximize_params.m/1.1.1.1/Wed May 29 15:59:54 2002// +/reset_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +/sample_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +/set_fields.m/1.1.1.1/Wed May 29 15:59:54 2002// +/softmax_CPD.m/1.1.1.1/Tue Jan 7 16:25:14 2003// +/update_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Entries.Log new file mode 100644 index 00000000..b2cd71e0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Entries.Log @@ -0,0 +1 @@ +A D/private//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Repository new file mode 100644 index 00000000..d5dac28b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@softmax_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/convert_to_pot.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/convert_to_pot.m new file mode 100644 index 00000000..518f4a50 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/convert_to_pot.m @@ -0,0 +1,58 @@ +function pot = convert_to_pot(CPD, pot_type, domain, evidence) +% CONVERT_TO_POT Convert a softmax CPD to a potential +% pots = convert_to_pot(CPD, pot_type, domain, evidence) +% +% pots = CPD evaluated using evidence(domain) + +ncases = size(domain,2); +assert(ncases==1); % not yet vectorized + +sz = dom_sizes(CPD); +ns = zeros(1, max(domain)); +ns(domain) = sz; + +odom = domain(~isemptycell(evidence(domain))); +T = convert_to_table(CPD, domain, evidence); + +switch pot_type + case 'u', + pot = upot(domain, sz, T, 0*myones(sz)); + case 'd', + ns(odom) = 1; + pot = dpot(domain, ns(domain), T); + + case {'c','g'}, + % Since we want the output to be a Gaussian, the whole family must be observed. + % In other words, the potential is really just a constant. + p = T; + %p = prob_node(CPD, evidence(domain(end)), evidence(domain(1:end-1))); + ns(domain) = 0; + pot = cpot(domain, ns(domain), log(p)); + + case 'cg', + T = T(:); + ns(odom) = 1; + can = cell(1, length(T)); + for i=1:length(T) + can{i} = cpot([], [], log(T(i))); + end + ps = domain(1:end-1); + dps = ps(CPD.dpndx); + cps = ps(CPD.cpndx); + ddom = [dps CPD.self]; + cdom = cps; + pot = cgpot(ddom, cdom, ns, can); + + case 'scg' + T = T(:); + ns(odom) = 1; + pot_array = cell(1, length(T)); + for i=1:length(T) + pot_array{i} = scgcpot([], [], T(i)); + end + pot = scgpot(domain, [], [], ns, pot_array); + + otherwise, + error(['unrecognized pot type ' pot_type]) +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/convert_to_table.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/convert_to_table.m new file mode 100644 index 00000000..f703d79b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/convert_to_table.m @@ -0,0 +1,52 @@ +function T = convert_to_table(CPD, domain, evidence) +% CONVERT_TO_TABLE Convert a softmax CPD to a table, incorporating any evidence +% T = convert_to_table(CPD, domain, evidence) + +self = domain(end); +ps = domain(1:end-1); +cnodes = domain(CPD.cpndx); +cps = myintersect(ps, cnodes); +dps = domain(CPD.dpndx); +dps_as_cps = domain(CPD.dps_as_cps.ndx); +all_dps = union(dps,dps_as_cps); +odom = domain(~isemptycell(evidence(domain))); +if ~isempty(cps), assert(myismember(cps, odom)); end % all cts parents must be observed + +ns = zeros(1, max(domain)); +ns(domain) = CPD.sizes; +ens = ns; % effective node sizes +ens(odom) = 1; + +% dpsize >= glimsz because the glm parameters are tied across the dps_as_cps parents +dpsize = prod(ens(all_dps)); % size of ALL self'discrete parents +dpvals = cat(1, evidence{myintersect(all_dps, odom)}); +cpvals = cat(1, evidence{cps}); +if ~isempty(dps_as_cps), + separator = CPD.dps_as_cps.separator; + dp_as_cpmap = find_equiv_posns(dps_as_cps, all_dps); + dops_map = find_equiv_posns(myintersect(all_dps, odom), all_dps); + puredp_map = find_equiv_posns(dps, all_dps); + subs = ind2subv(ens(all_dps), 1:prod(ens(all_dps))); + if ~isempty(dops_map), subs(:,dops_map) = subs(:,dops_map)+repmat(dpvals(:)',[size(subs,1) 1])-1; end +end + +[w,b] = extract_params(CPD); +T = zeros(dpsize, ns(self)); +for i=1:dpsize, + active_glm = i; + dp_as_cpvals=zeros(1,sum(ns(dps_as_cps))); + if ~isempty(dps_as_cps), + active_glm = max([1,subv2ind(ns(dps), subs(i,puredp_map))]); + % Extract the params compatible with the observations (if any) on the 'pure' discrete parents (if any) + where_one = separator + subs(i,dp_as_cpmap); + % and get in the dp_as_cp parents... + dp_as_cpvals(where_one)=1; + end + T(i,:) = normalise(exp([dp_as_cpvals(:); cpvals(:)]'*w(:,:,active_glm) + b(:,active_glm)')); +end +if myismember(self, odom) + r = evidence{self}; + T = T(:,r); +end + +T = myreshape(T, ens(domain)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/display.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/display.m new file mode 100644 index 00000000..06a0f02c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/display.m @@ -0,0 +1,4 @@ +function display(CPD) + +disp('softmax_CPD object'); +disp(struct(CPD)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/get_field.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/get_field.m new file mode 100644 index 00000000..240f1fd7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/get_field.m @@ -0,0 +1,18 @@ +function val = get_params(CPD, name) +% GET_PARAMS Get the parameters (fields) for a softmax_CPD object +% val = get_params(CPD, name) +% +% The following fields can be accessed +% +% weights - W(X,Y,Q) +% offset - b(Y,Q) +% +% e.g., W = get_params(CPD, 'weights') + +[W, b] = extract_params(CPD); +switch name + case 'weights', val = W; + case 'offset', val = b; + otherwise, + error(['invalid argument name ' name]); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/maximize_params.m new file mode 100644 index 00000000..15c94dd5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/maximize_params.m @@ -0,0 +1,41 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a CPD to their ML values (dsoftmax) using IRLS +% CPD = maximize_params(CPD, temperature) +% temperature parameter is ignored + +% Written by Pierpaolo Brutti + +if ~adjustable_CPD(CPD), return; end +options = foptions; + +if CPD.verbose + options(1) = 1; +else + options(1) = -1; +end +%options(1) = CPD.verbose; + +options(2) = CPD.wthresh; +options(3) = CPD.llthresh; +options(5) = CPD.approx_hess; +options(14) = CPD.max_iter; + +dpsize = size(CPD.self_vals,3); +for i=1:dpsize, + mask=find(CPD.eso_weights(:,:,i)>0); % for adapting the parameters we use only positive weighted example + if ~isempty(mask), + if ~isempty(CPD.dps_as_cps.ndx), + puredp_map = find_equiv_posns(CPD.dpndx, union(CPD.dpndx, CPD.dps_as_cps.ndx)); % find the glm structure + subs = ind2subv(CPD.sizes(union(CPD.dpndx, CPD.dps_as_cps.ndx)),i); % that corrisponds to the + active_glm = max([1,subv2ind(CPD.sizes(CPD.dpndx), subs(puredp_map))]); % i-th 'fictitious' example + + CPD.glim{active_glm} = netopt_weighted(CPD.glim{active_glm}, options, CPD.parent_vals(mask',:,i),... + CPD.self_vals(mask',:,i), CPD.eso_weights(mask',:,i), 'scg'); + else + alfa = 0.4; if CPD.solo, alfa = 1; end % learning step = 1 <=> self is all alone in the net + CPD.glim{i} = glmtrain_weighted(CPD.glim{i}, options, CPD.parent_vals(mask',:),... + CPD.self_vals(mask',:,i), CPD.eso_weights(mask',:,i), alfa); + end + end + mask=[]; +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Entries new file mode 100644 index 00000000..b6610f0d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Entries @@ -0,0 +1,2 @@ +/extract_params.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Repository new file mode 100644 index 00000000..1667449e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@softmax_CPD/private diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/extract_params.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/extract_params.m new file mode 100644 index 00000000..486af06e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/extract_params.m @@ -0,0 +1,18 @@ +function [W, b] = extract_params(CPD) + +% W(X,Y,Q), b(Y,Q) where Y = ns(self), X = ns(cps), Q = prod(ns(dps)) + +glimsz = prod(CPD.sizes(CPD.dpndx)); +ss = CPD.sizes(end); +cpsz = sum(CPD.sizes(CPD.cpndx)); +dp_as_cpsz = sum(CPD.sizes(CPD.dps_as_cps.ndx)); +W = zeros(dp_as_cpsz + cpsz, ss, glimsz); +b = zeros(ss, glimsz); + +for i=1:glimsz + W(:,:,i) = CPD.glim{i}.w1; + b(:,i) = CPD.glim{i}.b1(:); +end + +W = myreshape(W, [dp_as_cpsz + cpsz ss CPD.sizes(CPD.dpndx)]); +b = myreshape(b, [ss CPD.sizes(CPD.dpndx)]); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/reset_ess.m new file mode 100644 index 00000000..abf7d54e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/reset_ess.m @@ -0,0 +1,8 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics for a CPD (dsoftmax) +% CPD = reset_ess(CPD) + +CPD.parent_vals = []; +CPD.eso_weights=[]; +CPD.self_vals = []; +CPD.nsamples = 0; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/sample_node.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/sample_node.m new file mode 100644 index 00000000..1c519049 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/sample_node.m @@ -0,0 +1,14 @@ +function y = sample_node(CPD, pvals) +% SAMPLE_NODE Draw a random sample from P(Xi | x(pi_i), theta_i) (discrete) +% y = sample_node(CPD, parent_evidence) +% +% parent_evidence{i} is the value of the i'th parent + +n = length(pvals)+1; +dom = 1:n; +%evidence = cell(1,n); +%evidence(1:n-1) = pvals(:)'; +evidence = pvals; +evidence{end+1} = []; +T = convert_to_table(CPD, dom, evidence); +y = sample_discrete(T); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/set_fields.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/set_fields.m new file mode 100644 index 00000000..6c64b197 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/set_fields.m @@ -0,0 +1,45 @@ +function CPD = set_params(CPD, varargin) +% SET_PARAMS Set the parameters (fields) for a softmax_CPD object +% CPD = set_params(CPD, name/value pairs) +% +% The following optional arguments can be specified in the form of name/value pairs: +% (Let ns(i) be the size of node i, X = ns(X), Y = ns(Y), Q1=ns(dps(1)), Q2=ns(dps(2)), ... +% where dps are the discrete parents; if there are no discrete parents, we set Q1=1.) +% +% weights - (W(:,j,a,b,...) - W(:,j',a,b,...)) is ppn to dec. boundary +% between j,j' given Q1=a,Q2=b,... [ randn(X,Y,Q1,Q2,...) ] +% offset - (offset(j,a,b,...) - offset(j',a,b,...)) is the offset to dec. boundary +% between j,j' given Q1=a,Q2=b,... [ randn(Y,Q1,Q2,...) ] +% clamped - 'yes' means don't adjust params during learning ['no'] +% max_iter - the maximum number of steps to take [10] +% verbose - 'yes' means print the LL at each step of IRLS ['no'] +% wthresh - convergence threshold for weights [1e-2] +% llthresh - convergence threshold for log likelihood [1e-2] +% approx_hess - 'yes' means approximate the Hessian for speed ['no'] +% +% e.g., CPD = set_params(CPD,'offset', zeros(ns(i),1)); + +args = varargin; +nargs = length(args); +glimsz = prod(CPD.sizes(CPD.dpndx)); +for i=1:2:nargs + switch args{i}, + case 'discrete', str='nothing to do'; + case 'clamped', CPD = set_clamped(CPD, strcmp(args{i+1}, 'yes')); + case 'max_iter', CPD.max_iter = args{i+1}; + case 'verbose', CPD.verbose = strcmp(args{i+1}, 'yes'); + case 'max_iter', CPD.max_iter = args{i+1}; + case 'wthresh', CPD.wthresh = args{i+1}; + case 'llthresh', CPD.llthresh = args{i+1}; + case 'approx_hess', CPD.approx_hess = strcmp(args{i+1}, 'yes'); + case 'weights', for q=1:glimsz, CPD.glim{q}.w1 = args{i+1}(:,:,q); end; + case 'offset', + if glimsz == 1 + CPD.glim{1}.b1 = args{i+1}; + else + for q=1:glimsz, CPD.glim{q}.b1 = args{i+1}(:,q); end; + end + otherwise, + error(['invalid argument name ' args{i}]); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/softmax_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/softmax_CPD.m new file mode 100644 index 00000000..3d2e5153 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/softmax_CPD.m @@ -0,0 +1,187 @@ +function CPD = softmax_CPD(bnet, self, varargin) +% SOFTMAX_CPD Make a softmax (multinomial logit) CPD +% +% To define this CPD precisely, let W be an (m x n) matrix with W(i,:) = {i-th row of B} +% => we can define the following vectorial function: +% +% softmax: R^n |--> R^m +% softmax(z,i-th)=exp(W(i,:)*z)/sum_k(exp(W(k,:)*z)) +% +% (this constructor augments z with a one at the beginning to introduce an offset term (=bias, intercept)) +% Now call the continuous (cts) and always observed (obs) parents X, +% the discrete parents (if any) Q, and this node Y then we use the discrete parent(s) just to index +% the parameter vectors (c.f., conditional Gaussian nodes); that is: +% prob(Y=i | X=x, Q=j) = softmax(x,i-th|j) +% where '|j' means that we are using the j-th (m x n) parameters matrix W(:,:,j). +% If there are no discrete parents, this is a regular softmax node. +% If Y is binary, this is a logistic (sigmoid) function. +% +% CPD = softmax_CPD(bnet, node_num, ...) will create a softmax CPD with random parameters, +% where node is the number of a node in this equivalence class. +% +% The following optional arguments can be specified in the form of name/value pairs: +% [default value in brackets] +% (Let ns(i) be the size of node i, X = ns(X), Y = ns(Y), Q1=ns(dps(1)), Q2=ns(dps(2)), ... +% where dps are the discrete parents; if there are no discrete parents, we set Q1=1.) +% +% discrete - the discrete parents that we want to treat like the cts ones [ [] ]. +% This can be used to define sigmoid belief network - see below the reference. +% For example suppose that Y has one cts parents X and two discrete ones: Q, C1 where: +% -> Q is binary (1/2) and used just to index the parameters of 'self' +% -> C1 is ternary (1/2/3) and treated as a cts node <=> its values appear into the linear +% part of the softmax function +% then: +% prob(Y|X=x, Q=q, C1=c1)= softmax(W(:,:,q)' * y) +% where y = [1 | delta(C1,1) delta(C1,2) delta(C1,3) | x(:)']' and delta(Y,a)=indicator(Y=a). +% weights - (w(:,j,a,b,...) - w(:,j',a,b,...)) is ppn to dec. boundary +% between j,j' given Q1=a,Q2=b,... [ randn(X,Y,Q1,Q2,...) ] +% offset - (b(j,a,b,...) - b(j',a,b,...)) is the offset to dec. boundary +% between j,j' given Q1=a,Q2=b,... [ randn(Y,Q1,Q2,...) ] +% +% e.g., CPD = softmax_CPD(bnet, i, 'offset', zeros(ns(i),1)); +% +% The following fields control the behavior of the M step, which uses +% a weighted version of the Iteratively Reweighted Least Squares (WIRLS) if dps_as_cps=[]; or +% a weighted SCG otherwise, as implemented in Netlab, and modified by Pierpaolo Brutti. +% +% clamped - 'yes' means don't adjust params during learning ['no'] +% max_iter - the maximum number of steps to take [10] +% verbose - 'yes' means print the LL at each step of IRLS ['no'] +% wthresh - convergence threshold for weights [1e-2] +% llthresh - convergence threshold for log likelihood [1e-2] +% approx_hess - 'yes' means approximate the Hessian for speed ['no'] +% +% For backwards compatibility with BNT2, you can also specify the parameters in the following order +% softmax_CPD(bnet, self, w, b, clamped, max_iter, verbose, wthresh, llthresh, approx_hess) +% +% REFERENCE +% For details on the sigmoid belief nets, see: +% - Neal (1992). Connectionist learning of belief networks, Artificial Intelligence, 56, 71-113. +% - Saul, Jakkola, Jordan (1996). Mean field theory for sigmoid belief networks, Journal of Artificial Intelligence Reseach (4), pagg. 61-76. +% +% For details on the M step, see: +% - K. Chen, L. Xu, H. Chi (1999). Improved learning algorithms for mixtures of experts in multiclass +% classification. Neural Networks 12, pp. 1229-1252. +% - M.I. Jordan, R.A. Jacobs (1994). Hierarchical Mixtures of Experts and the EM algorithm. +% Neural Computation 6, pp. 181-214. +% - S.R. Waterhouse, A.J. Robinson (1994). Classification Using Hierarchical Mixtures of Experts. In Proc. IEEE +% Workshop on Neural Network for Signal Processing IV, pp. 177-186 + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'softmax_CPD', discrete_CPD(0, [])); + return; +elseif isa(bnet, 'softmax_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + +assert(myismember(self, bnet.dnodes)); +ns = bnet.node_sizes; +ps = parents(bnet.dag, self); +dps = myintersect(ps, bnet.dnodes); +cps = myintersect(ps, bnet.cnodes); + +clamped = 0; +CPD = class(CPD, 'softmax_CPD', discrete_CPD(clamped, ns([ps self]))); + +dps_as_cpssz = 0; +dps_as_cps = []; +% determine if any discrete parents are to be treated as cts +if nargin >= 3 && isstr(varargin{1}) % might have passed in 'discrete' + for i=1:2:length(varargin) + if strcmp(varargin{i}, 'discrete') + dps_as_cps = varargin{i+1}; + assert(myismember(dps_as_cps, dps)); + dps = mysetdiff(dps, dps_as_cps); % put out the dps treated as cts + CPD.dps_as_cps.ndx = find_equiv_posns(dps_as_cps, ps); + CPD.dps_as_cps.separator = [0 cumsum(ns(dps_as_cps(1:end-1)))]; % concatenated dps_as_cps dims separators + dps_as_cpssz = sum(ns(dps_as_cps)); + break; + end + end +end +assert(~isempty(union(cps, dps_as_cps))); % It have to be at least a cts or a dps_as_cps parents +self_size = ns(self); +cpsz = sum(ns(cps)); +glimsz = prod(ns(dps)); +CPD.dpndx = find_equiv_posns(dps, ps); % it contains only the indeces of the 'pure' dps +CPD.cpndx = find_equiv_posns(cps, ps); + +CPD.self = self; +CPD.solo = (length(ns)<=2); +CPD.sizes = bnet.node_sizes([ps self]); + +% set default params +CPD.max_iter = 10; +CPD.verbose = 0; +CPD.wthresh = 1e-2; +CPD.llthresh = 1e-2; +CPD.approx_hess = 0; +CPD.glim = cell(1,glimsz); +for i=1:glimsz + CPD.glim{i} = glm(dps_as_cpssz + cpsz, self_size, 'softmax'); +end + +if nargin >= 3 + args = varargin; + nargs = length(args); + if ~isstr(args{1}) + % softmax_CPD(bnet, self, w, b, clamped, max_iter, verbose, wthresh, llthresh, approx_hess) + if nargs >= 1 && ~isempty(args{1}), CPD = set_fields(CPD, 'weights', args{1}); end + if nargs >= 2 && ~isempty(args{2}), CPD = set_fields(CPD, 'offset', args{2}); end + if nargs >= 3 && ~isempty(args{3}), CPD = set_clamped(CPD, args{3}); end + if nargs >= 4 && ~isempty(args{4}), CPD.max_iter = args{4}; end + if nargs >= 5 && ~isempty(args{5}), CPD.verbose = args{5}; end + if nargs >= 6 && ~isempty(args{6}), CPD.wthresh = args{6}; end + if nargs >= 7 && ~isempty(args{7}), CPD.llthresh = args{7}; end + if nargs >= 8 && ~isempty(args{8}), CPD.approx_hess = args{8}; end + else + CPD = set_fields(CPD, args{:}); + end +end + +% sufficient statistics +% Since dsoftmax is not in the exponential family, we must store all the raw data. +CPD.parent_vals = []; % X(l,:) = value of cts parents in l'th example +CPD.self_vals = []; % Y(l,:) = value of self in l'th example + +CPD.eso_weights=[]; % weights used by the WIRLS algorithm + +% For BIC +CPD.nsamples = 0; +if ~adjustable_CPD(CPD), + CPD.nparams=0; +else + [W, b] = extract_params(CPD); + CPD.nparams= prod(size(W)) + prod(size(b)); +end + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.glim = {}; +CPD.self = []; +CPD.solo = []; +CPD.max_iter = []; +CPD.verbose = []; +CPD.wthresh = []; +CPD.llthresh = []; +CPD.approx_hess = []; +CPD.sizes = []; +CPD.parent_vals = []; +CPD.eso_weights=[]; +CPD.self_vals = []; +CPD.nsamples = []; +CPD.nparams = []; +CPD.dpndx = []; +CPD.cpndx = []; +CPD.dps_as_cps.ndx = []; +CPD.dps_as_cps.separator = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/update_ess.m new file mode 100644 index 00000000..143c567c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/update_ess.m @@ -0,0 +1,97 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a softmax node +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% +% fmarginal = overall posterior distribution of self and its parents +% fmarginal(i1,i2...,ik,s)=prob(Pa1=i1,...,Pak=ik, self=s| X) +% +% => 1) prob(self|Pa1,...,Pak)=fmarginal/prob(Pa1,...,Pak) with prob(Pa1,...,Pak)=sum{s,fmarginal} +% [self estimation -> CPD.self_vals] +% 2) prob(Pa1,...,Pak) [WIRLS weights -> CPD.eso_weights] +% +% Hidden_bitv is ignored + +% Written by Pierpaolo Brutti + +if ~adjustable_CPD(CPD), return; end + +domain = fmarginal.domain; +self = domain(end); +ps = domain(1:end-1); +cnodes = domain(CPD.cpndx); +cps = myintersect(domain, cnodes); +dps = mysetdiff(ps, cps); +dn_use = dps; +if isempty(evidence{self}) dn_use = [dn_use self]; end % if self is hidden we must consider its dimension +dps_as_cps = domain(CPD.dps_as_cps.ndx); +odom = domain(~isemptycell(evidence(domain))); + +ns = zeros(1, max(domain)); +ns(domain) = CPD.sizes; % CPD.sizes = bnet.node_sizes([ps self]); +ens = ns; % effective node sizes +ens(odom) = 1; +dpsize = prod(ns(dps)); + +% Extract the params compatible with the observations (if any) on the discrete parents (if any) +dops = myintersect(dps, odom); +dpvals = cat(1, evidence{dops}); + +subs = ind2subv(ens(dn_use), 1:prod(ens(dn_use))); +dpmap = find_equiv_posns(dops, dn_use); +if ~isempty(dpmap), subs(:,dpmap) = subs(:,dpmap)+repmat(dpvals(:)',[size(subs,1) 1])-1; end +supportedQs = subv2ind(ns(dn_use), subs); subs=subs(1:prod(ens(dps)),1:length(dps)); +Qarity = prod(ns(dn_use)); +if isempty(dn_use), Qarity = 1; end + +fullm.T = zeros(Qarity, 1); +fullm.T(supportedQs) = fmarginal.T(:); +rs_dim = CPD.sizes; rs_dim(CPD.cpndx) = 1; % +if ~isempty(evidence{self}), rs_dim(end)=1; end % reshaping the marginal +fullm.T = reshape(fullm.T, rs_dim); % + +% --------------------------------------------------------------------------------UPDATE-- + +CPD.nsamples = CPD.nsamples + 1; + +% 1) observations vector -> CPD.parents_vals --------------------------------------------- +cpvals = cat(1, evidence{cps}); + +if ~isempty(dps_as_cps), % ...get in the dp_as_cp parents... + separator = CPD.dps_as_cps.separator; + dp_as_cpmap = find_equiv_posns(dps_as_cps, dps); + for i=1:dpsize, + dp_as_cpvals=zeros(1,sum(ns(dps_as_cps))); + possible_vals = ind2subv(ns(dps),i); + ll=find(ismember(subs(:,dp_as_cpmap), possible_vals(dp_as_cpmap), 'rows')==1); + if ~isempty(ll), + where_one = separator + possible_vals(dp_as_cpmap); + dp_as_cpvals(where_one)=1; + end + CPD.parent_vals(CPD.nsamples,:,i) = [dp_as_cpvals(:); cpvals(:)]'; + end +else + CPD.parent_vals(CPD.nsamples,:) = cpvals(:)'; +end + +% 2) weights vector -> CPD.eso_weights ---------------------------------------------------- +if isempty(evidence{self}), % self is hidden + pesi=reshape(sum(fullm.T, length(rs_dim)),[dpsize,1]); +else + pesi=reshape(fullm.T,[dpsize,1]); +end +assert(approxeq(sum(pesi),1)); % check + +% 3) estimate (if R is hidden) or recover (if R is obs) self'value------------------------- +if isempty(evidence{self}) % P(self|Pa1,...,Pak)=fmarginal/prob(Pa1,...,Pak) + r=reshape(mk_stochastic(fullm.T), [dpsize ns(self)]); % matrix size: prod{j,ns(Paj)} x ns(self) +else + r = zeros(dpsize,ns(self)); + for i=1:dpsize, if pesi(i)~=0, r(i,evidence{self}) = 1; end; end +end +for i=1:dpsize, if pesi(i)~=0, assert(approxeq(sum(r(i,:)),1)); end; end % check + +% 4) save the previous values -------------------------------------------------------------- +for i=1:dpsize + CPD.eso_weights(CPD.nsamples,:,i)=pesi(i); + CPD.self_vals(CPD.nsamples,:,i) = r(i,:); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CPD_to_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CPD_to_CPT.m new file mode 100644 index 00000000..351f103c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CPD_to_CPT.m @@ -0,0 +1,5 @@ +function CPT = CPD_to_CPT(CPD) +% CPD_TO_CPT Convert the discrete CPD to tabular form (tabular) +% CPT = CPD_to_CPT(CPD) + +CPT = CPD.CPT; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Entries new file mode 100644 index 00000000..84ff987c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Entries @@ -0,0 +1,15 @@ +/CPD_to_CPT.m/1.1.1.1/Wed May 29 15:59:54 2002// +/bayes_update_params.m/1.1.1.1/Wed May 29 15:59:54 2002// +/display.m/1.1.1.1/Tue Apr 22 21:00:02 2003// +/get_field.m/1.1.1.1/Sun Jan 16 02:27:30 2005// +/learn_params.m/1.1.1.1/Thu Jun 10 01:25:02 2004// +/log_marg_prob_node.m/1.1.1.1/Fri Jun 11 21:16:00 2004// +/log_nextcase_prob_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +/log_prior.m/1.1.1.1/Wed May 29 15:59:54 2002// +/maximize_params.m/1.1.1.1/Sun Mar 9 22:44:40 2003// +/reset_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +/set_fields.m/1.1.1.1/Sun Jan 16 02:27:30 2005// +/tabular_CPD.m/1.1.1.1/Sun Jan 16 02:27:32 2005// +/update_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +/update_ess_simple.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Entries.Log new file mode 100644 index 00000000..24f16336 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Entries.Log @@ -0,0 +1 @@ +A D/Old//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Repository new file mode 100644 index 00000000..c64a17a7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@tabular_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/BIC_score_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/BIC_score_CPD.m new file mode 100644 index 00000000..ab4ef6cf --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/BIC_score_CPD.m @@ -0,0 +1,17 @@ +function score = BIC_score_CPD(CPD, fam, data, ns, cnodes) +% BIC_score_CPD Compute the BIC score of a tabular CPD +% score = BIC_score_CPD(CPD, fam, data, ns, cnodes) + +if iscell(data) + local_data = cell2num(data(fam,:)); +else + local_data = data(fam, :); +end +counts = compute_counts(local_data, CPD.sizes); +CPT = mk_stochastic(counts); % MLE +tiny = exp(-700); +CPT = CPT + (CPT==0)*tiny; % replace 0s by tiny +LL = sum(log(CPT(:)) .* counts(:)); +N = size(data, 2); +score = LL - 0.5*CPD.nparams*log(N); + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/CVS/Entries new file mode 100644 index 00000000..cbddfaa9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/CVS/Entries @@ -0,0 +1,11 @@ +/BIC_score_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +/bayesian_score_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +/log_marg_prob_node_case.m/1.1.1.1/Wed May 29 15:59:54 2002// +/mult_CPD_and_pi_msgs.m/1.1.1.1/Wed May 29 15:59:54 2002// +/prob_CPT.m/1.1.1.1/Wed May 29 15:59:54 2002// +/prob_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +/sample_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +/sample_node_single_case.m/1.1.1.1/Wed May 29 15:59:54 2002// +/tabular_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +/update_params.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/CVS/Repository new file mode 100644 index 00000000..b43e738b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@tabular_CPD/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/bayesian_score_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/bayesian_score_CPD.m new file mode 100644 index 00000000..083a00d7 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/bayesian_score_CPD.m @@ -0,0 +1,13 @@ +function score = bayesian_score_CPD(CPD, local_ev) +% bayesian_score_CPD Compute the Bayesian score of a tabular CPD using uniform Dirichlet prior +% score = bayesian_score_CPD(CPD, local_ev) +% +% The Bayesian score is the log marginal likelihood + +if iscell(local_ev) + data = num2cell(local_ev); +else + data = local_ev; +end + +score = dirichlet_score_family(compute_counts(data, CPD.sizes)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/log_marg_prob_node_case.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/log_marg_prob_node_case.m new file mode 100644 index 00000000..2a177fe6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/log_marg_prob_node_case.m @@ -0,0 +1,22 @@ +function L = log_marg_prob_node_case(CPD, y, x) +% LOG_MARG_PROB_NODE_CASE Compute prod_m log P(x(i,m)| x(pi_i,m)) for node i (tabular) +% L = log_marg_prob_node_case(CPD, self_ev, parent_ev) +% +% This is a slightly optimised version of log_marg_prob_node. +% We assume we have exactly 1 case, i.e., y is a scalar and x is a vector (not a cell array). + +sz = CPD.sizes; +nparents = length(sz)-1; + +% We assume the CPTs are already set to the mean of the posterior (due to update_params) + +switch nparents + case 0, p = CPD.CPT(y); + case 1, p = CPD.CPT(x(1), y); + case 2, p = CPD.CPT(x(1), x(2), y); + case 3, p = CPD.CPT(x(1), x(2), x(3), y); + otherwise, + ind = subv2ind(sz, [x y]); + p = CPD.CPT(ind); +end +L = log(p); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/mult_CPD_and_pi_msgs.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/mult_CPD_and_pi_msgs.m new file mode 100644 index 00000000..b67ed2e6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/mult_CPD_and_pi_msgs.m @@ -0,0 +1,17 @@ +function T = mult_CPD_and_pi_msgs(CPD, n, ps, msgs, except) +% MULT_CPD_AND_PI_MSGS Multiply the CPD and all the pi messages from parents, perhaps excepting one +% T = mult_CPD_and_pi_msgs(CPD, n, ps, msgs, except) + +if nargin < 5, except = -1; end + +dom = [ps n]; +%ns = sparse(1, max(dom)); +ns = zeros(1, max(dom)); +ns(dom) = mysize(CPD.CPT); +T = dpot(dom, ns(dom), CPD.CPT); +for i=1:length(ps) + p = ps(i); + if p ~= except + T = multiply_by_pot(T, dpot(p, ns(p), msgs{n}.pi_from_parent{i}.T)); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/prob_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/prob_CPT.m new file mode 100644 index 00000000..6685de30 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/prob_CPT.m @@ -0,0 +1,16 @@ +function p = prob_CPT(CPD, x) +% PROB_CPT Lookup the prob. of a family value in a tabular CPD +% p = prob_CPT(CPD, x) +% +% This is a version of prob_CPD optimized for tables. + +switch length(x) + case 1, p = CPD.CPT(x); + case 2, p = CPD.CPT(x(1), x(2)); + case 3, p = CPD.CPT(x(1), x(2), x(3)); + case 4, p = CPD.CPT(x(1), x(2), x(3), x(4)); + otherwise, + ind = subv2ind(mysize(CPD.CPT), x); + p = CPD.CPT(ind); +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/prob_node.m new file mode 100644 index 00000000..2764e6c1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/prob_node.m @@ -0,0 +1,40 @@ +function p = prob_node(CPD, self_ev, pev) +% PROB_NODE Compute P(y|pa(y), theta) (tabular) +% p = prob_node(CPD, self_ev, pev) +% +% self_ev{m} is the evidence on this node in case m +% pev{i,m} is the evidence on the i'th parent in case m +% If there is a single case, self_ev can be a scalar instead of a cell array + +ncases = size(pev, 2); + +%assert(~any(isemptycell(pev))); % slow +%assert(~any(isemptycell(self_ev))); % slow + +CPT = CPD_to_CPT(CPD); +sz = mysize(CPT); +nparents = length(sz)-1; +assert(nparents == size(pev, 1)); + +if ncases==1 + x = cat(1, pev{:}); + if iscell(y) + y = self_ev{1}; + else + y = self_ev; + end + switch nparents + case 0, p = CPT(y); + case 1, p = CPT(x(1), y); + case 2, p = CPT(x(1), x(2), y); + case 3, p = CPT(x(1), x(2), x(3), y); + otherwise, + ind = subv2ind(CPD.sizes, [x y]); + p = CPT(ind); + end +else + x = num2cell(pev)'; % each row is a case + y = cat(1, self_ev{:})'; + ind = subv2ind(CPD.sizes, [x y]); + p = CPT(ind); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/sample_node.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/sample_node.m new file mode 100644 index 00000000..3fd92d79 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/sample_node.m @@ -0,0 +1,53 @@ +function y = sample_node(CPD, pev, nsamples) +% SAMPLE_NODE Draw a random sample from P(Xi | x(pi_i), theta_i) (tabular) +% Y = SAMPLE_NODE(CPD, PEV, NSAMPLES) +% +% pev(i,m) is the value of the i'th parent in sample m (if there are any parents). +% y(m) is the m'th sampled value (a row vector). +% (If pev is a cell array, so is y.) +% nsamples defaults to 1. + +if nargin < 3, nsamples = 1; end + +%if nargin < 4, usecell = 0; end +if iscell(pev), usecell = 1; else usecell = 0; end + +if nsamples == 1, pev = pev(:); end + +sz = CPD.sizes; +nparents = length(sz)-1; +if nparents==0 + y = sample_discrete(CPD.CPT, 1, nsamples); + if usecell + y = num2cell(y); + end + return; +end + +sz = CPD.sizes; +[nparents nsamples] = size(pev); + +if usecell + pvals = cell2num(pev)'; % each row is a case +else + pvals = pev'; +end + +psz = sz(1:end-1); +ssz = sz(end); +ndx = subv2ind(psz, pvals); +T = reshape(CPD.CPT, [prod(psz) ssz]); +T2 = T(ndx,:); % each row is a distribution selected by the parents +C = cumsum(T2, 2); % sum across columns +R = rand(nsamples, 1); +y = ones(nsamples, 1); +for i=1:ssz-1 + y = y + (R > C(:,i)); +end +y = y(:)'; +if usecell + y = num2cell(y); +end + + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/sample_node_single_case.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/sample_node_single_case.m new file mode 100644 index 00000000..3e1dcf34 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/sample_node_single_case.m @@ -0,0 +1,39 @@ +function y = sample_node(CPD, pev) +% SAMPLE_NODE Draw a random sample from P(Xi | x(pi_i), theta_i) (tabular) +% y = sample_node(CPD, pev) +% +% pev{i} is the value of the i'th parent (if any) + +%assert(~any(isemptycell(pev))); + +%CPT = CPD_to_CPT(CPD); +%sz = mysize(CPT); +sz = CPD.sizes; +nparents = length(sz)-1; +if nparents > 0 + pvals = cat(1, pev{:}); +end +switch nparents + case 0, T = CPD.CPT; + case 1, T = CPD.CPT(pvals(1), :); + case 2, T = CPD.CPT(pvals(1), pvals(2), :); + case 3, T = CPD.CPT(pvals(1), pvals(2), pvals(3), :); + case 4, T = CPD.CPT(pvals(1), pvals(2), pvals(3), pvals(4), :); + otherwise, + psz = sz(1:end-1); + ssz = sz(end); + i = subv2ind(psz, pvals(:)'); + T = reshape(CPD.CPT, [prod(psz) ssz]); + T = T(i,:); +end + +if sz(end)==2 + r = rand(1,1); + if r > T(1) + y = 2; + else + y = 1; + end +else + y = sample_discrete(T); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/tabular_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/tabular_CPD.m new file mode 100644 index 00000000..2227e051 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/tabular_CPD.m @@ -0,0 +1,186 @@ +function CPD = tabular_CPD(bnet, self, varargin) +% TABULAR_CPD Make a multinomial conditional prob. distrib. (CPT) +% +% CPD = tabular_CPD(bnet, node) creates a random CPT. +% +% The following arguments can be specified [default in brackets] +% +% CPT - specifies the params ['rnd'] +% - T means use table T; it will be reshaped to the size of node's family. +% - 'rnd' creates rnd params (drawn from uniform) +% - 'unif' creates a uniform distribution +% - 'leftright' only transitions from i to i/i+1 are allowed, for each non-self parent context. +% The non-self parents are all parents except oldself. +% selfprob - The prob of transition from i to i if CPT = 'leftright' [0.1] +% old_self - id of the node corresponding to self in the previous slice [self-ss] +% adjustable - 0 means don't adjust the parameters during learning [1] +% prior_type - defines type of prior ['none'] +% - 'none' means do ML estimation +% - 'dirichlet' means add pseudo-counts to every cell +% - 'entropic' means use a prior P(theta) propto exp(-H(theta)) (see Brand) +% dirichlet_weight - equivalent sample size (ess) of the dirichlet prior [1] +% dirichlet_type - defines the type of Dirichlet prior ['BDeu'] +% - 'unif' means put dirichlet_weight in every cell +% - 'BDeu' means we put 'dirichlet_weight/(r q)' in every cell +% where r = self_sz and q = prod(parent_sz) (see Heckerman) +% trim - 1 means trim redundant params (rows in CPT) when using entropic prior [0] +% +% e.g., tabular_CPD(bnet, i, 'CPT', T) +% e.g., tabular_CPD(bnet, i, 'CPT', 'unif', 'dirichlet_weight', 2, 'dirichlet_type', 'unif') +% +% REFERENCES +% M. Brand - "Structure learning in conditional probability models via an entropic prior +% and parameter extinction", Neural Computation 11 (1999): 1155--1182 +% M. Brand - "Pattern discovery via entropy minimization" [covers annealing] +% AI & Statistics 1999. Equation numbers refer to this paper, which is available from +% www.merl.com/reports/docs/TR98-21.pdf +% D. Heckerman, D. Geiger and M. Chickering, +% "Learning Bayesian networks: the combination of knowledge and statistical data", +% Microsoft Research Tech Report, 1994 + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'tabular_CPD', discrete_CPD(0, [])); + return; +elseif isa(bnet, 'tabular_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + +ns = bnet.node_sizes; +ps = parents(bnet.dag, self); +fam_sz = ns([ps self]); +CPD.sizes = fam_sz; +CPD.leftright = 0; + +% set defaults +CPD.CPT = mk_stochastic(myrand(fam_sz)); +CPD.adjustable = 1; +CPD.prior_type = 'none'; +dirichlet_type = 'BDeu'; +dirichlet_weight = 1; +CPD.trim = 0; +selfprob = 0.1; + +% extract optional args +args = varargin; +% check for old syntax CPD(bnet, i, CPT) as opposed to CPD(bnet, i, 'CPT', CPT) +if ~isempty(args) && ~ischar(args{1}) + CPD.CPT = myreshape(args{1}, fam_sz); + args = []; +end + +% if old_self is specified, read in the value before CPT is created +old_self = []; +for i=1:2:length(args) + switch args{i}, + case 'old_self', old_self = args{i+1}; + end +end + +for i=1:2:length(args) + switch args{i}, + case 'CPT', + T = args{i+1}; + if ischar(T) + switch T + case 'unif', CPD.CPT = mk_stochastic(myones(fam_sz)); + case 'rnd', CPD.CPT = mk_stochastic(myrand(fam_sz)); + case 'leftright', + % we just initialise the CPT to leftright - this structure will + % be maintained by EM, assuming we don't use a prior... + CPD.leftright = 1; + if isempty(old_self) % we assume the network is a DBN + ss = bnet.nnodes_per_slice; + old_self = self-ss; + end + other_ps = mysetdiff(ps, old_self); + Qps = prod(ns(other_ps)); + Q = ns(self); + p = selfprob; + LR = mk_leftright_transmat(Q, p); + transprob = repmat(reshape(LR, [1 Q Q]), [Qps 1 1]); % transprob(k,i,j) + transprob = permute(transprob, [2 1 3]); % now transprob(i,k,j) + CPD.CPT = myreshape(transprob, fam_sz); + otherwise, error(['invalid CPT ' T]); + end + else + CPD.CPT = myreshape(T, fam_sz); + end + + case 'prior_type', CPD.prior_type = args{i+1}; + case 'dirichlet_type', dirichlet_type = args{i+1}; + case 'dirichlet_weight', dirichlet_weight = args{i+1}; + case 'adjustable', CPD.adjustable = args{i+1}; + case 'clamped', CPD.adjustable = ~args{i+1}; + case 'trim', CPD.trim = args{i+1}; + case 'old_self', noop = 1; % already read in + otherwise, error(['invalid argument name: ' args{i}]); + end +end + +switch CPD.prior_type + case 'dirichlet', + switch dirichlet_type + case 'unif', CPD.dirichlet = dirichlet_weight * myones(fam_sz); + case 'BDeu', CPD.dirichlet = dirichlet_weight * mk_stochastic(myones(fam_sz)); + otherwise, error(['invalid dirichlet_type ' dirichlet_type]) + end + case {'entropic', 'none'} + CPD.dirichlet = []; + otherwise, error(['invalid prior_type ' prior_type]) +end + + + +% fields to do with learning +if ~CPD.adjustable + CPD.counts = []; + CPD.nparams = 0; + CPD.nsamples = []; +else + CPD.counts = zeros(size(CPD.CPT)); + psz = fam_sz(1:end-1); + ss = fam_sz(end); + if CPD.leftright + % For each of the Qps contexts, we specify Q elements on the diagoanl + CPD.nparams = Qps * Q; + else + % sum-to-1 constraint reduces the effective arity of the node by 1 + CPD.nparams = prod([psz ss-1]); + end + CPD.nsamples = 0; +end + +fam_sz = CPD.sizes; +psz = prod(fam_sz(1:end-1)); +ssz = fam_sz(end); +CPD.trimmed_trans = zeros(psz, ssz); % must declare before reading + +CPD = class(CPD, 'tabular_CPD', discrete_CPD(~CPD.adjustable, fam_sz)); + + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.CPT = []; +CPD.sizes = []; +CPD.prior_type = []; +CPD.dirichlet = []; +CPD.adjustable = []; +CPD.counts = []; +CPD.nparams = []; +CPD.nsamples = []; +CPD.trim = []; +CPD.trimmed_trans = []; +CPD.leftright = []; + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/update_params.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/update_params.m new file mode 100644 index 00000000..5a1e93a8 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/Old/update_params.m @@ -0,0 +1,15 @@ +function CPD = update_params(CPD, ev, counts) +% UPDATE_PARAMS Update the Dirichlet pseudo counts and compute the new MAP param estimates (tabular) +% +% CPD = update_params(CPD, ev) uses the evidence on the family from a single case. +% +% CPD = update_params(CPD, [], counts) does a batch update using the specified suff. stats. + +if nargin < 3 + n = length(ev); + data = cat(1, ev{:}); % convert to a vector of scalars + counts = compute_counts(data(:)', 1:n, mysize(CPD.CPT)); +end + +CPD.prior = CPD.prior + counts; +CPD.CPT = mk_stochastic(CPD.prior); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/bayes_update_params.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/bayes_update_params.m new file mode 100644 index 00000000..0de0f8b8 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/bayes_update_params.m @@ -0,0 +1,55 @@ +function CPD = bayes_update_params(CPD, self_ev, pev) +% UPDATE_PARAMS_COMPLETE Bayesian parameter updating given completely observed data (tabular) +% CPD = update_params_complete(CPD, self_ev, pev) +% +% self_ev(m) is the evidence on this node in case m. +% pev(i,m) is the evidence on the i'th parent in case m (if there are any parents). +% These can be arrays or cell arrays. +% +% We update the Dirichlet pseudo counts and set the CPT to the mean of the posterior. + +if iscell(self_ev), usecell = 1; else usecell = 0; end + +ncases = length(self_ev); +sz = CPD.sizes; +nparents = length(sz)-1; +assert(nparents == size(pev,1)); + +if ncases == 0 | ~adjustable_CPD(CPD) + return; +elseif ncases == 1 % speedup the sequential learning case by avoiding normalization of the whole array + if usecell + x = cat(1, pev{:})'; + y = self_ev{1}; + else + x = pev(:)'; + y = self_ev; + end + switch nparents + case 0, + CPD.dirichlet(y) = CPD.dirichlet(y)+1; + CPD.CPT = CPD.dirichlet / sum(CPD.dirichlet); + case 1, + CPD.dirichlet(x(1), y) = CPD.dirichlet(x(1), y)+1; + CPD.CPT(x(1), :) = CPD.dirichlet(x(1), :) ./ sum(CPD.dirichlet(x(1), :)); + case 2, + CPD.dirichlet(x(1), x(2), y) = CPD.dirichlet(x(1), x(2), y)+1; + CPD.CPT(x(1), x(2), :) = CPD.dirichlet(x(1), x(2), :) ./ sum(CPD.dirichlet(x(1), x(2), :)); + case 3, + CPD.dirichlet(x(1), x(2), x(3), y) = CPD.dirichlet(x(1), x(2), x(3), y)+1; + CPD.CPT(x(1), x(2), x(3), :) = CPD.dirichlet(x(1), x(2), x(3), :) ./ sum(CPD.dirichlet(x(1), x(2), x(3), :)); + otherwise, + ind = subv2ind(sz, [x y]); + CPD.dirichlet(ind) = CPD.dirichlet(ind) + 1; + CPD.CPT = mk_stochastic(CPD.dirichlet); + end +else + if usecell + data = [cell2num(pev); cell2num(self_ev)]; + else + data = [pev; self_ev]; + end + counts = compute_counts(data, sz); + CPD.dirichlet = CPD.dirichlet + counts; + CPD.CPT = mk_stochastic(CPD.dirichlet); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/display.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/display.m new file mode 100644 index 00000000..6c9be2c3 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/display.m @@ -0,0 +1,5 @@ +function display(CPD) + +disp('tabular_CPD object'); +disp(struct(CPD)); + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/get_field.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/get_field.m new file mode 100644 index 00000000..ba233db9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/get_field.m @@ -0,0 +1,16 @@ +function val = get_field(CPD, name) +% GET_PARAMS Get the parameters (fields) for a tabular_CPD object +% val = get_params(CPD, name) +% +% The following fields can be accessed +% +% cpt, counts +% +% e.g., CPT = get_params(CPD, 'cpt') + +switch name + case 'cpt', val = CPD.CPT; + case 'counts', val = CPD.counts; + otherwise, + error(['invalid argument name ' name]); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/learn_params.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/learn_params.m new file mode 100644 index 00000000..970da8b1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/learn_params.m @@ -0,0 +1,17 @@ +function CPD = learn_params(CPD, fam, data, ns, cnodes) +%function CPD = learn_params(CPD, local_data) +% LEARN_PARAMS Compute the ML/MAP estimate of the params of a tabular CPD given complete data +% CPD = learn_params(CPD, local_data) +% +% local_data(i,m) is the value of i'th family member in case m (can be cell array). + +local_data = data(fam, :); +if iscell(local_data) + local_data = cell2num(local_data); +end +counts = compute_counts(local_data, CPD.sizes); +switch CPD.prior_type + case 'none', CPD.CPT = mk_stochastic(counts); + case 'dirichlet', CPD.CPT = mk_stochastic(counts + CPD.dirichlet); + otherwise, error(['unrecognized prior ' CPD.prior_type]) +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/log_marg_prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/log_marg_prob_node.m new file mode 100644 index 00000000..8a819488 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/log_marg_prob_node.m @@ -0,0 +1,69 @@ +function L = log_marg_prob_node(CPD, self_ev, pev, usecell) +% LOG_MARG_PROB_NODE Compute sum_m log P(x(i,m)| x(pi_i,m)) for node i (tabular) +% L = log_marg_prob_node(CPD, self_ev, pev) +% +% This differs from log_prob_node because we integrate out the parameters. +% self_ev(m) is the evidence on this node in case m. +% pev(i,m) is the evidence on the i'th parent in case m (if there are any parents). +% (These may also be cell arrays.) + +ncases = length(self_ev); +sz = CPD.sizes; +nparents = length(sz)-1; +assert(ncases == size(pev, 2)); + +if nargin < 4 + %usecell = 0; + if iscell(self_ev) + usecell = 1; + else + usecell = 0; + end +end + + +if ncases==0 + L = 0; + return; +elseif ncases==1 % speedup the sequential learning case + CPT = CPD.CPT; + % We assume the CPTs are already set to the mean of the posterior (due to bayes_update_params) + if usecell + x = cat(1, pev{:})'; + y = self_ev{1}; + else + %x = pev(:)'; + x = pev; + y = self_ev; + end + switch nparents + case 0, p = CPT(y); + case 1, p = CPT(x(1), y); + case 2, p = CPT(x(1), x(2), y); + case 3, p = CPT(x(1), x(2), x(3), y); + otherwise, + ind = subv2ind(sz, [x y]); + p = CPT(ind); + end + L = log(p); +else + % We ignore the CPTs here and assume the prior has not been changed + + % We arrange the data as in the following example. + % Let there be 2 parents and 3 cases. Let p(i,m) be parent i in case m, + % and y(m) be the child in case m. Then we create the data matrix + % + % p(1,1) p(1,2) p(1,3) + % p(2,1) p(2,2) p(2,3) + % y(1) y(2) y(3) + if usecell + data = [cell2num(pev); cell2num(self_ev)]; + else + data = [pev; self_ev]; + end + %S = struct(CPD); fprintf('log marg prob node %d, ps\n', S.self); disp(S.parents) + counts = compute_counts(data, sz); + L = dirichlet_score_family(counts, CPD.dirichlet); +end + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/log_nextcase_prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/log_nextcase_prob_node.m new file mode 100644 index 00000000..c946de69 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/log_nextcase_prob_node.m @@ -0,0 +1,72 @@ +function L = log_nextcase_prob_node(CPD, self_ev, pev, test_self_ev, test_pev) +% LOG_NEXTCASE_PROB_NODE compute the joint distribution of a node (tabular) of a new case given +% completely observed data. +% +% The input arguments are mainly similar with log_marg_prob_node(CPD, self_ev, pev, usecell), +% but add test_self_ev, test_pev, and without usecell +% test_self_ev(m) is the evidence on this node in a test case. +% test_pev(i) is the evidence on the i'th parent in the test case (if there are any parents). +% +% Written by qian.diao@intel.com + +ncases = length(self_ev); +sz = CPD.sizes; +nparents = length(sz)-1; +assert(ncases == size(pev, 2)); + +if nargin < 6 + %usecell = 0; + if iscell(self_ev) + usecell = 1; + else + usecell = 0; + end +end + + +if ncases==0 + L = 0; + return; +elseif ncases==1 % speedup the sequential learning case; here need correction!!! + CPT = CPD.CPT; + % We assume the CPTs are already set to the mean of the posterior (due to bayes_update_params) + if usecell + x = cat(1, pev{:})'; + y = self_ev{1}; + else + %x = pev(:)'; + x = pev; + y = self_ev; + end + switch nparents + case 0, p = CPT(y); + case 1, p = CPT(x(1), y); + case 2, p = CPT(x(1), x(2), y); + case 3, p = CPT(x(1), x(2), x(3), y); + otherwise, + ind = subv2ind(sz, [x y]); + p = CPT(ind); + end + L = log(p); +else + % We ignore the CPTs here and assume the prior has not been changed + + % We arrange the data as in the following example. + % Let there be 2 parents and 3 cases. Let p(i,m) be parent i in case m, + % and y(m) be the child in case m. Then we create the data matrix + % + % p(1,1) p(1,2) p(1,3) + % p(2,1) p(2,2) p(2,3) + % y(1) y(2) y(3) + if usecell + data = [cell2num(pev); cell2num(self_ev)]; + else + data = [pev; self_ev]; + end + counts = compute_counts(data, sz); + + % compute the (N_ijk'+ N_ijk)/(N_ij' + N_ij) under the condition of 1_m+1,ijk = 1 + L = predict_family(counts, CPD.prior, test_self_ev, test_pev); +end + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/log_prior.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/log_prior.m new file mode 100644 index 00000000..1ac2dbd4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/log_prior.m @@ -0,0 +1,18 @@ +function L = log_prior(CPD) +% LOG_PRIOR Return log P(theta) for a tabular CPD +% L = log_prior(CPD) + +switch CPD.prior_type + case 'none', + L = 0; + case 'dirichlet', + D = CPD.dirichlet(:); + L = sum(log(D + (D==0))); + case 'entropic', + % log-prior = log exp(-H(theta)) = sum_i theta_i log (theta_i) + fam_sz = CPD.sizes; + psz = prod(fam_sz(1:end-1)); + ssz = fam_sz(end); + C = reshape(CPD.CPT, psz, ssz); + L = sum(sum(C .* log(C + (C==0)))); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/maximize_params.m new file mode 100644 index 00000000..c4317a78 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/maximize_params.m @@ -0,0 +1,52 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Set the params of a tabular node to their ML/MAP values. +% CPD = maximize_params(CPD, temp) + +if ~adjustable_CPD(CPD), return; end + +%assert(approxeq(sum(CPD.counts(:)), CPD.nsamples)); % false! +switch CPD.prior_type + case 'none', + counts = reshape(CPD.counts, size(CPD.CPT)); + CPD.CPT = mk_stochastic(counts); + case 'dirichlet', + counts = reshape(CPD.counts, size(CPD.CPT)); + CPD.CPT = mk_stochastic(counts + CPD.dirichlet); + + % case 'entropic', +% % For an HMM, +% % CPT(i,j) = pr(X(t)=j | X(t-1)=i) = transprob(i,j) +% % counts(i,j) = E #(X(t-1)=i, X(t)=j) = exp_num_trans(i,j) +% Z = 1-temp; +% fam_sz = CPD.sizes; +% psz = prod(fam_sz(1:end-1)); +% ssz = fam_sz(end); +% counts = reshape(CPD.counts, psz, ssz); +% CPT = zeros(psz, ssz); +% for i=CPD.entropic_pcases(:)' +% [CPT(i,:), logpost] = entropic_map_estimate(counts(i,:), Z); +% end +% non_entropic_pcases = mysetdiff(1:psz, CPD.entropic_pcases); +% for i=non_entropic_pcases(:)' +% CPT(i,:) = mk_stochastic(counts(i,:)); +% end +% %for i=1:psz +% % [CPT(i,:), logpost] = entropic_map(counts(i,:), Z); +% %end +% if CPD.trim & (temp < 2) % at high temps, we would trim everything! +% % grad(j) = d log lik / d theta(i ->j) +% % CPT(i,j) = 0 => counts(i,j) = 0 +% % so we can safely replace 0s by 1s in the denominator +% denom = CPT(i,:) + (CPT(i,:)==0); +% grad = counts(i,:) ./ denom; +% trim = find(CPT(i,:) <= exp(-(1/Z)*grad)); % eqn 32 +% if ~isempty(trim) +% CPT(i,trim) = 0; +% if all(CPD.trimmed_trans(i,trim)==0) % trimming for 1st time +% disp(['trimming CPT(' num2str(i) ',' num2str(trim) ')']) +% end +% CPD.trimmed_trans(i,trim) = 1; +% end +% end +% CPD.CPT = myreshape(CPT, CPD.sizes); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/reset_ess.m new file mode 100644 index 00000000..0ce90e3a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/reset_ess.m @@ -0,0 +1,7 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics of a tabular node. +% CPD = reset_ess(CPD) + +%CPD.counts = zeros(size(CPD.CPT)); +CPD.counts = zeros(prod(size(CPD.CPT)), 1); +CPD.nsamples = 0; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/set_fields.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/set_fields.m new file mode 100644 index 00000000..19c99ac0 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/set_fields.m @@ -0,0 +1,52 @@ +function CPD = set_fields(CPD, varargin) +% SET_PARAMS Set the parameters (fields) for a tabular_CPD object +% CPD = set_params(CPD, name/value pairs) +% +% The following optional arguments can be specified in the form of name/value pairs: +% +% CPT, prior, clamped, counts +% +% e.g., CPD = set_params(CPD, 'CPT', 'rnd') + +args = varargin; +nargs = length(args); +for i=1:2:nargs + switch args{i}, + case 'CPT', + if ischar(args{i+1}) + switch args{i+1} + case 'unif', CPD.CPT = mk_stochastic(myones(CPD.sizes)); + case 'rnd', CPD.CPT = mk_stochastic(myrand(CPD.sizes)); + otherwise, error(['invalid type ' args{i+1}]); + end + elseif isscalarBNT(args{i+1}) + p = args{i+1}; + k = CPD.sizes(end); + % Bug fix by Hervé Boutrouille 10/1/01 + CPD.CPT = myreshape(sample_dirichlet(p*ones(1,k), prod(CPD.sizes(1:end-1)), CPD.sizes)); + %CPD.CPT = myreshape(sample_dirichlet(p*ones(1,k), prod(CPD.sizes(1:end-1))), CPD.sizes); + else + CPD.CPT = myreshape(args{i+1}, CPD.sizes); + end + + case 'prior', + if ischar(args{i+1}) & strcmp(args{i+1}, 'unif') + CPD.prior = myones(CPD.sizes); + elseif isscalarBNT(args{i+1}) + CPD.prior = args{i+1} * normalise(myones(CPD.sizes)); + else + CPD.prior = myreshape(args{i+1}, CPD.sizes); + end + + %case 'clamped', CPD.clamped = strcmp(args{i+1}, 'yes'); + %case 'clamped', CPD = set_clamped(CPD, strcmp(args{i+1}, 'yes')); + case 'clamped', CPD = set_clamped(CPD, args{i+1}); + + case 'counts', CPD.counts = args{i+1}; + + otherwise, + %error(['invalid argument name ' args{i}]); + end +end + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/tabular_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/tabular_CPD.m new file mode 100644 index 00000000..728302d4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/tabular_CPD.m @@ -0,0 +1,173 @@ +function CPD = tabular_CPD(bnet, self, varargin) +% TABULAR_CPD Make a multinomial conditional prob. distrib. (CPT) +% +% CPD = tabular_CPD(bnet, node) creates a random CPT. +% +% The following arguments can be specified [default in brackets] +% +% CPT - specifies the params ['rnd'] +% - T means use table T; it will be reshaped to the size of node's family. +% - 'rnd' creates rnd params (drawn from uniform) +% - 'unif' creates a uniform distribution +% adjustable - 0 means don't adjust the parameters during learning [1] +% prior_type - defines type of prior ['none'] +% - 'none' means do ML estimation +% - 'dirichlet' means add pseudo-counts to every cell +% - 'entropic' means use a prior P(theta) propto exp(-H(theta)) (see Brand) +% dirichlet_weight - equivalent sample size (ess) of the dirichlet prior [1] +% dirichlet_type - defines the type of Dirichlet prior ['BDeu'] +% - 'unif' means put dirichlet_weight in every cell +% - 'BDeu' means we put 'dirichlet_weight/(r q)' in every cell +% where r = self_sz and q = prod(parent_sz) (see Heckerman) +% trim - 1 means trim redundant params (rows in CPT) when using entropic prior [0] +% entropic_pcases - list of assignments to the parents nodes when we should use +% the entropic prior; all other cases will be estimated using ML [1:psz] +% sparse - 1 means use 1D sparse array to represent CPT [0] +% +% e.g., tabular_CPD(bnet, i, 'CPT', T) +% e.g., tabular_CPD(bnet, i, 'CPT', 'unif', 'dirichlet_weight', 2, 'dirichlet_type', 'unif') +% +% REFERENCES +% M. Brand - "Structure learning in conditional probability models via an entropic prior +% and parameter extinction", Neural Computation 11 (1999): 1155--1182 +% M. Brand - "Pattern discovery via entropy minimization" [covers annealing] +% AI & Statistics 1999. Equation numbers refer to this paper, which is available from +% www.merl.com/reports/docs/TR98-21.pdf +% D. Heckerman, D. Geiger and M. Chickering, +% "Learning Bayesian networks: the combination of knowledge and statistical data", +% Microsoft Research Tech Report, 1994 + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'tabular_CPD', discrete_CPD(0, [])); + return; +elseif isa(bnet, 'tabular_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + +ns = bnet.node_sizes; +ps = parents(bnet.dag, self); +fam_sz = ns([ps self]); +psz = prod(ns(ps)); +CPD.sizes = fam_sz; +CPD.leftright = 0; +CPD.sparse = 0; + +% set defaults +CPD.CPT = mk_stochastic(myrand(fam_sz)); +CPD.adjustable = 1; +CPD.prior_type = 'none'; +dirichlet_type = 'BDeu'; +dirichlet_weight = 1; +CPD.trim = 0; +selfprob = 0.1; +CPD.entropic_pcases = 1:psz; + +% extract optional args +args = varargin; +% check for old syntax CPD(bnet, i, CPT) as opposed to CPD(bnet, i, 'CPT', CPT) +if ~isempty(args) && ~ischar(args{1}) + CPD.CPT = myreshape(args{1}, fam_sz); + args = []; +end + +for i=1:2:length(args) + switch args{i}, + case 'CPT', + T = args{i+1}; + if ischar(T) + switch T + case 'unif', CPD.CPT = mk_stochastic(myones(fam_sz)); + case 'rnd', CPD.CPT = mk_stochastic(myrand(fam_sz)); + otherwise, error(['invalid CPT ' T]); + end + else + CPD.CPT = myreshape(T, fam_sz); + end + case 'prior_type', CPD.prior_type = args{i+1}; + case 'dirichlet_type', dirichlet_type = args{i+1}; + case 'dirichlet_weight', dirichlet_weight = args{i+1}; + case 'adjustable', CPD.adjustable = args{i+1}; + case 'clamped', CPD.adjustable = ~args{i+1}; + case 'trim', CPD.trim = args{i+1}; + case 'entropic_pcases', CPD.entropic_pcases = args{i+1}; + case 'sparse', CPD.sparse = args{i+1}; + otherwise, error(['invalid argument name: ' args{i}]); + end +end + +switch CPD.prior_type + case 'dirichlet', + switch dirichlet_type + case 'unif', CPD.dirichlet = dirichlet_weight * myones(fam_sz); + case 'BDeu', CPD.dirichlet = (dirichlet_weight/psz) * mk_stochastic(myones(fam_sz)); + otherwise, error(['invalid dirichlet_type ' dirichlet_type]) + end + case {'entropic', 'none'} + CPD.dirichlet = []; + otherwise, error(['invalid prior_type ' prior_type]) +end + + + +% fields to do with learning +if ~CPD.adjustable + CPD.counts = []; + CPD.nparams = 0; + CPD.nsamples = []; +else + %CPD.counts = zeros(size(CPD.CPT)); + CPD.counts = zeros(prod(size(CPD.CPT)), 1); + psz = fam_sz(1:end-1); + ss = fam_sz(end); + if CPD.leftright + % For each of the Qps contexts, we specify Q elements on the diagoanl + CPD.nparams = Qps * Q; + else + % sum-to-1 constraint reduces the effective arity of the node by 1 + CPD.nparams = prod([psz ss-1]); + end + CPD.nsamples = 0; +end + +CPD.trimmed_trans = []; +fam_sz = CPD.sizes; + +%psz = prod(fam_sz(1:end-1)); +%ssz = fam_sz(end); +%CPD.trimmed_trans = zeros(psz, ssz); % must declare before reading + +%sparse CPT +if CPD.sparse + CPD.CPT = sparse(CPD.CPT(:)); +end + +CPD = class(CPD, 'tabular_CPD', discrete_CPD(~CPD.adjustable, fam_sz)); + + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.CPT = []; +CPD.sizes = []; +CPD.prior_type = []; +CPD.dirichlet = []; +CPD.adjustable = []; +CPD.counts = []; +CPD.nparams = []; +CPD.nsamples = []; +CPD.trim = []; +CPD.trimmed_trans = []; +CPD.leftright = []; +CPD.entropic_pcases = []; +CPD.sparse = []; + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/update_ess.m new file mode 100644 index 00000000..7602ce9d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/update_ess.m @@ -0,0 +1,15 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a tabular node. +% function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) + +dom = fmarginal.domain; + +if all(hidden_bitv(dom)) + CPD = update_ess_simple(CPD, fmarginal.T); + %fullm = add_ev_to_dmarginal(fmarginal, evidence, ns); + %assert(approxeq(fullm.T(:), fmarginal.T(:))) +else + fullm = add_ev_to_dmarginal(fmarginal, evidence, ns); + CPD = update_ess_simple(CPD, fullm.T); +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/update_ess_simple.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/update_ess_simple.m new file mode 100644 index 00000000..da3ee023 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_CPD/update_ess_simple.m @@ -0,0 +1,6 @@ +function CPD = update_ess_simple(CPD, counts) +% UPDATE_ESS_SIMPLE Update the Expected Sufficient Statistics of a tabular node. +% function CPD = update_ess_simple(CPD, counts) + +CPD.nsamples = CPD.nsamples + 1; +CPD.counts = CPD.counts + counts(:); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CPD_to_CPT.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CPD_to_CPT.m new file mode 100644 index 00000000..f3509acf --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CPD_to_CPT.m @@ -0,0 +1,5 @@ +function CPT = CPD_to_CPT(CPD) +% CPD_TO_CPT Convert the tabular_decision_node to a CPT +% CPT = CPD_to_CPT(CPD) + +CPT = CPD.CPT; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Entries new file mode 100644 index 00000000..70a7b527 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Entries @@ -0,0 +1,6 @@ +/CPD_to_CPT.m/1.1.1.1/Wed May 29 15:59:54 2002// +/display.m/1.1.1.1/Wed May 29 15:59:54 2002// +/get_field.m/1.1.1.1/Wed May 29 15:59:54 2002// +/set_fields.m/1.1.1.1/Wed May 29 15:59:54 2002// +/tabular_decision_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Entries.Log new file mode 100644 index 00000000..24f16336 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Entries.Log @@ -0,0 +1 @@ +A D/Old//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Repository new file mode 100644 index 00000000..df9f8a23 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@tabular_decision_node diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/CVS/Entries new file mode 100644 index 00000000..f11d0269 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/CVS/Entries @@ -0,0 +1,2 @@ +/tabular_decision_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/CVS/Repository new file mode 100644 index 00000000..c14a3f7d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@tabular_decision_node/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/tabular_decision_node.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/tabular_decision_node.m new file mode 100644 index 00000000..7c4c26d5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/Old/tabular_decision_node.m @@ -0,0 +1,39 @@ +function CPD = tabular_decision_node(sz, CPT) +% TABULAR_DECISION_NODE Represent the randomized policy over a discrete decision/action node as a table +% CPD = tabular_decision_node(sz, CPT) +% +% sz(1:end-1) is the sizes of the parents, sz(end) is the size of this node +% By default, CPT is set to the uniform random policy + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'tabular_decision_node'); + return; +elseif isa(sz, 'tabular_decision_node') + % This might occur if we are copying an object. + CPD = sz; + return; +end +CPD = init_fields; + +if nargin < 2 + CPT = mk_stochastic(myones(sz)); +else + CPT = myreshape(CPT, sz); +end + +CPD.CPT = CPT; +CPD.size = sz; + +CPD = class(CPD, 'tabular_decision_node'); + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.CPT = []; +CPD.size = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/display.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/display.m new file mode 100644 index 00000000..a029e5d6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/display.m @@ -0,0 +1,4 @@ +function display(CPD) + +disp('tabular decision node object'); +disp(struct(CPD)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/get_field.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/get_field.m new file mode 100644 index 00000000..24c2cedc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/get_field.m @@ -0,0 +1,19 @@ +function vals = get_field(CPD, name) +% GET_PARAMS Get the parameters (fields) for a tabular_decision_node object +% vals = get_params(CPD, name) +% +% The following fields can be accessed +% +% policy - the table containing the policy +% +% e.g., policy = get_params(CPD, 'policy') + +args = varargin; +nargs = length(args); +for i=1:2:nargs + switch args{i}, + case 'policy', vals = CPD.CPT; + otherwise, + error(['invalid argument name ' args{i}]); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/set_fields.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/set_fields.m new file mode 100644 index 00000000..4fe62292 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/set_fields.m @@ -0,0 +1,19 @@ +function CPD = set_params(CPD, varargin) +% SET_PARAMS Set the parameters (fields) for a tabular_decision_node object +% CPD = set_params(CPD, name/value pairs) +% +% The following optional arguments can be specified in the form of name/value pairs: +% +% policy - the table containing the policy +% +% e.g., CPD = set_params(CPD, 'policy', T) + +args = varargin; +nargs = length(args); +for i=1:2:nargs + switch args{i}, + case 'policy', CPD.CPT = args{i+1}; + otherwise, + error(['invalid argument name ' args{i}]); + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/tabular_decision_node.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/tabular_decision_node.m new file mode 100644 index 00000000..75ca5780 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_decision_node/tabular_decision_node.m @@ -0,0 +1,45 @@ +function CPD = tabular_decision_node(bnet, self, CPT) +% TABULAR_DECISION_NODE Represent a stochastic policy over a discrete decision/action node as a table +% CPD = tabular_decision_node(bnet, self, CPT) +% +% node is the number of a node in this equivalence class. +% CPT is an optional argument (see tabular_CPD for details); by default, it is the uniform policy. + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'tabular_decision_node', discrete_CPD(1, [])); + return; +elseif isa(bnet, 'tabular_decision_node') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + +ns = bnet.node_sizes; +fam = family(bnet.dag, self); +ps = parents(bnet.dag, self); +sz = ns(fam); + +if nargin < 3 + CPT = mk_stochastic(myones(sz)); +else + CPT = myreshape(CPT, sz); +end + +CPD.CPT = CPT; +CPD.sizes = sz; + +clamped = 1; % don't update using EM +CPD = class(CPD, 'tabular_decision_node', discrete_CPD(clamped, ns([ps self]))); + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.CPT = []; +CPD.sizes = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Entries new file mode 100644 index 00000000..45d8ee0d --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Entries @@ -0,0 +1,6 @@ +/convert_to_pot.m/1.1.1.1/Wed May 29 15:59:54 2002// +/convert_to_table.m/1.1.1.1/Wed May 29 15:59:54 2002// +/get_field.m/1.1.1.1/Wed May 29 15:59:54 2002// +/set_fields.m/1.1.1.1/Wed May 29 15:59:54 2002// +/tabular_kernel.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Entries.Log new file mode 100644 index 00000000..24f16336 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Entries.Log @@ -0,0 +1 @@ +A D/Old//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Repository new file mode 100644 index 00000000..61f9dcd8 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@tabular_kernel diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/CVS/Entries new file mode 100644 index 00000000..b6c6e11c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/CVS/Entries @@ -0,0 +1,2 @@ +/tabular_kernel.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/CVS/Repository new file mode 100644 index 00000000..d2036843 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@tabular_kernel/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/tabular_kernel.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/tabular_kernel.m new file mode 100644 index 00000000..99f74450 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/Old/tabular_kernel.m @@ -0,0 +1,45 @@ +function K = tabular_kernel(fg, self) +% TABULAR_KERNEL Make a table-based local kernel (discrete potential) +% K = tabular_kernel(fg, self) +% +% fg is a factor graph +% self is the number of a representative domain +% +% Use 'set_params_kernel' to adjust the following fields +% table - a q[1]xq[2]x... array, where q[i] is the number of values for i'th node +% in this domain [default: random values from [0,1], which need not sum to 1] + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + K = init_fields; + K = class(K, 'tabular_kernel'); + return; +elseif isa(fg, 'tabular_kernel') + % This might occur if we are copying an object. + K = fg; + return; +end +K = init_fields; + +ns = fg.node_sizes; +dom = fg.doms{self}; +% we don't store the actual domain since it may vary due to parameter tieing +K.sz = ns(dom); +K.table = myrand(K.sz); + +K = class(K, 'tabular_kernel'); + + +%%%%%%% + + +function K = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +K.table = []; +K.sz = []; + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/convert_to_pot.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/convert_to_pot.m new file mode 100644 index 00000000..8f9adff1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/convert_to_pot.m @@ -0,0 +1,37 @@ +function pot = convert_to_pot(CPD, pot_type, domain, evidence) +% CONVERT_TO_POT Convert a tabular CPD to one or more potentials +% pot = convert_to_pot(CPD, pot_type, domain, evidence) + +% This is the same as discrete_CPD/convert_to_pot, +% except we didn't want to the kernel to inherit methods like sample_node etc. + +sz = CPD.sz; +ns = zeros(1, max(domain)); +ns(domain) = sz; + +odom = domain(~isemptycell(evidence(domain))); +T = convert_to_table(CPD, domain, evidence); + +switch pot_type + case 'u', + pot = upot(domain, sz, T, 0*myones(sz)); + case 'd', + ns(odom) = 1; + pot = dpot(domain, ns(domain), T); + case 'c', + % Since we want the output to be a Gaussian, the whole family must be observed. + % In other words, the potential is really just a constant. + p = T.p; + %p = prob_node(CPD, evidence(domain(end)), evidence(domain(1:end-1))); + ns(domain) = 0; + pot = cpot(domain, ns(domain), log(p)); + case 'cg', + T = T(:); + ns(odom) = 1; + can = cell(1, length(T)); + for i=1:length(T) + can{i} = cpot([], [], log(T(i))); + end + pot = cgpot(domain, [], ns, can); +end + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/convert_to_table.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/convert_to_table.m new file mode 100644 index 00000000..30703f3a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/convert_to_table.m @@ -0,0 +1,13 @@ +function T = convert_to_table(CPD, domain, evidence) +% CONVERT_TO_TABLE Convert a discrete CPD to a table +% T = convert_to_table(CPD, domain, evidence) +% +% We convert the CPD to a CPT, and then lookup the evidence on the discrete parents. +% The resulting table can easily be converted to a potential. + +CPT = CPD.table; +odom = domain(~isemptycell(evidence(domain))); +vals = cat(1, evidence{odom}); +map = find_equiv_posns(odom, domain); +index = mk_multi_index(length(domain), map, vals); +T = CPT(index{:}); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/get_field.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/get_field.m new file mode 100644 index 00000000..3319eadb --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/get_field.m @@ -0,0 +1,11 @@ +function val = get_params_kernel(K, name) +% GET_PARAMS_KERNEL Accessor function for a field (tabular_kernel) +% val = get_params_kernel(K, name) +% +% e.g., get_params_kernel(K, 'table') + +switch name + case 'table', val = K.table; + otherwise, + error(['invalid field name ' name]); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/set_fields.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/set_fields.m new file mode 100644 index 00000000..2f7ac435 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/set_fields.m @@ -0,0 +1,13 @@ +function K = set_params_kernel(K, name, val) +% SET_PARAMS_KERNEL Accessor function for a field (table_kernel) +% K = set_params_kernel(K, name, val) +% +% e.g., K = set_params_kernel(K, 'table', rand(2,3,2)) for a kernel on 3 nodes with 2,3,2 values each + +% We should check if the arguments are valid... + +switch name + case 'table', K.table = val; + otherwise, + error(['invalid field name ' name]); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/tabular_kernel.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/tabular_kernel.m new file mode 100644 index 00000000..74a64450 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_kernel/tabular_kernel.m @@ -0,0 +1,40 @@ +function K = tabular_kernel(sz, table) +% TABULAR_KERNEL Make a table-based local kernel (discrete potential) +% K = tabular_kernel(sz, table) +% +% sz(i) is the number of values the i'th member of this kernel can have +% table is an optional array of size sz[1] x sz[2] x... [default: random] + +if nargin==0 + % This occurs if we are trying to load an object from a file. + K = init_fields; + K = class(K, 'tabular_kernel'); + return; +elseif isa(sz, 'tabular_kernel') + % This might occur if we are copying an object. + K = sz; + return; +end +K = init_fields; + +if nargin < 2, table = myrand(sz); end + +K.sz = sz; +K.table = table; + +K = class(K, 'tabular_kernel'); + + +%%%%%%% + + +function K = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +K.sz = []; +K.table = []; + + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/CVS/Entries new file mode 100644 index 00000000..c4a82aa3 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/CVS/Entries @@ -0,0 +1,4 @@ +/convert_to_pot.m/1.1.1.1/Wed May 29 15:59:54 2002// +/display.m/1.1.1.1/Wed May 29 15:59:54 2002// +/tabular_utility_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/CVS/Repository new file mode 100644 index 00000000..93b1ac57 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@tabular_utility_node diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/convert_to_pot.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/convert_to_pot.m new file mode 100644 index 00000000..05eb287f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/convert_to_pot.m @@ -0,0 +1,11 @@ +function pot = convert_to_pot(CPD, pot_type, domain, evidence) +% CONVERT_TO_POT Convert a tabular utility node to one or more potentials +% pot = convert_to_pot(CPD, pot_type, domain, evidence) + +switch pot_type + case 'u', + sz = [CPD.sizes 1]; % the utility node itself has size 1 + pot = upot(domain, sz, 1*myones(sz), myreshape(CPD.T, sz)); + otherwise, + error(['can''t convert a utility node to a ' pot_type ' potential']); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/display.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/display.m new file mode 100644 index 00000000..45b5c01a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/display.m @@ -0,0 +1,4 @@ +function display(CPD) + +disp('tabular utility node object'); +disp(struct(CPD)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/tabular_utility_node.m b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/tabular_utility_node.m new file mode 100644 index 00000000..36dcad66 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tabular_utility_node/tabular_utility_node.m @@ -0,0 +1,46 @@ +function CPD = tabular_utility_node(bnet, node, T) +% TABULAR_UTILITY_NODE Represent a utility function as a table +% CPD = tabular_utility_node(bnet, node, T) +% +% node is the number of a node in this equivalence class. +% T is an optional argument (same shape as the CPT in tabular_CPD, but missing the last (child) +% dimension). By default, entries in T are chosen u.a.r. from 0:1 (using 'rand'). + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + clamp = 0; + CPD = class(CPD, 'tabular_utility_node'); + return; +elseif isa(bnet, 'tabular_utility_node') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + + +ns = bnet.node_sizes; +ps = parents(bnet.dag, node); +sz = ns(ps); + +if nargin < 3 + T = myrand(sz); +else + T = myreshape(T, sz); +end + +CPD.T = T; +CPD.sizes = sz; + +CPD = class(CPD, 'tabular_utility_node'); + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.T = []; +CPD.sizes = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/CVS/Entries new file mode 100644 index 00000000..62632403 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/CVS/Entries @@ -0,0 +1,8 @@ +/display.m/1.1.1.1/Wed May 29 15:59:54 2002// +/evaluate_tree_performance.m/1.1.1.1/Wed May 29 15:59:54 2002// +/get_field.m/1.1.1.1/Wed May 29 15:59:54 2002// +/learn_params.m/1.1.1.1/Wed May 29 15:59:54 2002// +/readme.txt/1.1.1.1/Wed May 29 15:59:54 2002// +/set_fields.m/1.1.1.1/Wed May 29 15:59:54 2002// +/tree_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/CVS/Repository new file mode 100644 index 00000000..5ec8512b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@tree_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/display.m b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/display.m new file mode 100644 index 00000000..4e405bec --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/display.m @@ -0,0 +1,4 @@ +function display(CPD) + +disp('dtree_CPD object'); +disp(struct(CPD)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/evaluate_tree_performance.m b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/evaluate_tree_performance.m new file mode 100644 index 00000000..2f72a5b9 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/evaluate_tree_performance.m @@ -0,0 +1,82 @@ +function [score,outputs] = evaluate(CPD, fam, data, ns, cnodes) +% Evaluate evaluate the performance of the classification/regression tree on given complete data +% score = evaluate(CPD, fam, data, ns, cnodes) +% +% fam(i) is the node id of the i-th node in the family of nodes, self node is the last one +% data(i,m) is the value of node i in case m (can be cell array). +% ns(i) is the node size for the i-th node in the whold bnet +% cnodes(i) is the node id for the i-th continuous node in the whole bnet +% +% Output +% score is the classification accuracy (for classification) +% or mean square deviation (for regression) +% here for every case we use the mean value at the tree leaf node as its predicted value +% outputs(i) is the predicted output value for case i +% +% Author: yimin.zhang@intel.com +% Last updated: Jan. 19, 2002 + + +if iscell(data) + local_data = cell2num(data(fam,:)); +else + local_data = data(fam, :); +end + +%get local node sizes and node types +node_sizes = ns(fam); +node_types = zeros(1,size(ns,2)); %all nodes are disrete +node_types(cnodes)=1; +node_types=node_types(fam); + +fam_size=size(fam,2); +output_type = node_types(fam_size); + +num_cases=size(local_data,2); +total_error=0; + +outputs=zeros(1,num_cases); +for i=1:num_cases + %class one case using the tree + cur_node=CPD.tree.root; % at the root node of the tree + while (1) + if (CPD.tree.nodes(cur_node).is_leaf==1) + if (output_type==0) %output is discrete + %use the class with max probability as the output + [maxvalue,class_id]=max(CPD.tree.nodes(cur_node).probs); + outputs(i)=class_id; + if (class_id~=local_data(fam_size,i)) + total_error=total_error+1; + end + else %output is continuous + %use the mean as the value + outputs(i)=CPD.tree.nodes(cur_node).mean; + cur_deviation = CPD.tree.nodes(cur_node).mean-local_data(fam_size,i); + total_error=total_error+cur_deviation*cur_deviation; + end + break; + end + cur_attr = CPD.tree.nodes(cur_node).split_id; + attr_val = local_data(cur_attr,i); + if (node_types(cur_attr)==0) %discrete attribute + % goto the attr_val -th child + cur_node = CPD.tree.nodes(cur_node).children(attr_val); + else + if (attr_val <= CPD.tree.nodes(cur_node).split_threshhold) + cur_node = CPD.tree.nodes(cur_node).children(1); + else + cur_node = CPD.tree.nodes(cur_node).children(2); + end + end + if (cur_node > CPD.tree.num_node) + fprintf('Fatal error: Tree structure corrupted.\n'); + return; + end + end + %update the classification error number +end +if (output_type==0) + score=1-total_error/num_cases; +else + score=total_error/num_cases; +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/get_field.m b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/get_field.m new file mode 100644 index 00000000..a299831f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/get_field.m @@ -0,0 +1,16 @@ +function val = get_params(CPD, name) +% GET_PARAMS Get the parameters (fields) for a tabular_CPD object +% val = get_params(CPD, name) +% +% The following fields can be accessed +% +% cpt - the CPT +% +% e.g., CPT = get_params(CPD, 'cpt') + +switch name + case 'cpt', val = CPD.CPT; + case 'tree', val = CPD.tree; + otherwise, + error(['invalid argument name ' name]); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/learn_params.m b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/learn_params.m new file mode 100644 index 00000000..baa48ed1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/learn_params.m @@ -0,0 +1,642 @@ +function CPD = learn_params(CPD, fam, data, ns, cnodes, varargin) +% LEARN_PARAMS Construct classification/regression tree given complete data +% CPD = learn_params(CPD, fam, data, ns, cnodes) +% +% fam(i) is the node id of the i-th node in the family of nodes, self node is the last one +% data(i,m) is the value of node i in case m (can be cell array). +% ns(i) is the node size for the i-th node in the whold bnet +% cnodes(i) is the node id for the i-th continuous node in the whole bnet +% +% The following optional arguments can be specified in the form of name/value pairs: +% stop_cases: for early stop (pruning). A node is not split if it has less than k cases. default is 0. +% min_gain: for early stop (pruning). +% For discrete output: A node is not split when the gain of best split is less than min_gain. default is 0. +% For continuous (cts) outpt: A node is not split when the gain of best split is less than min_gain*score(root) +% (we denote it cts_min_gain). default is 0.006 +% %%%%%%%%%%%%%%%%%%%Struction definition of dtree_CPD.tree%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% tree.num_node the last position in tree.nodes array for adding new nodes, +% it is not always same to number of nodes in a tree, because some position in the +% tree.nodes array can be set to unused (e.g. in tree pruning) +% tree.nodes is the array of nodes in the tree plus some unused nodes. +% tree.nodes(1) is the root for the tree. +% +% Below is the attributes for each node +% tree.nodes(i).used; % flag this node is used (0 means node not used, it can be removed from tree to save memory) +% tree.nodes(i).is_leaf; % if 1 means this node is a leaf, if 0 not a leaf. +% tree.nodes(i).children; % children(i) is the node number in tree.nodes array for the i-th child node +% tree.nodes(i).split_id; % the attribute id used to split this node +% tree.nodes(i).split_threshhold; % the threshhold for continuous attribute to split this node +% %%%%%attributes specially for classification tree (discrete output) +% tree.nodes(i).probs % probs(i) is the prob for i-th value of class node +% % For three output class, the probs = [0.9 0.1 0.0] means the probability of +% % class 1 is 0.9, for class 2 is 0.1, for class 3 is 0.0. +% %%%%%attributes specially for regression tree (continuous output) +% tree.nodes(i).mean % mean output value for this node +% tree.nodes(i).std % standard deviation for output values in this node +% +% Author: yimin.zhang@intel.com +% Last updated: Jan. 19, 2002 + +% Want list: +% (1) more efficient for cts attributes: get the values of cts attributes at first (the begining of build_tree function), then doing bi_search in finding threshhold +% (2) pruning classification tree using Pessimistic Error Pruning +% (3) bi_search for strings (used for transform data to BNT format) + +global tree %tree must be global so that it can be accessed in recursive slitting function +global cts_min_gain +tree=[]; % clear the tree +tree.num_node=0; +cts_min_gain=0; + +stop_cases=0; +min_gain=0; + +args = varargin; +nargs = length(args); +if (nargs>0) + if isstr(args{1}) + for i=1:2:nargs + switch args{i}, + case 'stop_cases', stop_cases = args{i+1}; + case 'min_gain', min_gain = args{i+1}; + end + end + else + error(['error in input parameters']); + end +end + +if iscell(data) + local_data = cell2num(data(fam,:)); +else + local_data = data(fam, :); +end +%counts = compute_counts(local_data, CPD.sizes); +%CPD.CPT = mk_stochastic(counts + CPD.prior); % bug fix 11/5/01 +node_types = zeros(1,size(ns,2)); %all nodes are disrete +node_types(cnodes)=1; +%make the data be BNT compliant (values for discrete nodes are from 1-n, here n is the node size) +%trans_data=transform_data(local_data,'tmp.dat',[]); %here no cts nodes + +build_dtree (CPD, local_data, ns(fam), node_types(fam),stop_cases,min_gain); +%CPD.tree=copy_tree(tree); +CPD.tree=tree; %copy the tree constructed to CPD + + +function new_tree = copy_tree(tree) +% copy the tree to new_tree +new_tree.num_node=tree.num_node; +new_tree.root = tree.root; +for i=1:tree.num_node + new_tree.nodes(i)=tree.nodes(i); +end + + +function build_dtree (CPD, fam_ev, node_sizes, node_types,stop_cases,min_gain) +global tree +global cts_min_gain + +tree.num_node=0; %the current number of nodes in the tree +tree.root=1; + +T = 1:size(fam_ev,2) ; %all cases +candidate_attrs = 1:(size(node_sizes,2)-1); %all attributes +node_id=1; %the root node +lastnode=size(node_sizes,2); %the last element in all nodes is the dependent variable (category node) +num_cat=node_sizes(lastnode); + +% get minimum gain for cts output (used in stop splitting) +if (node_types(size(fam_ev,1))==1) %cts output + N = size(fam_ev,2); + output_id = size(fam_ev,1); + cases_T = fam_ev(output_id,:); %get all the output value for cases T + std_T = std(cases_T); + avg_y_T = mean(cases_T); + sqr_T = cases_T - avg_y_T; + cts_min_gain = min_gain*(sum(sqr_T.*sqr_T)/N); % min_gain * (R(root) = 1/N * SUM(y-avg_y)^2) +end + +split_dtree (CPD, fam_ev, node_sizes, node_types, stop_cases,min_gain, T, candidate_attrs, num_cat); + + + +% pruning method +% (1) Restrictions on minimum node size: A node is not split if it has smaller than k cases. +% (2) Threshholds on impurity: a threshhold is imposed on the splitting test score. Threshhold can be +% imposed on local goodness measure (the gain_ratio of a node) or global goodness. +% (3) Mininum Error Pruning (MEP), (no need pruning set) +% Prune if static error<=backed-up error +% Static error at node v: e(v) = (Nc + 1)/(N+k) (laplace estimate, prior for each class equal) +% here N is # of all examples, Nc is # of majority class examples, k is number of classes +% Backed-up error at node v: (Ti is the i-th subtree root) +% E(T) = Sum_1_to_n(pi*e(Ti)) +% (4) Pessimistic Error Pruning (PEP), used in Quilan C4.5 (no need pruning set, efficient because of pruning top-down) +% Probability of error (apparent error rate) +% q = (N-Nc+0.5)/N +% where N=#examples, Nc=#examples in majority class +% Error of a node v (if pruned) q(v)= (Nv- Nc,v + 0.5)/Nv +% Error of a subtree q(T)= Sum_of_l_leaves(Nl - Nc,l + 0.5)/Sum_of_l_leaves(Nl) +% Prune if q(v)<=q(T) +% +% Implementation statuts: +% (1)(2) has been implemented as the input parameters of learn_params. +% (4) is implemented in this function +function pruning(fam_ev,node_sizes,node_types) +% PRUNING prune the constructed tree using PEP +% pruning(fam_ev,node_sizes,node_types) +% +% fam_ev(i,j) is the value of attribute i in j-th training cases (for whole tree), the last row is for the class label (self_ev) +% node_sizes(i) is the node size for the i-th node in the family +% node_types(i) is the node type for the i-th node in the family, 0 for disrete node, 1 for continous node +% the global parameter 'tree' is for storing the input tree and the pruned tree + + +function split_T = split_cases(fam_ev,node_sizes,node_types,T,node_i, threshhold) +% SPLIT_CASES split the cases T according to values of node_i in the family +% split_T = split_cases(fam_ev,node_sizes,node_types,T,node_i) +% +% fam_ev(i,j) is the value of attribute i in j-th training cases (for whole tree), the last row is for the class label (self_ev) +% node_sizes(i) is the node size for the i-th node in the family +% node_types(i) is the node type for the i-th node in the family, 0 for disrete node, 1 for continous node +% node_i is the attribute we need to split + +if (node_types(node_i)==0) %discrete attribute + %init the subsets of T + split_T = cell(1,node_sizes(node_i)); %T will be separated into |node_size of i| subsets according to different values of node i + for i=1:node_sizes(node_i) % here we assume that the value of an attribute is 1:node_size + split_T{i}=zeros(1,0); + end + + size_t = size(T,2); + for i=1:size_t + case_id = T(i); + %put this case into one subset of split_T according to its value for node_i + value = fam_ev(node_i,case_id); + pos = size(split_T{value},2)+1; + split_T{value}(pos)=case_id; % here assumes the value of an attribute is 1:node_size + end +else %continuous attribute + %init the subsets of T + split_T = cell(1,2); %T will be separated into 2 subsets (<=threshhold) (>threshhold) + for i=1:2 + split_T{i}=zeros(1,0); + end + + size_t = size(T,2); + for i=1:size_t + case_id = T(i); + %put this case into one subset of split_T according to its value for node_i + value = fam_ev(node_i,case_id); + subset_num=1; + if (value>threshhold) + subset_num=2; + end + pos = size(split_T{subset_num},2)+1; + split_T{subset_num}(pos)=case_id; + end +end + + + +function new_node = split_dtree (CPD, fam_ev, node_sizes, node_types, stop_cases, min_gain, T, candidate_attrs, num_cat) +% SPLIT_TREE Split the tree at node node_id with cases T (actually it is just indexes to family evidences). +% new_node = split_dtree (fam_ev, node_sizes, node_types, T, node_id, num_cat, method) +% +% fam_ev(i,j) is the value of attribute i in j-th training cases (for whole tree), the last row is for the class label (self_ev) +% node_sizes{i} is the node size for the i-th node in the family +% node_types{i} is the node type for the i-th node in the family, 0 for disrete node, 1 for continous node +% stop_cases is the threshold of number of cases to stop slitting +% min_gain is the minimum gain need to split a node +% T(i) is the index of i-th cases in current decision tree node, we need split it further +% candidate_attrs(i) the node id for the i-th attribute that still need to be considered as split attribute +%%%%% node_id is the index of current node considered for a split +% num_cat is the number of output categories for the decision tree +% output: +% new_node is the new node created +global tree +global cts_min_gain + +size_fam = size(fam_ev,1); %number of family size +output_type = node_types(size_fam); %the type of output for the tree (0 is discrete, 1 is continuous) +size_attrs = size(candidate_attrs,2); %number of candidate attributes +size_t = size(T,2); %number of training cases in this tree node + +%(1)computeFrequenceyForEachClass(T) +if (output_type==0) %discrete output + class_freqs = zeros(1,num_cat); + for i=1:size_t + case_id = T(i); + case_class = fam_ev(size_fam,case_id); %get the class label for this case + class_freqs(case_class)=class_freqs(case_class)+1; + end +else %cts output + N = size(fam_ev,2); + cases_T = fam_ev(size(fam_ev,1),T); %get the output value for cases T + std_T = std(cases_T); +end + +%(2) if OneClass (for discrete output) or same output value (for cts output) or Class With #examples < stop_cases +% return a leaf; +% create a decision node N; + +% get majority class in this node +if (output_type == 0) + top1_class = 0; %the class with the largest number of cases + top1_class_cases = 0; %the number of cases in top1_class + [top1_class_cases,top1_class]=max(class_freqs); +end + +if (size_t==0) %impossble + new_node=-1; + fprintf('Fatal error: please contact the author. \n'); + return; +end + +% stop splitting if needed + %for discrete output: one class + %for cts output, all output value in cases are same + %cases too little +if ( (output_type==0 & top1_class_cases == size_t) | (output_type==1 & std_T == 0) | (size_t < stop_cases)) + %create one new leaf node + tree.num_node=tree.num_node+1; + tree.nodes(tree.num_node).used=1; %flag this node is used (0 means node not used, it will be removed from tree at last to save memory) + tree.nodes(tree.num_node).is_leaf=1; + tree.nodes(tree.num_node).children=[]; + tree.nodes(tree.num_node).split_id=0; %the attribute(parent) id to split this tree node + tree.nodes(tree.num_node).split_threshhold=0; + if (output_type==0) + tree.nodes(tree.num_node).probs=class_freqs/size_t; %the prob for each value of class node + + % tree.nodes(tree.num_node).probs=zeros(1,num_cat); %the prob for each value of class node + % tree.nodes(tree.num_node).probs(top1_class)=1; %use the majority class of parent node, like for binary class, + %and majority is class 2, then the CPT is [0 1] + %we may need to use prior to do smoothing, to get [0.001 0.999] + tree.nodes(tree.num_node).error.self_error=1-top1_class_cases/size_t; %the classfication error in this tree node when use default class + tree.nodes(tree.num_node).error.all_error=1-top1_class_cases/size_t; %no total classfication error in this tree node and its subtree + tree.nodes(tree.num_node).error.all_error_num=size_t - top1_class_cases; + fprintf('Create leaf node(onecla) %d. Class %d Cases %d Error %d \n',tree.num_node, top1_class, size_t, size_t - top1_class_cases ); + else + avg_y_T = mean(cases_T); + tree.nodes(tree.num_node).mean = avg_y_T; + tree.nodes(tree.num_node).std = std_T; + fprintf('Create leaf node(samevalue) %d. Mean %8.4f Std %8.4f Cases %d \n',tree.num_node, avg_y_T, std_T, size_t); + end + new_node = tree.num_node; + return; +end + +%create one new node +tree.num_node=tree.num_node+1; +tree.nodes(tree.num_node).used=1; %flag this node is used (0 means node not used, it will be removed from tree at last to save memory) +tree.nodes(tree.num_node).is_leaf=1; +tree.nodes(tree.num_node).children=[]; +tree.nodes(tree.num_node).split_id=0; +tree.nodes(tree.num_node).split_threshhold=0; +if (output_type==0) + tree.nodes(tree.num_node).error.self_error=1-top1_class_cases/size_t; + tree.nodes(tree.num_node).error.all_error=0; + tree.nodes(tree.num_node).error.all_error_num=0; +else + avg_y_T = mean(cases_T); + tree.nodes(tree.num_node).mean = avg_y_T; + tree.nodes(tree.num_node).std = std_T; +end +new_node = tree.num_node; + +%Stop splitting if no attributes left in this node +if (size_attrs==0) + if (output_type==0) + tree.nodes(tree.num_node).probs=class_freqs/size_t; %the prob for each value of class node + tree.nodes(tree.num_node).error.all_error=1-top1_class_cases/size_t; + tree.nodes(tree.num_node).error.all_error_num=size_t - top1_class_cases; + fprintf('Create leaf node(noattr) %d. Class %d Cases %d Error %d \n',tree.num_node, top1_class, size_t, size_t - top1_class_cases ); + else + fprintf('Create leaf node(noattr) %d. Mean %8.4f Std %8.4f Cases %d \n',tree.num_node, avg_y_T, std_T, size_t); + end + return; +end + + +%(3) for each attribute A +% ComputeGain(A); +max_gain=0; %the max gain score (for discrete information gain or gain ration, for cts node the R(T)) +best_attr=0; %the attribute with the max_gain +best_split = []; %the split of T according to the value of best_attr +cur_best_threshhold = 0; %the threshhold for split continuous attribute +best_threshhold=0; + +% compute Info(T) (for discrete output) +if (output_type == 0) + class_split_T = split_cases(fam_ev,node_sizes,node_types,T,size(fam_ev,1),0); %split cases according to class + info_T = compute_info (fam_ev, T, class_split_T); +else % compute R(T) (for cts output) +% N = size(fam_ev,2); +% cases_T = fam_ev(size(fam_ev,1),T); %get the output value for cases T +% std_T = std(cases_T); +% avg_y_T = mean(cases_T); + sqr_T = cases_T - avg_y_T; + R_T = sum(sqr_T.*sqr_T)/N; % get R(T) = 1/N * SUM(y-avg_y)^2 + info_T = R_T; +end + +for i=1:(size_fam-1) + if (myismember(i,candidate_attrs)) %if this attribute still in the candidate attribute set + if (node_types(i)==0) %discrete attibute + split_T = split_cases(fam_ev,node_sizes,node_types,T,i,0); %split cases according to value of attribute i + % For cts output, we compute the least square gain. + % For discrete output, we compute gain ratio + cur_gain = compute_gain(fam_ev,node_sizes,node_types,T,info_T,i,split_T,0,output_type); %gain ratio + else %cts attribute + %get the values of this attribute + ev = fam_ev(:,T); + values = ev(i,:); + sort_v = sort(values); + %remove the duplicate values in sort_v + v_set = unique(sort_v); + best_gain = 0; + best_threshhold = 0; + best_split1 = []; + + %find the best split for this cts attribute + % see "Quilan 96: Improved Use of Continuous Attributes in C4.5" + for j=1:(size(v_set,2)-1) + mid_v = (v_set(j)+v_set(j+1))/2; + split_T = split_cases(fam_ev,node_sizes,node_types,T,i,mid_v); %split cases according to value of attribute i (<=mid_v) + % For cts output, we compute the least square gain. + % For discrete output, we use Quilan 96: use information gain instead of gain ratio to select threshhold + cur_gain = compute_gain(fam_ev,node_sizes,node_types,T,info_T,i,split_T,1,output_type); + %if (i==6) + % fprintf('gain %8.5f threshhold %6.3f spliting %d\n', cur_gain, mid_v, size(split_T{1},2)); + %end + + if (best_gain < cur_gain) + best_gain = cur_gain; + best_threshhold = mid_v; + %best_split1 = split_T; %here we need to copy array, not good!!! (maybe we can compute after we get best_attr + end + end + %recalculate the gain_ratio of the best_threshhold + split_T = split_cases(fam_ev,node_sizes,node_types,T,i,best_threshhold); + best_gain = compute_gain(fam_ev,node_sizes,node_types,T,info_T,i,split_T,0,output_type); %gain_ratio + if (output_type==0) %for discrete output + cur_gain = best_gain-log2(size(v_set,2)-1)/size_t; % Quilan 96: use the gain_ratio-log2(N-1)/|D| as the gain of this attr + else %for cts output + cur_gain = best_gain; + end + end + + if (max_gain < cur_gain) + max_gain = cur_gain; + best_attr = i; + cur_best_threshhold=best_threshhold; %save the threshhold + %best_split = split_T; %here we need to copy array, not good!!! So we will recalculate in below line 313 + end + end +end + +% stop splitting if gain is too small +if (max_gain==0 | (output_type==0 & max_gain < min_gain) | (output_type==1 & max_gain < cts_min_gain)) + if (output_type==0) + tree.nodes(tree.num_node).probs=class_freqs/size_t; %the prob for each value of class node + tree.nodes(tree.num_node).error.all_error=1-top1_class_cases/size_t; + tree.nodes(tree.num_node).error.all_error_num=size_t - top1_class_cases; + fprintf('Create leaf node(nogain) %d. Class %d Cases %d Error %d \n',tree.num_node, top1_class, size_t, size_t - top1_class_cases ); + else + fprintf('Create leaf node(nogain) %d. Mean %8.4f Std %8.4f Cases %d \n',tree.num_node, avg_y_T, std_T, size_t); + end + return; +end + +%get the split of cases according to the best split attribute +if (node_types(best_attr)==0) %discrete attibute + best_split = split_cases(fam_ev,node_sizes,node_types,T,best_attr,0); +else + best_split = split_cases(fam_ev,node_sizes,node_types,T,best_attr,cur_best_threshhold); +end + +%(4) best_attr = AttributeWithBestGain; +%(5) if best_attr is continuous ???? why need this? maybe the value in the decision tree must appeared in data +% find threshhold in all cases that <= max_V +% change the split of T +tree.nodes(tree.num_node).split_id=best_attr; +tree.nodes(tree.num_node).split_threshhold=cur_best_threshhold; %for cts attribute only + +%note: below threshhold rejust is linera search, so it is slow. A better method is described in paper "Efficient C4.5" +%if (output_type==0) +if (node_types(best_attr)==1) %is a continuous attribute + %find the value that approximate best_threshhold from below (the largest that <= best_threshhold) + best_value=0; + for i=1:size(fam_ev,2) %note: need to search in all cases for all tree, not just in cases for this node + val = fam_ev(best_attr,i); + if (val <= cur_best_threshhold & val > best_value) %val is more clear to best_threshhold + best_value=val; + end + end + tree.nodes(tree.num_node).split_threshhold=best_value; %for cts attribute only +end +%end + +if (output_type == 0) + fprintf('Create node %d split at %d gain %8.4f Th %d. Class %d Cases %d Error %d \n',tree.num_node, best_attr, max_gain, tree.nodes(tree.num_node).split_threshhold, top1_class, size_t, size_t - top1_class_cases ); +else + fprintf('Create node %d split at %d gain %8.4f Th %d. Mean %8.4f Cases %d\n',tree.num_node, best_attr, max_gain, tree.nodes(tree.num_node).split_threshhold, avg_y_T, size_t ); +end + +%(6) Foreach T' in the split_T +% if T' is Empty +% Child of node_id is a leaf +% else +% Child of node_id = split_tree (T') +tree.nodes(new_node).is_leaf=0; %because this node will be split, it is not leaf now +for i=1:size(best_split,2) + if (size(best_split{i},2)==0) %T(i) is empty + %create one new leaf node + tree.num_node=tree.num_node+1; + tree.nodes(tree.num_node).used=1; %flag this node is used (0 means node not used, it will be removed from tree at last to save memory) + tree.nodes(tree.num_node).is_leaf=1; + tree.nodes(tree.num_node).children=[]; + tree.nodes(tree.num_node).split_id=0; + tree.nodes(tree.num_node).split_threshhold=0; + if (output_type == 0) + tree.nodes(tree.num_node).probs=zeros(1,num_cat); %the prob for each value of class node + tree.nodes(tree.num_node).probs(top1_class)=1; %use the majority class of parent node, like for binary class, + %and majority is class 2, then the CPT is [0 1] + %we may need to use prior to do smoothing, to get [0.001 0.999] + tree.nodes(tree.num_node).error.self_error=0; + tree.nodes(tree.num_node).error.all_error=0; + tree.nodes(tree.num_node).error.all_error_num=0; + else + tree.nodes(tree.num_node).mean = avg_y_T; %just use parent node's mean value + tree.nodes(tree.num_node).std = std_T; + end + %add the new leaf node to parents + num_children=size(tree.nodes(new_node).children,2); + tree.nodes(new_node).children(num_children+1)=tree.num_node; + if (output_type==0) + fprintf('Create leaf node(nullset) %d. %d-th child of Father %d Class %d\n',tree.num_node, i, new_node, top1_class ); + else + fprintf('Create leaf node(nullset) %d. %d-th child of Father %d \n',tree.num_node, i, new_node ); + end + + else + if (node_types(best_attr)==0) % if attr is discrete, it should be removed from the candidate set + new_candidate_attrs = mysetdiff(candidate_attrs,[best_attr]); + else + new_candidate_attrs = candidate_attrs; + end + new_sub_node = split_dtree (CPD, fam_ev, node_sizes, node_types, stop_cases, min_gain, best_split{i}, new_candidate_attrs, num_cat); + %tree.nodes(parent_id).error.all_error += tree.nodes(new_sub_node).error.all_error; + fprintf('Add subtree node %d to %d. #nodes %d\n',new_sub_node,new_node, tree.num_node ); + +% tree.nodes(new_node).error.all_error_num = tree.nodes(new_node).error.all_error_num + tree.nodes(new_sub_node).error.all_error_num; + %add the new leaf node to parents + num_children=size(tree.nodes(new_node).children,2); + tree.nodes(new_node).children(num_children+1)=new_sub_node; + end +end + +%(7) Compute errors of N; for doing pruning +% get the total error for the subtree +if (output_type==0) + tree.nodes(new_node).error.all_error=tree.nodes(new_node).error.all_error_num/size_t; +end +%doing pruning, but doing here is not so efficient, because it is bottom up. +%if tree.nodes() +%after doing pruning, need to update the all_error to self_error + +%(8) Return N + + + + +%(1) For discrete output, we use GainRatio defined as below +% Gain(X,T) +% GainRatio(X,T) = ---------- +% SplitInfo(X,T) +% where +% Gain(X,T) = Info(T) - Info(X,T) +% |Ti| +% Info(X,T) = Sum for i from 1 to n of ( ---- * Info(Ti)) +% |T| + +% SplitInfo(D,T) is the information due to the split of T on the basis +% of the value of the categorical attribute D. Thus SplitInfo(D,T) is +% I(|T1|/|T|, |T2|/|T|, .., |Tm|/|T|) +% where {T1, T2, .. Tm} is the partition of T induced by the value of D. + +% Definition of Info(Ti) +% If a set T of records is partitioned into disjoint exhaustive classes C1, C2, .., Ck on the basis of the +% value of the categorical attribute, then the information needed to identify the class of an element of T +% is Info(T) = I(P), where P is the probability distribution of the partition (C1, C2, .., Ck): +% P = (|C1|/|T|, |C2|/|T|, ..., |Ck|/|T|) +% Here I(P) is defined as +% I(P) = -(p1*log(p1) + p2*log(p2) + .. + pn*log(pn)) +% +%(2) For continuous output (regression tree), we use least squares score (adapted from Leo Breiman's book "Classification and regression trees", page 231 +% The original support only binary split, we further extend it to permit multiple-child split +% +% Delta_R = R(T) - Sum for all childe nodes Ti (R(Ti)) +% Where R(Ti)= 1/N * Sum for all cases i in node Ti ((yi - avg_y(Ti))^2) +% here N is the number of all training cases for construct the regression tree +% avg_y(Ti) is the average value for output variable for the cases in node Ti + +function gain_score = compute_gain (fam_ev, node_sizes, node_types, T, info_T, attr_id, split_T, score_type, output_type) +% COMPUTE_GAIN Compute the score for the split of cases T using attribute attr_id +% gain_score = compute_gain (fam_ev, T, attr_id, node_size, method) +% +% fam_ev(i,j) is the value of attribute i in j-th training cases, the last row is for the class label (self_ev) +% T(i) is the index of i-th cases in current decision tree node, we need split it further +% attr_id is the index of current node considered for a split +% split_T{i} is the i_th subset in partition of cases T according to the value of attribute attr_id +% score_type if 0, is gain ratio, 1 is information gain (only apply to discrete output) +% node_size(i) the node size of i-th node in the family +% output_type: 0 means discrete output, 1 means continuous output. +gain_score=0; +% ***********for DISCRETE output******************************************************* +if (output_type == 0) + % compute Info(T) + total_cnt = size(T,2); + if (total_cnt==0) + return; + end; + %class_split_T = split_cases(fam_ev,node_sizes,node_types,T,size(fam_ev,1),0); %split cases according to class + %info_T = compute_info (fam_ev, T, class_split_T); + + % compute Info(X,T) + num_class = size(split_T,2); + subset_sizes = zeros(1,num_class); + info_ti = zeros(1,num_class); + for i=1:num_class + subset_sizes(i)=size(split_T{i},2); + if (subset_sizes(i)~=0) + class_split_Ti = split_cases(fam_ev,node_sizes,node_types,split_T{i},size(fam_ev,1),0); %split cases according to class + info_ti(i) = compute_info(fam_ev, split_T{i}, class_split_Ti); + end + end + ti_ratios = subset_sizes/total_cnt; %get the |Ti|/|T| + info_X_T = sum(ti_ratios.*info_ti); + + %get Gain(X,T) + gain_X_T = info_T - info_X_T; + + if (score_type == 1) %information gain + gain_score=gain_X_T; + return; + end + %compute the SplitInfo(X,T) //is this also for cts attr, only split into two subsets + splitinfo_T = compute_info (fam_ev, T, split_T); + if (splitinfo_T~=0) + gain_score = gain_X_T/splitinfo_T; + end + +% ************for continuous output************************************************** +else + N = size(fam_ev,2); + + % compute R(Ti) + num_class = size(split_T,2); + R_Ti = zeros(1,num_class); + for i=1:num_class + if (size(split_T{i},2)~=0) + cases_T = fam_ev(size(fam_ev,1),split_T{i}); + avg_y_T = mean(cases_T); + sqr_T = cases_T - avg_y_T; + R_Ti(i) = sum(sqr_T.*sqr_T)/N; % get R(Ti) = 1/N * SUM(y-avg_y)^2 + end + end + %delta_R = R(T) - SUM(R(Ti)) + gain_score = info_T - sum(R_Ti); + +end + + +% Definition of Info(Ti) +% If a set T of records is partitioned into disjoint exhaustive classes C1, C2, .., Ck on the basis of the +% value of the categorical attribute, then the information needed to identify the class of an element of T +% is Info(T) = I(P), where P is the probability distribution of the partition (C1, C2, .., Ck): +% P = (|C1|/|T|, |C2|/|T|, ..., |Ck|/|T|) +% Here I(P) is defined as +% I(P) = -(p1*log(p1) + p2*log(p2) + .. + pn*log(pn)) +function info = compute_info (fam_ev, T, split_T) +% COMPUTE_INFO compute the information for the split of T into split_T +% info = compute_info (fam_ev, T, split_T) + +total_cnt = size(T,2); +num_class = size(split_T,2); +subset_sizes = zeros(1,num_class); +probs = zeros(1,num_class); +log_probs = zeros(1,num_class); +for i=1:num_class + subset_sizes(i)=size(split_T{i},2); +end + +probs = subset_sizes/total_cnt; +%log_probs = log2(probs); % if probs(i)=0, the log2(probs(i)) will be Inf +for i=1:size(probs,2) + if (probs(i)~=0) + log_probs(i)=log2(probs(i)); + end +end + +info = sum(-(probs.*log_probs)); + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/readme.txt b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/readme.txt new file mode 100644 index 00000000..d938d972 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/readme.txt @@ -0,0 +1,8 @@ +Decision/regression tree CPD +Author: Yimin Zhang yimin.zhang@intel.com +21 Jan 2002 + + +See also Paul Bradley's Multisurface Method-Tree matlab code + http://www.cs.wisc.edu/~paulb/msmt/ +http://www.cs.wisc.edu/~olvi/uwmp/msmt.html diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/set_fields.m b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/set_fields.m new file mode 100644 index 00000000..a8e94ba1 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/set_fields.m @@ -0,0 +1,52 @@ +function CPD = set_fields(CPD, varargin) +% SET_PARAMS Set the parameters (fields) for a tabular_CPD object +% CPD = set_params(CPD, name/value pairs) +% +% The following optional arguments can be specified in the form of name/value pairs: +% +% CPT - the CPT +% prior - the prior +% clamped - 1 means don't adjust during EM +% +% e.g., CPD = set_params(CPD, 'CPT', 'rnd') + +args = varargin; +nargs = length(args); +for i=1:2:nargs + switch args{i}, + case 'CPT', + if ischar(args{i+1}) + switch args{i+1} + case 'unif', CPD.CPT = mk_stochastic(myones(CPD.sizes)); + case 'rnd', CPD.CPT = mk_stochastic(myrand(CPD.sizes)); + otherwise, error(['invalid type ' args{i+1}]); + end + elseif isscalarBNT(args{i+1}) + p = args{i+1}; + k = CPD.sizes(end); + % Bug fix by Hervé BOUTROUILLE 10/1/01 + CPD.CPT = myreshape(sample_dirichlet(p*ones(1,k), prod(CPD.sizes(1:end-1)), CPD.sizes)); + %CPD.CPT = myreshape(sample_dirichlet(p*ones(1,k), prod(CPD.sizes(1:end-1))), CPD.sizes); + else + CPD.CPT = myreshape(args{i+1}, CPD.sizes); + end + + case 'prior', + if ischar(args{i+1}) & strcmp(args{i+1}, 'unif') + CPD.prior = myones(CPD.sizes); + elseif isscalarBNT(args{i+1}) + CPD.prior = args{i+1} * normalise(myones(CPD.sizes)); + else + CPD.prior = myreshape(args{i+1}, CPD.sizes); + end + + %case 'clamped', CPD.clamped = strcmp(args{i+1}, 'yes'); + %case 'clamped', CPD = set_clamped(CPD, strcmp(args{i+1}, 'yes')); + case 'clamped', CPD = set_clamped(CPD, args{i+1}); + + otherwise, + %error(['invalid argument name ' args{i}]); + end +end + + diff --git a/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/tree_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/tree_CPD.m new file mode 100644 index 00000000..a9ef3776 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@tree_CPD/tree_CPD.m @@ -0,0 +1,37 @@ +function CPD = tree_CPD(varargin) +%DTREE_CPD Make a conditional prob. distrib. which is a decision/regression tree. +% +% CPD =dtree_CPD() will create an empty tree. + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + clamp = 0; + CPD = class(CPD, 'tree_CPD', discrete_CPD(clamp, [])); + return; +elseif isa(varargin{1}, 'tree_CPD') + % This might occur if we are copying an object. + CPD = varargin{1}; + return; +end + +CPD = init_fields; + + +clamped = 0; +fam_sz = []; +CPD = class(CPD, 'tree_CPD', discrete_CPD(clamped, fam_sz)); + + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +%init the decision tree set the root to null +CPD.tree.num_node = 0; +CPD.tree.root=1; +CPD.tree.nodes=[]; + diff --git a/sourcecodes/bnt-master/BNT/CPDs/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/CVS/Entries new file mode 100644 index 00000000..a527d2ad --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/CVS/Entries @@ -0,0 +1,2 @@ +/mk_isolated_tabular_CPD.m/1.1.1.1/Mon Jun 24 18:58:32 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/CVS/Entries.Log new file mode 100644 index 00000000..b7997b3c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/CVS/Entries.Log @@ -0,0 +1,19 @@ +A D/@boolean_CPD//// +A D/@deterministic_CPD//// +A D/@discrete_CPD//// +A D/@gaussian_CPD//// +A D/@generic_CPD//// +A D/@gmux_CPD//// +A D/@hhmm2Q_CPD//// +A D/@hhmmF_CPD//// +A D/@hhmmQ_CPD//// +A D/@mlp_CPD//// +A D/@noisyor_CPD//// +A D/@root_CPD//// +A D/@softmax_CPD//// +A D/@tabular_CPD//// +A D/@tabular_decision_node//// +A D/@tabular_kernel//// +A D/@tabular_utility_node//// +A D/@tree_CPD//// +A D/Old//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/CVS/Repository new file mode 100644 index 00000000..a8bb51a5 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs diff --git a/sourcecodes/bnt-master/BNT/CPDs/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/CVS/Entries new file mode 100644 index 00000000..96e99049 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/CVS/Entries @@ -0,0 +1,4 @@ +/linear_gaussian_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +/log_marg_prob_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +/update_params_complete.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/CVS/Repository new file mode 100644 index 00000000..ac2255d4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/Old/@linear_gaussian_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/linear_gaussian_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/linear_gaussian_CPD.m new file mode 100644 index 00000000..55076c4b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/linear_gaussian_CPD.m @@ -0,0 +1,87 @@ +function CPD = linear_gaussian_CPD(bnet, self, theta, sigma, theta0, n0, alpha0, beta0) +% LINEAR_GAUSSIAN_CPD Make a linear Gaussian distrib. +% +% CPD = linear_gaussian_CPD(bnet, self, theta, lambda) +% This defines the distribution P(Y|X) = N(y | theta'*x, sigma), +% where y (self) is a scalar, theta is a regression vector, and sigma is the variance. +% Pass in [] to generate a default random value for a parameter. +% +% CPD = linear_gaussian_CPD(bnet, self, [], [], theta0, n0, alpha0, beta0) +% defines a Normal-Gamma prior over the parameters: +% P(theta | lambda) = N(theta | theta0, n0*lambda) +% P(lambda) = Gamma(lambda | alpha0, beta0) +% where lambda = 1/sigma is the precision for y. +% n0 is a precision matrix, beta0 is a scale factor. +% Pass in [] to generate a default value for a hyperparameter. +% theta and sigma will be set to their prior expected values. +% See "Bayesian Theory", Bernardo and Smith (2000), p442. + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'linear_gaussian_CPD', generic_CPD(0)); + return; +elseif isa(bnet, 'linear_gaussian_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + + +ns = bnet.node_sizes; +ps = parents(bnet.dag, self); +d = sum(ns(ps)); +assert(ns(self)==1); + + +if nargin < 5, + prior = []; + if isempty(theta), theta = randn(d, 1); end + if isempty(sigma), sigma = 1; end +else + + %if isempty(theta0), theta0 = zeros(d, 1); end + %if isempty(n0), n0 = 0.1*eye(d); end + %if isempty(alpha0), alpha0 = 0.1; end + %if isempty(beta0), beta0 = 0.1; end + + % use non-informative priors + if isempty(theta0), theta0 = zeros(d, 1); end + if isempty(n0), n0 = 0.001*ones(d); end + if isempty(alpha0), alpha0 = -d/2 + 0.001; end + if isempty(beta0), beta0 = 0.001; end + + prior.theta = theta0; + prior.n = n0; + prior.alpha = alpha0; + prior.beta = beta0; + + % set params to their mean + theta = prior.theta; + %sigma = prior.beta/prior.alpha; % mean of Gamma is E[lambda] = alpha/beta +end + + +CPD.self = self; +CPD.theta = theta; +CPD.sigma = sigma; +CPD.prior = prior; + + +clamped = 0; +CPD = class(CPD, 'linear_gaussian_CPD', generic_CPD(clamped)); + + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.self = []; +CPD.theta = []; +CPD.sigma = []; +CPD.prior = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/log_marg_prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/log_marg_prob_node.m new file mode 100644 index 00000000..3d06244f --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/log_marg_prob_node.m @@ -0,0 +1,23 @@ +function L = log_marg_prob_node(CPD, self_ev, pev) +% LOG_MARG_PROB_NODE Compute prod_m log P(x(i,m)| x(pi_i,m)) for node i (linear_gaussian) +% L = log_marg_prob_node(CPD, self_ev, pev) +% +% This differs from log_prob_node because we integrate out the parameters. +% self_ev{m} is the evidence on this node in case m. +% pev{i,m} is the evidence on the i'th parent in case m +% We assume there is <= 1 case. + +ncases = length(self_ev); + +if ncases==0 + L = 0; + return; +elseif ncases==1 + y = self_ev{1}; + x = cat(1, pev{:}); % column vector + f = 1-x'*inv(x*x' + CPD.prior.n)*x; + alpha = CPD.prior.alpha; + L = log_student_pdf(y, x'*CPD.prior.theta, f*alpha/CPD.prior.beta, 2*alpha); +else + error('can''t handle batch data'); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/update_params_complete.m b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/update_params_complete.m new file mode 100644 index 00000000..dbe8d5da --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@linear_gaussian_CPD/update_params_complete.m @@ -0,0 +1,25 @@ +function CPD = update_params_complete(CPD, self_ev, pev) +% UPDATE_PARAMS_COMPLETE Bayesian parameter updating given completely observed data (linear_gaussian) +% CPD = update_params_complete(CPD, self_ev, pev) +% +% self_ev{m} is the evidence on this node in case m. +% pev{i,m} is the evidence on the i'th parent in case m +% +% We update the hyperparams and set the params to the mean of the posterior. + +y = cat(1, self_ev{:}); +X = cell2num(pev)'; +[N k] = size(X); % each row is a case + +n0 = CPD.prior.n; +th0 = CPD.prior.theta; +CPD.prior.theta = inv(n0 + X'*X)*(n0*th0 + X'*y); +thn = CPD.prior.theta; +CPD.prior.beta = CPD.prior.beta + 0.5*(y-X*thn)'*y + 0.5*(th0-thn)'*n0*th0; +CPD.prior.alpha = CPD.prior.alpha + 0.5*N; +CPD.prior.n = CPD.prior.n + X'*X; + + +% set params to their mean +CPD.theta = CPD.prior.theta; +%CPD.sigma = CPD.prior.beta/CPD.prior.alpha; % mean of Gamma is E[lambda] = alpha/beta diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/CVS/Entries new file mode 100644 index 00000000..5335ec72 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/CVS/Entries @@ -0,0 +1,4 @@ +/log_marg_prob_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +/root_gaussian_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +/update_params_complete.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/CVS/Repository new file mode 100644 index 00000000..ff9bf8d4 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/Old/@root_gaussian_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/log_marg_prob_node.m b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/log_marg_prob_node.m new file mode 100644 index 00000000..4d4e21fc --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/log_marg_prob_node.m @@ -0,0 +1,26 @@ +function L = log_marg_prob_node(CPD, self_ev, pev) +% LOG_MARG_PROB_NODE Compute prod_m log P(x(i,m)| x(pi_i,m)) for node i (root_gaussian) +% L = log_marg_prob_node(CPD, self_ev, pev) +% +% This differs from log_prob_node because we integrate out the parameters. +% self_ev{m} is the evidence on this node in case m. +% pev{i,m} is the evidence on the i'th parent in case m (ignored). + +ncases = length(self_ev); + +if ncases==0 + L = 0; + return; +elseif ncases==1 + x = cat(1, self_ev{:}); + k = length(x); + n0 = CPD.prior.n; + mu = CPD.prior.mu; + alpha = CPD.prior.alpha; + beta = CPD.prior.beta; + gamma = 2*alpha - k + 1; + % Bernardo and Smith p441 + L = log_student_pdf(x, mu, n0/(n0+1)*0.5*gamma*inv(beta), gamma); +else + error('can''t handle batch data'); +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/root_gaussian_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/root_gaussian_CPD.m new file mode 100644 index 00000000..bd4ffd9e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/root_gaussian_CPD.m @@ -0,0 +1,74 @@ +function CPD = root_gaussian_CPD(bnet, self, mu, Sigma, mu0, n0, alpha0, beta0) +% ROOT_GAUSSIAN_CPD Make an unconditional Gaussian distrib. +% +% CPD = root_gaussian_CPD(bnet, self, mu, Sigma) +% This defines the distribution Y ~ N(mu, Sigma), +% Pass in [] to generate a default random value for a parameter. +% +% CPD = root_gaussian_CPD(bnet, self, [], [], mu0, n0, alpha0, beta0) +% defines a Normal-Wishart prior over the parameters: +% P(mu | lambda) = N(mu | mu0, n0*lambda) +% P(lambda) = Wishart(lambda | alpha0, beta0) +% where lambda = inv(Sigma) is the precision matrix of mu. +% n0 is a scale factor, beta0 is a precision matrix. +% Pass in [] to generate a default value for a hyperparameter. +% mu and Sigma will be set to their prior expected values. +% See "Bayesian Theory", Bernardo and Smith (2000), p441. + + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'root_gaussian_CPD', generic_CPD(0)); + return; +elseif isa(bnet, 'root_gaussian_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + + +ns = bnet.node_sizes; +d = ns(self); + +if nargin < 5, + prior = []; + if isempty(mu), mu = randn(d, 1); end + if isempty(Sigma), Sigma = eye(d); end +else + if isempty(mu0), mu0 = zeros(d, 1); end + if isempty(n0), n0 = 0.1; end + if isempty(alpha0), alpha0 = (d-1)/2 + 1; end % Wishart requires 2 alpha > d-1 + if isempty(beta0), beta0 = eye(d); end + + prior.mu = mu0; + prior.n = n0; + prior.alpha = alpha0; + prior.beta = beta0; + + % set params to their mean + mu = prior.mu; + Sigma = prior.beta/prior.alpha; % mean of Wishart is E[lambda] = alpha*inv(beta) +end + +CPD.self = self; +CPD.mu = mu; +CPD.Sigma = Sigma; +CPD.prior = prior; + +clamped = 0; +CPD = class(CPD, 'root_gaussian_CPD', generic_CPD(clamped)); + + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.self = []; +CPD.mu = []; +CPD.Sigma = []; +CPD.prior = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/update_params_complete.m b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/update_params_complete.m new file mode 100644 index 00000000..7ce58944 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@root_gaussian_CPD/update_params_complete.m @@ -0,0 +1,29 @@ +function CPD = update_params_complete(CPD, self_ev, pev) +% UPDATE_PARAMS_COMPLETE Bayesian parameter updating given completely observed data (root_gaussian) +% CPD = update_params_complete(CPD, self_ev, pev) +% +% self_ev{m} is the evidence on this node in case m. +% pev{i,m} is the evidence on the i'th parent in case m (ignored) +% +% We update the hyperparams and set the params to the mean of the posterior. + +X = cell2num(self_ev); +[k N] = size(X); % each column is a case + +one = ones(N,1); +xbar = X*one / N; % = mean(X')' +S = X*(eye(N) - one*one'/N)*X'; + +n0 = CPD.prior.n; +nn = 1/(n0 + N); +mu0 = CPD.prior.mu; +CPD.prior.mu = nn*(n0*mu0 + N*xbar); +CPD.prior.alpha = CPD.prior.alpha + 0.5*N; +CPD.prior.beta = CPD.prior.beta + 0.5*S + 0.5*nn*N*n0*(mu0-xbar)*(mu0-xbar)'; +CPD.prior.n = CPD.prior.n + N; + +% set params to their mean +CPD.mu = CPD.prior.mu; +% E[Cov] = E inv(n lambda) = 1/(n (alpha-(k+1)/2)) beta +CPD.Sigma = CPD.prior.beta /(CPD.prior.n * (CPD.prior.alpha - (k+1)/2)); + diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CPD_to_upot.m b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CPD_to_upot.m new file mode 100644 index 00000000..3ce87d0b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CPD_to_upot.m @@ -0,0 +1,6 @@ +function pot = CPD_to_upot(CPD, domain) +% CPD_TO_UPOT Convert a CPD to a utility potential +% pot = CPD_to_upot(CPD, domain) + +sz = CPD.size; % mysize(CPD.CPT); +pot = upot(domain, sz, CPD.CPT, 0*myones(sz)); diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CVS/Entries new file mode 100644 index 00000000..02628802 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CVS/Entries @@ -0,0 +1,3 @@ +/CPD_to_upot.m/1.1.1.1/Wed May 29 15:59:54 2002// +/tabular_chance_node.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CVS/Repository new file mode 100644 index 00000000..3a232db2 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/Old/@tabular_chance_node diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/tabular_chance_node.m b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/tabular_chance_node.m new file mode 100644 index 00000000..3476536c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/@tabular_chance_node/tabular_chance_node.m @@ -0,0 +1,39 @@ +function CPD = tabular_chance_node(sz, CPT) +% TABULAR_CHANCE_NODE Like tabular_CPD, but simplified +% CPD = tabular_chance_node(sz, CPT) +% +% sz(1:end-1) is the sizes of the parents, sz(end) is the size of this node +% By default, CPT is a random stochastic matrix. + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'tabular_chance_node'); + return; +elseif isa(sz, 'tabular_chance_node') + % This might occur if we are copying an object. + CPD = sz; + return; +end +CPD = init_fields; + +if nargin < 2, + CPT = mk_stochastic(myones(sz)); +else + CPT = myreshape(CPT, sz); +end + +CPD.CPT = CPT; +CPD.size = sz; + +CPD = class(CPD, 'tabular_chance_node'); + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.CPT = []; +CPD.size = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Entries new file mode 100644 index 00000000..17848105 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Entries.Log b/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Entries.Log new file mode 100644 index 00000000..ed4a9516 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Entries.Log @@ -0,0 +1,3 @@ +A D/@linear_gaussian_CPD//// +A D/@root_gaussian_CPD//// +A D/@tabular_chance_node//// diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Repository new file mode 100644 index 00000000..cf1b510a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/Old diff --git a/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/Old/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/mk_isolated_tabular_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/mk_isolated_tabular_CPD.m new file mode 100644 index 00000000..6c2c237e --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/mk_isolated_tabular_CPD.m @@ -0,0 +1,14 @@ +function CPD = mk_isolated_tabular_CPD(fam_sz, args) +% function CPD = mk_isolated_tabular_CPD(fam_sz, args) +% function CPD = mk_isolated_tabular_CPD(fam_sz, args) +% Make a single CPD by creating a mini-bnet containing just this one family. +% This is necessary because the CPD constructor requires a bnet. + +n = length(fam_sz); +dag = zeros(n,n); +ps = 1:(n-1); +if ~isempty(ps) + dag(ps,n) = 1; +end +bnet = mk_bnet(dag, fam_sz); +CPD = tabular_CPD(bnet, n, args{:}); |
