package aima.core.probability.temporal.generic; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import aima.core.probability.CategoricalDistribution; import aima.core.probability.FiniteProbabilityModel; import aima.core.probability.RandomVariable; import aima.core.probability.proposition.AssignmentProposition; import aima.core.probability.proposition.Proposition; import aima.core.probability.temporal.ForwardBackwardInference; import aima.core.probability.util.ProbUtil; import aima.core.probability.util.ProbabilityTable; import aima.core.probability.util.RandVar; /** * Artificial Intelligence A Modern Approach (3rd Edition): page 576.
*
* *
 * function FORWARD-BACKWARD(ev, prior) returns a vector of probability distributions
 *   inputs: ev, a vector of evidence values for steps 1,...,t
 *           prior, the prior distribution on the initial state, P(X0)
 *   local variables: fv, a vector of forward messages for steps 0,...,t
 *                    b, a representation of the backward message, initially all 1s
 *                    sv, a vector of smoothed estimates for steps 1,...,t
 *                    
 *   fv[0] <- prior
 *   for i = 1 to t do
 *       fv[i] <- FORWARD(fv[i-1], ev[i])
 *   for i = t downto 1 do
 *       sv[i] <- NORMALIZE(fv[i] * b)
 *       b <- BACKWARD(b, ev[i])
 *   return sv
 * 
* * Figure 15.4 The forward-backward algorithm for smoothing: computing posterior * probabilities of a sequence of states given a sequence of observations. The * FORWARD and BACKWARD operators are defined by Equations (15.5) and (15.9), * respectively.
*
* Note: An implementation of the FORWARD-BACKWARD algorithm using the * general purpose probability APIs, i.e. the underlying model implementation is * abstracted away. * * @author Ciaran O'Reilly */ public class ForwardBackward implements ForwardBackwardInference { private FiniteProbabilityModel transitionModel = null; private Map tToTm1StateVarMap = new HashMap(); private FiniteProbabilityModel sensorModel = null; /** * Instantiate an instance of the Forward Backward algorithm. * * @param transitionModel * the transition model. * @param tToTm1StateVarMap * a map from the Xt random variables in the transition * model the to Xt-1 random variables. * @param sensorModel * the sensor model. */ public ForwardBackward(FiniteProbabilityModel transitionModel, Map tToTm1StateVarMap, FiniteProbabilityModel sensorModel) { this.transitionModel = transitionModel; this.tToTm1StateVarMap.putAll(tToTm1StateVarMap); this.sensorModel = sensorModel; } // // START-ForwardBackwardInference // function FORWARD-BACKWARD(ev, prior) returns a vector of probability // distributions @Override public List forwardBackward( List> ev, CategoricalDistribution prior) { // local variables: fv, a vector of forward messages for steps 0,...,t List fv = new ArrayList( ev.size() + 1); // b, a representation of the backward message, initially all 1s CategoricalDistribution b = initBackwardMessage(); // sv, a vector of smoothed estimates for steps 1,...,t List sv = new ArrayList( ev.size()); // fv[0] <- prior fv.add(prior); // for i = 1 to t do for (int i = 0; i < ev.size(); i++) { // fv[i] <- FORWARD(fv[i-1], ev[i]) fv.add(forward(fv.get(i), ev.get(i))); } // for i = t downto 1 do for (int i = ev.size() - 1; i >= 0; i--) { // sv[i] <- NORMALIZE(fv[i] * b) sv.add(0, fv.get(i + 1).multiplyBy(b).normalize()); // b <- BACKWARD(b, ev[i]) b = backward(b, ev.get(i)); } // return sv return sv; } @Override public CategoricalDistribution forward(CategoricalDistribution f1_t, List e_tp1) { final CategoricalDistribution s1 = new ProbabilityTable(f1_t.getFor()); // Set up required working variables Proposition[] props = new Proposition[s1.getFor().size()]; int i = 0; for (RandomVariable rv : s1.getFor()) { props[i] = new RandVar(rv.getName(), rv.getDomain()); i++; } final Proposition Xtp1 = ProbUtil.constructConjunction(props); final AssignmentProposition[] xt = new AssignmentProposition[tToTm1StateVarMap .size()]; final Map xtVarAssignMap = new HashMap(); i = 0; for (RandomVariable rv : tToTm1StateVarMap.keySet()) { xt[i] = new AssignmentProposition(tToTm1StateVarMap.get(rv), ""); xtVarAssignMap.put(rv, xt[i]); i++; } // Step 1: Calculate the 1 time step prediction // ∑xt CategoricalDistribution.Iterator if1_t = new CategoricalDistribution.Iterator() { public void iterate(Map possibleWorld, double probability) { // P(Xt+1 | xt)* // P(xt | e1:t) for (Map.Entry av : possibleWorld .entrySet()) { xtVarAssignMap.get(av.getKey()).setValue(av.getValue()); } int i = 0; for (double tp : transitionModel .posteriorDistribution(Xtp1, xt).getValues()) { s1.setValue(i, s1.getValues()[i] + (tp * probability)); i++; } } }; f1_t.iterateOver(if1_t); // Step 2: multiply by the probability of the evidence // and normalize // P(et+1 | Xt+1) CategoricalDistribution s2 = sensorModel.posteriorDistribution(ProbUtil .constructConjunction(e_tp1.toArray(new Proposition[e_tp1 .size()])), Xtp1); return s2.multiplyBy(s1).normalize(); } @Override public CategoricalDistribution backward(CategoricalDistribution b_kp2t, List e_kp1) { final CategoricalDistribution b_kp1t = new ProbabilityTable( b_kp2t.getFor()); // Set up required working variables Proposition[] props = new Proposition[b_kp1t.getFor().size()]; int i = 0; for (RandomVariable rv : b_kp1t.getFor()) { RandomVariable prv = tToTm1StateVarMap.get(rv); props[i] = new RandVar(prv.getName(), prv.getDomain()); i++; } final Proposition Xk = ProbUtil.constructConjunction(props); final AssignmentProposition[] ax_kp1 = new AssignmentProposition[tToTm1StateVarMap .size()]; final Map x_kp1VarAssignMap = new HashMap(); i = 0; for (RandomVariable rv : b_kp1t.getFor()) { ax_kp1[i] = new AssignmentProposition(rv, ""); x_kp1VarAssignMap.put(rv, ax_kp1[i]); i++; } final Proposition x_kp1 = ProbUtil.constructConjunction(ax_kp1); props = new Proposition[e_kp1.size()]; final Proposition pe_kp1 = ProbUtil.constructConjunction(e_kp1 .toArray(props)); // ∑xk+1 CategoricalDistribution.Iterator ib_kp2t = new CategoricalDistribution.Iterator() { public void iterate(Map possibleWorld, double probability) { // Assign current values for xk+1 for (Map.Entry av : possibleWorld .entrySet()) { x_kp1VarAssignMap.get(av.getKey()).setValue(av.getValue()); } // P(ek+1 | xk+1) // P(ek+2:t | xk+1) double p = sensorModel.posterior(pe_kp1, x_kp1) * probability; // P(xk+1 | Xk) int i = 0; for (double tp : transitionModel.posteriorDistribution(x_kp1, Xk).getValues()) { b_kp1t.setValue(i, b_kp1t.getValues()[i] + (tp * p)); i++; } } }; b_kp2t.iterateOver(ib_kp2t); return b_kp1t; } // END-ForwardBackwardInference // // // PRIVATE METHODS // private CategoricalDistribution initBackwardMessage() { ProbabilityTable b = new ProbabilityTable(tToTm1StateVarMap.keySet()); for (int i = 0; i < b.size(); i++) { b.setValue(i, 1.0); } return b; } }