diff --git a/exercises/ex08_solution/hartree-fock.ipynb b/exercises/ex08_solution/hartree-fock.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..c538bd0183e512b495c89bc4dcb2a7529b0a68fc
--- /dev/null
+++ b/exercises/ex08_solution/hartree-fock.ipynb
@@ -0,0 +1,389 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Hartree-Fock for Electron Ground States of Atoms and Molecules"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "import scipy.linalg as la\n",
+    "from numpy import dot, pi, exp, sqrt\n",
+    "from scipy.special import erf\n",
+    "\n",
+    "# interactive plots\n",
+    "#%matplotlib notebook\n",
+    "# nice inline plots\n",
+    "%matplotlib inline\n",
+    "\n",
+    "import matplotlib.pyplot as plt\n",
+    "plt.rcParams['figure.figsize'] = 16, 9"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## H Atom"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We approximate the single Slater-type orbital with Gaussian functions, using pre-calculated exponents. This is called the STO-nG basis set (with $n=4$ in our case)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "alpha = [13.00773, 1.962079, 0.444529, 0.1219492]\n",
+    "dim = len(alpha)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Set up and solve the generalised eigenvalue problem $\\sum H_{i,j} d_j = \\epsilon \\sum S_{i,j} d_j$ as derived in the script / exercise session."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "def spq(p,q):\n",
+    "    return (np.pi/(alpha[p]+alpha[q]))**1.5\n",
+    "\n",
+    "def tpq(p,q):\n",
+    "    return 3*alpha[p]*alpha[q]*np.pi**1.5/(alpha[p]+alpha[q])**2.5\n",
+    "\n",
+    "def apq(p,q):\n",
+    "    return -2*np.pi/(alpha[p]+alpha[q])\n",
+    "\n",
+    "h = np.array([[tpq(p,q)+apq(p,q) for q in range(dim)] for p in range(dim)]) # Hamiltonian\n",
+    "s = np.array([[spq(p,q)          for q in range(dim)] for p in range(dim)]) # overlap matrix\n",
+    "energies = la.eigh(h,s,eigvals_only=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Ground state energy [Hartree]: -0.499278405667\n"
+     ]
+    }
+   ],
+   "source": [
+    "print('Ground state energy [Hartree]:', energies[0])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The ground state for the 1s electron of the H atom is known to be $-0.5 E_h$ ($\\simeq 13.6 eV$), which makes the approximation using four Gaussian-type-orbitals decent."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## He Atom"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Again we refer to a published set of GTO basis function exponential coefficients for the He atom (e.g. from https://bse.pnl.gov)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "alpha = [0.297104, 1.236745, 5.749982, 38.216677]\n",
+    "dim = len(alpha)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Build equation (9) from the exercise sheet, derived as shown in the exercise session."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def sij(p,q):\n",
+    "    \"\"\"overlap matrix elements\"\"\"\n",
+    "    return (np.pi/(alpha[p]+alpha[q]))**1.5\n",
+    "\n",
+    "def tij(p,q):\n",
+    "    \"\"\"non-interacting matrix elements\"\"\"\n",
+    "    return 3*alpha[p]*alpha[q]*np.pi**1.5/(alpha[p]+alpha[q])**2.5 - 4*np.pi/(alpha[p]+alpha[q])\n",
+    "\n",
+    "def vijkl(i,j,k,l):\n",
+    "    \"\"\"Hartree matrix elements\"\"\"\n",
+    "    return 2*np.pi**2.5/(alpha[i]+alpha[j])/(alpha[k]+alpha[l])/np.sqrt(alpha[i]+alpha[j]+alpha[k]+alpha[l])\n",
+    "\n",
+    "s = np.array([[sij(p,q) for q in range(dim)] for p in range(dim)]) # overlap matrix\n",
+    "t = np.array([[tij(p,q) for q in range(dim)] for p in range(dim)]) # non-interacting matrix\n",
+    "v = np.array([[[[vijkl(i,j,k,l) for i in range(dim)] for j in range(dim)] \n",
+    "                                for k in range(dim)] for l in range(dim)]) # Hartree matrix\n",
+    "\n",
+    "d = np.ones(dim) # initial coefficient vector\n",
+    "d /= np.sqrt(dot(d,dot(s,d))) # normalize\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Note how in the derivation, we have seen terms of $d$ in the Fock matrix $f$.\n",
+    "Since our equation is not a real generalised eigenvalue problem anymore, we need to emulate one through a self-consistency iteration. Convergence depends heavily on the choice of basis, for which STO-4G is known to behave nicely."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "eps = -0.669956328027\n",
+      "eps = -0.669956328027\n",
+      "Ground state energy [Hartree]: -2.07854760879\n"
+     ]
+    }
+   ],
+   "source": [
+    "tol = 1e-10\n",
+    "eps = 0; oldeps = eps+2*tol\n",
+    "while abs(eps-oldeps) > tol:\n",
+    "    f = t + dot(dot(v,d),d) # Fock operator matrix\n",
+    "    ens,vecs = la.eigh(f,s) # solve GEV problem\n",
+    "    oldeps = eps\n",
+    "    minidx = np.argmin(ens)\n",
+    "    eps = ens[minidx]\n",
+    "    d  = vecs[:,minidx]\n",
+    "    d /= np.sqrt(dot(d,dot(s,d))) # normalize\n",
+    "    print('eps =', eps)\n",
+    "\n",
+    "e0 = 2*dot(dot(t,d),d) + dot(dot(dot(dot(v,d),d),d),d)\n",
+    "print('Ground state energy [Hartree]:', e0)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The experimental value is $-2.903$ Hartree. STO-4G already starts to show inadequacies."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## H$_2$ Molecule"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Let's try to use the exponentials for the GTOs of a single H atom to approximate a H$_2$ molecule."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "alpha = [13.00773, 1.962079, 0.444529, 0.1219492]*2\n",
+    "centr = [0]*4 + [1]*4   # center of basis functions\n",
+    "dim = len(alpha)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Take care to center each set of GTOs around one nucleus at $R_A, R_B$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 32,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "alpha = [13.00773, 1.962079, 0.444529, 0.1219492]*2\n",
+    "centr = [0]*4 + [1]*4   # center of basis functions\n",
+    "dim = len(alpha)\n",
+    "\n",
+    "def kexp(i,j):\n",
+    "    \"\"\"recurring factor in Gaussian overlap integrals\"\"\"\n",
+    "    return exp(-alpha[i]*alpha[j]/(alpha[i]+alpha[j])*(centr[i]-centr[j])**2)\n",
+    "\n",
+    "def rp(i,j):\n",
+    "    \"\"\"weighted center position R_P\"\"\"\n",
+    "    return (alpha[i]*centr[i] + alpha[j]*centr[j])/(alpha[i]+alpha[j])\n",
+    "\n",
+    "def f0(q):\n",
+    "    \"\"\"F_0(q)\"\"\"\n",
+    "    if q == 0: return 1\n",
+    "    return 0.5*sqrt(pi/q)*erf(sqrt(q))\n",
+    "\n",
+    "def sij(i,j):\n",
+    "    \"\"\"overlap matrix elements\"\"\"\n",
+    "    return (pi/(alpha[i]+alpha[j]))**1.5 * kexp(i,j)\n",
+    "\n",
+    "def kinij(i,j):\n",
+    "    \"\"\"kinetic energy matrix element\"\"\"\n",
+    "    a = alpha[i]; b = alpha[j]\n",
+    "    return a*b/(a+b) * (3 - 2*a*b/(a+b)*(centr[i]-centr[j])**2) * (pi/(a+b))**1.5 * kexp(i,j)\n",
+    "\n",
+    "def nucij(i,j,rc):\n",
+    "    \"\"\"nuclear attraction matrix element for nucleus at position rc\"\"\"\n",
+    "    a = alpha[i]; b = alpha[j]\n",
+    "    return -2*pi/(a+b) * kexp(i,j) * f0((a+b)*(rp(i,j)-rc)**2)\n",
+    "\n",
+    "def tij(i,j):\n",
+    "    \"\"\"non-interacting matrix elements\"\"\"\n",
+    "    return kinij(i,j) + nucij(i,j,0) + nucij(i,j,1)\n",
+    "\n",
+    "def vijkl(i,j,k,l):\n",
+    "    \"\"\"Hartree matrix elements\"\"\"\n",
+    "    aij = alpha[i]+alpha[j]\n",
+    "    akl = alpha[k]+alpha[l]\n",
+    "    q = aij*akl/(aij+akl) * (rp(i,j) - rp(k,l))**2\n",
+    "    return 2*sqrt(aij*akl/pi/(aij+akl))*sij(i,j)*sij(k,l)*f0(q)\n",
+    "\n",
+    "s = np.array([[sij(p,q) for q in range(dim)] for p in range(dim)]) # overlap matrix\n",
+    "t = np.array([[tij(p,q) for q in range(dim)] for p in range(dim)]) # non-interacting matrix\n",
+    "v = np.array([[[[vijkl(i,j,k,l) for i in range(dim)] for j in range(dim)] \n",
+    "                                for k in range(dim)] for l in range(dim)]) # Hartree matrix\n",
+    "\n",
+    "d = np.ones(dim) # initial coefficient vector\n",
+    "d /= sqrt(dot(d,dot(s,d))) # normalize"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "eps = -0.669956328027\n",
+      "eps = -0.669956328027\n",
+      "Ground state energy [Hartree]: -1.07854760879\n"
+     ]
+    }
+   ],
+   "source": [
+    "tol = 1e-10\n",
+    "eps = 0; oldeps = eps+2*tol\n",
+    "while abs(eps-oldeps) > tol:\n",
+    "    f = t + dot(dot(v,d),d) # Fock operator matrix\n",
+    "    ens,vecs = la.eigh(f,s) # solve GEV problem\n",
+    "    oldeps = eps\n",
+    "    minidx = np.argmin(ens)\n",
+    "    eps = ens[minidx]\n",
+    "    d  = vecs[:,minidx]\n",
+    "    d /= sqrt(dot(d,dot(s,d))) # normalize\n",
+    "    print('eps =', eps)\n",
+    "\n",
+    "e0 = 1 + 2*dot(dot(t,d),d) + dot(dot(dot(dot(v,d),d),d),d)\n",
+    "print('Ground state energy [Hartree]:', e0)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Don't forget to add 1 to the resulting energy: during derivation, the repulsion of the H cores (which is normalised to $1$ Hartree) has been left out."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.4.3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/exercises/exercise8.pdf b/exercises/exercise8.pdf
index 822d3cf353deacf12b9f1997c45233cad9825aa9..f1935e489fdcf3d0eab0f575e3f23885a55cb8f5 100644
Binary files a/exercises/exercise8.pdf and b/exercises/exercise8.pdf differ