{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The usual magic stuff"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pythran\n",
    "%load_ext pythran.magic"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Test basic types"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "# simple types\n",
    "#pythran export identity(int)\n",
    "#pythran export identity(None)\n",
    "#pythran export identity(str)\n",
    "\n",
    "# parametric types\n",
    "#pythran export identity(int list)\n",
    "#pythran export identity(int set)\n",
    "#pythran export identity(int:str dict)\n",
    "#pythran export identity((int, int, str))\n",
    "\n",
    "# numpy stuff\n",
    "#pythran export identity(int[])\n",
    "#pythran export identity(int[:,:])\n",
    "#pythran export identity(int[][][])\n",
    "\n",
    "def identity(x):\n",
    "    return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "for elem in (int, str, list, set, dict):\n",
    "    assert isinstance(identity(elem()), elem), elem"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert identity(None) is None\n",
    "assert isinstance(identity((1,1,\"1\")), tuple)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Numpy arrays keep the same id when passed through pythran. this is not guaranteed for other containers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy\n",
    "x = numpy.ones(1, dtype=int)\n",
    "assert x is identity(x)\n",
    "\n",
    "y = numpy.ones((1, 1), dtype=int)\n",
    "assert y is identity(y)\n",
    "\n",
    "z = numpy.ones((1, 1, 1), dtype=int)\n",
    "assert z is identity(z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING: Exporting function 'inplace_modification' that modifies its List argument. Beware that this argument won't be modified at Python call site\n",
      "Exporting function 'inplace_modification' that modifies its List argument. Beware that this argument won't be modified at Python call site\n"
     ]
    }
   ],
   "source": [
    "%%pythran\n",
    "#pythran export inplace_modification(int list)\n",
    "def inplace_modification(l):\n",
    "    l[0] = 0\n",
    "    return l"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "l = [1, 2, 3]\n",
    "lp = inplace_modification(l)\n",
    "assert l is not lp\n",
    "assert l != lp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's possible to declare the overloads in a single export using the ``or`` keyword"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export strint(str or int, str or int)\n",
    "def strint(x, y):\n",
    "    return y, x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 1)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "strint(1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('2', '1')"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "strint('1', '2')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('2', 1)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "strint(1, '2')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, '1')"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "strint('1', 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``or`` operator also works inside polymorphic types, but it has lower precedence than ``set``, ``dict`` etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export set_of(int or str set)\n",
    "def set_of(x): return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "set_of(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'1'}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "set_of({'1'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Use  ``[ ]``  to force ordering"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export set_of([int or str] set)\n",
    "def set_of(x): return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{1}"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "set_of({1})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'1'}"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "set_of({'1'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Overload for different scalar types are most of the time not ambiguous:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export scalar(bool)\n",
    "#pythran export scalar(int)\n",
    "#pythran export scalar(float)\n",
    "#pythran export scalar(complex)\n",
    "def scalar(x): return str(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "1\n",
      "1.1\n",
      "(1.1,0)\n"
     ]
    }
   ],
   "source": [
    "print(scalar(True))\n",
    "print(scalar(1))\n",
    "print(scalar(1.1))\n",
    "print(scalar(1.1+0j))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It works fine for scalars of differents size / sign"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export dtype(complex64)\n",
    "#pythran export dtype(complex128)\n",
    "#pythran export dtype(complex256)\n",
    "import numpy as np\n",
    "def dtype(x): return x.real, x.imag"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.5 -1.5\n",
      "1.5 -1.5\n",
      "1.5 -1.5\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "x64r, x64i = dtype(np.complex64(1.5 + -1.5j))\n",
    "print(x64r, x64i)\n",
    "x128r, x128i = dtype(np.complex128(1.5 + -1.5j))\n",
    "print(x128r, x128i)\n",
    "x256r, x264i = dtype(np.complex256(1.5 + -1.5j))\n",
    "print(x256r, x264i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It also works correctly for ndarray of different dimension and dtype:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export array(int8[])\n",
    "#pythran export array(int16[][])\n",
    "#pythran export array(int16[][][])\n",
    "import numpy\n",
    "def array(x): return x.shape, x.itemsize"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "((1,), 1)\n",
      "((1, 1), 2)\n",
      "((1, 1, 1), 2)\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "print(array(np.array([1], dtype=np.int8)))\n",
    "print(array(np.array([[1]], dtype=np.int16)))\n",
    "print(array(np.array([[[1]]], dtype=np.int16)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is however ambiguous to use numpy's dtype that actually have the same sign and size (in that case on a 64bit machine)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<unknown>:2:16 error: Ambiguous overloads\n",
      "\tambiguous(int64)\n",
      "\tambiguous(int).\n"
     ]
    }
   ],
   "source": [
    "code = '''\n",
    "#pythran export ambiguous(int)\n",
    "#pythran export ambiguous(int64)\n",
    "def ambiguous(x): return x\n",
    "'''\n",
    "try:\n",
    "    pythran.compile_pythrancode('dummy_module_name', code)\n",
    "except pythran.syntax.PythranSyntaxError as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And in case of invalid argument types, each overload is printed, as well as some information about the call site."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export some(float32)\n",
    "#pythran export some(int)\n",
    "def some(x): return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Invalid call to pythranized function `some(bool)'\n",
      "Candidates are:\n",
      "\n",
      "    - some(int)\n",
      "    - some(float32)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    some(True)\n",
    "except TypeError as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Overloads are useful to handle function with default parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "# pythran export func(int, str, float64)\n",
    "# pythran export func(int, str)\n",
    "# pythran export func(int, None, float64)\n",
    "# pythran export func(int, None)\n",
    "# pythran export func(int)\n",
    "# pythran export func()\n",
    "\n",
    "def func(a=1, b=None, c=1.0):\n",
    "    return a, b, c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 'hello', 2.0)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func(1, \"hello\", 2.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, None, 1.0)"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's possible to declare multiple entires in the same ``pythran export`` line"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export foo(int), foo(str)\n",
    "def foo(s): return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, '1')"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "foo(1), foo('1')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The pythran export can also be used to export a global variable. But the global variable is not going to be shared, consider it as a read only view!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "# pythran export thing\n",
    "thing = 'stuff that matter'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'stuff that matter'"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "thing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's also possible to ask pythran to export raw function pointer, using the ``capsule`` keyword."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export capsule corp(int, float)\n",
    "def corp(x, y):\n",
    "    return x + y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'<capsule object \"corp(int, float)\" at 0x...'"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "str(corp)[:40] + '...'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pythran accepts pointer type, but it's only meaningful inside a capsule"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export capsule corp(int*, int)\n",
    "def corp(data, size):\n",
    "    return data[size/2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A Pythran function can take a capsule as input, using function type signatures."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export higher_order(int(int), int)\n",
    "def higher_order(f, val):\n",
    "    return f(val)\n",
    "#pythran export capsule dummy(int)\n",
    "def dummy(n):\n",
    "    return n + 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "higher_order(dummy, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Numpy arrays have a restriction on the supported data types: you cannot make arrays of string, sets etc!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<unknown>:2:27 error: Unexpected token `[` at that point.\n"
     ]
    }
   ],
   "source": [
    "code = '''\n",
    "#pythran export invalid(str[])\n",
    "def invalid(x): return x\n",
    "'''\n",
    "try:\n",
    "    pythran.compile_pythrancode('dummy_module_name', code)\n",
    "except pythran.syntax.PythranSyntaxError as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pythran tries its best to provide detailed type information about parameters in case of mismatch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export basic(float32)\n",
    "def basic(x): return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Invalid call to pythranized function `basic(int64[:] (is a view))'\n",
      "Candidates are:\n",
      "\n",
      "    - basic(float32)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    import numpy as np\n",
    "    x = np.arange(10)[::2]\n",
    "    basic(x)\n",
    "except TypeError as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pythran supports views with new axis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export views(float64[:,:])\n",
    "def views(x):\n",
    "    return x.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((5, 1), (1, 5))"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.ones(5)[:, None]\n",
    "y = np.ones(5)[None, :]\n",
    "views(x), views(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When usure of an object type, just print it!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pythran\n",
    "#pythran export dump_type((float, bool) list)\n",
    "#pythran export dump_type((int, complex) list)\n",
    "def dump_type(arg1):\n",
    "    return str([(type(x[0]), type(x[1])) for x in arg1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('[(float, bool)]', '[(int_, complex)]')"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dump_type([(1., True)]), dump_type([(1, 1j)])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
