{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Lecture 1: grid and lattice basics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import gpt as g"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " ### 1) A first quick look\n",
    " \n",
    " Let us first create a $2^4$ grid in single precision:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "grid = g.grid([2, 2, 2, 2], g.single)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " Each grid has a string representation of its key features, which we can access by:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :       1.563752 s : fdimensions = [2, 2, 2, 2]; mpi = [1, 1, 1, 1]; precision = single; checkerboard = full\n"
     ]
    }
   ],
   "source": [
    "g.message(grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " The grid is four-dimensional with two sites in each direction and not split over MPI ranks.  The grid is in single precision, as requested.  Finally, the grid is defined on all points, which is indicated by \"checkerboard = full\".  We will investigate grids which only live on even/odd sites later."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " Next, we create a field of complex numbers living on this grid.  We then initialize the entire field to zero and set the value of a specific site."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "c = g.complex(grid)\n",
    "\n",
    "c[:] = 0\n",
    "c[0, 1, 1, 0] = 2 + 3j"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " We can inspect the contents of this field by accessing its text representation.  The easiest way to do this is."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :       2.360188 s : lattice(ot_complex_additive_group,single)\n",
      "                       : [0,0,0,0]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,0,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,0,0]\tS {S {S {(0,0)}}}\n",
      "                       : [1,1,0,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,0,1,0]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,1,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,1,0]\tS {S {S {(2,3)}}}\n",
      "                       : [1,1,1,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,0,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,1,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [0,0,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,1,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : \n"
     ]
    }
   ],
   "source": [
    "g.message(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " We can access data of the lattice also as a numpy array.  The entire lattice data that is stored **on the local MPI rank**, e.g., is accessable by:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [2.+3.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j],\n",
       "       [0.+0.j]], dtype=complex64)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c[:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " We can also pick just a few points by giving the desired coordinates in a list or numpy array:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[2.+3.j],\n",
       "       [0.+0.j]], dtype=complex64)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c[[[0, 1, 1, 0], [1, 1, 1, 1]]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " This syntax can also be used to set multiple field values at once:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :       3.478581 s : lattice(ot_complex_additive_group,single)\n",
      "                       : [0,0,0,0]\tS {S {S {(2,3)}}}\n",
      "                       : [1,0,0,0]\tS {S {S {(2,3)}}}\n",
      "                       : [0,1,0,0]\tS {S {S {(0,0)}}}\n",
      "                       : [1,1,0,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,0,1,0]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,1,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,1,0]\tS {S {S {(2,3)}}}\n",
      "                       : [1,1,1,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,0,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,1,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [0,0,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,1,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : \n"
     ]
    }
   ],
   "source": [
    "c[[[0, 0, 0, 0], [1, 0, 0, 0]]] = c[0, 1, 1, 0]\n",
    "\n",
    "g.message(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " Equivalently, we can also use the slice syntax.  If lower and upper bounds are not given for a specific dimension, it is bound to the view assigned to the current MPI rank."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :       3.873353 s : lattice(ot_complex_additive_group,single)\n",
      "                       : [0,0,0,0]\tS {S {S {(-1,0)}}}\n",
      "                       : [1,0,0,0]\tS {S {S {(-1,0)}}}\n",
      "                       : [0,1,0,0]\tS {S {S {(0,0)}}}\n",
      "                       : [1,1,0,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,0,1,0]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,1,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,1,0]\tS {S {S {(2,3)}}}\n",
      "                       : [1,1,1,0]\tS {S {S {(0,0)}}}\n",
      "                       : [0,0,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,1,0,1]\tS {S {S {(0,0)}}}\n",
      "                       : [0,0,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,0,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : [0,1,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : [1,1,1,1]\tS {S {S {(0,0)}}}\n",
      "                       : \n"
     ]
    }
   ],
   "source": [
    "c[:,0,0,0] = -1\n",
    "\n",
    "g.message(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " Next, let us investigate a field with internal indices such as a SU(3) color vector."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :       4.285784 s : lattice(ot_vector_color(3),single)\n",
      "                       : [0,0,0,0]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,0,0,0]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,1,0,0]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,1,0,0]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,0,1,0]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,0,1,0]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,1,1,0]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,1,1,0]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,0,0,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,0,0,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,1,0,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,1,0,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,0,1,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,0,1,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,1,1,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,1,1,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : \n"
     ]
    }
   ],
   "source": [
    "v = g.vcolor(grid)\n",
    "\n",
    "v[:] = g.vcolor([0,1,2])\n",
    "\n",
    "g.message(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " Here, we initialized all positions of the field to a vector $[0,1,2]$.  We note that the same syntax `g.vcolor` can create a field if defined using a grid and a tensor object if initialized with its contents.\n",
    " \n",
    " The syntax to access the contents of the lattice fields trivially extends to internal indices.  Setting the top entry of the color vector to $-1$ on all points with fourth coordinate $0$ can be accomplished, e.g., by:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :       4.832814 s : lattice(ot_vector_color(3),single)\n",
      "                       : [0,0,0,0]\tS {S {V<3>{(-1,0),(1,0),(2,0)}}}\n",
      "                       : [1,0,0,0]\tS {S {V<3>{(-1,0),(1,0),(2,0)}}}\n",
      "                       : [0,1,0,0]\tS {S {V<3>{(-1,0),(1,0),(2,0)}}}\n",
      "                       : [1,1,0,0]\tS {S {V<3>{(-1,0),(1,0),(2,0)}}}\n",
      "                       : [0,0,1,0]\tS {S {V<3>{(-1,0),(1,0),(2,0)}}}\n",
      "                       : [1,0,1,0]\tS {S {V<3>{(-1,0),(1,0),(2,0)}}}\n",
      "                       : [0,1,1,0]\tS {S {V<3>{(-1,0),(1,0),(2,0)}}}\n",
      "                       : [1,1,1,0]\tS {S {V<3>{(-1,0),(1,0),(2,0)}}}\n",
      "                       : [0,0,0,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,0,0,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,1,0,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,1,0,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,0,1,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,0,1,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [0,1,1,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : [1,1,1,1]\tS {S {V<3>{(0,0),(1,0),(2,0)}}}\n",
      "                       : \n"
     ]
    }
   ],
   "source": [
    "v[:, :, :, 0, 0] = -1\n",
    "\n",
    "g.message(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2) In-depth discussion\n",
    "\n",
    "We now explore additional features of grids and lattice objects.\n",
    "\n",
    "#### 2.1) Single, double, quadruple precision\n",
    "First, we can convert easily between different precision values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :      34.671567 s : fdimensions = [2, 2, 2, 2]; mpi = [1, 1, 1, 1]; precision = double; checkerboard = full\n"
     ]
    }
   ],
   "source": [
    "grid_dp = grid.converted(g.double)\n",
    "g.message(grid_dp)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or we can directly convert a lattice object to a different precision."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "c_dp = g.convert(c, g.double)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The grids know about their precision as well as their corresponding numpy data types:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :     227.540801 s : Double-precision epsilon: 1e-15\n",
      "GPT :     227.542473 s : Single-precision epsilon: 1e-07\n"
     ]
    }
   ],
   "source": [
    "g.message(\"Double-precision epsilon:\", grid_dp.precision.eps)\n",
    "g.message(\"Single-precision epsilon:\", grid.precision.eps)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, even grids with quadruple precision for reduction between ranks is available.\n",
    "It is implemented via the Dekker tuple algorithm (Dekker, T. J. Numerische Mathematik 18 (1971) 224-242)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "c_qp = g.convert(c, g.double_quadruple)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :     748.301637 s : (0.0 + 0.0) + (3.0 + 0.0)j\n",
      "GPT :     748.303402 s : <class 'gpt.core.quadruple_precision.qcomplex.qcomplex'>\n"
     ]
    }
   ],
   "source": [
    "quadruple_precision_sum = g.sum(c_qp)\n",
    "g.message(quadruple_precision_sum)\n",
    "g.message(type(quadruple_precision_sum))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can demonstrate the precision difference by testing the non-commutative nature of the product of floating point numbers.  In the Dekker tuple approach the error is absent for a product of two such numbers but reduced to a quadruple-precision error for more factors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :    1339.329145 s : double-precision: 1.1102230246251565e-16\n"
     ]
    }
   ],
   "source": [
    "g.message(\"double-precision:\", 4/5 * 6/7 - 6/7 * 4/5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :    1348.313134 s : quadruple-precision: (0.0 + 0.0) + (0.0 + 0.0)j\n"
     ]
    }
   ],
   "source": [
    "eps = g.qcomplex(4/5)*g.qcomplex(6/7) - g.qcomplex(6/7)*g.qcomplex(4/5)\n",
    "g.message(\"quadruple-precision:\", eps)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GPT :    1359.853741 s : quadruple-precision: (6.162975822039155e-33 + 0.0) + (0.0 + 0.0)j\n"
     ]
    }
   ],
   "source": [
    "eps = (\n",
    "    g.qcomplex(4/5)*g.qcomplex(5/6)*g.qcomplex(6/7) \n",
    "    - g.qcomplex(6/7)*g.qcomplex(5/6)*g.qcomplex(4/5)\n",
    ")\n",
    "g.message(\"quadruple-precision:\", eps)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2) Checkerboarding\n",
    "\n",
    "Next, we study checkerboarded lattices"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3) Split grids"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TODO: \n",
    "object types, g.group, g.matrix, g.component\n",
    "\n",
    "random number generator\n",
    "\n",
    "cshifts\n",
    "\n",
    "covariant_shifts\n",
    "\n",
    "stencil\n",
    "\n",
    "accelerator_buffer / blas\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (default)",
   "language": "python",
   "name": "python3-default"
  },
  "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.13.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
