about summary refs log tree commit diff
path: root/sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD
diff options
context:
space:
mode:
Diffstat (limited to 'sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD')
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Entries11
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Entries.Log1
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Repository1
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/CVS/Root1
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/convert_to_pot.m58
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/convert_to_table.m52
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/display.m4
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/get_field.m18
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/maximize_params.m41
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Entries2
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Repository1
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/CVS/Root1
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/private/extract_params.m18
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/reset_ess.m8
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/sample_node.m14
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/set_fields.m45
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/softmax_CPD.m187
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@softmax_CPD/update_ess.m97
18 files changed, 560 insertions, 0 deletions
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