From c3d90f16aa65f8113d0a5b30fe4e8b8246359ae6 Mon Sep 17 00:00:00 2001
From: Hendrik Buschmeier <hbuschme@uni-bielefeld.de>
Date: Mon, 2 Mar 2015 11:34:05 +0100
Subject: [PATCH] Added PRIMO ipython notebook tutorial.

---
 .../chapters/NaiveBayesDS.py                  | 169 ++++++++++++
 .../chapters/belief_approx.ipynb              | 173 ++++++++++++
 .../chapters/belief_cont.ipynb                | 129 +++++++++
 .../chapters/belief_exact.ipynb               | 186 +++++++++++++
 .../chapters/belief_networks.ipynb            | 248 ++++++++++++++++++
 .../chapters/dbn.ipynb                        | 208 +++++++++++++++
 .../chapters/decision_networks.ipynb          | 127 +++++++++
 .../chapters/dependency_test.ipynb            |  75 ++++++
 .../chapters/img/dbn.png                      | Bin 0 -> 12500 bytes
 .../chapters/img/decnet.png                   | Bin 0 -> 21449 bytes
 .../chapters/img/samiam.png                   | Bin 0 -> 9110 bytes
 .../chapters/naive_bayes_ev.ipynb             | 172 ++++++++++++
 doc/ipython-notebook-tutorial/index.ipynb     |  71 +++++
 13 files changed, 1558 insertions(+)
 create mode 100644 doc/ipython-notebook-tutorial/chapters/NaiveBayesDS.py
 create mode 100755 doc/ipython-notebook-tutorial/chapters/belief_approx.ipynb
 create mode 100755 doc/ipython-notebook-tutorial/chapters/belief_cont.ipynb
 create mode 100755 doc/ipython-notebook-tutorial/chapters/belief_exact.ipynb
 create mode 100755 doc/ipython-notebook-tutorial/chapters/belief_networks.ipynb
 create mode 100644 doc/ipython-notebook-tutorial/chapters/dbn.ipynb
 create mode 100644 doc/ipython-notebook-tutorial/chapters/decision_networks.ipynb
 create mode 100755 doc/ipython-notebook-tutorial/chapters/dependency_test.ipynb
 create mode 100644 doc/ipython-notebook-tutorial/chapters/img/dbn.png
 create mode 100644 doc/ipython-notebook-tutorial/chapters/img/decnet.png
 create mode 100755 doc/ipython-notebook-tutorial/chapters/img/samiam.png
 create mode 100644 doc/ipython-notebook-tutorial/chapters/naive_bayes_ev.ipynb
 create mode 100755 doc/ipython-notebook-tutorial/index.ipynb

