{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ".. _how-to:data:visualise-provenance:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to visualize provenance\n",
    "\n",
    ".. meta::\n",
    "   :keywords: graph,graphviz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ".. note::\n",
    "\n",
    "    This tutorial can be downloaded and run as a Jupyter Notebook: :download:`visualising_graphs.ipynb`\n",
    "    \n",
    "The provenance graph of a database can be visually inspected, *via* [graphviz](https://www.graphviz.org/), using both the python API and command-line interface.\n",
    "\n",
    ".. seealso::\n",
    "\n",
    "    `verdi graph generate -h`\n",
    "\n",
    "We first load the database and required modules:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "init_cell": true
   },
   "outputs": [],
   "source": [
    "from aiida import load_profile\n",
    "profile = load_profile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "init_cell": true
   },
   "outputs": [],
   "source": [
    "from aiida.common import LinkType\n",
    "from aiida.orm.utils.links import LinkPair\n",
    "from aiida.tools.visualization import Graph, pstate_node_styles"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The example provenance graph, used in this tutorial, can be downloaded :download:`from this link <graph1.aiida>` :fa:`download`\n",
    "\n",
    "It can then be imported into the database:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "!verdi import -n graph1.aiida"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dict1_uuid = '0ea79a16-501f-408a-8c84-a2704a778e4b'\n",
    "calc1_uuid = 'b23e692e-4e01-48dd-b515-4c63877d73a4'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The :py:class:`~aiida.tools.visualization.graph.Graph` class is used to store visual representations of the nodes and edges, which can be added separately or cumulatively by one of the graph traversal methods.\n",
    "The :py:attr:`~aiida.tools.visualization.graph.Graph.graphviz` attribute returns a [graphviz.Digraph](https://graphviz.readthedocs.io/en/stable/) instance, which will auto-magically render the graph in the notebook, or can be used to save the graph to file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph = Graph()\n",
    "graph.add_node(dict1_uuid)\n",
    "graph.add_node(calc1_uuid)\n",
    "graph.graphviz"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph.add_edge(\n",
    "    dict1_uuid, calc1_uuid, \n",
    "    link_pair=LinkPair(LinkType.INPUT_CALC, \"input1\"))\n",
    "graph.graphviz"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph.add_incoming(calc1_uuid)\n",
    "graph.add_outgoing(calc1_uuid)\n",
    "graph.graphviz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The :py:class:`~aiida.tools.visualization.graph.Graph` can also be initialized with global style attributes,\n",
    "as outlined in the [graphviz attributes table](https://www.graphviz.org/doc/info/attrs.html)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph = Graph(node_id_type=\"uuid\",\n",
    "              global_node_style={\"penwidth\": 1},\n",
    "              global_edge_style={\"color\": \"blue\"},\n",
    "              graph_attr={\"size\": \"8!,8!\", \"rankdir\": \"LR\"})\n",
    "graph.add_incoming(calc1_uuid)\n",
    "graph.add_outgoing(calc1_uuid)\n",
    "graph.graphviz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Additionally functions can be parsed to the :py:class:`~aiida.tools.visualization.graph.Graph` initializer, to specify exactly how each node will be represented. For example, the :py:func:`~aiida.tools.visualization.graph.pstate_node_styles` function colors process nodes by their process state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def link_style(link_pair, **kwargs):\n",
    "    return {\"color\": \"blue\"}\n",
    "\n",
    "graph = Graph(node_style_fn=pstate_node_styles,\n",
    "              link_style_fn=link_style,\n",
    "              graph_attr={\"size\": \"8!,8!\", \"rankdir\": \"LR\"})\n",
    "graph.add_incoming(calc1_uuid)\n",
    "graph.add_outgoing(calc1_uuid)\n",
    "graph.graphviz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Edges can be annotated by one or both of their edge label and link type."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph = Graph(graph_attr={\"size\": \"8!,8!\", \"rankdir\": \"LR\"})\n",
    "graph.add_incoming(calc1_uuid,\n",
    "                   annotate_links=\"both\")\n",
    "graph.add_outgoing(calc1_uuid,\n",
    "                   annotate_links=\"both\")\n",
    "graph.graphviz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The :meth:`~aiida.tools.visualization.graph.Graph.recurse_descendants` and :meth:`~aiida.tools.visualization.graph.Graph.recurse_ancestors` methods can be used to construct a full provenance graph. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph = Graph(graph_attr={\"size\": \"8!,8!\", \"rankdir\": \"LR\"})\n",
    "graph.recurse_descendants(\n",
    "    dict1_uuid,\n",
    "    origin_style=None,\n",
    "    include_process_inputs=True,\n",
    "    annotate_links=\"both\"\n",
    ")\n",
    "graph.graphviz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The link types can also be filtered, to view only the 'data' or 'logical' provenance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph = Graph(graph_attr={\"size\": \"8,8!\", \"rankdir\": \"LR\"})\n",
    "graph.recurse_descendants(\n",
    "    dict1_uuid, \n",
    "    origin_style=None,\n",
    "    include_process_inputs=True,\n",
    "    annotate_links=\"both\",\n",
    "    link_types=(\"input_calc\", \"create\")\n",
    ")\n",
    "graph.graphviz"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph = Graph(graph_attr={\"size\": \"8,8!\", \"rankdir\": \"LR\"})\n",
    "graph.recurse_descendants(\n",
    "    dict1_uuid,\n",
    "    origin_style=None,\n",
    "    include_process_inputs=True,\n",
    "    annotate_links=\"both\",\n",
    "    link_types=(\"input_work\", \"return\")\n",
    ")\n",
    "graph.graphviz"
   ]
   },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you wish to highlight specific node classes,\n",
    "then the `highlight_classes` option can be used\n",
    "to only color specified nodes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph = Graph(graph_attr={\"size\": \"20,20\", \"rankdir\": \"LR\"})\n",
    "graph.recurse_descendants(\n",
    "    dict1_uuid, \n",
    "    highlight_classes=['Dict']\n",
    ")\n",
    "graph.graphviz"
   ]
  }
 ],
 "metadata": {
  "hide_input": false,
  "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.7.1"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
