diff --git a/README.md b/README.md
index 8bac287b6..43e55ce98 100644
--- a/README.md
+++ b/README.md
@@ -121,14 +121,14 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and
| 13.1 | DT-Agent | `DTAgent` | [`probability.py`][probability] | | |
| 14.9 | Enumeration-Ask | `enumeration_ask` | [`probability.py`][probability] | Done | Included |
| 14.11 | Elimination-Ask | `elimination_ask` | [`probability.py`][probability] | Done | Included |
-| 14.13 | Prior-Sample | `prior_sample` | [`probability.py`][probability] | | Included |
+| 14.13 | Prior-Sample | `prior_sample` | [`probability.py`][probability] | Done | Included |
| 14.14 | Rejection-Sampling | `rejection_sampling` | [`probability.py`][probability] | Done | Included |
| 14.15 | Likelihood-Weighting | `likelihood_weighting` | [`probability.py`][probability] | Done | Included |
| 14.16 | Gibbs-Ask | `gibbs_ask` | [`probability.py`][probability] | Done | Included |
-| 15.4 | Forward-Backward | `forward_backward` | [`probability.py`][probability] | Done | |
-| 15.6 | Fixed-Lag-Smoothing | `fixed_lag_smoothing` | [`probability.py`][probability] | Done | |
-| 15.17 | Particle-Filtering | `particle_filtering` | [`probability.py`][probability] | Done | |
-| 16.9 | Information-Gathering-Agent | | | | |
+| 15.4 | Forward-Backward | `forward_backward` | [`probability.py`][probability] | Done | Included |
+| 15.6 | Fixed-Lag-Smoothing | `fixed_lag_smoothing` | [`probability.py`][probability] | Done | Included |
+| 15.17 | Particle-Filtering | `particle_filtering` | [`probability.py`][probability] | Done | Included |
+| 16.9 | Information-Gathering-Agent | `InformationGatheringAgent` | [`probability.py`][probability] | Done | Included |
| 17.4 | Value-Iteration | `value_iteration` | [`mdp.py`][mdp] | Done | Included |
| 17.7 | Policy-Iteration | `policy_iteration` | [`mdp.py`][mdp] | Done | Included |
| 17.9 | POMDP-Value-Iteration | | | | |
@@ -147,7 +147,7 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and
| 22.1 | HITS | `HITS` | [`nlp.py`][nlp] | Done | Included |
| 23 | Chart-Parse | `Chart` | [`nlp.py`][nlp] | Done | Included |
| 23.5 | CYK-Parse | `CYK_parse` | [`nlp.py`][nlp] | Done | Included |
-| 25.9 | Monte-Carlo-Localization | `monte_carlo_localization` | [`probability.py`][probability] | Done | |
+| 25.9 | Monte-Carlo-Localization | `monte_carlo_localization` | [`probability.py`][probability] | Done | Included |
# Index of data structures
diff --git a/probability.ipynb b/probability.ipynb
index 58e9b1994..d7f09eb3a 100644
--- a/probability.ipynb
+++ b/probability.ipynb
@@ -6,39 +6,221 @@
"source": [
"# Probability \n",
"\n",
- "This IPy notebook acts as supporting material for **Chapter 13 Quantifying Uncertainty**, **Chapter 14 Probabilistic Reasoning** and **Chapter 15 Probabilistic Reasoning over Time** of the book* Artificial Intelligence: A Modern Approach*. This notebook makes use of the implementations in probability.py module. Let us import everything from the probability module. It might be helpful to view the source of some of our implementations. Please refer to the Introductory IPy file for more details on how to do so."
+ "This IPy notebook acts as supporting material for topics covered in **Chapter 13 Quantifying Uncertainty**, **Chapter 14 Probabilistic Reasoning**, **Chapter 15 Probabilistic Reasoning over Time**, **Chapter 16 Making Simple Decisions** and parts of **Chapter 25 Robotics** of the book* Artificial Intelligence: A Modern Approach*. This notebook makes use of the implementations in probability.py module. Let us import everything from the probability module. It might be helpful to view the source of some of our implementations. Please refer to the Introductory IPy file for more details on how to do so."
]
},
{
"cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 1,
+ "metadata": {},
"outputs": [],
"source": [
"from probability import *\n",
- "from notebook import *"
+ "from utils import print_table\n",
+ "from notebook import psource, pseudocode, heatmap"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## CONTENTS\n",
+ "- Probability Distribution\n",
+ " - Joint probability distribution\n",
+ " - Inference using full joint distributions\n",
+ "
\n",
+ "- Bayesian Networks\n",
+ " - BayesNode\n",
+ " - BayesNet\n",
+ " - Exact Inference in Bayesian Networks\n",
+ " - Enumeration\n",
+ " - Variable elimination\n",
+ " - Approximate Inference in Bayesian Networks\n",
+ " - Prior sample\n",
+ " - Rejection sampling\n",
+ " - Likelihood weighting\n",
+ " - Gibbs sampling\n",
+ "
\n",
+ "- Hidden Markov Models\n",
+ " - Inference in Hidden Markov Models\n",
+ " - Forward-backward\n",
+ " - Fixed lag smoothing\n",
+ " - Particle filtering\n",
+ "
\n",
+ "
\n",
+ "- Monte Carlo Localization\n",
+ "- Information Gathering Agent"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Probability Distribution\n",
+ "## PROBABILITY DISTRIBUTION\n",
"\n",
"Let us begin by specifying discrete probability distributions. The class **ProbDist** defines a discrete probability distribution. We name our random variable and then assign probabilities to the different values of the random variable. Assigning probabilities to the values works similar to that of using a dictionary with keys being the Value and we assign to it the probability. This is possible because of the magic methods **_ _getitem_ _** and **_ _setitem_ _** which store the probabilities in the prob dict of the object. You can keep the source window open alongside while playing with the rest of the code to get a better understanding."
]
},
{
"cell_type": "code",
- "execution_count": 34,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ "
class ProbDist:\n",
+ " """A discrete probability distribution. You name the random variable\n",
+ " in the constructor, then assign and query probability of values.\n",
+ " >>> P = ProbDist('Flip'); P['H'], P['T'] = 0.25, 0.75; P['H']\n",
+ " 0.25\n",
+ " >>> P = ProbDist('X', {'lo': 125, 'med': 375, 'hi': 500})\n",
+ " >>> P['lo'], P['med'], P['hi']\n",
+ " (0.125, 0.375, 0.5)\n",
+ " """\n",
+ "\n",
+ " def __init__(self, varname='?', freqs=None):\n",
+ " """If freqs is given, it is a dictionary of values - frequency pairs,\n",
+ " then ProbDist is normalized."""\n",
+ " self.prob = {}\n",
+ " self.varname = varname\n",
+ " self.values = []\n",
+ " if freqs:\n",
+ " for (v, p) in freqs.items():\n",
+ " self[v] = p\n",
+ " self.normalize()\n",
+ "\n",
+ " def __getitem__(self, val):\n",
+ " """Given a value, return P(value)."""\n",
+ " try:\n",
+ " return self.prob[val]\n",
+ " except KeyError:\n",
+ " return 0\n",
+ "\n",
+ " def __setitem__(self, val, p):\n",
+ " """Set P(val) = p."""\n",
+ " if val not in self.values:\n",
+ " self.values.append(val)\n",
+ " self.prob[val] = p\n",
+ "\n",
+ " def normalize(self):\n",
+ " """Make sure the probabilities of all values sum to 1.\n",
+ " Returns the normalized distribution.\n",
+ " Raises a ZeroDivisionError if the sum of the values is 0."""\n",
+ " total = sum(self.prob.values())\n",
+ " if not isclose(total, 1.0):\n",
+ " for val in self.prob:\n",
+ " self.prob[val] /= total\n",
+ " return self\n",
+ "\n",
+ " def show_approx(self, numfmt='{:.3g}'):\n",
+ " """Show the probabilities rounded and sorted by key, for the\n",
+ " sake of portable doctests."""\n",
+ " return ', '.join([('{}: ' + numfmt).format(v, p)\n",
+ " for (v, p) in sorted(self.prob.items())])\n",
+ "\n",
+ " def __repr__(self):\n",
+ " return "P({})".format(self.varname)\n",
+ "class JointProbDist(ProbDist):\n",
+ " """A discrete probability distribute over a set of variables.\n",
+ " >>> P = JointProbDist(['X', 'Y']); P[1, 1] = 0.25\n",
+ " >>> P[1, 1]\n",
+ " 0.25\n",
+ " >>> P[dict(X=0, Y=1)] = 0.5\n",
+ " >>> P[dict(X=0, Y=1)]\n",
+ " 0.5"""\n",
+ "\n",
+ " def __init__(self, variables):\n",
+ " self.prob = {}\n",
+ " self.variables = variables\n",
+ " self.vals = defaultdict(list)\n",
+ "\n",
+ " def __getitem__(self, values):\n",
+ " """Given a tuple or dict of values, return P(values)."""\n",
+ " values = event_values(values, self.variables)\n",
+ " return ProbDist.__getitem__(self, values)\n",
+ "\n",
+ " def __setitem__(self, values, p):\n",
+ " """Set P(values) = p. Values can be a tuple or a dict; it must\n",
+ " have a value for each of the variables in the joint. Also keep track\n",
+ " of the values we have seen so far for each variable."""\n",
+ " values = event_values(values, self.variables)\n",
+ " self.prob[values] = p\n",
+ " for var, val in zip(self.variables, values):\n",
+ " if val not in self.vals[var]:\n",
+ " self.vals[var].append(val)\n",
+ "\n",
+ " def values(self, var):\n",
+ " """Return the set of possible values for a variable."""\n",
+ " return self.vals[var]\n",
+ "\n",
+ " def __repr__(self):\n",
+ " return "P({})".format(self.variables)\n",
+ "def enumerate_joint_ask(X, e, P):\n",
- " """Return a probability distribution over the values of the variable X,\n",
- " given the {var:val} observations e, in the JointProbDist P. [Section 13.3]\n",
- " >>> P = JointProbDist(['X', 'Y'])\n",
- " >>> P[0,0] = 0.25; P[0,1] = 0.5; P[1,1] = P[2,1] = 0.125\n",
- " >>> enumerate_joint_ask('X', dict(Y=1), P).show_approx()\n",
- " '0: 0.667, 1: 0.167, 2: 0.167'\n",
- " """\n",
- " assert X not in e, "Query variable must be distinct from evidence"\n",
- " Q = ProbDist(X) # probability distribution for X, initially empty\n",
- " Y = [v for v in P.variables if v != X and v not in e] # hidden variables.\n",
- " for xi in P.values(X):\n",
- " Q[xi] = enumerate_joint(Y, extend(e, X, xi), P)\n",
- " return Q.normalize()\n",
+ "def enumerate_joint(variables, e, P):\n",
+ " """Return the sum of those entries in P consistent with e,\n",
+ " provided variables is P's remaining variables (the ones not in e)."""\n",
+ " if not variables:\n",
+ " return P[e]\n",
+ " Y, rest = variables[0], variables[1:]\n",
+ " return sum([enumerate_joint(rest, extend(e, Y, y), P)\n",
+ " for y in P.values(Y)])\n",
"
\n",
"\n",
"