{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import numpy\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Coffee Machine Ideation\n",
    "\n",
    "Parts:\n",
    " * Portafilter - The thing you put coffee in that clamps into place\n",
    " * Group head - Where the portafilter goes. (also called brew head)\n",
    " * Group gasket - O-ring that ensures a seal between group head and portafilter\n",
    " * Reciprocating pump\n",
    " * Heating element\n",
    " * Boiler\n",
    " * Solenoid to switch between steam/group head\n",
    " * Pressure gauge\n",
    " * On/off lights.\n",
    " * Buttons.\n",
    " * Pressure release valve - attached to boiler and dumps water into group head when pressure is high enough.\n",
    " * PSU\n",
    " * Temperature sensors.\n",
    "\n",
    "Failure modes:\n",
    " * Buildup of calcium\n",
    " * Burnout on heating element - mulitimeter to check resistance not infinity\n",
    " * Failed gasket at portafilter\n",
    " * Burst tube\n",
    " * Failed circuit board\n",
    " * Blocked diffusuion plate in group head\n",
    " * Steam wand blocked - poke through ends with wire after removal\n",
    " * Lights failed.\n",
    " * Dial failed.\n",
    " * Motor can melt, but usually a thermal safety sensor.\n",
    " * Power connector comming lose.\n",
    " * No electricity"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Graphical Model\n",
    "\n",
    "Failures:\n",
    " * `P_he:` $P(\\texttt{no electricity})$\n",
    " * `P_fp:` $P(\\texttt{fried psu})$\n",
    " * `P_fc:` $P(\\texttt{fried circuit board})$\n",
    " * `P_wr:` $P(\\texttt{water reservoir empty})$\n",
    " * `P_gs:` $P(\\texttt{group head gasket seal broken})$\n",
    " * `P_dp:` $P(\\texttt{dead pump})$\n",
    " * `P_fh:` $P(\\texttt{fried heating element})$\n",
    "\n",
    " \n",
    "Mechanism:\n",
    " * `P_pw_he_fp:` $P(\\texttt{psu works}\\enspace|\\enspace\\texttt{no electricity},\\enspace\\texttt{fried psu})$\n",
    " * `P_cb_pw_fc:` $P(\\texttt{circuit board works}\\enspace|\\enspace\\texttt{psu works},\\enspace\\texttt{fried circuit board})$\n",
    " * `P_gw_cb_wr_dp:` $P(\\texttt{get water}\\enspace|\\enspace\\texttt{circuit board works},\\enspace\\texttt{water reservoir empty},\\enspace\\texttt{dead pump})$\n",
    "\n",
    "Diagnostic:\n",
    " * `P_ls_he:` $P(\\texttt{lights switch on}\\enspace|\\enspace\\texttt{no electricity})$\n",
    " * `P_vp_pw:` $P(\\texttt{voltage across psu}\\enspace|\\enspace\\texttt{psu works})$\n",
    " * `P_lo_cb:` $P(\\texttt{power light on}\\enspace|\\enspace\\texttt{circuit board works})$\n",
    " * `P_wv_wr:` $P(\\texttt{water visible}\\enspace|\\enspace\\texttt{water reservoir empty})$\n",
    " * `P_hp_dp:` $P(\\texttt{can hear pump}\\enspace|\\enspace\\texttt{dead pump})$\n",
    " * `P_me_gw_gs:` $P(\\texttt{makes espresso}\\enspace|\\enspace\\texttt{get water},\\enspace\\texttt{group head gasket seal broken})$\n",
    " * `P_ta_me_fh:` $P(\\texttt{tasty}\\enspace|\\enspace\\texttt{makes espresso},\\enspace\\texttt{fried heating element})$\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Variable names...\n",
    "names = ['he', 'fp', 'fc', 'wr', 'gs', 'dp', 'fh',\n",
    "         'pw', 'cb', 'gw',\n",
    "         'ls', 'vp', 'lo', 'wv', 'hp', 'me', 'ta']\n",
    "\n",
    "\n",
    "\n",
    "# All conditionals pdfs...\n",
    "P_he          = numpy.array([0.99, 0.01])\n",
    "P_fp          = numpy.array([0.8, 0.2])\n",
    "P_fc          = numpy.array([0.98, 0.02])\n",
    "P_wr          = numpy.array([0.9, 0.1])\n",
    "P_gs          = numpy.array([0.95, 0.05])\n",
    "P_dp          = numpy.array([0.95, 0.05])\n",
    "P_fh          = numpy.array([0.97, 0.03])\n",
    "\n",
    "\n",
    "P_pw_he_fp    = numpy.array([[[0.0, 1.0], [1.0, 1.0]],\n",
    "                             [[1.0, 0.0], [0.0, 0.0]]])\n",
    "P_cb_pw_fc    = numpy.array([[[1.0, 1.0], [0.0, 1.0]],\n",
    "                             [[0.0, 0.0], [1.0, 0.0]]])\n",
    "P_gw_cb_wr_dp = numpy.array([[[[1.0, 1.0], [1.0, 1.0]], [[0.0, 1.0], [0.9, 1.0]]],\n",
    "                             [[[0.0, 0.0], [0.0, 0.0]], [[1.0, 0.0], [0.1, 0.0]]]])\n",
    "\n",
    "P_ls_he       = numpy.array([[0.1, 1.0],\n",
    "                             [0.9, 0.0]])\n",
    "P_vp_pw       = numpy.array([[1.0, 0.01],\n",
    "                             [0.0, 0.99]])\n",
    "P_lo_cb       = numpy.array([[1.0, 0.001],\n",
    "                             [0.0, 0.999]])\n",
    "P_wv_wr       = numpy.array([[0.2, 1.0],\n",
    "                             [0.8, 0.0]])\n",
    "P_hp_dp       = numpy.array([[0.1, 1.0],\n",
    "                             [0.9, 0.0]])\n",
    "P_me_gw_gs    = numpy.array([[[1.0, 1.0], [0.1, 0.9]],\n",
    "                             [[0.0, 0.0], [0.9, 0.1]]])\n",
    "P_ta_me_fh    = numpy.array([[[1.0, 1.0], [0.05, 1.0]],\n",
    "                             [[0.0, 0.0], [0.95, 0.0]]])\n",
    "\n",
    "\n",
    "\n",
    "# Verify they are normalised...\n",
    "for pdf in [P_he, P_fp, P_fc, P_wr, P_gs, P_dp, P_fh,\n",
    "            P_pw_he_fp, P_cb_pw_fc, P_gw_cb_wr_dp,\n",
    "            P_ls_he, P_vp_pw, P_lo_cb, P_wv_wr, P_hp_dp, P_me_gw_gs, P_ta_me_fh]:\n",
    "    totals = pdf.sum(axis=0)\n",
    "    if not numpy.all(numpy.isclose(totals, 1.0)):\n",
    "        print('Bad distribution')\n",
    "        print(pdf)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "he fp fc wr gs dp fh pw cb gw ls vp lo wv hp me ta\n",
      "F  F  F  F  F  F  F  T  T  T  T  T  T  T  T  T  T \n",
      "F  F  F  F  F  F  F  T  T  T  T  T  T  F  T  F  F \n",
      "F  T  F  T  F  F  F  F  F  F  T  F  F  F  T  F  F \n",
      "F  F  F  F  F  F  F  T  T  T  T  T  T  T  T  T  T \n",
      "F  F  F  T  F  F  F  T  T  F  T  T  T  F  T  F  F \n",
      "F  T  F  T  F  F  F  F  F  F  T  F  F  F  T  F  F \n",
      "F  F  F  T  F  F  F  T  T  F  T  T  T  F  T  F  F \n",
      "F  F  F  F  F  T  F  T  T  F  T  T  T  T  F  F  F \n",
      "F  F  F  F  F  F  F  T  T  T  T  T  T  F  T  T  T \n",
      "F  T  F  F  F  F  F  F  F  F  T  F  F  T  T  F  F \n",
      "F  F  F  T  F  T  F  T  T  F  T  T  T  F  F  F  F \n",
      "F  F  F  F  F  F  F  T  T  T  T  T  T  T  T  T  T \n",
      "F  F  F  F  F  F  F  T  T  T  T  T  T  T  T  T  T \n",
      "F  F  F  F  F  F  F  T  T  T  T  T  T  T  F  T  F \n",
      "F  F  F  T  F  F  F  T  T  F  T  T  T  F  T  F  F \n",
      "F  F  F  F  F  F  F  T  T  T  T  T  T  T  T  T  T \n"
     ]
    }
   ],
   "source": [
    "def draw_machine():\n",
    "    \"\"\"Draws and returns a machine state, as a boolean vector,\n",
    "    in the order given in the 'names' list.\"\"\"\n",
    "    ret = numpy.empty(len(names), dtype=bool)\n",
    "    \n",
    "    ret[0]  = P_he[1] > numpy.random.rand()\n",
    "    ret[1]  = P_fp[1] > numpy.random.rand()\n",
    "    ret[2]  = P_fc[1] > numpy.random.rand()\n",
    "    ret[3]  = P_wr[1] > numpy.random.rand()\n",
    "    ret[4]  = P_gs[1] > numpy.random.rand()\n",
    "    ret[5]  = P_dp[1] > numpy.random.rand()\n",
    "    ret[6]  = P_fh[1] > numpy.random.rand()\n",
    "    \n",
    "    ret[7]  = P_pw_he_fp[1,int(ret[0]),int(ret[1])] > numpy.random.rand()\n",
    "    ret[8]  = P_cb_pw_fc[1,int(ret[7]),int(ret[2])] > numpy.random.rand()\n",
    "    ret[9]  = P_gw_cb_wr_dp[1,int(ret[8]),int(ret[3]),int(ret[5])] > numpy.random.rand()\n",
    "    \n",
    "    ret[10] = P_ls_he[1,int(ret[0])] > numpy.random.rand()\n",
    "    ret[11] = P_vp_pw[1,int(ret[7])] > numpy.random.rand()\n",
    "    ret[12] = P_lo_cb[1,int(ret[8])] > numpy.random.rand()\n",
    "    ret[13] = P_wv_wr[1,int(ret[3])] > numpy.random.rand()\n",
    "    ret[14] = P_hp_dp[1,int(ret[5])] > numpy.random.rand()\n",
    "    ret[15] = P_me_gw_gs[1,int(ret[9]),int(ret[4])] > numpy.random.rand()\n",
    "    ret[16] = P_ta_me_fh[1,int(ret[15]),int(ret[6])] > numpy.random.rand()\n",
    "    \n",
    "    return ret\n",
    "\n",
    "\n",
    "\n",
    "# Quick test of above...\n",
    "print(*names)\n",
    "for _ in range(16):\n",
    "    d = draw_machine()\n",
    "    d = [('T ' if v else 'F ') for v in d]\n",
    "    print(*d)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "he: False = 259647, true = 2497\n",
      "fp: False = 209863, true = 52281\n",
      "fc: False = 256914, true = 5230\n",
      "wr: False = 235817, true = 26327\n",
      "gs: False = 249065, true = 13079\n",
      "dp: False = 248954, true = 13190\n",
      "fh: False = 254369, true = 7775\n",
      "pw: False = 54262, true = 207882\n",
      "cb: False = 58443, true = 203701\n",
      "gw: False = 86405, true = 175739\n",
      "ls: False = 28453, true = 233691\n",
      "vp: False = 56360, true = 205784\n",
      "lo: False = 58653, true = 203491\n",
      "wv: False = 73456, true = 188688\n",
      "hp: False = 37909, true = 224235\n",
      "me: False = 110826, true = 151318\n",
      "ta: False = 122603, true = 139541\n"
     ]
    }
   ],
   "source": [
    "# Generate data...\n",
    "dm = numpy.empty((256*1024, len(names)), dtype=bool)\n",
    "\n",
    "numpy.random.seed(0)\n",
    "for r in range(dm.shape[0]):\n",
    "    dm[r,:] = draw_machine()\n",
    "\n",
    "\n",
    "\n",
    "# Stats...\n",
    "for c in range(dm.shape[1]):\n",
    "    tc = dm[:,c].sum()\n",
    "    fc = dm.shape[0] - tc\n",
    "    print('{}: False = {}, true = {}'.format(names[c], fc, tc))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Dump to disk..\n",
    "with open('coffee_machines.csv', 'w') as fout:\n",
    "    fout.write(','.join(names) + '\\n')\n",
    "    for r in range(dm.shape[0]):\n",
    "        fout.write(','.join([str(int(dm[r,c])) for c in range(dm.shape[1])]) + '\\n')\n"
   ]
  }
 ],
 "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.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
