diff options
| author | ziejd2 | 2018-03-14 23:23:33 -0500 |
|---|---|---|
| committer | GitHub | 2018-03-14 23:23:33 -0500 |
| commit | 1ff6baa44e22b91eefb48aea6f3befa078c0489b (patch) | |
| tree | e0fd79d2e32fd2aedda2eadaed0f19af3514c520 /sourcecodes/bnt-master/BNT/CPDs | |
| parent | 6882395afdadf4e982b25b5215071a0932730950 (diff) | |
| parent | c80226899f5cdd9f11c163817d59445213f5bef0 (diff) | |
| download | BNW-1ff6baa44e22b91eefb48aea6f3befa078c0489b.tar.gz | |
Merge pull request #1 from ziejd2/octave_php_separate
Octave php separate
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{:}); |