diff --git a/doc/ipython-notebook-tutorial/chapters/NaiveBayesDS.py b/doc/ipython-notebook-tutorial/chapters/NaiveBayesDS.py
new file mode 100644
index 0000000..1789dad
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/chapters/NaiveBayesDS.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+import numpy
+
+
+
+class NaiveBayesDS:
+    def __init__(self):
+        self.rootNode = None
+        self.featureNodes = []
+        self.evidence = []
+
+    def calcRootDistribution(self):
+        """
+        Berechnung der Verteilung des Wurzelknotens.
+        P(A | B1, ... Bn) [für alle B mit Evidenz]
+        
+        A: Wurzelknoten/self.rootNode
+        B: featureNode
+        """
+        # Array für Verteilung anlegen und mit Nullen füllen
+        distributionOfA = numpy.zeros(len(self.rootNode[1]))
+    
+        # Der Divisor für die Formel wird inkrementell aufsummiert
+        divisor = 0
+      
+        # Summe über alle Zustände von A
+        for j in range(len(self.rootNode[2])):
+       
+        # Produkt über alle Evidenzen gegebener Bs, multipliziert mit Aj
+            tmpprod = self.rootNode[2][j]
+            for i in range(len(self.evidence)):
+                tmpNode = self.evidence[i][0]
+                evIdx = self.evidence[i][1]
+                evIdx = self.getIndex(tmpNode, evIdx)
+                evConfidence = self.evidence[i][2]
+                
+                # Confidence der Evidenz auf CPT rechnen
+                tmpCpt = numpy.copy(tmpNode[2])
+                ix = 0
+                for row in tmpCpt:
+                    for ridx in range(len(row)):
+                        row[ridx] *= evConfidence
+                    remains = 1 - numpy.sum(row)
+                    remains /= len(row)
+                    for ridx in range(len(row)):
+                        row[ridx] += remains
+                    tmpCpt[ix] = row
+                    ix += 1
+                tmpprod *= tmpCpt[j][evIdx]
+    
+        # Achtung! Hier ist die Verteilung von A noch nicht feritg (s.u.)
+            distributionOfA[j] = tmpprod
+            divisor += tmpprod
+        
+        # Abschließend die Verteilung von A durch den Divisor teilen
+        distributionOfA = numpy.array(distributionOfA) / divisor
+        return distributionOfA
+
+    
+    def calcFeatureDistribution(self, featureNode):
+        """
+        Berechnung der Verteilung eines Feature-Knoten:
+        P(B_i | B1, ... B_i-1, B_i+1, ... Bn) [für alle featureNode mit Evidenz]
+        
+        A: Wurzelknoten/self.rootNode
+        B: featureNode
+        """
+        # Array für Verteilung anlegen und mit Nullen füllen
+        distributionOfB = numpy.zeros(len(featureNode[1]))
+    
+        # Die Verteilung von A kann aus Aufgabe 1 genutzt werden
+        distributionOfA = self.calcRootDistribution()
+    
+        # Verteilung für featureNode iterativ berechnen
+        for i in range(len(featureNode[1])):
+            tmpsum = 0
+            # Multiplikationssatz:
+            for j in range(len(distributionOfA)):
+                # P(B_i|A_j) * PRODUKT über P(A_j|evidence)
+                tmpsum += featureNode[2][j][i] * distributionOfA[j]
+            distributionOfB[i] = tmpsum
+    
+        return distributionOfB
+    
+
+    def hasEvidence(self, node):
+        """
+        Prüft, ob für einen Featureknoten Evidenz gesetzt ist.
+        """
+        for e in self.evidence:
+            enode = e[0]
+            if node == enode:
+                return True
+        return False
+            
+
+    def getIndex(self, node, value):
+        """
+        Index für gegebenen Evidenz-Wert zurückgeben.
+        """
+        for i in range(len(node[1])):
+            if node[1][i] == value:
+                return i
+        return -1
+
+    def getFeatureEntropy(self, featureNode):
+        """
+        Entropie für einen Featureknoten berechnen.
+        """
+        if self.hasEvidence(featureNode):
+            return 0
+        return self.getEntropy(self.calcFeatureDistribution(featureNode))
+    
+    def getRootEntropy(self):
+        """
+        Entropie für den Wurzelknoten berechnen.
+        """
+        if self.hasEvidence(self.rootNode):
+            return 0
+        distribution = self.calcRootDistribution()
+        return self.getEntropy(distribution)
+    
+    def getEntropy(self, distribution):
+        """
+        Entropie für eine Verteilung berechnen.
+        
+        SUMME über alle Werte der Verteilung mit P(B=b) * log2( P(B=b) )
+        """
+        entropy = 0.0
+        for d in distribution:
+            if (d > 0):
+                entropy -= d * numpy.log2(d)
+        return entropy
+    
+    
+    def getConditionalEntropy(self, featureNode):
+        """
+        Bedingte Entropie für den Wurzelknoten bei gegebenen Featureknoten
+        berechnen, für welchen noch keine Evidenz vorliegt.
+        
+        SUMME über alle b aus B mit P(B=b) * H(A|B=b)
+        """
+        entropy = 0.0
+        if self.hasEvidence(featureNode):
+            return 0
+        
+        idx = 0
+        for value in featureNode[1]:
+            # P(B=b)
+            distB = self.calcFeatureDistribution(featureNode)
+            probValue = distB[idx]
+            
+            # H(A|B=b)
+            evEntry = [featureNode, value, 1.0]
+            self.evidence.append(evEntry)
+            rootEntropy = self.getRootEntropy()
+            self.evidence = self.evidence[:-1]
+            
+            # SUMME += P(B=b) * H(A|B=b)
+            entropy += probValue * rootEntropy
+            
+            idx += 1
+            
+        return entropy
+
+
+
+
+
diff --git a/doc/ipython-notebook-tutorial/chapters/belief_approx.ipynb b/doc/ipython-notebook-tutorial/chapters/belief_approx.ipynb
new file mode 100755
index 0000000..8d1e7d4
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/chapters/belief_approx.ipynb
@@ -0,0 +1,173 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:5d6d1074166d83c785ce5c1c3faa78e5f5285c404ea2117b8d56e2bc402e5ff9"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h1>Belief Networks - Approximierte Inferenz</h1>\n",
+      "Mittels MCMC-Verfahren kann in *PRIMO* auch approximierte Inferenz berechnet werden. Hier wird das gleiche Beispiel wie bei der exakten Inferenz verwendet:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%matplotlib inline\n",
+      "from primo.networks import BayesianNetwork\n",
+      "from primo.nodes import DiscreteNode\n",
+      "import numpy\n",
+      "\n",
+      "# Netz initialisieren\n",
+      "bn = BayesianNetwork()\n",
+      "\n",
+      "# Knoten und deren m\u00f6gliche Zust\u00e4nde festlegen\n",
+      "burglary = DiscreteNode(\"Burglary\", [\"Intruder\",\"Safe\"])\n",
+      "alarm = DiscreteNode(\"Alarm\", [\"Ringing\", \"Silent\"])\n",
+      "earthquake = DiscreteNode(\"Earthquake\", [\"Shaking\", \"Calm\"])\n",
+      "john_calls = DiscreteNode(\"John calls\", [\"Calling\", \"Not Calling\"])\n",
+      "baum_calls = DiscreteNode(\"Baum calls\", [\"Calling\", \"Not Calling\"])\n",
+      "\n",
+      "# Knoten ins Netz hinzuf\u00fcgen\n",
+      "bn.add_node(burglary)\n",
+      "bn.add_node(alarm)\n",
+      "bn.add_node(earthquake)\n",
+      "bn.add_node(john_calls)\n",
+      "bn.add_node(baum_calls)\n",
+      "\n",
+      "# Kanten einf\u00fcgen\n",
+      "bn.add_edge(burglary,alarm)\n",
+      "bn.add_edge(earthquake, alarm)\n",
+      "bn.add_edge(alarm, john_calls)\n",
+      "bn.add_edge(alarm, baum_calls)\n",
+      "\n",
+      "# F\u00fcr Wurzelknoten (ohne Eltern) werden hier im Beispiel die CPTs direkt gesetzt\n",
+      "cpt_burglary = numpy.array([0.001,0.999])\n",
+      "burglary.set_probability_table(cpt_burglary,[burglary])\n",
+      "\n",
+      "cpt_earthquake = numpy.array([0.002,0.998])\n",
+      "earthquake.set_probability_table(cpt_earthquake,[earthquake])\n",
+      "\n",
+      "# Setzen der CPT-Werte f\u00fcr Knoten mit Eltern\n",
+      "alarm.set_probability(0.95,[(alarm,\"Ringing\"),(burglary,\"Intruder\"),(earthquake,\"Shaking\")])\n",
+      "alarm.set_probability(0.05,[(alarm,\"Silent\"),(burglary,\"Intruder\"),(earthquake,\"Shaking\")])\n",
+      "alarm.set_probability(0.29,[(alarm,\"Ringing\"),(burglary,\"Safe\"),(earthquake,\"Shaking\")])\n",
+      "alarm.set_probability(0.71,[(alarm,\"Silent\"),(burglary,\"Safe\"),(earthquake,\"Shaking\")])\n",
+      "alarm.set_probability(0.94,[(alarm,\"Ringing\"),(burglary,\"Intruder\"),(earthquake,\"Calm\")])\n",
+      "alarm.set_probability(0.06,[(alarm,\"Silent\"),(burglary,\"Intruder\"),(earthquake,\"Calm\")])\n",
+      "alarm.set_probability(0.001,[(alarm,\"Ringing\"),(burglary,\"Safe\"),(earthquake,\"Calm\")])\n",
+      "alarm.set_probability(0.999,[(alarm,\"Silent\"),(burglary,\"Safe\"),(earthquake,\"Calm\")])\n",
+      "\n",
+      "baum_calls.set_probability(0.9,[(alarm,\"Ringing\"),(baum_calls,\"Calling\")])\n",
+      "baum_calls.set_probability(0.1,[(alarm,\"Ringing\"),(baum_calls,\"Not Calling\")])\n",
+      "baum_calls.set_probability(0.05,[(alarm,\"Silent\"),(baum_calls,\"Calling\")])\n",
+      "baum_calls.set_probability(0.95,[(alarm,\"Silent\"),(baum_calls,\"Not Calling\")])\n",
+      "\n",
+      "john_calls.set_probability(0.7,[(alarm,\"Ringing\"),(john_calls,\"Calling\")])\n",
+      "john_calls.set_probability(0.3,[(alarm,\"Ringing\"),(john_calls,\"Not Calling\")])\n",
+      "john_calls.set_probability(0.01,[(alarm,\"Silent\"),(john_calls,\"Calling\")])\n",
+      "john_calls.set_probability(0.99,[(alarm,\"Silent\"),(john_calls,\"Not Calling\")])\n",
+      "\n",
+      "# Grafische Ausgabe des Netzes\n",
+      "bn.draw()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2>Funktionen</h2>\n",
+      "<p>\n",
+      "Um MCMC zu initialisieren sind mindestens zwei Parameter notwendig: Das Bayesnetz selbst und die Anzahl der Iterationen.\n",
+      "</p>\n",
+      "<p>Optional k\u00f6nnen auch das \u00dcbergangsmodell und das Limit f\u00fcr den Konvergenztest bei der Berechnung der Markov-Kette gesetzt werden.\n",
+      "Metropolis-Hastings:\n",
+      "```\n",
+      "mcmc = MCMC(bn, 5000, transition_model=MetropolisHastingsTransitionModel()) # DEFAULT\n",
+      "```\n",
+      "Gibbs-Sampling:\n",
+      "```\n",
+      "mcmc = MCMC(bn, 5000, transition_model=GibbsTransitionModel())\n",
+      "```\n",
+      "\n",
+      "Limt f\u00fcr den Konvergenztest:\n",
+      "```\n",
+      "mcmc = MCMC(bn, 5000, convergence_test=ConvergenceTestSimpleCounting(500)) # DEFAULT\n",
+      "mcmc = MCMC(bn, 5000, convergence_test=ConvergenceTestSimpleCounting(100))\n",
+      "```\n",
+      "\n",
+      "Im Beispiel wird gezeigt, wie mit MCMC a-priori und a-posteriori Wahrscheinlichkeiten, sowie die Evidenzwahrscheinlichkeit (<i>Probability of Evidence</i>) und die MAP-Hypothese berechnet werden k\u00f6nnen."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "from primo.inference.mcmc import MCMC\n",
+      "from primo.inference.mcmc import GibbsTransitionModel\n",
+      "from primo.inference.mcmc import MetropolisHastingsTransitionModel\n",
+      "from primo.evidence import EvidenceEqual as EvEq\n",
+      "from primo.densities import ProbabilityTable\n",
+      "from primo.inference.mcmc import ConvergenceTestSimpleCounting\n",
+      "\n",
+      "# MCMC initialisieren: Bayesnetz, Iterationen, transition model\n",
+      "#mcmc = MCMC(bn, 5000, transition_model=GibbsTransitionModel())\n",
+      "#mcmc = MCMC(bn, 5000, transition_model=MetropolisHastingsTransitionModel())\n",
+      "mcmc = MCMC(bn, 5000, transition_model=GibbsTransitionModel(), convergence_test=ConvergenceTestSimpleCounting(500))\n",
+      "\n",
+      "\n",
+      "\n",
+      "print \"-------PriorMarginal:-------\"\n",
+      "pm = mcmc.calculate_PriorMarginal([alarm],ProbabilityTable)\n",
+      "print \"P(Alarm)= \" + str(pm)\n",
+      "print \"Ground truth=[0.0025  0.9975]\\n\"\n",
+      "\n",
+      "pm = mcmc.calculate_PriorMarginal([burglary],ProbabilityTable)\n",
+      "print \"P(Burglary)= \" + str(pm)\n",
+      "print \"Ground truth=[0.001  0.999]\\n\"\n",
+      "\n",
+      "# Evidenz setzen\n",
+      "evidence = {burglary:EvEq(\"Intruder\")}\n",
+      "\n",
+      "\n",
+      "\n",
+      "print \"-------ProbabilityOfEvidence:-------\" \n",
+      "poe = mcmc.calculate_PoE(evidence)\n",
+      "print \"p(evidence=Intruder)=\"+str(poe)\n",
+      "print \"Ground truth=0.001\\n\"\n",
+      "\n",
+      "print \"-------PosteriorMarginal:-------\"\n",
+      "pm = mcmc.calculate_PosteriorMarginal([alarm],evidence,ProbabilityTable)\n",
+      "print \"P(alarm|burglary=Intruder)=\"+str(pm)\n",
+      "print \"Ground truth=[0.94  0.06]\\n\"\n",
+      "\n",
+      "\n",
+      "\n",
+      "print \"-------MAP:-------\"\n",
+      "hyp = mcmc.calculate_MAP([alarm],evidence,ProbabilityTable)\n",
+      "print \"MAP(alarm|burglary=intruder)=\" + str(hyp)\n",
+      "print \"Ground truth=\\\"Ringing\\\"\\n\"\n",
+      "\n",
+      "hyp = mcmc.calculate_MAP([burglary,alarm],{},ProbabilityTable)\n",
+      "print \"MAP(burglary,alarm)=\"+str(hyp)\n",
+      "print \"Ground truth=\\\"Safe\\\",\\\"Silent\\\"\\n\"\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/ipython-notebook-tutorial/chapters/belief_cont.ipynb b/doc/ipython-notebook-tutorial/chapters/belief_cont.ipynb
new file mode 100755
index 0000000..40a172e
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/chapters/belief_cont.ipynb
@@ -0,0 +1,129 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:b5510da9fa62058537da539fc44d713d34ed6b88e5a9a7acab0e555001879c5a"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h1>Belief Networks - Kontinuierliche Variablen</h1>\n",
+      "<p>MCMC (siehe <a href=\"chapters/belief_approx.ipynb\">Belief Networks - Approximierte Inferenz</a>) kann neben diskreten auch mit kontinuierlichen Variablen Inferenzen ziehen. Allerdings kann ein Bayesnetz nur aus kontinuierlichen oder nur aus diskreten Variablen bestehen, eine Berechnung auf einem Netz, wo diese Typen vermischt sind, ist aktuell noch nicht m\u00f6glich.\n",
+      "</p>\n",
+      "<p>\n",
+      "Sowohl bei der Parametrisierung von Knoten als auch bei der Inferenz k\u00f6nnen Verteilungen angegeben werden. Diese sind in `primo.densities` implementiert, dort bereits enthalten sind: Beta-Verteilung, Exponential-Verteilung und Gauss-Verteilung.\n",
+      "</p>\n",
+      "<p>\n",
+      "Im folgenden Beispiel ist ein Bayesnetz modelliert, welches das lineare Verh\u00e4ltnis zwischen Alter und H\u00f6he einer Pflanze modelliert, zuz\u00fcglich Rauschen.\n",
+      "</p>"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%matplotlib inline\n",
+      "from primo.networks import BayesianNetwork\n",
+      "from primo.nodes import ContinuousNodeFactory\n",
+      "from primo.densities import ExponentialParameters\n",
+      "from primo.densities import BetaParameters\n",
+      "from primo.densities import GaussParameters\n",
+      "\n",
+      "# Bayesnetz initialisieren\n",
+      "bn = BayesianNetwork()\n",
+      "\n",
+      "# Knoten erstellen und hinzuf\u00fcgen\n",
+      "cnf = ContinuousNodeFactory()\n",
+      "\n",
+      "age = cnf.createExponentialNode(\"Age\")\n",
+      "sun = cnf.createBetaNode(\"Sun\")\n",
+      "ground = cnf.createGaussNode(\"Ground\")\n",
+      "growth = cnf.createGaussNode(\"Growth\")\n",
+      "height = cnf.createBetaNode(\"Height\")\n",
+      "diameter = cnf.createExponentialNode(\"Diameter\")\n",
+      "children = cnf.createExponentialNode(\"Children\")\n",
+      "\n",
+      "bn.add_node(age)\n",
+      "bn.add_node(sun)\n",
+      "bn.add_node(ground)\n",
+      "bn.add_node(growth)\n",
+      "bn.add_node(height)\n",
+      "bn.add_node(diameter)\n",
+      "bn.add_node(children)\n",
+      "\n",
+      "# Kanten erstellen\n",
+      "bn.add_edge(age, growth)\n",
+      "bn.add_edge(ground, growth)\n",
+      "bn.add_edge(sun, growth)\n",
+      "bn.add_edge(growth, diameter)\n",
+      "bn.add_edge(growth, height)\n",
+      "bn.add_edge(height, children)\n",
+      "bn.add_edge(ground, children)\n",
+      "\n",
+      "# Parametrisierung\n",
+      "age.set_density_parameters(ExponentialParameters(0.1, {}))\n",
+      "sun.set_density_parameters(BetaParameters(2, {}, 2, {}))\n",
+      "ground.set_density_parameters(GaussParameters(2.0, {}, 1.5))\n",
+      "growth.set_density_parameters(GaussParameters(0.1, {age:5.0, ground:1.0, sun:4.0}, 2.5))\n",
+      "height.set_density_parameters(BetaParameters(0.1, {growth:1}, 0.5, {growth:0.5}))\n",
+      "diameter.set_density_parameters(ExponentialParameters(0.01, {growth:0.2}))\n",
+      "children.set_density_parameters(ExponentialParameters(0.1, {ground:1.0, height:1.0}))\n",
+      "\n",
+      "bn.draw()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2>Funktionen</h2>\n",
+      "<p>\n",
+      "Zur Inferenz auf kontinuierlichen Netzen k\u00f6nnen die gleichen Funktionen wie auf diskreten Netzen verwendet werden. (F\u00fcr die Parameter des MCMC-Konstruktors siehe ebenfalls <a href=\"chapters/belief_approx.ipynb\">Belief Networks - Approximierte Inferenz</a>).\n",
+      "</p>\n",
+      "<p>\n",
+      "Bei den R\u00fcckgabewerten werden die Verteilungsparameter angegeben. Im Beispiel f\u00fcr die Gauss-Verteilung sind dies \"`mu`\" f\u00fcr den Erwartungswert $\\mu$ und \"`c`\" f\u00fcr die Standardabweichung $\\sigma$.\n",
+      "</p>\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "from primo.inference.mcmc import MCMC\n",
+      "from primo.evidence import EvidenceEqual as EvEqual\n",
+      "from primo.densities import NDGauss\n",
+      "from primo.densities import Gauss\n",
+      "\n",
+      "# MCMC mit Standardwerten initialisieren\n",
+      "mcmc = MCMC(bn, 100)\n",
+      "\n",
+      "print \"PriorMarginal:\"\n",
+      "pm = mcmc.calculate_PriorMarginal([age], NDGauss)\n",
+      "print pm\n",
+      "pm = mcmc.calculate_PriorMarginal([height], NDGauss)\n",
+      "print pm\n",
+      "\n",
+      "# Evidenz setzen\n",
+      "evidence = {age:EvEqual(2)}\n",
+      "\n",
+      "print \"PosteriorMarginal:\"\n",
+      "pm = mcmc.calculate_PosteriorMarginal([age, height], evidence, NDGauss)\n",
+      "print pm"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/ipython-notebook-tutorial/chapters/belief_exact.ipynb b/doc/ipython-notebook-tutorial/chapters/belief_exact.ipynb
new file mode 100755
index 0000000..b975cf6
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/chapters/belief_exact.ipynb
@@ -0,0 +1,186 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:8d5da5c534ffd680f845f7a01f34a54839873823dcd8d641a54447577f16576a"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h1 id=\"inferenz\">Belief Networks - Exakte Inferenz</h1>\n",
+      "<ul>\n",
+      "<li><a href=\"#fe\">Factor Elimination</a></li>\n",
+      "<li><a href=\"#ftree\">Factor Tree</a></li>\n",
+      "</ul>\n",
+      "\n",
+      "Im Folgenden werden verschiedene Methoden f\u00fcr die exakte Inferenz vorgestellt. Daf\u00fcr soll das Beispiel aus *A. Darwiche - Modeling and Reasoning with Bayesian Networks, S. 54* dienen:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%matplotlib inline\n",
+      "from primo.networks import BayesianNetwork\n",
+      "from primo.nodes import DiscreteNode\n",
+      "import numpy\n",
+      "\n",
+      "# Netz initialisieren\n",
+      "bn = BayesianNetwork()\n",
+      "\n",
+      "# Knoten und deren m\u00f6gliche Zust\u00e4nde festlegen\n",
+      "burglary = DiscreteNode(\"Burglary\", [\"Intruder\",\"Safe\"])\n",
+      "alarm = DiscreteNode(\"Alarm\", [\"Ringing\", \"Silent\"])\n",
+      "earthquake = DiscreteNode(\"Earthquake\", [\"Shaking\", \"Calm\"])\n",
+      "john_calls = DiscreteNode(\"John calls\", [\"Calling\", \"Not Calling\"])\n",
+      "baum_calls = DiscreteNode(\"Baum calls\", [\"Calling\", \"Not Calling\"])\n",
+      "\n",
+      "# Knoten ins Netz hinzuf\u00fcgen\n",
+      "bn.add_node(burglary)\n",
+      "bn.add_node(alarm)\n",
+      "bn.add_node(earthquake)\n",
+      "bn.add_node(john_calls)\n",
+      "bn.add_node(baum_calls)\n",
+      "\n",
+      "# Kanten einf\u00fcgen\n",
+      "bn.add_edge(burglary,alarm)\n",
+      "bn.add_edge(earthquake, alarm)\n",
+      "bn.add_edge(alarm, john_calls)\n",
+      "bn.add_edge(alarm, baum_calls)\n",
+      "\n",
+      "# F\u00fcr Wurzelknoten (ohne Eltern) werden hier im Beispiel die CPTs direkt gesetzt\n",
+      "cpt_burglary = numpy.array([0.001,0.999])\n",
+      "burglary.set_probability_table(cpt_burglary,[burglary])\n",
+      "\n",
+      "cpt_earthquake = numpy.array([0.002,0.998])\n",
+      "earthquake.set_probability_table(cpt_earthquake,[earthquake])\n",
+      "\n",
+      "# Setzen der CPT-Werte f\u00fcr Knoten mit Eltern\n",
+      "alarm.set_probability(0.95,[(alarm,\"Ringing\"),(burglary,\"Intruder\"),(earthquake,\"Shaking\")])\n",
+      "alarm.set_probability(0.05,[(alarm,\"Silent\"),(burglary,\"Intruder\"),(earthquake,\"Shaking\")])\n",
+      "alarm.set_probability(0.29,[(alarm,\"Ringing\"),(burglary,\"Safe\"),(earthquake,\"Shaking\")])\n",
+      "alarm.set_probability(0.71,[(alarm,\"Silent\"),(burglary,\"Safe\"),(earthquake,\"Shaking\")])\n",
+      "alarm.set_probability(0.94,[(alarm,\"Ringing\"),(burglary,\"Intruder\"),(earthquake,\"Calm\")])\n",
+      "alarm.set_probability(0.06,[(alarm,\"Silent\"),(burglary,\"Intruder\"),(earthquake,\"Calm\")])\n",
+      "alarm.set_probability(0.001,[(alarm,\"Ringing\"),(burglary,\"Safe\"),(earthquake,\"Calm\")])\n",
+      "alarm.set_probability(0.999,[(alarm,\"Silent\"),(burglary,\"Safe\"),(earthquake,\"Calm\")])\n",
+      "\n",
+      "baum_calls.set_probability(0.9,[(alarm,\"Ringing\"),(baum_calls,\"Calling\")])\n",
+      "baum_calls.set_probability(0.1,[(alarm,\"Ringing\"),(baum_calls,\"Not Calling\")])\n",
+      "baum_calls.set_probability(0.05,[(alarm,\"Silent\"),(baum_calls,\"Calling\")])\n",
+      "baum_calls.set_probability(0.95,[(alarm,\"Silent\"),(baum_calls,\"Not Calling\")])\n",
+      "\n",
+      "john_calls.set_probability(0.7,[(alarm,\"Ringing\"),(john_calls,\"Calling\")])\n",
+      "john_calls.set_probability(0.3,[(alarm,\"Ringing\"),(john_calls,\"Not Calling\")])\n",
+      "john_calls.set_probability(0.01,[(alarm,\"Silent\"),(john_calls,\"Calling\")])\n",
+      "john_calls.set_probability(0.99,[(alarm,\"Silent\"),(john_calls,\"Not Calling\")])\n",
+      "\n",
+      "# Grafische Ausgabe des Netzes\n",
+      "bn.draw()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2 id=\"fe\">Exakte Inferenz - Factor Elimination</h2>\n",
+      "<i>(zuerst den Beispiel-Code aus <a href=\"#inferenz\">Exakte Inferenz</a> ausf\u00fchren)</i><br>\n",
+      "<i>EasiestFactorElimination</i> bildet zun\u00e4chst den Joint Probability Table (jpt), um anschlie\u00dfend auf die angefragten Variablen abzubilden:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%matplotlib inline\n",
+      "from primo.inference.factor import EasiestFactorElimination\n",
+      "\n",
+      "# Initialisieren und Netz setzen\n",
+      "fe = EasiestFactorElimination(bn)\n",
+      "\n",
+      "# a-priori\n",
+      "print \"Prior Marginals:\"\n",
+      "print \"Prior Alarm:   \" + str(fe.calculate_PriorMarginal([alarm]))\n",
+      "print \"Prior John_Calls: \" + str(fe.calculate_PriorMarginal([john_calls]))\n",
+      "print \"Prior Baum_Calls: \" + str(fe.calculate_PriorMarginal([baum_calls]))\n",
+      "print \"Prior Burglary: \" + str(fe.calculate_PriorMarginal([burglary]))\n",
+      "print \"Prior Earthquake: \" + str(fe.calculate_PriorMarginal([earthquake]))\n",
+      "\n",
+      "# Probability of Evidence f\u00fcr zwei Evidenz-Beispiele\n",
+      "print \"PoE Earthquake: \" + str(fe.calculate_PoE([(earthquake, \"Calm\")]))\n",
+      "print \"PoE BaumCalls is Calling: \" + str(fe.calculate_PoE([(baum_calls, \"Calling\")]))\n",
+      "\n",
+      "# a-posteriori (mit gegebener Evidenz f\u00fcr alarm und earthquake)\n",
+      "print \"Posterior of burglary : \" + str(fe.calculate_PosteriorMarginal([burglary],[(alarm, \"Ringing\"),(earthquake, \"Calm\")]))\n",
+      "print \"Posterior of alarm: \" + str(fe.calculate_PosteriorMarginal([alarm],[(burglary, \"Intruder\")]))\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2 id=\"ftree\">Exakte Inferenz - Factor Tree</h2>\n",
+      "<i>(zuerst den Beispiel-Code aus <a href=\"#inferenz\">Exakte Inferenz</a> ausf\u00fchren)</i><br>\n",
+      "<i>FactorTree</i> ist bei gro\u00dfen Netzen und/oder vielen Anfragen effizienter als <i>EasiestFactorElimination</i>. Die erste Anfrage ist zwar aufw\u00e4ndig, daf\u00fcr werden die folgenden Anfragen viel schneller verarbeitet.\n",
+      "Nach ver\u00e4nderter Evidenz m\u00fcssen zun\u00e4chst alle zwischengespeicherten Werte neu berechnet werden, was wieder relativ teuer ist, anschlie\u00dfend jedoch wieder zu schnellerer Verarbeitung f\u00fchrt.\n",
+      "\n",
+      "Im Beispiel werden die Berechungen f\u00fcr Marginals, sowohl a-priori sowie a-posteriori und die Berechnung der Evidenzwahrscheinlichkeit (<i>Probability of Evidence</i>) dargestellt."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "from primo.inference.factor import FactorTreeFactory\n",
+      "\n",
+      "# Initialisieren\n",
+      "factorTreeFactory = FactorTreeFactory()\n",
+      "\n",
+      "# Netz setzen\n",
+      "factorTree = factorTreeFactory.create_greedy_factortree(bn)\n",
+      "\n",
+      "# Grafische Ausgabe des FactorTrees\n",
+      "factorTree.draw()\n",
+      "\n",
+      "# a-priori\n",
+      "print \"Prior Marginals:\"\n",
+      "print \"AlarmFT: \" + str(factorTree.calculate_marginal([alarm]))\n",
+      "print \"John_CallsFT: \" + str(factorTree.calculate_marginal([john_calls]))\n",
+      "print \"Baum_CallsFT: \" + str(factorTree.calculate_marginal([baum_calls]))\n",
+      "print \"BurglaryFT: \" + str(factorTree.calculate_marginal([burglary]))\n",
+      "print \"EarthquakeFT: \" + str(factorTree.calculate_marginal([earthquake]))\n",
+      "\n",
+      "# Evidenz setzen\n",
+      "factorTree.set_evidences([(alarm, \"Ringing\"),(earthquake, \"Calm\")])\n",
+      "\n",
+      "# Probability of Evidence\n",
+      "print \"PoE: \" + str(factorTree.calculate_PoE())\n",
+      "\n",
+      "# a-posteriori (mit gegebener Evidenz)\n",
+      "print \"Posterior Marginal (alarm->ringing , earthquake->calm):\"\n",
+      "print \"AlarmFT: \" + str(factorTree.calculate_marginal([alarm]))\n",
+      "print \"John_CallsFT: \" + str(factorTree.calculate_marginal([john_calls]))\n",
+      "print \"Baum_CallsFT: \" + str(factorTree.calculate_marginal([baum_calls]))\n",
+      "print \"BurglaryFT: \" + str(factorTree.calculate_marginal([burglary]))\n",
+      "print \"EarthquakeFT: \" + str(factorTree.calculate_marginal([earthquake]))\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/ipython-notebook-tutorial/chapters/belief_networks.ipynb b/doc/ipython-notebook-tutorial/chapters/belief_networks.ipynb
new file mode 100755
index 0000000..7f857a8
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/chapters/belief_networks.ipynb
@@ -0,0 +1,248 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:65a93d4eac3d4833aa873df158fb44763b355addcdd8fd960ef8780af43c06c1"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "#Belief Networks\n",
+      "* <a href=\"#aufbau\">Aufbau und Struktur</a><br>\n",
+      "* <a href=\"#cpt\">CPTs</a><br>\n",
+      "* <a href=\"#valid\">Validit\u00e4t</a><br>\n",
+      "* <a href=\"#xbif\">Aus XMLBIF-Format laden</a><br>\n"
+     ]
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2 id=\"aufbau\">Aufbau und Struktur</h2>\n",
+      "Im ersten Beispiel zu Belief Networks wird ein minimales Bayesnetz erstellt. Es gibt einen Knoten *A* und einen Knoten *B*, beide enthalten die Werte `true` und `false`. Es exisiert eine gerichtete Kante von *A* nach *B*.\n",
+      "In der grafischen Darstellung unter dem Code-Beispiel ist die Richtung der Kante am dickeren Ende erkennbar, welche auf den Kindknoten zeigt."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%matplotlib inline\n",
+      "from  primo.networks import BayesianNetwork\n",
+      "from  primo.nodes import DiscreteNode\n",
+      "\n",
+      "# Bayesnetz initialisieren\n",
+      "bn = BayesianNetwork()\n",
+      "\n",
+      "# Knoten erstellen\n",
+      "A = DiscreteNode(\"A\", [\"true\",\"false\"])\n",
+      "B = DiscreteNode(\"B\", [\"true\", \"false\"])\n",
+      "\n",
+      "# Knoten zum Bayesnetz hinzuf\u00fcgen\n",
+      "bn.add_node(A)\n",
+      "bn.add_node(B)\n",
+      "\n",
+      "# Eine Kante einf\u00fcgen\n",
+      "bn.add_edge(A,B)\n",
+      "\n",
+      "# Netzwerk in Textform darstellen\n",
+      "print \"Netzwerk in Textform:\", bn.node_lookup\n",
+      "\n",
+      "# Netzwerk grafisch darstellen\n",
+      "bn.draw()\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2 id=\"cpt\">CPTs</h2>\n",
+      "Das n\u00e4chste Beispiel mit vier Knoten zeigt zus\u00e4tzlich, wie CPTs zu den Knoten hinzugef\u00fcgt werden. Diese k\u00f6nnen entweder direkt als numpy-array gesetzt werden, alternativ werden die Werte f\u00fcr jede Variablenbelegung einzeln \u00fcbergeben."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%matplotlib inline\n",
+      "from  primo.networks import BayesianNetwork\n",
+      "from  primo.nodes import DiscreteNode\n",
+      "import numpy\n",
+      "\n",
+      "# Bayesnetz initialisieren\n",
+      "bn = BayesianNetwork()\n",
+      "\n",
+      "# Knoten erstellen\n",
+      "A = DiscreteNode(\"A\", [\"true\",\"false\"])\n",
+      "B = DiscreteNode(\"B\", [\"true\", \"false\"])\n",
+      "C = DiscreteNode(\"C\", [\"true\", \"false\"])\n",
+      "D = DiscreteNode(\"D\", [\"true\", \"false\"])\n",
+      "\n",
+      "# Knoten zum Bayesnetz hinzuf\u00fcgen\n",
+      "bn.add_node(A)\n",
+      "bn.add_node(B)\n",
+      "bn.add_node(C)\n",
+      "bn.add_node(D)\n",
+      "\n",
+      "# Kanten einf\u00fcgen\n",
+      "bn.add_edge(A,B)\n",
+      "bn.add_edge(A,C)\n",
+      "bn.add_edge(B,D)\n",
+      "bn.add_edge(C,D)\n",
+      "\n",
+      "\n",
+      "# A h\u00e4ngt von keinem weiteren Knoten ab. Ein einfaches Beispiel, wie man den CPT diret als numpy-array setzen kann.\n",
+      "cpt_A = numpy.array([0.001,0.999])\n",
+      "A.set_probability_table(cpt_A,[A])\n",
+      "\n",
+      "\n",
+      "# Setzen der CPT-Werte f\u00fcr B gegeben A\n",
+      "B.set_probability(0.9,[(A,\"true\"),(B,\"true\")])\n",
+      "B.set_probability(0.1,[(A,\"true\"),(B,\"false\")])\n",
+      "B.set_probability(0.25,[(A,\"false\"),(B,\"true\")])\n",
+      "B.set_probability(0.75,[(A,\"false\"),(B,\"false\")])\n",
+      "\n",
+      "\n",
+      "# Setzen der CPT-Werte f\u00fcr C gegeben A\n",
+      "C.set_probability(0.66,[(A,\"true\"),(C,\"true\")])\n",
+      "C.set_probability(0.34,[(A,\"true\"),(C,\"false\")])\n",
+      "C.set_probability(0.9,[(A,\"false\"),(C,\"true\")])\n",
+      "C.set_probability(0.1,[(A,\"false\"),(C,\"false\")])\n",
+      "\n",
+      "# Setzen der CPT-Werte f\u00fcr D gegeben B und C\n",
+      "D.set_probability(0.55,[(B,\"true\"),(C,\"true\"),(D,\"true\")])\n",
+      "D.set_probability(0.45,[(B,\"true\"),(C,\"true\"),(D,\"false\")])\n",
+      "D.set_probability(0.23,[(B,\"true\"),(C,\"false\"),(D,\"true\")])\n",
+      "D.set_probability(0.77,[(B,\"true\"),(C,\"false\"),(D,\"false\")])\n",
+      "D.set_probability(0.15,[(B,\"false\"),(C,\"true\"),(D,\"true\")])\n",
+      "D.set_probability(0.85,[(B,\"false\"),(C,\"true\"),(D,\"false\")])\n",
+      "D.set_probability(0.99,[(B,\"false\"),(C,\"false\"),(D,\"true\")])\n",
+      "D.set_probability(0.01,[(B,\"false\"),(C,\"false\"),(D,\"false\")])\n",
+      "\n",
+      "# Ausgabe der CPTs (hei\u00dft hier cpd...)\n",
+      "print \"CPT des Knoten A:\", A.cpd, \"\\n\"\n",
+      "print \"CPT des Knoten B:\", B.cpd, \"\\n\"\n",
+      "print \"CPT des Knoten C:\", C.cpd, \"\\n\"\n",
+      "print \"CPT des Knoten D:\", D.cpd, \"\\n\"\n",
+      "\n",
+      "# Netzwerk in Textform darstellen\n",
+      "print \"Netzwerk in Textform:\", bn.node_lookup\n",
+      "\n",
+      "# Netzwerk grafisch darstellen\n",
+      "bn.draw()\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2 id=\"valid\">Validit\u00e4t</h2>\n",
+      "Im n\u00e4chsten Beispiel wird ein Knoten hinzugef\u00fcgt, welcher ein Netz aus drei Knoten zu einem zyklischen Graphen macht. Der Graph l\u00e4sst sich zwar erstellen, die Abfrage `is_valid()` gibt jedoch `False` zur\u00fcck, da das Netz nicht mehr azyklisch (und damit nicht mehr valide) ist.\n",
+      "\n",
+      "Weiter werden M\u00f6glichkeiten dargestellt, warum Knoten m\u00f6glicherweise nicht valide sind. Dies ist des Fall, wenn kein CPT gesetzt ist, bzw. dieses Fehler enth\u00e4lt."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%matplotlib inline\n",
+      "from  primo.networks import BayesianNetwork\n",
+      "from  primo.nodes import DiscreteNode\n",
+      "import numpy\n",
+      "\n",
+      "# Bayesnetz initialisieren\n",
+      "bn = BayesianNetwork()\n",
+      "\n",
+      "# Knoten erstellen\n",
+      "A = DiscreteNode(\"A\", [\"true\",\"false\"])\n",
+      "B = DiscreteNode(\"B\", [\"true\", \"false\"])\n",
+      "C = DiscreteNode(\"C\", [\"true\", \"false\"])\n",
+      "\n",
+      "# Knoten zum Bayesnetz hinzuf\u00fcgen\n",
+      "bn.add_node(A)\n",
+      "bn.add_node(B)\n",
+      "bn.add_node(C)\n",
+      "\n",
+      "# Kanten einf\u00fcgen\n",
+      "bn.add_edge(A,B)\n",
+      "bn.add_edge(B,C)\n",
+      "bn.add_edge(C,A)\n",
+      "\n",
+      "# F\u00fcr A ist kein CPT gesetzt, daher ist A NICHT valide\n",
+      "print \"Ist A valide?\", A.is_valid()\n",
+      "\n",
+      "# FALSCHES Setzen der CPT-Werte f\u00fcr B gegeben A\n",
+      "# 1. Die Summe der Wahrscheinlichkeiten f\u00fcr einen Fall muss immer 1 sein (hier ist 0.9+0.2=1.1)\n",
+      "B.set_probability(0.9,[(A,\"true\"),(B,\"true\")])\n",
+      "B.set_probability(0.2,[(A,\"true\"),(B,\"false\")])\n",
+      "\n",
+      "# 2. Die Belegungen m\u00fcssen vollst\u00e4ndig definiert sein\n",
+      "B.set_probability(0.25,[(A,\"false\"),(B,\"true\")])\n",
+      "# Die Belegungen m\u00fcssen vollst\u00e4ndig definiert sein\n",
+      "#B.set_probability(0.75,[(A,\"false\"),(B,\"false\")])\n",
+      "\n",
+      "\n",
+      "print \"Ist B valide?\", B.is_valid()\n",
+      "\n",
+      "\n",
+      "# Setzen der CPT-Werte f\u00fcr C gegeben B\n",
+      "# Hier ist ALLES IN ORDNUNG\n",
+      "C.set_probability(0.66,[(B,\"true\"),(C,\"true\")])\n",
+      "C.set_probability(0.34,[(B,\"true\"),(C,\"false\")])\n",
+      "C.set_probability(0.9,[(B,\"false\"),(C,\"true\")])\n",
+      "C.set_probability(0.1,[(B,\"false\"),(C,\"false\")])\n",
+      "print \"Ist C valide?\", C.is_valid()\n",
+      "\n",
+      "\n",
+      "# Netzwerk grafisch darstellen\n",
+      "bn.draw()\n",
+      "\n",
+      "print \"Ist das ein valider Graph?\", bn.is_valid()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2 id=\"xbif\">Aus XMLBIF-Format laden</h2>\n",
+      "XMLBIF ist ein XML-Format f\u00fcr Bayesnetze. Eine detaillierte Erkl\u00e4rung ist hier zu finden: [http://www.cs.cmu.edu/~fgcozman/Research/InterchangeFormat/](http://www.cs.cmu.edu/~fgcozman/Research/InterchangeFormat/)\n",
+      "\n",
+      "Das Laden aus einer XMLBIF-Datei verk\u00fcrzt den Code im Vergleich zu den von Hand erstellten Netzen weiter oben enorm. Die Methode `XMLBIF.read()` gibt direkt ein Netz zur\u00fcck. Im folgenden Code wird das *Slippery-*Beispiel aus *A. Darwiche - Modeling and Reasoning with Bayesian Networks, S. 57* geladen:\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "from primo.io import XMLBIF\n",
+      "\n",
+      "# Construct a DynmaicBayesianNetwork\n",
+      "bn = XMLBIF.read(\"xml/slippery.xbif\")\n",
+      "\n",
+      "# Netzwerk in Textform darstellen\n",
+      "print \"Netzwerk in Textform:\", bn.node_lookup"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/ipython-notebook-tutorial/chapters/dbn.ipynb b/doc/ipython-notebook-tutorial/chapters/dbn.ipynb
new file mode 100644
index 0000000..00bf0c5
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/chapters/dbn.ipynb
@@ -0,0 +1,208 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:4c4ddf6d6d8303f0a44a42263d6888fadfa3c20f03de1f40c70658f3c43c9f06"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h1>Dynamische Bayesnetze (DBN)</h1>\n",
+      "<p>\n",
+      "Mit Dynamischen Bayesnetzen lassen sich Netze mit zeitlicher Abh\u00e4ngigkeit modellieren. Das Beispiel, welches hier verwendet wird, ist angelehnt an D. Barber - Bayesian Reasoning and Machine Learning\n",
+      ", S. 466ff(DRAFT Feb. 2014), wo es darum geht, dass ein Roboter einen Belief \u00fcber seine aktuelle Position hat. In der folgenden Abbildung des Beispiels ist x die letzte Position und x0 die aktuelle Position. Die Variable door kann interpretiert werden als Hindernis, welches zu durchqueren ist.\n",
+      "\n",
+      "<img src=\"img/dbn.png\" />\n",
+      "</p>\n",
+      "\n",
+      "<h2>Evidenzfunktion</h2>\n",
+      "<p>\n",
+      "Vor der eigentlichen Anwendung der *PRIMO*-Methoden f\u00fcr Dynamische Bayesnetze muss eine Evidenzfunktion definiert werden, welche als Eingabeparameter f\u00fcr das Particle Filtering ben\u00f6tigt wird. Dabei handelt es sich neben der Evidenzfunktion selbst um Unteraufrife, welche die n\u00e4chste Position und Evidenz in einzelnen Samples simulieren sollen. Die Variablen f\u00fcr die Evidenz sowie die aktuelle und die letzte Position sollen \u00fcber alle Iterationsschritte erhalten bleiben und sind daher global deklariert.\n",
+      "</p>"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Die aktuelle und letzte Position sowie die Evidenz werden in jedem Schritt\n",
+      "# ben\u00f6tigt, deshalb werden sie global verwendet.\n",
+      "currentPosition = 0\n",
+      "lastPosition = 0\n",
+      "evidence = {}\n",
+      "\n",
+      "# Die Evidenzfunktion wird f\u00fcr das Particle Filtering ben\u00f6tigt\n",
+      "def get_evidence_function():\n",
+      "    global currentPosition\n",
+      "    global evidence\n",
+      "    simulate_evidence()\n",
+      "    simulate_next_pos()\n",
+      "    return evidence\n",
+      "    \n",
+      "# Simulation f\u00fcr die n\u00e4chste Position\n",
+      "def simulate_next_pos():\n",
+      "    global currentPosition\n",
+      "    global lastPosition\n",
+      "    lastPosition = currentPosition\n",
+      "    random_pos = random.random()\n",
+      "    if random_pos >= 0.1:\n",
+      "        currentPosition = currentPosition + 1\n",
+      "    elif random_pos >= 0.05:\n",
+      "        currentPosition = currentPosition\n",
+      "    else:\n",
+      "        currentPosition =  currentPosition - 1\n",
+      "    if currentPosition == 10:\n",
+      "        currentPosition = 0\n",
+      "    if currentPosition == -1:\n",
+      "        currentPosition = 9\n",
+      "\n",
+      "# Simulation der Evidenz  \n",
+      "def simulate_evidence():\n",
+      "    global currentPosition\n",
+      "    global evidence\n",
+      "    global door\n",
+      "    err = False\n",
+      "    if random.random() > 0.99:\n",
+      "        err = True\n",
+      "    if currentPosition == 1 or currentPosition == 3 or currentPosition == 7:\n",
+      "        if err:\n",
+      "            evidence = {door:\"False\"}\n",
+      "        else:\n",
+      "            evidence = {door:\"True\"}\n",
+      "    else:\n",
+      "        if err:\n",
+      "            evidence = {door:\"True\"}\n",
+      "        else:\n",
+      "            evidence = {door:\"False\"}"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2>Netz laden und DBN initialisieren</h2>\n",
+      "Nachdem nun die Evidenzfunktion gegeben ist, kann auf die eigentliche Funktionalit\u00e4t eingegangen werden. Das Netz wird dazu aus einer XBIF Datei geladen."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "from primo.networks import DynamicBayesianNetwork\n",
+      "from primo.networks import BayesianNetwork\n",
+      "from primo.networks import TwoTBN\n",
+      "from primo.io import XMLBIF\n",
+      "from primo.nodes import DiscreteNode\n",
+      "\n",
+      "import numpy\n",
+      "\n",
+      "\n",
+      "# Initialisieren\n",
+      "# DBN\n",
+      "dbn = DynamicBayesianNetwork()\n",
+      "# BN zum Zeitpunkt 0\n",
+      "B0 = BayesianNetwork()\n",
+      "# Zeitschritt-BN\n",
+      "twoTBN = TwoTBN(XMLBIF.read(\"xml/Robot_Localization.xmlbif\"))\n",
+      "\n",
+      "# Knoten in Variablen packen (door wird sp\u00e4ter f\u00fcr Evidenz ben\u00f6tigt)\n",
+      "x0 = twoTBN.get_node(\"x0\")\n",
+      "x = twoTBN.get_node(\"x\")\n",
+      "door = twoTBN.get_node(\"door\")\n",
+      "\n",
+      "# Initialisierung der zeitabh\u00e4ngigen Knoten im Zeitschritt-BN\n",
+      "twoTBN.set_initial_node(x0.name, x.name)\n",
+      "\n",
+      "# Die f\u00fcr twoTBN eingelesenen Knoten werden auf das BN f\u00fcr Zeitpunkt 0 \u00fcbertragen\n",
+      "x0_init = DiscreteNode(x0.name, [\"p0\", \"p1\", \"p2\", \"p3\", \"p4\", \"p5\", \"p6\", \"p7\", \"p8\", \"p9\"])\n",
+      "B0.add_node(x0_init)\n",
+      "cpt_x0_init = numpy.array([.1, .1, .1, .1, .1, .1, .1, .1, .1, .1])\n",
+      "x0_init.set_probability_table(cpt_x0_init, [x0_init])\n",
+      "\n",
+      "# Die Netze f\u00fcr den Startpunkt und weitere Iterationsschritte werden gesetzt.\n",
+      "dbn.B0 = B0\n",
+      "dbn.twoTBN = twoTBN"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2>Berechnen mit Particle Filtering</h2>\n",
+      "Nachdem das Dynamische Bayesnetz nun initialisiert ist, kann darauf gearbeitet werden. Die Funktion `particle_filtering` ben\u00f6tigt vier Parameter:<ul>\n",
+      "<li>dbn: das Dynamische Bayesnetz selbst</li>\n",
+      "<li>N: Anzahl der Samples</li>\n",
+      "<li>T: Zeitschritte (-1 f\u00fcr unendlich)</li>\n",
+      "<li>GEF: die Evidenzfunktion</li>\n",
+      "</ul>\n",
+      "Das Ergebnis daraus ist eine Liste von Samples, welches im nachfolgenden Beispiel aufaddiert und normalisiert werden, um daraus Wahrscheinlichkeiten zu erhalten.\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "from primo.inference import particlefilter as pf\n",
+      "\n",
+      "import random\n",
+      "\n",
+      "\n",
+      "N = 1000 # Anzahl der Samples\n",
+      "T = 10 # Zeitschritte\n",
+      "GEF = get_evidence_function # Evidenzfunktion\n",
+      "\n",
+      "# Particle Filtering\n",
+      "result = pf.particle_filtering_DBN(dbn, N, T, GEF)\n",
+      "\n",
+      "# Auswertung der einzelnen Samples des Ergebnisses\n",
+      "for samples in result:\n",
+      "    w_hit = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\n",
+      "    for sample in samples:\n",
+      "        state = sample.get_state()\n",
+      "        x_ = x\n",
+      "        if x not in state:\n",
+      "            x_ = x0_init\n",
+      "        if state[x_] == \"p0\":\n",
+      "            w_hit[0] += 1\n",
+      "        if state[x_] == \"p1\":\n",
+      "            w_hit[1] += 1\n",
+      "        if state[x_] == \"p2\":\n",
+      "            w_hit[2] += 1\n",
+      "        if state[x_] == \"p3\":\n",
+      "            w_hit[3] += 1\n",
+      "        if state[x_] == \"p4\":\n",
+      "            w_hit[4] += 1\n",
+      "        if state[x_] == \"p5\":\n",
+      "            w_hit[5] += 1\n",
+      "        if state[x_] == \"p6\":\n",
+      "            w_hit[6] += 1\n",
+      "        if state[x_] == \"p7\":\n",
+      "            w_hit[7] += 1\n",
+      "        if state[x_] == \"p8\":\n",
+      "            w_hit[8] += 1\n",
+      "        if state[x_] == \"p9\":\n",
+      "            w_hit[9] += 1\n",
+      "    prob = [w / N for w in w_hit]\n",
+      "    print \"Real position: \" + str(lastPosition) + \" (Door: \" + str(evidence[door]) + \")\"\n",
+      "    print str(prob)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/ipython-notebook-tutorial/chapters/decision_networks.ipynb b/doc/ipython-notebook-tutorial/chapters/decision_networks.ipynb
new file mode 100644
index 0000000..2385de8
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/chapters/decision_networks.ipynb
@@ -0,0 +1,127 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:5522c7bc0fd005f67da9ee2a1f19d3e74c1540436271cf6c3d2dcc50c23acdd2"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h1>Decision Networks</h1>\n",
+      "<p>\n",
+      "In diesem kurzen Beispiel (aus D. Barber - Bayesian Reasoning and Machine Learning\n",
+      ", S. 128, DRAFT Feb.2014) wird die Entscheidung modelliert, ob es sich lohnt ein PhD-Studium aufzunehmen.\n",
+      "<img src=\"img/decnet.png\" />\n",
+      "</p>\n",
+      "<p>\n",
+      "Neben den <i>Chance Node</i> (oval) der Belief Networks kann ein <i>Decision</i> Network au\u00dferdem <i>Decision Nodes</i> (rechteckig) und <i>Utility Nodes</i> (oval) enthalten.\n",
+      "</p>\n",
+      "<p>\n",
+      "Das oben dargestellte Beispiel ist im folgenden Code implementiert. In der Klasse `MakeDecision` ist der MaxSum-Algorithmus implementiert (`MakeDecision.max_sum()`), welcher nach dem MEU-Prinzip die beste Entscheidung liefert.\n",
+      "</p>\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%matplotlib inline\n",
+      "from primo.networks import BayesianDecisionNetwork\n",
+      "from primo.nodes import DecisionNode\n",
+      "from primo.nodes import UtilityNode\n",
+      "from primo.nodes import DiscreteNode\n",
+      "from primo.inference.decision import MakeDecision\n",
+      "import numpy\n",
+      "\n",
+      "# Netz initialisieren\n",
+      "bdn = BayesianDecisionNetwork()\n",
+      "\n",
+      "# Knoten anlegen\n",
+      "education = DecisionNode(\"education\", [\"do Phd\", \"no Phd\"]) # E\n",
+      "cost = UtilityNode(\"cost\") # Uc\n",
+      "prize = DiscreteNode(\"prize\", [\"prize\", \"no prize\"]) # P\n",
+      "income = DiscreteNode(\"income\", [\"low\", \"average\", \"high\"]) # I\n",
+      "benefit = UtilityNode(\"benefit\") # Ub\n",
+      "startup = DecisionNode(\"startUp\", [\"do startUp\", \"no startUp\"]) # S\n",
+      "costStartup = UtilityNode(\"costStartup\") # Us\n",
+      "\n",
+      "bdn.add_node(education)\n",
+      "bdn.add_node(cost)\n",
+      "bdn.add_node(prize)\n",
+      "bdn.add_node(income)\n",
+      "bdn.add_node(benefit)\n",
+      "bdn.add_node(startup)\n",
+      "bdn.add_node(costStartup)\n",
+      "\n",
+      "# Kanten anlegen\n",
+      "bdn.add_edge(education, cost)\n",
+      "bdn.add_edge(education, prize)\n",
+      "bdn.add_edge(prize, startup)\n",
+      "bdn.add_edge(startup, income)\n",
+      "bdn.add_edge(startup, costStartup)\n",
+      "bdn.add_edge(prize, income)\n",
+      "bdn.add_edge(income, benefit)\n",
+      "\n",
+      "# Utilities definieren\n",
+      "costut=numpy.array([-50000, 0])\n",
+      "cost.set_utility_table(costut, [education])\n",
+      "\n",
+      "benefitut=numpy.array([100000,200000,500000])\n",
+      "benefit.set_utility_table(benefitut,[income])\n",
+      "\n",
+      "startuput=numpy.array([-20000,0])\n",
+      "costStartup.set_utility_table(startuput,[startup])\n",
+      "\n",
+      "# Setzen der CPT-Werte f\u00fcr Knoten mit Eltern\n",
+      "income.set_probability(0.1,[(income,\"low\"),(startup,\"do startUp\"), (prize,\"no prize\")])\n",
+      "income.set_probability(0.2,[(income,\"low\"),(startup,\"no startUp\"), (prize,\"no prize\")])\n",
+      "income.set_probability(0.005,[(income,\"low\"),(startup,\"do startUp\"), (prize,\"prize\")])\n",
+      "income.set_probability(0.005,[(income,\"low\"),(startup,\"no startUp\"), (prize,\"prize\")])\n",
+      "income.set_probability(0.5,[(income,\"average\"),(startup,\"do startUp\"), (prize,\"no prize\")])\n",
+      "income.set_probability(0.6,[(income,\"average\"),(startup,\"no startUp\"), (prize,\"no prize\")])\n",
+      "income.set_probability(0.005,[(income,\"average\"),(startup,\"do startUp\"), (prize,\"prize\")])\n",
+      "income.set_probability(0.015,[(income,\"average\"),(startup,\"no startUp\"), (prize,\"prize\")])\n",
+      "income.set_probability(0.4,[(income,\"high\"),(startup,\"do startUp\"), (prize,\"no prize\")])\n",
+      "income.set_probability(0.2,[(income,\"high\"),(startup,\"no startUp\"), (prize,\"no prize\")])\n",
+      "income.set_probability(0.99,[(income,\"high\"),(startup,\"do startUp\"), (prize,\"prize\")])\n",
+      "income.set_probability(0.8,[(income,\"high\"),(startup,\"no startUp\"), (prize,\"prize\")])\n",
+      "\n",
+      "prize.set_probability(0.0000001,[(prize,\"prize\"),(education,\"no Phd\")])\n",
+      "prize.set_probability(0.001,[(prize,\"prize\"),(education,\"do Phd\")])\n",
+      "prize.set_probability(0.9999999,[(prize,\"no prize\"),(education,\"no Phd\")])\n",
+      "prize.set_probability(0.999,[(prize,\"no prize\"),(education,\"do Phd\")])\n",
+      "\n",
+      "# Reihenfolge der Decision Nodes/Entscheidungsknoten festlegen\n",
+      "bdn.set_partialOrdering([education, [prize], startup, [income]])\n",
+      "\n",
+      "\n",
+      "\n",
+      "# Klasse f\u00fcr Entscheidungen initialisieren\n",
+      "md = MakeDecision(bdn)\n",
+      "\n",
+      "# a-priori Entscheidung \u00fcber Education\n",
+      "decision = md.max_sum(education)\n",
+      "print \"Entscheidung Education =\", decision[1]\n",
+      "\n",
+      "\n",
+      "# Entscheidung \u00fcber StartUp, nachdem Education auf \"no Phd gesetzt\" ist\n",
+      "education.set_state(decision[1])\n",
+      "start=md.max_sum(startup)\n",
+      "print \"Entscheidung StartUp =\", start[1]\n",
+      "\n",
+      "bdn.draw()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/ipython-notebook-tutorial/chapters/dependency_test.ipynb b/doc/ipython-notebook-tutorial/chapters/dependency_test.ipynb
new file mode 100755
index 0000000..77d47c7
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/chapters/dependency_test.ipynb
@@ -0,0 +1,75 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:3d28b5ba7900703cf14a53b485cf0514346e2a8ce0094c0df330eb3186a8a76e"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "# Download\n",
+      "Herunterladen von <a href=\"https://github.com/hbuschme/PRIMO\">https://github.com/hbuschme/PRIMO</a> oder\n",
+      "```\n",
+      "git clone https://github.com/hbuschme/PRIMO\n",
+      "```\n",
+      "im Zielverzeichnis aufrufen.\n",
+      "\n",
+      "# Installation\n",
+      "Um PRIMO nutzen zu k\u00f6nnen, muss entweder der Ordner *primo* aus dem Hauptordner *PRIMO* in das Projektverzeichnis kopiert werden oder der Umgebungsvariable `PYTHONPATH` muss der Pfad des Ordners, in dem der *primo*-Ordner liegt hinzugef\u00fcgt werden, z.B.:\n",
+      "```\n",
+      "export PYTHONPATH=$PYTHONPATH:/home/juser/path/to/PRIMO\n",
+      "```\n",
+      "\n",
+      "# Dependency-Test\n",
+      "Um zu \u00fcberpr\u00fcfen, ob alle Abh\u00e4ngigkeiten f\u00fcr *PRIMO* erf\u00fcllt sind, kann der nachfolgenden Code genutzt werden.\n",
+      "Wenn das Ausf\u00fchren ohne Fehlermeldung durchl\u00e4uft, ist alles in Ordnung. Andernfalls wird in der Fehlermeldung angezeigt, welches Modul noch fehlt."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "try:\n",
+      "  # logging: wird nur hier zur Ausgabe ben\u00f6tigt\n",
+      "  import logging\n",
+      "\n",
+      "  # Die nachfolgenden Module werden auch von PRIMO ben\u00f6tigt\n",
+      "  import abc\n",
+      "  import copy\n",
+      "  import itertools\n",
+      "  import matplotlib.pyplot\n",
+      "  import networkx\n",
+      "  import numpy\n",
+      "  import operator\n",
+      "  import os\n",
+      "  import random\n",
+      "  import re\n",
+      "  import scipy.stats\n",
+      "  import time\n",
+      "  import unittest\n",
+      "  import xml.dom.minidom\n",
+      "  print \"Es sind alle Abh\u00e4ngigkeiten f\u00fcr PRIMO erf\u00fcllt!\"\n",
+      "except Exception:\n",
+      "  logging.exception(\"FEHLER: Es sind nicht alle Abh\u00e4ngigkeiten f\u00fcr PRIMO erf\u00fcllt!\")\n",
+      "  \n",
+      "    \n",
+      "try:\n",
+      "    import primo\n",
+      "except Exception:\n",
+      "    print \"primo selbst fehlt :(\"\n",
+      "    \n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/ipython-notebook-tutorial/chapters/img/dbn.png b/doc/ipython-notebook-tutorial/chapters/img/dbn.png
new file mode 100644
index 0000000000000000000000000000000000000000..b068c260e53b32a1968b0e30e00038c6fb2f2b68
GIT binary patch
literal 12500
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

