{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Backcalculate process mixes\n",
    "\n",
    "This notebook is an attempt to figure out how easy it would be to backcalculate the inputs of an aggregated process, assuming that you knew the background database. We will use `ecoinvent 3.1 cutoff` as the background database and inventories, without applying LCIA methods.\n",
    "\n",
    "We first set up our project."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from brightway2 import *\n",
    "from scipy.optimize import *\n",
    "import numpy as np\n",
    "import pyprind\n",
    "from scipy.optimize import minimize\n",
    "from time import time"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create new project"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "projects.current = \"backcalculate\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Basic setup: biosphere, LCIA methods, etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Creating default biosphere\n",
      "\n",
      "Applying strategy: normalize_units"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Writing activities to SQLite3 database:\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 0.620 sec\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Applying strategy: drop_unspecified_subcategories\n",
      "Applied 2 strategies in 0.01 seconds\n",
      "Title: Writing activities to SQLite3 database:\n",
      "  Started: 10/09/2015 11:22:18\n",
      "  Finished: 10/09/2015 11:22:18\n",
      "  Total time elapsed: 0.620 sec\n",
      "  CPU %: 91.100000\n",
      "  Memory %: 1.296997\n",
      "Created database: biosphere3\n",
      "Creating default LCIA methods\n",
      "\n",
      "Applying strategy: normalize_units\n",
      "Applying strategy: set_biosphere_type\n",
      "Applying strategy: drop_unspecified_subcategories\n",
      "Applying strategy: link_iterable_by_fields\n",
      "Applied 4 strategies in 1.84 seconds\n",
      "Wrote 692 LCIA methods with 170915 characterization factors\n",
      "Creating core data migrations\n",
      "\n"
     ]
    }
   ],
   "source": [
    "bw2setup()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Import ecoinvent 3.1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Extracting ecospold2 files:\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 | Item ID: fff527b1-0fe4-4\n",
      "Total time elapsed: 118.483 sec\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Title: Extracting ecospold2 files:\n",
      "  Started: 10/09/2015 11:23:33\n",
      "  Finished: 10/09/2015 11:25:31\n",
      "  Total time elapsed: 118.483 sec\n",
      "  CPU %: 90.100000\n",
      "  Memory %: 11.830235\n",
      "Extracted 11301 datasets in 120.45 seconds\n",
      "Applying strategy: normalize_units\n",
      "Applying strategy: remove_zero_amount_coproducts\n",
      "Applying strategy: remove_zero_amount_inputs_with_no_activity\n",
      "Applying strategy: remove_unnamed_parameters\n",
      "Applying strategy: es2_assign_only_product_with_amount_as_reference_product\n",
      "Applying strategy: assign_single_product_as_activity\n",
      "Applying strategy: create_composite_code\n",
      "Applying strategy: drop_unspecified_subcategories\n",
      "Applying strategy: link_biosphere_by_flow_uuid\n",
      "Applying strategy: link_internal_technosphere_by_composite_code\n",
      "Applying strategy: delete_exchanges_missing_activity\n",
      "Applying strategy: delete_ghost_exchanges\n",
      "Applied 12 strategies in 4.21 seconds\n",
      "11301 datasets\n",
      "521712 exchanges\n",
      "0 unlinked exchanges\n",
      "  \n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(11301, 521712, 0)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ei31cutoff = SingleOutputEcospold2Importer(\n",
    "    \"/Users/cmutel/Documents/LCA Documents/Ecoinvent/3.1/cutoff/datasets\",\n",
    "    \"ecoinvent 3.1 cutoff\"\n",
    ")\n",
    "ei31cutoff.apply_strategies()\n",
    "ei31cutoff.statistics()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Writing activities to SQLite3 database:\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 65.508 sec\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Title: Writing activities to SQLite3 database:\n",
      "  Started: 10/09/2015 11:25:52\n",
      "  Finished: 10/09/2015 11:26:57\n",
      "  Total time elapsed: 65.508 sec\n",
      "  CPU %: 83.400000\n",
      "  Memory %: 13.546133\n",
      "Created database: ecoinvent 3.1 cutoff\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "Brightway2 SQLiteBackend: ecoinvent 3.1 cutoff"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ei31cutoff.write_database()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Clear import object to free memory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "ei31cutoff = None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Calculate all product inventory vectors\n",
    "\n",
    "We next need to know all inventory vectors for all processes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "db = Database(\"ecoinvent 3.1 cutoff\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning: (almost) singular matrix! (estimated cond. number: 2.20e+12)\n"
     ]
    }
   ],
   "source": [
    "lca = LCA({db.random(): 1})\n",
    "lca.lci(factorize=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Datasets:\n",
      "0%                          100%\n",
      "[                              ]Warning: (almost) singular matrix! (estimated cond. number: 2.20e+12)\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 206.648 sec\n"
     ]
    }
   ],
   "source": [
    "mapping, vectors = {}, []\n",
    "\n",
    "pbar = pyprind.ProgBar(len(db), title=\"Datasets:\")\n",
    "\n",
    "vectors = np.zeros((len(db), lca.inventory.shape[0]))\n",
    "\n",
    "for index, ds in enumerate(db):\n",
    "    lca.redo_lci({ds: 1})\n",
    "    vectors[index, :] = np.array(lca.inventory.sum(axis=1)).ravel()\n",
    "    mapping[ds.key] = index\n",
    "    pbar.update()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Get optimization function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class ScoringFunction(object):\n",
    "    \"\"\"Scoring function for inventory vector.\n",
    "    \n",
    "    Calculate the unweighted Manhattan distance in n-dimensional space between \n",
    "    answer * vectors and guess * vectors, where n is the number of processes \n",
    "    in the database. Zeros are included and weighted the same as other values.\n",
    "    \n",
    "    Returns an number when called with a vector guess.\"\"\"\n",
    "    def __init__(self, lca, vectors):\n",
    "        assert isinstance(lca, LCA) \n",
    "        assert isinstance(vectors, np.ndarray)\n",
    "        assert hasattr(lca, \"inventory\")\n",
    "\n",
    "        self.lca = lca\n",
    "        self.vectors = vectors\n",
    "\n",
    "    def sum_inventory_matrix(self, matrix):\n",
    "        return np.array(matrix.sum(axis=1)).ravel()\n",
    "        \n",
    "    def set_answer(self, answer):\n",
    "        \"\"\"Given functional unit ``answer``, create an inventory vector of shape ``(flows,)``\"\"\"\n",
    "        self.lca.redo_lci(answer)\n",
    "        self.answer = self.sum_inventory_matrix(lca.inventory)\n",
    "        \n",
    "    def __call__(self, guess):\n",
    "        \"\"\"Evaluate a guess vector of shape ``(processes,)``\"\"\"\n",
    "        assert guess.shape[0] == self.vectors.shape[0]\n",
    "        assert len(guess.shape) == 1\n",
    "        # Translate from (processes,) to (flows,)\n",
    "        guess = guess.reshape((1, -1)).dot(self.vectors)\n",
    "        # Get manhattan distance between two vectors\n",
    "        distance = np.abs(guess - self.answer).sum()\n",
    "        return distance"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Create an artificial system"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "answer = {db.random().key: float(np.random.random() * 10) for _ in range(5)}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Set up and check our scoring function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning: (almost) singular matrix! (estimated cond. number: 2.20e+12)\n"
     ]
    }
   ],
   "source": [
    "scorer = ScoringFunction(lca, vectors)\n",
    "scorer.set_answer(answer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Get test guess which should be perfect"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "perfect = np.zeros((len(db),))\n",
    "for key, value in answer.items():\n",
    "    perfect[mapping[key]] = value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(11301,) 11301\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "5.4815357723086221e-12"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "scorer(perfect)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Warning: Desired error not necessarily achieved due to precision loss.\n",
      "         Current function value: 156104120668565.406250\n",
      "         Iterations: 0\n",
      "         Function evaluations: 124345\n",
      "         Gradient evaluations: 11\n",
      "Took 20.973269168535868 minutes\n"
     ]
    }
   ],
   "source": [
    "start = time()\n",
    "result = minimize(scorer, np.ones(perfect.shape), options={'maxiter': 50, 'disp': True})\n",
    "print(\"Took {} minutes\".format((time() - start)/60))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Display how close we can get"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def how_close(guess):\n",
    "    for key, value in answer.items():\n",
    "        print(get_, value, mapping[key])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "ALGORITHMS = ('Nelder-Mead', 'Powell', 'CG', 'BFGS', 'L-BFGS-B', 'TNC', 'COBYLA', 'SLSQP')\n",
    "\n",
    "for algo in ALGORITHMS:\n",
    "    r = minimize(MCDA, 5500., method=algo)\n",
    "    print(algo, r['x'])"
   ]
  },
  {
   "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
}
