{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# `bsym` – a basic symmetry module\n",
    "\n",
    "`bsym` is a basic Python symmetry module. It consists of some core classes that describe configuration vector spaces, their symmetry operations, and specific configurations of objects withing these spaces. The module also contains an interface for working with [`pymatgen`](http://pymatgen.org) `Structure` objects, to allow simple generation of disordered symmetry-inequivalent structures from a symmetric parent crystal structure.\n",
    "\n",
    "API documentation is [here](http://bsym.readthedocs.io)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configuration Spaces, Symmetry Operations, and Groups\n",
    "\n",
    "The central object described by `bsym` is the **configuration space**. This defines a vector space that can be occupied by other objects. For example; the three points $a, b, c$ defined by an equilateral triangle,\n",
    "\n",
    "<img src='figures/triangular_configuration_space.pdf'>\n",
    "\n",
    "which can be described by a length 3 vector:\n",
    "\n",
    "\\begin{pmatrix}a\\\\b\\\\c\\end{pmatrix}\n",
    "\n",
    "If these points can be coloured black or white, then we can define a **configuration** for each different colouring (0 for white, 1 for black), e.g. \n",
    "\n",
    "<img src='figures/triangular_configuration_example_1.pdf'>\n",
    "\n",
    "with the corresponding vector\n",
    "\n",
    "\\begin{pmatrix}1\\\\1\\\\0\\end{pmatrix}\n",
    "\n",
    "A specific **configuration** therefore defines how objects are distributed within a particular **configuration space**.\n",
    "\n",
    "The symmetry relationships between the different vectors in a **configuration space** are described by **symmetry operations**. A **symmetry operation** describes a transformation of a **configuration space** that leaves it indistinguishable. Each **symmetry operation** can be describes as a matrix that maps the vectors in a **configuration space** onto each other, e.g. in the case of the equiateral triangle the simplest **symmetry operation** is the identity, $E$, which leaves every corner unchanged, and can be represented by the matrix \n",
    "\n",
    "\\begin{equation}\n",
    "E=\\begin{pmatrix}1 & 0 & 0\\\\0 & 1 & 0 \\\\ 0 & 0 & 1\\end{pmatrix}\n",
    "\\end{equation}\n",
    "\n",
    "For this triangular example, there are other **symmetry operations**, including reflections, $\\sigma$ and rotations, $C_n$:\n",
    "\n",
    "<img src='figures/triangular_example_symmetry_operations.pdf'>\n",
    "\n",
    "In this example reflection operation, $b$ is mapped to $c$; $b\\to c$, and $c$ is mapped to $b$; $b\\to c$. \n",
    "\n",
    "The matrix representation of this **symmetry operation** is\n",
    "\n",
    "\\begin{equation}\n",
    "\\sigma_\\mathrm{a}=\\begin{pmatrix}1 & 0 & 0\\\\0 & 0 & 1 \\\\ 0 & 1 & 0\\end{pmatrix}\n",
    "\\end{equation}\n",
    "\n",
    "For the example rotation operation, $a\\to b$, $b\\to c$, and $c\\to a$, with matrix representation\n",
    "\n",
    "\\begin{equation}\n",
    "C_3=\\begin{pmatrix}0 & 0 & 1\\\\ 1 & 0 & 0 \\\\ 0 & 1 & 0\\end{pmatrix}\n",
    "\\end{equation}\n",
    "\n",
    "Using this matrix and vector notation, the effect of a symmetry operation on a specific **configuration** can be calculated as the [matrix product](https://en.wikipedia.org/wiki/Matrix_multiplication#Square_matrix_and_column_vector) of the **symmetry operation** matrix and the **configuration** vector:\n",
    "\n",
    "<img src='figures/triangular_rotation_operation.pdf'>\n",
    "\n",
    "In matrix notation this is represented as\n",
    "\n",
    "\\begin{equation}\n",
    "\\begin{pmatrix}0\\\\1\\\\1\\end{pmatrix} = \\begin{pmatrix}0 & 0 & 1\\\\ 1 & 0 & 0 \\\\ 0 & 1 & \n",
    "0\\end{pmatrix}\\begin{pmatrix}1\\\\1\\\\0\\end{pmatrix}\n",
    "\\end{equation}\n",
    "\n",
    "or more compactly\n",
    "\n",
    "\\begin{equation}\n",
    "c_\\mathrm{f} = C_3 c_\\mathrm{i}.\n",
    "\\end{equation}\n",
    "\n",
    "The set of all symmetry operations for a particular **configuration space** is a **group**. \n",
    "\n",
    "For an equilateral triangle this group is the $C_{3v}$ [point group](https://en.wikipedia.org/wiki/Point_group), which contains six symmetry operations: the identity, three reflections (each with a mirror plane bisecting the triangle and passing through $a$, $b$, or $c$ respectively) and two rotations (120° clockwise and counterclockwise).\n",
    "\n",
    "\\begin{equation}\n",
    "C_{3v} = \\left\\{ E, \\sigma_\\mathrm{a}, \\sigma_\\mathrm{b}, \\sigma_\\mathrm{c}, C_3, C_3^\\prime \\right\\}\n",
    "\\end{equation}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Modelling this using `bsym`\n",
    "\n",
    "### The `SymmetryOperation` class\n",
    "\n",
    "In `bsym`, a **symmetry operation** is represented by an instance of the `SymmetryOperation` class. A `SymmetryOperation` instance can be initialised from the matrix representation of the corresponding **symmetry operation**. \n",
    "\n",
    "For example, in the trigonal **configuration space** above, a `SymmetryOperation` describing the identify, $E$, can be created with"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bsym import SymmetryOperation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryOperation\n",
       "label(---)\n",
       "array([[1, 0, 0],\n",
       "       [0, 1, 0],\n",
       "       [0, 0, 1]])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "SymmetryOperation([[ 1, 0, 0 ], \n",
    "                   [ 0, 1, 0 ], \n",
    "                   [ 0, 0, 1 ]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Each `SymmetryOperation` has an optional `label` attribute. This can be set at records the matrix representation of the **symmetry operation** and an optional label. We can provide the label when creating a `SymmetryOperation`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryOperation\n",
       "label(E)\n",
       "array([[1, 0, 0],\n",
       "       [0, 1, 0],\n",
       "       [0, 0, 1]])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "SymmetryOperation([[ 1, 0, 0 ], \n",
    "                   [ 0, 1, 0 ], \n",
    "                   [ 0, 0, 1 ]], label='E' )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "or set it afterwards:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryOperation\n",
       "label(E)\n",
       "array([[1, 0, 0],\n",
       "       [0, 1, 0],\n",
       "       [0, 0, 1]])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "e = SymmetryOperation([[ 1, 0, 0 ], \n",
    "                       [ 0, 1, 0 ], \n",
    "                       [ 0, 0, 1 ]])\n",
    "e.label = 'E'\n",
    "e"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or for $C_3$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryOperation\n",
       "label(C3)\n",
       "array([[0, 0, 1],\n",
       "       [1, 0, 0],\n",
       "       [0, 1, 0]])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c_3 = SymmetryOperation( [ [ 0, 0, 1 ],\n",
    "                           [ 1, 0, 0 ],\n",
    "                           [ 0, 1, 0 ] ], label='C3' )\n",
    "c_3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Vector representations of symmetry operations\n",
    "\n",
    "The matrix representation of a **symmetry operation** is a [permutation matrix](https://en.wikipedia.org/wiki/Permutation_matrix). Each row maps one position in the corresponding **configuration space** to one other position. An alternative, condensed, representation for each **symmetry operation** matrix uses vector notation, where each element gives the row containing `1` in the equivalent matrix column. e.g. for $C_3$ the vector mapping is given by $\\left[2,3,1\\right]$, corresponding to the mapping $1\\to2$, $2\\to3$, $3\\to1$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryOperation\n",
       "label(C3)\n",
       "array([[0., 0., 1.],\n",
       "       [1., 0., 0.],\n",
       "       [0., 1., 0.]])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c_3_from_vector = SymmetryOperation.from_vector( [ 2, 3, 1 ], label='C3' )\n",
    "c_3_from_vector"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The vector representation of a `SymmetryOperation` can be accessed using the `as_vector()` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2, 3, 1]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c_3.as_vector()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Inverting symmetry operations\n",
    "\n",
    "For every **symmetry operation**, $A$, there is an **inverse** operation, $A^{-1}$, such that \n",
    "\n",
    "\\begin{equation}\n",
    "A \\cdot A^{-1}=E.\n",
    "\\end{equation}\n",
    "\n",
    "For example, the inverse of $C_3$ (clockwise rotation by 120°) is $C_3^\\prime$ (anticlockwise rotation by 120°):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SymmetryOperation\n",
      "label(C3)\n",
      "array([[0., 0., 1.],\n",
      "       [1., 0., 0.],\n",
      "       [0., 1., 0.]]) \n",
      "\n",
      "SymmetryOperation\n",
      "label(C3_inv)\n",
      "array([[0., 1., 0.],\n",
      "       [0., 0., 1.],\n",
      "       [1., 0., 0.]]) \n",
      "\n"
     ]
    }
   ],
   "source": [
    "c_3     = SymmetryOperation.from_vector( [ 2, 3, 1 ], label='C3' )\n",
    "c_3_inv = SymmetryOperation.from_vector( [ 3, 1, 2 ], label='C3_inv' )\n",
    "\n",
    "print( c_3, '\\n' )\n",
    "print( c_3_inv, '\\n' )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The product of $C_3$ and $C_3^\\prime$ is the identity, $E$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryOperation\n",
       "label(---)\n",
       "array([[1., 0., 0.],\n",
       "       [0., 1., 0.],\n",
       "       [0., 0., 1.]])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c_3 * c_3_inv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"figures/triangular_c3_inversion.pdf\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`c_3_inv` can also be generated using the `.invert()` method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryOperation\n",
       "label(---)\n",
       "array([[0, 1, 0],\n",
       "       [0, 0, 1],\n",
       "       [1, 0, 0]])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c_3.invert()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The resulting `SymmetryOperation` does not have a label defined. This can be set directly, or by chaining the `.set_label()` method, e.g."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryOperation\n",
       "label(C3_inv)\n",
       "array([[0, 1, 0],\n",
       "       [0, 0, 1],\n",
       "       [1, 0, 0]])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c_3.invert( label= 'C3_inv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryOperation\n",
       "label(C3_inv)\n",
       "array([[0, 1, 0],\n",
       "       [0, 0, 1],\n",
       "       [1, 0, 0]])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c_3.invert().set_label( 'C3_inv' )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The `SymmetryGroup` class\n",
    "\n",
    "A `SymmetryGroup` is a collections of `SymmetryOperation` objects. A `SymmetryGroup` is not required to contain _all_ the symmetry operations of a particular **configuration space**, and therefore is not necessarily a complete mathematical <a href=\"https://en.wikipedia.org/wiki/Group_(mathematics)#Definition\">group</a>.\n",
    "\n",
    "For convenience `bsym` has `PointGroup` and `SpaceGroup` classes, that are equivalent to the `SymmetryGroup` parent class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bsym import PointGroup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# construct SymmetryOperations for C_3v group\n",
    "e       = SymmetryOperation.from_vector( [ 1, 2, 3 ], label='e' )\n",
    "c_3     = SymmetryOperation.from_vector( [ 2, 3, 1 ], label='C_3' )\n",
    "c_3_inv = SymmetryOperation.from_vector( [ 3, 1, 2 ], label='C_3_inv' )\n",
    "sigma_a = SymmetryOperation.from_vector( [ 1, 3, 2 ], label='S_a' )\n",
    "sigma_b = SymmetryOperation.from_vector( [ 3, 2, 1 ], label='S_b' )\n",
    "sigma_c = SymmetryOperation.from_vector( [ 2, 1, 3 ], label='S_c' )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"figures/triangular_c3v_symmetry_operations.pdf\" />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "c3v = PointGroup( [ e, c_3, c_3_inv, sigma_a, sigma_b, sigma_c ] )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "PointGroup\n",
       "e\t[1, 2, 3]\n",
       "C_3\t[2, 3, 1]\n",
       "C_3_inv\t[3, 1, 2]\n",
       "S_a\t[1, 3, 2]\n",
       "S_b\t[3, 2, 1]\n",
       "S_c\t[2, 1, 3]"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c3v"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The `ConfigurationSpace` class\n",
    "\n",
    "A `ConfigurationSpace` consists of a set of objects that represent the **configuration space** vectors, and the `SymmetryGroup` containing the relevant **symmetry operations**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bsym import ConfigurationSpace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "c = ConfigurationSpace( objects=['a', 'b', 'c' ], symmetry_group=c3v )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ConfigurationSpace\n",
       "['a', 'b', 'c']\n",
       "e\t[1, 2, 3]\n",
       "C_3\t[2, 3, 1]\n",
       "C_3_inv\t[3, 1, 2]\n",
       "S_a\t[1, 3, 2]\n",
       "S_b\t[3, 2, 1]\n",
       "S_c\t[2, 1, 3]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The `Configuration` class\n",
    "\n",
    "A `Configuration` instance describes a particular **configuration**, i.e. how a set of objects are arranged within a **configuration space**. Internally, a `Configuration` is represented as a vector (as a `numpy` array).\n",
    "Each element in a configuration is represented by a single digit non-negative integer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Configuration([1 1 0])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from bsym import Configuration\n",
    "\n",
    "conf_1 = Configuration( [ 1, 1, 0 ] )\n",
    "conf_1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The effect of a particular **symmetry operation** acting on a **configuration** can now be calculated using the `SymmetryOperation.operate_on()` method, or by direct multiplication, e.g."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Configuration([0 1 1])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c1 = Configuration( [ 1, 1, 0 ] )\n",
    "c_3 = SymmetryOperation.from_vector( [ 2, 3, 1 ] )\n",
    "c_3.operate_on( c1 )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Configuration([0 1 1])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c_3 * conf_1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"figures/triangular_rotation_operation.pdf\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Finding symmetry-inequivalent permutations."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A common question that comes up when considering the symmetry properties of arrangements of objects is: how many ways can these be arranged that are not equivalent by symmetry?\n",
    "\n",
    "As a simple example of solving this problem using `bsym` consider four equivalent sites arranged in a square.\n",
    "\n",
    "<img src=\"figures/square_configuration_space.pdf\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "c = ConfigurationSpace( [ 'a', 'b', 'c', 'd' ] ) # four vector configuration space"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This `ConfigurationSpace` has been created without a `symmetry_group` argument. The default behaviour in this case is to create a `SymmetryGroup` containing only the identity, $E$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ConfigurationSpace\n",
       "['a', 'b', 'c', 'd']\n",
       "E\t[1, 2, 3, 4]"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now calculate all symmetry inequivalent arrangements where two sites are occupied and two are unoccupied, using the `unique_configurations()` method. This takes as a argument a `dict` with the numbers of labels to be arranged in the **configuration space**. Here, we use the labels `1` and `0` to represent occupied and unoccupied sites, respectively, and the distribution of sites is given by `{ 1:2, 0:2 }`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Configuration([0 0 1 1]),\n",
       " Configuration([0 1 0 1]),\n",
       " Configuration([0 1 1 0]),\n",
       " Configuration([1 0 0 1]),\n",
       " Configuration([1 0 1 0]),\n",
       " Configuration([1 1 0 0])]"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c.unique_configurations( {1:2, 0:2} )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because we have not yet taken into account the symmetry of the **configuration space**, we get\n",
    "\n",
    "\\begin{equation}\n",
    "\\frac{4\\times3}{2}\n",
    "\\end{equation}\n",
    "\n",
    "unique configurations (where the factor of 2 comes from the occupied sites being indistinguishable).\n",
    "\n",
    "The configurations generated by `unique_configurations` have a `count` attribute that records the number of *symmetry equivalent* configurations of each case:\n",
    "\n",
    "In this example, each configuration appears once:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 1, 1, 1, 1, 1]"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ uc.count for uc in c.unique_configurations( {1:2, 0:2} ) ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also calculate the result when all symmetry operations of this **configuration space** are included. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ConfigurationSpace\n",
       "['a', 'b', 'c', 'd']\n",
       "E\t[1, 2, 3, 4]\n",
       "C4\t[2, 3, 4, 1]\n",
       "C4i\t[4, 1, 2, 3]\n",
       "C2\t[3, 4, 1, 2]\n",
       "s_x\t[4, 3, 2, 1]\n",
       "s_y\t[2, 1, 4, 3]\n",
       "s_ac\t[1, 4, 3, 2]\n",
       "s_bd\t[3, 2, 1, 4]"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# construct point group\n",
    "e        = SymmetryOperation.from_vector( [ 1, 2, 3, 4 ], label='E' )\n",
    "c4       = SymmetryOperation.from_vector( [ 2, 3, 4, 1 ], label='C4' )\n",
    "c4_inv   = SymmetryOperation.from_vector( [ 4, 1, 2, 3 ], label='C4i' )\n",
    "c2       = SymmetryOperation.from_vector( [ 3, 4, 1, 2 ], label='C2' )\n",
    "sigma_x  = SymmetryOperation.from_vector( [ 4, 3, 2, 1 ], label='s_x' )\n",
    "sigma_y  = SymmetryOperation.from_vector( [ 2, 1, 4, 3 ], label='s_y' )\n",
    "sigma_ac = SymmetryOperation.from_vector( [ 1, 4, 3, 2 ], label='s_ac' )\n",
    "sigma_bd = SymmetryOperation.from_vector( [ 3, 2, 1, 4 ], label='s_bd' )\n",
    "c4v = PointGroup( [ e, c4, c4_inv, c2, sigma_x, sigma_y, sigma_ac, sigma_bd ] )\n",
    "\n",
    "# create ConfigurationSpace with the c4v PointGroup.\n",
    "c = ConfigurationSpace( [ 'a', 'b', 'c', 'd' ], symmetry_group=c4v )\n",
    "c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Configuration([0 0 1 1]), Configuration([0 1 0 1])]"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c.unique_configurations( {1:2, 0:2} )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4, 2]"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ uc.count for uc in c.unique_configurations( {1:2, 0:2 } ) ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Taking symmetry in to account, we now only have two unique configurations: either two adjacent site are occupied (four possible ways), or two diagonal sites are occupied (two possible ways):\n",
    "\n",
    "<img src=\"figures/square_unique_configurations.pdf\" >"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `unique_configurations()` method can also handle non-binary site occupations:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Configuration([0 0 1 2]), Configuration([0 1 0 2])]"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c.unique_configurations( {2:1, 1:1, 0:2} )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[8, 4]"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ uc.count for uc in c.unique_configurations( {2:1, 1:1, 0:2 } ) ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"figures/square_unique_configurations_2.pdf\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Working with crystal structures using `pymatgen`\n",
    "\n",
    "One example where the it can be useful to identify symmetry-inequivalent arrangements of objects in a vector space, is when considering the possible arrangements of disordered atoms on a crystal lattice. \n",
    "\n",
    "To solve this problem for an arbitrary crystal structure, `bsym` contains an interface to [`pymatgen`](http://pymatgen.org) that will identify symmetry-inequivalent atom substitutions in a given `pymatgen` `Structure`.\n",
    "\n",
    "As an example, consider a $4\\times4$ square-lattice supercell populated by lithium atoms."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymatgen.core.lattice import Lattice\n",
    "from pymatgen.core.structure import Structure\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.,  0.,  0.],\n",
       "       [-0.,  1.,  0.],\n",
       "       [-0.,  2.,  0.],\n",
       "       [-0.,  3.,  0.],\n",
       "       [ 1.,  0.,  0.],\n",
       "       [ 1.,  1.,  0.],\n",
       "       [ 1.,  2.,  0.],\n",
       "       [ 1.,  3.,  0.],\n",
       "       [ 2.,  0.,  0.],\n",
       "       [ 2.,  1.,  0.],\n",
       "       [ 2.,  2.,  0.],\n",
       "       [ 2.,  3.,  0.],\n",
       "       [ 3.,  0.,  0.],\n",
       "       [ 3.,  1.,  0.],\n",
       "       [ 3.,  2.,  0.],\n",
       "       [ 3.,  3.,  0.]])"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# construct a pymatgen Structure instance using the site fractional coordinates\n",
    "coords = np.array( [ [ 0.0, 0.0, 0.0 ] ] )\n",
    "atom_list = [ 'Li' ]\n",
    "lattice = Lattice.from_parameters( a=1.0, b=1.0, c=1.0, alpha=90, beta=90, gamma=90 )\n",
    "parent_structure = Structure( lattice, atom_list, coords ) * [ 4, 4, 1 ]\n",
    "parent_structure.cart_coords.round(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can use the `bsym.interface.pymatgen.unique_structure_substitutions()` function to identify symmetry-inequivalent structures generated by substituting at different sites."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bsym.interface.pymatgen import unique_structure_substitutions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "    Generate all symmetry-unique structures formed by substituting a set of sites in a `pymatgen` structure.\n",
      "\n",
      "    Args:\n",
      "        structure (pymatgen.Structure): The parent structure.\n",
      "        to_substitute (str): atom label for the sites to be substituted.\n",
      "        site_distribution (dict): A dictionary that defines the number of each substituting element.\n",
      "        verbose (bool): verbose output.\n",
      "        atol      (Optional [float]):       tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation. Default=1e-5.\n",
      "        show_progress (opt:default=False): Show a progress bar.\n",
      "                                           Setting to `True` gives a simple progress bar.\n",
      "                                           Setting to `\"notebook\"` gives a Jupyter notebook compatible progress bar.\n",
      "\n",
      "\n",
      "    Returns:\n",
      "        (list[Structure]): A list of Structure objects for each unique substitution.\n",
      "    \n",
      "    Notes:\n",
      "        The number of symmetry-equivalent configurations for each structure \n",
      "        is stored in the `number_of_equivalent_configurations` attribute. \n",
      "     \n",
      "        If the parent structure was previously generated using this function\n",
      "        (as part of a sequence of substitutions) the full configuration\n",
      "        degeneracy of each symmetry inequivalent configuration is stored in\n",
      "        the `full_configuration_degeneracy` attribute. If the parent structure\n",
      "        is a standard Pymatgen Structure object, `number_of_equivalent_configurations`\n",
      "        and `full_configuration_degeneracy` will be equal.\n",
      "\n",
      "    .. _coordinate mapping:\n",
      "        http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping_pbc\n",
      "\n",
      "    \n"
     ]
    }
   ],
   "source": [
    "print( unique_structure_substitutions.__doc__ )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As a trivial example, when substituting one Li atom for Na, we get a single unique structure"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "unique_structures = unique_structure_substitutions( parent_structure, 'Li', { 'Na':1, 'Li':15 } )\n",
    "len( unique_structures )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"figures/pymatgen_example_one_site.pdf\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "na_substituted = unique_structures[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This Li$\\to$Na substitution breaks the symmetry of the $4\\times4$ supercell. \n",
    "\n",
    "If we now replace a second lithium with a magnesium atom, we generate five symmetry inequivalent structures:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "unique_structures_with_Mg = unique_structure_substitutions( na_substituted, 'Li', { 'Mg':1, 'Li':14 } )\n",
    "len( unique_structures_with_Mg )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4, 2, 4, 4, 1]"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ s.number_of_equivalent_configurations for s in unique_structures_with_Mg ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`number_of_equivalent_configurations` only lists the number of equivalent configurations found when performing the second substitution, when the list of structures `unique_structures_with_Mg` was created. The full configuration degeneracy relative to the initial empty 4×4 lattice can be queried using `full_configuration_degeneracy`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[64, 32, 64, 64, 16]"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ s.full_configuration_degeneracy for s in unique_structures_with_Mg ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"figures/pymatgen_example_two_sites.pdf\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 2., 4., 5., 8.])"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Check the squared distances between the Na and Mg sites in these unique structures are [1, 2, 4, 5, 8]\n",
    "np.array( sorted( [ s.get_distance( s.indices_from_symbol('Na')[0], \n",
    "                                    s.indices_from_symbol('Mg')[0] )**2 for s in unique_structures_with_Mg ] ) )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This double substitution can also be done in a single step:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "unique_structures = unique_structure_substitutions( parent_structure, 'Li', { 'Mg':1, 'Na':1, 'Li':14 } )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(unique_structures)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 2., 4., 5., 8.])"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.array( sorted( [ s.get_distance( s.indices_from_symbol('Na')[0], \n",
    "                                    s.indices_from_symbol('Mg')[0] ) for s in unique_structures ] ) )**2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[64, 32, 64, 64, 16]"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ s.number_of_equivalent_configurations for s in unique_structures ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because both substitutions were performed in a single step, `number_of_equivalent_configurations` and `full_configuration_degeneracy` now contain the same data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[64, 32, 64, 64, 16]"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ s.full_configuration_degeneracy for s in unique_structures ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Constructing `SpaceGroup` and `ConfigurationSpace` objects using `pymatgen`\n",
    "\n",
    "The `bsym.interface.pymatgen` module contains functions for generating `SpaceGroup` and `ConfigurationSpace` objects directly from `pymatgen` `Structure` objects."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bsym.interface.pymatgen import ( space_group_symbol_from_structure, \n",
    "                                      space_group_from_structure, \n",
    "                                      configuration_space_from_structure )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Documentation:\n",
    "\n",
    "- [`space_group_symbol_from_structure`](http://bsym.readthedocs.io/en/latest/api/interface/pymatgen.html#bsym.interface.pymatgen.space_group_symbol_from_structure)\n",
    "- [`space_group_from_structure`](http://bsym.readthedocs.io/en/latest/api/interface/pymatgen.html#bsym.interface.pymatgen.space_group_from_structure)\n",
    "- [`configuration_space_from_structure`](http://bsym.readthedocs.io/en/latest/api/interface/pymatgen.html#bsym.interface.pymatgen.configuration_space_from_structure)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "coords = np.array( [ [ 0.0, 0.0, 0.0 ],\n",
    "                     [ 0.5, 0.5, 0.0 ],\n",
    "                     [ 0.0, 0.5, 0.5 ],\n",
    "                     [ 0.5, 0.0, 0.5 ] ] )\n",
    "atom_list = [ 'Li' ] * len( coords )\n",
    "lattice = Lattice.from_parameters( a=3.0, b=3.0, c=3.0, alpha=90, beta=90, gamma=90 )\n",
    "structure = Structure( lattice, atom_list, coords )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Fm-3m'"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "space_group_symbol_from_structure( structure )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SymmetryGroup\n",
       "None\t[1, 2, 3, 4]\n",
       "None\t[3, 4, 1, 2]\n",
       "None\t[1, 4, 3, 2]\n",
       "None\t[2, 3, 4, 1]\n",
       "None\t[4, 3, 2, 1]\n",
       "None\t[3, 2, 1, 4]\n",
       "None\t[2, 1, 4, 3]\n",
       "None\t[4, 1, 2, 3]\n",
       "None\t[4, 1, 3, 2]\n",
       "None\t[3, 2, 4, 1]\n",
       "None\t[2, 4, 1, 3]\n",
       "None\t[1, 3, 2, 4]\n",
       "None\t[1, 4, 2, 3]\n",
       "None\t[3, 1, 4, 2]\n",
       "None\t[2, 3, 1, 4]\n",
       "None\t[4, 2, 3, 1]\n",
       "None\t[3, 1, 2, 4]\n",
       "None\t[3, 4, 2, 1]\n",
       "None\t[2, 4, 3, 1]\n",
       "None\t[2, 1, 3, 4]\n",
       "None\t[1, 3, 4, 2]\n",
       "None\t[1, 2, 4, 3]\n",
       "None\t[4, 2, 1, 3]\n",
       "None\t[4, 3, 1, 2]"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "space_group_from_structure( structure )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ConfigurationSpace\n",
       "[1, 2, 3, 4]\n",
       "None\t[1, 2, 3, 4]\n",
       "None\t[3, 4, 1, 2]\n",
       "None\t[1, 4, 3, 2]\n",
       "None\t[2, 3, 4, 1]\n",
       "None\t[4, 3, 2, 1]\n",
       "None\t[3, 2, 1, 4]\n",
       "None\t[2, 1, 4, 3]\n",
       "None\t[4, 1, 2, 3]\n",
       "None\t[4, 1, 3, 2]\n",
       "None\t[3, 2, 4, 1]\n",
       "None\t[2, 4, 1, 3]\n",
       "None\t[1, 3, 2, 4]\n",
       "None\t[1, 4, 2, 3]\n",
       "None\t[3, 1, 4, 2]\n",
       "None\t[2, 3, 1, 4]\n",
       "None\t[4, 2, 3, 1]\n",
       "None\t[3, 1, 2, 4]\n",
       "None\t[3, 4, 2, 1]\n",
       "None\t[2, 4, 3, 1]\n",
       "None\t[2, 1, 3, 4]\n",
       "None\t[1, 3, 4, 2]\n",
       "None\t[1, 2, 4, 3]\n",
       "None\t[4, 2, 1, 3]\n",
       "None\t[4, 3, 1, 2]"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "configuration_space_from_structure( structure )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Progress bars\n",
    "\n",
    "`bsym.ConfigurationSpace.unique_configurations()` and `bsym.interface.pymatgen.unique_structure_substitutions()` both accept optional `show_progress` arguments, which can be used to display progress bars (using `tqdm`(https://tqdm.github.io). \n",
    "\n",
    "Setting `show_progress=True` will give a simple progress bar. If you are running `bsym` in a Jupyter notebook, setting `show_progress=\"notebook\"` will give you a progress bar as a notebook widget.\n",
    "\n",
    "(note, the widget status is not saved with this notebook, and may not display correctly on GitHub or using nbviewer)\n",
    "\n",
    "In the example below, we find all unique configurations for the pseudo-ReO<sub>3</sub> structured TiOF<sub>2</sub> in a 2&times;2&times;2 supercell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "9dc42b56c8d84b5b845e337d076ec324",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(IntProgress(value=0, max=735471), HTML(value='')))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "a = 3.798 # lattice parameter\n",
    "\n",
    "coords = np.array( [ [ 0.0, 0.0, 0.0 ],\n",
    "                     [ 0.5, 0.0, 0.0 ],\n",
    "                     [ 0.0, 0.5, 0.0 ],\n",
    "                     [ 0.0, 0.0, 0.5 ] ] )\n",
    "atom_list = [ 'Ti', 'X', 'X', 'X' ]\n",
    "lattice = Lattice.from_parameters( a=a, b=a, c=a, alpha=90, beta=90, gamma=90 )\n",
    "unit_cell = Structure( lattice, atom_list, coords )\n",
    "\n",
    "parent_structure = unit_cell * [ 2, 2, 2 ]\n",
    "unique_structures = unique_structure_substitutions( parent_structure, 'X', { 'O':8, 'F':16 }, \n",
    "                                                    show_progress='notebook' )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": {
       "Software versions": [
        {
         "module": "Python",
         "version": "3.7.0 64bit [Clang 10.0.0 (clang-1000.10.44.2)]"
        },
        {
         "module": "IPython",
         "version": "7.0.1"
        },
        {
         "module": "OS",
         "version": "Darwin 18.2.0 x86_64 i386 64bit"
        },
        {
         "module": "bsym",
         "version": "1.1.1"
        },
        {
         "module": "numpy",
         "version": "1.15.2"
        },
        {
         "module": "jupyter",
         "version": "1.0.0"
        },
        {
         "module": "pymatgen",
         "version": "2018.10.18"
        },
        {
         "module": "tqdm",
         "version": "4.28.1"
        }
       ]
      },
      "text/html": [
       "<table><tr><th>Software</th><th>Version</th></tr><tr><td>Python</td><td>3.7.0 64bit [Clang 10.0.0 (clang-1000.10.44.2)]</td></tr><tr><td>IPython</td><td>7.0.1</td></tr><tr><td>OS</td><td>Darwin 18.2.0 x86_64 i386 64bit</td></tr><tr><td>bsym</td><td>1.1.1</td></tr><tr><td>numpy</td><td>1.15.2</td></tr><tr><td>jupyter</td><td>1.0.0</td></tr><tr><td>pymatgen</td><td>2018.10.18</td></tr><tr><td>tqdm</td><td>4.28.1</td></tr><tr><td colspan='2'>Sun Feb 10 12:08:49 2019 GMT</td></tr></table>"
      ],
      "text/latex": [
       "\\begin{tabular}{|l|l|}\\hline\n",
       "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n",
       "Python & 3.7.0 64bit [Clang 10.0.0 (clang-1000.10.44.2)] \\\\ \\hline\n",
       "IPython & 7.0.1 \\\\ \\hline\n",
       "OS & Darwin 18.2.0 x86\\_64 i386 64bit \\\\ \\hline\n",
       "bsym & 1.1.1 \\\\ \\hline\n",
       "numpy & 1.15.2 \\\\ \\hline\n",
       "jupyter & 1.0.0 \\\\ \\hline\n",
       "pymatgen & 2018.10.18 \\\\ \\hline\n",
       "tqdm & 4.28.1 \\\\ \\hline\n",
       "\\hline \\multicolumn{2}{|l|}{Sun Feb 10 12:08:49 2019 GMT} \\\\ \\hline\n",
       "\\end{tabular}\n"
      ],
      "text/plain": [
       "Software versions\n",
       "Python 3.7.0 64bit [Clang 10.0.0 (clang-1000.10.44.2)]\n",
       "IPython 7.0.1\n",
       "OS Darwin 18.2.0 x86_64 i386 64bit\n",
       "bsym 1.1.1\n",
       "numpy 1.15.2\n",
       "jupyter 1.0.0\n",
       "pymatgen 2018.10.18\n",
       "tqdm 4.28.1\n",
       "Sun Feb 10 12:08:49 2019 GMT"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%load_ext version_information\n",
    "%version_information bsym, numpy, jupyter, pymatgen, tqdm"
   ]
  }
 ],
 "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.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}