literal 0
HcmV?d00001

diff --git a/doc/ipython-notebook-tutorial/chapters/img/decnet.png b/doc/ipython-notebook-tutorial/chapters/img/decnet.png
new file mode 100644
index 0000000000000000000000000000000000000000..4e5b7cdf21d26c0f5fb7cdb3fef1e5336353eeb0
GIT binary patch
literal 21449
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

literal 0
HcmV?d00001

diff --git a/doc/ipython-notebook-tutorial/chapters/img/samiam.png b/doc/ipython-notebook-tutorial/chapters/img/samiam.png
new file mode 100755
index 0000000000000000000000000000000000000000..3d450ba41a4ac63b43463d76aef7d774d08dea2d
GIT binary patch
literal 9110
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

literal 0
HcmV?d00001

diff --git a/doc/ipython-notebook-tutorial/chapters/naive_bayes_ev.ipynb b/doc/ipython-notebook-tutorial/chapters/naive_bayes_ev.ipynb
new file mode 100644
index 0000000..a65c5b1
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/chapters/naive_bayes_ev.ipynb
@@ -0,0 +1,172 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:90a9d74d92118f490bf4db28493687c0ede1b81b9e3ca489862c92c0251b285e"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h1>Naive Bayes + Evidenztheorie</h1>\n",
+      "<p>\n",
+      "Mit dem Naive Bayes-Klassifikator, welcher um die Evidenztheorie (Dempster-Shafer) erweitert wurde, k\u00f6nnen Evidenzen mit einer Unsicherheit angegeben werden. Ist eine Evidenz nicht sicher oder soll sie als weniger wichtig interpretiert werden, kann sie mit einer `confidence` aus dem Wertebereich `[0.0, 1.0]` versehen werden.\n",
+      "</p>\n",
+      "<p>\n",
+      "Im folgenden Beispiel wird ein Klassifikator erstellt, welcher anhand einiger technischer Features einen Gegenstand erkennen soll. Durch die Berechnung der Entropie kann eine Annahme dar\u00fcber getroffen werden, welches Feature im Mittel die Entropie des Wurzelknoten am weitesten einschr\u00e4nkt und somit am ehesten zu einer sicheren Klassifikation f\u00fchrt.\n",
+      "</p>"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# -*- coding: utf-8 -*-\n",
+      "import numpy\n",
+      "import primo.NaiveBayesDS as NBDS\n",
+      "\n",
+      "# NaiveBayesDS initialisieren\n",
+      "nb = NBDS.NaiveBayesDS()\n",
+      "\n",
+      "# Knoten:\n",
+      "# Index 0: Name des Knoten\n",
+      "# Index 1: Werte/Zust\u00e4nde des Knoten\n",
+      "# Index 2 (Wurzelknoten): Verteilung \u00fcber Zust\u00e4nde\n",
+      "# Index 2 (Featureknoten): Vom Wurzelknoten abh\u00e4ngige Wahrscheinlichkeiten, \u00e4hnlich wie CPT bei komplexeren Bayesnetzen\n",
+      "\n",
+      "# Wurzelknoten\n",
+      "rootNode = (\"gegenstand\", [\"server\", \"laptop\", \"smartphone\", \"fernseher\", \"akkuschrauber\"], numpy.array([0.2, 0.2, 0.2, 0.2, 0.2]))\n",
+      "# Featureknoten\n",
+      "nodeBildschirm = (\"bildschirm\", [\"ja\", \"nein\"], numpy.array([[0.01, 0.99], [1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [0.05, 0.95]]))\n",
+      "nodeAntrieb = (\"antrieb\", [\"stromnetz\", \"akku\", \"hybrid\"], numpy.array([[1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]))\n",
+      "nodeInternet = (\"internet\", [\"ja\", \"nein\"], numpy.array([[1.0, 0.0], [0.9, 0.1], [0.8, 0.2], [0.5, 0.5], [0.01, 0.99]]))\n",
+      "nodeEinsatz = (\"einsatz\", [\"buero\", \"heim\", \"mobil\"], numpy.array([[0.98, 0.02, 0.0], [0.3333333333333333, 0.3333333333333333, 0.3333333333333333], [0.05, 0.35, 0.6], [0.0, 1.0, 0.0], [0.02, 0.8, 0.18]]))\n",
+      "nodeFarbe = (\"farbe\", [\"schwarz\", \"grau\", \"blau\", \"gruen\"], numpy.array([ [0.5, 0.5, 0.0, 0.0], [0.6, 0.3, 0.05, 0.05], [0.3, 0.2, 0.3, 0.2], [0.7, 0.25, 0.05, 0.0], [0.25, 0.25, 0.25, 0.25]]))\n",
+      "\n",
+      "# Knoten dem Klassifikator hinzuf\u00fcgen\n",
+      "nb.rootNode = rootNode\n",
+      "nb.featureNodes.append(nodeBildschirm)\n",
+      "nb.featureNodes.append(nodeAntrieb)\n",
+      "nb.featureNodes.append(nodeInternet)\n",
+      "nb.featureNodes.append(nodeEinsatz)\n",
+      "nb.featureNodes.append(nodeFarbe)\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2>Beispiel 1: Ohne Evidenz</h2>\n",
+      "Im ersten Anwendungsbeispiel ist die Evidenz noch leer. Die Ausgabe liefert Informationen \u00fcber den Wurzelknoten und \u00fcber den Featureknoten \"einsatz\". Die bedingte Entropie des Featureknoten ist zu interpretieren als die Entropie des Wurzelknoten, wenn der Featureknoten gegeben w\u00e4re."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Keine Evidenz setzen\n",
+      "# Harte Evidenz bestimmen\n",
+      "evidence = []\n",
+      "nb.evidence = evidence\n",
+      "\n",
+      "print \"++++ Ohne Evidenz ++++\"\n",
+      "print nb.rootNode[0], \"(Root)\"\n",
+      "print nb.rootNode[1]\n",
+      "print nb.calcRootDistribution()\n",
+      "print \"Entropie\", nb.getRootEntropy()\n",
+      "print \"---- ----\"\n",
+      "print nodeEinsatz[0], \"(Feature)\"\n",
+      "print nodeEinsatz[1]\n",
+      "print nb.calcFeatureDistribution(nodeEinsatz)\n",
+      "print \"Bedingte Entropie\", nb.getConditionalEntropy(nodeEinsatz)\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2>Beispiel 2: Harte Evidenz</h2>\n",
+      "Das n\u00e4chste Anwendungsbeispiel enth\u00e4lt <i>harte</i> Evidenz f\u00fcr zwei Featureknoten\n",
+      "```\n",
+      "(bildschirm=ja mit confidence=1.0; farbe=blau mit confidence=1.0)\n",
+      "```\n",
+      "Im Vergleich zum ersten Beispiel lassen sich Ver\u00e4nderungen in Verteilung und Entropien erkennen."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Harte Evidenz bestimmen\n",
+      "evidence = []\n",
+      "evidence.append([nodeBildschirm, \"ja\", 1.0])\n",
+      "evidence.append([nodeFarbe, \"blau\", 1.0])\n",
+      "nb.evidence = evidence\n",
+      "\n",
+      "print \"++++ Mit Evidenz [bildschirm=ja, farbe=blau] ++++\"\n",
+      "print nb.rootNode[0], \"(Root)\"\n",
+      "print nb.rootNode[1]\n",
+      "print nb.calcRootDistribution()\n",
+      "print \"Entropie\", nb.getRootEntropy()\n",
+      "print \"---- ----\"\n",
+      "print nodeEinsatz[0], \"(Feature)\"\n",
+      "print nodeEinsatz[1]\n",
+      "print nb.calcFeatureDistribution(nodeEinsatz)\n",
+      "print \"Bedingte Entropie\", nb.getConditionalEntropy(nodeEinsatz)\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h2>Beispiel 3: Weiche Evidenz</h2>\n",
+      "Das dritte Anwendungsbeispiel zeigt, wie Unsicherheiten bei der Evidenz angegeben werden k\u00f6nnen:\n",
+      "```\n",
+      "(bildschirm=ja mit confidence=0.8; farbe=blau mit confidence=0.5)\n",
+      "```\n",
+      "In der Ausgabe wird ersichtlich, wie die Verteilungen gegen\u00fcber der harten Evidenz des vorherigen Beispiels gegl\u00e4ttet werden und auch die Entropie etwas gr\u00f6\u00dfer ist."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Unsichere Evidenz bestimmen\n",
+      "evidence = []\n",
+      "evidence.append([nodeBildschirm, \"ja\", 0.8])\n",
+      "evidence.append([nodeFarbe, \"blau\", 0.5])\n",
+      "nb.evidence = evidence\n",
+      "\n",
+      "print \"++++ Mit unsicherer Evidenz [bildschirm=ja(0.8), farbe=blau(0.5)] ++++\"\n",
+      "print nb.rootNode[0], \"(Root)\"\n",
+      "print nb.rootNode[1]\n",
+      "print nb.calcRootDistribution()\n",
+      "print \"Entropie\", nb.getRootEntropy()\n",
+      "print \"---- ----\"\n",
+      "print nodeEinsatz[0], \"(Feature)\"\n",
+      "print nodeEinsatz[1]\n",
+      "print nb.calcFeatureDistribution(nodeEinsatz)\n",
+      "print \"Bedingte Entropie\", nb.getConditionalEntropy(nodeEinsatz)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/ipython-notebook-tutorial/index.ipynb b/doc/ipython-notebook-tutorial/index.ipynb
new file mode 100755
index 0000000..6bec841
--- /dev/null
+++ b/doc/ipython-notebook-tutorial/index.ipynb
@@ -0,0 +1,71 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:9c9890f254a26ad9bf5f2dd21bc36508e7fbe48692ea074f1f4d7f030748a31b"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "<h1>PRIMO - [PR]obabilistic [I]nference [MO]dules</h1>\n",
+      "\n",
+      "<h2>Inhalt</h2>\n",
+      "In <i>PRIMO</i> werden verschiedene Varianten von Bayesnetzen implementiert. Im Folgenden werden alle Varianten an Code-Beispielen vorgestellt. Es wird jeweils gezeigt, wie sie erstellt und modifiziert werden und wie man auf ihnen Inferenzen ziehen und weitere Funktionen anwenden kann.\n",
+      "\n",
+      "<ul>\n",
+      "<li><a href=\"chapters/dependency_test.ipynb\">Installation und Dependency-Test:</a><br>\n",
+      "Eine kurze Anleitung, wie man *PRIMO* in ein Python-Projekt einbindet und ein Check, ob alle ben\u00f6tigten Python-Module installiert sind.\n",
+      "</li>\n",
+      "<br>\n",
+      "\n",
+      "<li><a href=\"chapters/belief_networks.ipynb\">Belief Networks:</a><br>\n",
+      "Belief Networks sind die \"herk\u00f6mmlich\" Form von Bayesnetzen. Sie modellieren die kausalen Beziehungen zwischen mehreren Variablen in einem gerichteten Graphen:<br>\n",
+      "  <ul>\n",
+      "  \n",
+      "    <br><li> <a href=\"chapters/belief_exact.ipynb\">Belief Networks - Exakte Inferenz:</a><br>\n",
+      "    *PRIMO* bietet zwei verschiedene Funktionen zur exakten Inferenz, also der Berechnung von Wahrscheinlichkeitsverteilungen, sowohl a-priori (ohne Evidenz) als auch a-posteriori (mit Evidenz). \n",
+      "    </li>\n",
+      "    \n",
+      "    <br><li> <a href=\"chapters/belief_approx.ipynb\">Belief Networks - Approximierte Inferenz:</a><br>\n",
+      "    Mit MCMC enth\u00e4lt *PRIMO* au\u00dferdem ein approximatives Sampling-Verfahren, mit dem ebenfalls Inferenzen und eine MAP-Hypothese berechnet werden k\u00f6nnen.\n",
+      "    </li>\n",
+      "    \n",
+      "    <br><li> <a href=\"chapters/belief_cont.ipynb\">Belief Networks - Kontinuierliche Variablen:</a><br>\n",
+      "    Das MCMC-Verfahren bietet au\u00dferdem die M\u00f6glichkeit, auf Bayesnetzen mit kontinuierlichen Variablen zu arbeiten. In diesem Fall liegen keine diskreten Variablen mit einer festen Menge an Zust\u00e4nden vor, stattdessen enthalten Knoten Wahrscheinlichkeitsverteilungen.\n",
+      "    </li>\n",
+      "    \n",
+      "    </ul>\n",
+      "</li>\n",
+      "\n",
+      "<br><li> <a href=\"chapters/decision_networks.ipynb\">Decision Networks:</a><br>\n",
+      "Decision Networks sind eine erweiterte Form von Bayesnetzen, welche zus\u00e4tzlich <i>Utilities</i> f\u00fcr Entscheidungen enthalten. So k\u00f6nnen Entscheidungssituationen durch ein Decision Network modelliert werden, welches f\u00fcr bestimmte Variablenbelegungen (Entscheidungen) deren Utility berechnet und so die Entscheidung bestimmt werden kann, welche wahrscheinlich den gr\u00f6\u00dften Nutzen (maximale Utility) bietet.\n",
+      "</li>\n",
+      "\n",
+      "<br><li> <a href=\"chapters/dbn.ipynb\">Dynamische Bayesnetze:</a><br>\n",
+      "Dynamische Bayesnetze dienen dazu, ein modelliertes Problem zeitlich anzupassen. Dabei hat der Zustand des vorherigen Zeitschritts jeweils Einfluss auf bestimmte Variablen des aktuellen Zeitschritts (gleiches Prinzip wie HMMs).\n",
+      "</li>\n",
+      "\n",
+      "<br><li> <a href=\"chapters/naive_bayes_ev.ipynb\">Naive Bayes + Evidenztheorie:</a><br>\n",
+      "Einfache Form eines Bayesnetzes mit Erweiterungen f\u00fcr die Angabe von Unsicherheit (weiche Evidenz mittels Dempster-Shafer-Theory) und der Berechnung von Entropien.\n",
+      "</li>\n",
+      "</ul>\n",
+      "\n",
+      "<h2>Sonstiges</h2>\n",
+      "Definition des XBIF-Formats: <a href=\"http://www.cs.cmu.edu/~fgcozman/Research/InterchangeFormat/\">http://www.cs.cmu.edu/~fgcozman/Research/InterchangeFormat/</a>\n",
+      "<br>\n",
+      "Kurzer Guide zum Plotten mit `pyplot` aus `matplotlib`: <a href=\"doc/pyplot.pdf\">pyplot.pdf</a>\n",
+      "<br>\n",
+      "Kurzer Guide zum externen Modul `networkx`: <a href=\"doc/networkx.pdf\">networkx.pdf</a>\n",
+      "<br>"
+     ]
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
-- 
GitLab