{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Cython, que no CPython" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No, no nos hemos equivocado en el título, hoy vamos a hablar de Cython.\n", "\n", "¿Qué es Cython?\n", "\n", "Cython son dos cosas:\n", "\n", "* Por una parte, Cython es un lenguaje de programación (un superconjunto de Python) que une Python con el sistema de tipado estático de C y C++.\n", "* Por otra parte, `cython` es un compilador que traduce codigo fuente escrito en Cython en eficiente código C o C++. El código resultante se podría usar como una extensión Python o como un ejecutable." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "¡Guau! ¿Cómo os habéis quedado?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lo que se pretende es, básicamente, aprovechar las fortalezas de Python y C, combinar una sintaxis sencilla con el poder y la velocidad." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Salvando algunas [excepciones](http://docs.cython.org/src/userguide/limitations.html#cython-limitations), el código Python (tanto Python 2 como Python 3) es código Cython válido. Además, Cython añade una serie de palabras clave para poder usar el sistema de tipado de C con Python y que el compilador `cython` pueda generar código C eficiente." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pero, ¿quién usa Cython?\n", "\n", "Pues mira, igual no lo sabes pero seguramente estés usando Cython todos los días. Sage tiene casi medio millón de líneas de Cython (que se dice pronto), Scipy y Pandas más de 20000, scikit-learn unas 15000,..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# ¿Nos empezamos a meter en harina?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La idea principal de este primer acercamiento a Cython será empezar con un código Python que sea nuestro cuello de botella e iremos creando versiones que sean cada vez más rápidas, o eso intentaremos." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo, imaginemos que tenemos que detectar valores mínimos locales dentro de una malla. Los valores mínimos deberán ser simplemente valores más bajos que los que haya en los 8 nodos de su entorno inmediato. En el siguiente gráfico, el nodo en verde será un nodo con un mínimo y en su entorno son todo valores superiores:\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
(2, 0)(2, 1)(2, 2)
(1, 0)(1. 1)(1, 2)
(0, 0)(0, 1)(0, 2)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[INCISO] Los números y porcentajes que veáis a continuación pueden variar levemente dependiendo de la máquina donde se ejecute. Tomad los valores como aproximativos." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como siempre, importamos algunas librerías antes de empezar a picar código:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Creamos una matriz cuadrada relativamente grande (4 millones de elementos)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "np.random.seed(0)\n", "data = np.random.randn(2000, 2000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ya tenemos los datos listos para empezar a trabajar. \n", "\n", "Vamos a crear una función en Python que busque los mínimos tal como los hemos definido." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def busca_min(malla):\n", " minimosx = []\n", " minimosy = []\n", " for i in range(1, malla.shape[1]-1):\n", " for j in range(1, malla.shape[0]-1):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ "Veamos cuanto tarda esta función en mi máquina:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 3.63 s per loop\n" ] } ], "source": [ "%timeit busca_min(data)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ "Buff, tres segundos y pico en un i7... Si tengo que buscar los mínimos en 500 de estos casos me va a tardar casi media hora." ] }, { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ "Por casualidad, vamos a probar numba a ver si es capaz de resolver el problema sin mucho esfuerzo, es código Python muy sencillo en el cual no usamos cosas muy 'extrañas' del lenguaje." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from numba import jit\n", "\n", "@jit\n", "def busca_min_numba(malla):\n", " minimosx = []\n", " minimosy = []\n", " for i in range(1, malla.shape[1]-1):\n", " for j in range(1, malla.shape[0]-1):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 4.97 s per loop\n" ] } ], "source": [ "%timeit busca_min_numba(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ooooops! Parece que la magia de numba no funciona aquí." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos a especificar los tipos de entrada y de salida (y a modificar el output) a ver si mejora algo:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from numba import jit\n", "from numba import int32, float64\n", "\n", "@jit(int32[:,:](float64[:,:]))\n", "def busca_min_numba(malla):\n", " minimosx = []\n", " minimosy = []\n", " for i in range(1, malla.shape[1]-1):\n", " for j in range(1, malla.shape[0]-1):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array([minimosx, minimosy], dtype = np.int32)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 5.25 s per loop\n" ] } ], "source": [ "%timeit busca_min_numba(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pues parece que no, el resultado es del mismo pelo. Usando la opción `nopython` me casca un error un poco feo,... \n", "\n", "Habrá que seguir esperando a que numba esté un poco más maduro. En mis pocas experiencias no he conseguido aun el efecto que buscaba y en la mayoría de los casos obtengo errores muy crípticos. No es que no tenga confianza en la gente que está detrás, solo estoy diciendo que aun no está listo para 'producción'. Esto no pretende ser una guerra Cython/numba, solo he usado numba para ver si a pelo era capaz de mejorar algo el tema. Como no ha sido así, nos olvidamos de numba de momento." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Cythonizando, que es gerundio (toma 1)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lo más sencillo y evidente es usar directamente el compilador `cython` y ver si usando el código python tal cual es un poco más rápido. Para ello, vamos a usar las funciones mágicas que Cython pone a nuestra disposición en el notebook. Solo vamos a hablar de la función mágica `%%cython`, de momento, aunque hay otras." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# antes cythonmagic\n", "%load_ext Cython" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "EL comando `%%cython` nos permite escribir código Cython en una celda. Una vez que ejecutamos la celda, IPython se encarga de coger el código, crear un fichero de código Cython con extensión *.pyx*, compilarlo a C y, si todo está correcto, importar ese fichero para que todo esté disponible dentro del notebook.\n", "\n", "[INCISO] a la función mágica `%%cython` le podemos pasar una serie de argumentos. Veremos alguno en este análisis pero ahora vamos a definir uno que sirve para que podamos nombrar a la funcíon que se crea y compila al vuelo, `-n` o `--name`." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --name probandocython1\n", "import numpy as np\n", "\n", "def busca_min_cython1(malla):\n", " minimosx = []\n", " minimosy = []\n", " for i in range(1, malla.shape[1]-1):\n", " for j in range(1, malla.shape[0]-1):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "El fichero se creará dentro de la carpeta *cython* disponible dentro del directorio resultado de la función `get_ipython_cache_dir`. Veamos la localización del fichero en mi equipo:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from IPython.utils.path import get_ipython_cache_dir" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/home/kiko/.cache/ipython/cython/probandocython1.c\n" ] } ], "source": [ "print(get_ipython_cache_dir() + '/cython/probandocython1.c')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No lo muestro por aquí porque el resultado son más de ¡¡2400!! líneas de código C.\n", "\n", "Veamos ahora lo que tarda." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 3.34 s per loop\n" ] } ], "source": [ "%timeit busca_min_cython1(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bueno, parece que sin hacer mucho esfuerzo hemos conseguido ganar en torno a un 5% - 25% de rendimiento (dependerá del caso). No es gran cosa pero Cython es capaz de mucho más..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Cythonizando, que es gerundio (toma 2)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En esta parte vamos a introducir una de las palabras clave que Cython introduce para extender Python, `cdef`. La palabra clave `cdef` sirve para 'tipar' estáticamente variables en Cython (luego veremos que se usa también para definir funciones). Por ejemplo:\n", "\n", "```Python\n", "cdef int var1, var2\n", "cdef float var3\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En el bloque de código de más arriba he creado dos variables de tipo entero, `var1` y `var2`, y una variable de tipo float, `var3`. Los [tipos anteriores son la nomenclatura C](http://docs.cython.org/src/userguide/language_basics.html#automatic-type-conversions)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos a intentar usar `cdef` con algunos tipos de datos que tenemos dentro de nuestra función. Para empezar, veo evidente que tengo varias listas (`minimosx` y `minimosy`), tenemos los índices de los bucles (`i` y `j`) y voy a convertir los parámetros de los `range` en tipos estáticos (`ii` y `jj`):" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --name probandocython2\n", "import numpy as np\n", "\n", "def busca_min_cython2(malla):\n", " cdef list minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " minimosx = []\n", " minimosy = []\n", " for i in range(1, ii):\n", " for j in range(1, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 3.55 s per loop\n" ] } ], "source": [ "%timeit busca_min_cython2(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vaya decepción... No hemos conseguido gran cosa, tenemos un código un poco más largo y estamos peor que en la **toma 1**.\n", "\n", "En realidad, estamos usando objetos Python como listas (no es un tipo C/C++ puro pero Cython lo declara como puntero a algún tipo `struct` de Python) o numpy arrays y no hemos definido las variables de entrada y de salida.\n", "\n", "[INCISO] Cuando existe un tipo Python y C que tienen el mismo nombre (por ejemplo, `int`) predomina el de C (porque es lo deseable, ¿no?)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Cythonizando, que es gerundio (toma 3)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En Cython existen tres tipos de funciones, las definidas en el espacio Python con `def`, las definidas en el espacio C con `cdef` (sí, lo mismo que usamos para declarar los tipos) y las definidas en ambos espacios con `cpdef`.\n", "\n", "* `def`: ya lo hemos visto y funciona como se espera. Accesible desde Python\n", "* `cdef`: No es accesible desde Python y la tendremos que envolver con una función Python para poder acceder a la misma.\n", "* `cpdef`: Es accesible tanto desde Python como desde C y Cython se encargará de hacer el 'envoltorio' para nosotros. Esto meterá un poco más de código y empeorará levemente el rendimiento.\n", "\n", "Si definimos una función con `cdef` debería ser una función que se usa internamente dentro del módulo Cython que vayamos a crear y que no sea necesario llamar desde Python." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Veamos un ejemplo de lo dicho anteriormente definiendo la salida de la función como tupla:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --name probandocython3\n", "import numpy as np\n", "\n", "cdef tuple cbusca_min_cython3(malla):\n", " cdef list minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " minimosx = []\n", " minimosy = []\n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)\n", "\n", "def busca_min_cython3(malla):\n", " return cbusca_min_cython3(malla)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 3.62 s per loop\n" ] } ], "source": [ "%timeit busca_min_cython3(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vaya, seguimos sin estar muy a gusto con estos resultados.\n", "\n", "Seguimos sin definir el tipo del valor de entrada.\n", "\n", "La función mágica `%%cython` dispone de una serie de funcionalidades entre la que se encuentra `-a` o `--annotate` (además del `-n` o `--name` que ya hemos visto). Si le pasamos este parámetro podremos ver una representación del código con colores marcando las partes más lentas (amarillo más oscuro) y más optmizadas (más claro) o a la velocidad de C (blanco). Vamos a usarlo para saber donde tenemos cuellos de botella (aplicado a nuestra última versión del código):" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "

Generated by Cython 0.22

\n", "
+01: import numpy as np
\n", "
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_numpy, 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "
 02: 
\n", "
+03: cdef tuple cbusca_min_cython3(malla):
\n", "
static PyObject *__pyx_f_46_cython_magic_b76d9f95ffc9db5b7e97e92e04623490_cbusca_min_cython3(PyObject *__pyx_v_malla) {\n",
       "  PyObject *__pyx_v_minimosx = 0;\n",
       "  PyObject *__pyx_v_minimosy = 0;\n",
       "  unsigned int __pyx_v_i;\n",
       "  unsigned int __pyx_v_j;\n",
       "  unsigned int __pyx_v_ii;\n",
       "  unsigned int __pyx_v_jj;\n",
       "  unsigned int __pyx_v_start;\n",
       "  PyObject *__pyx_r = NULL;\n",
       "  __Pyx_RefNannyDeclarations\n",
       "  __Pyx_RefNannySetupContext(\"cbusca_min_cython3\", 0);\n",
       "/* … */\n",
       "  /* function exit code */\n",
       "  __pyx_L1_error:;\n",
       "  __Pyx_XDECREF(__pyx_t_1);\n",
       "  __Pyx_XDECREF(__pyx_t_2);\n",
       "  __Pyx_XDECREF(__pyx_t_8);\n",
       "  __Pyx_XDECREF(__pyx_t_9);\n",
       "  __Pyx_XDECREF(__pyx_t_12);\n",
       "  __Pyx_AddTraceback(\"_cython_magic_b76d9f95ffc9db5b7e97e92e04623490.cbusca_min_cython3\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
       "  __pyx_r = 0;\n",
       "  __pyx_L0:;\n",
       "  __Pyx_XDECREF(__pyx_v_minimosx);\n",
       "  __Pyx_XDECREF(__pyx_v_minimosy);\n",
       "  __Pyx_XGIVEREF(__pyx_r);\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return __pyx_r;\n",
       "}\n",
       "
 04:     cdef list minimosx, minimosy
\n", "
 05:     cdef unsigned int i, j
\n", "
+06:     cdef unsigned int ii = malla.shape[1]-1
\n", "
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_malla, __pyx_n_s_shape); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 6; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_t_2 = __Pyx_GetItemInt(__pyx_t_1, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(__pyx_t_2 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 6; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "  __Pyx_GOTREF(__pyx_t_2);\n",
       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "  __pyx_t_1 = PyNumber_Subtract(__pyx_t_2, __pyx_int_1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 6; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "  __pyx_t_3 = __Pyx_PyInt_As_unsigned_int(__pyx_t_1); if (unlikely((__pyx_t_3 == (unsigned int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 6; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "  __pyx_v_ii = __pyx_t_3;\n",
       "
+07:     cdef unsigned int jj = malla.shape[0]-1
\n", "
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_malla, __pyx_n_s_shape); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_t_2 = __Pyx_GetItemInt(__pyx_t_1, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(__pyx_t_2 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "  __Pyx_GOTREF(__pyx_t_2);\n",
       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "  __pyx_t_1 = PyNumber_Subtract(__pyx_t_2, __pyx_int_1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "  __pyx_t_3 = __Pyx_PyInt_As_unsigned_int(__pyx_t_1); if (unlikely((__pyx_t_3 == (unsigned int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "  __pyx_v_jj = __pyx_t_3;\n",
       "
+08:     cdef unsigned int start = 1
\n", "
  __pyx_v_start = 1;\n",
       "
+09:     minimosx = []
\n", "
  __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_v_minimosx = ((PyObject*)__pyx_t_1);\n",
       "  __pyx_t_1 = 0;\n",
       "
+10:     minimosy = []
\n", "
  __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_v_minimosy = ((PyObject*)__pyx_t_1);\n",
       "  __pyx_t_1 = 0;\n",
       "
+11:     for i in range(start, ii):
\n", "
  __pyx_t_3 = __pyx_v_ii;\n",
       "  for (__pyx_t_4 = __pyx_v_start; __pyx_t_4 < __pyx_t_3; __pyx_t_4+=1) {\n",
       "    __pyx_v_i = __pyx_t_4;\n",
       "
+12:         for j in range(start, jj):
\n", "
    __pyx_t_5 = __pyx_v_jj;\n",
       "    for (__pyx_t_6 = __pyx_v_start; __pyx_t_6 < __pyx_t_5; __pyx_t_6+=1) {\n",
       "      __pyx_v_j = __pyx_t_6;\n",
       "
+13:             if (malla[j, i] < malla[j-1, i-1] and
\n", "
      __pyx_t_1 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __pyx_t_2 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __pyx_t_8 = PyTuple_New(2); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_1);\n",
       "      __Pyx_GIVEREF(__pyx_t_1);\n",
       "      PyTuple_SET_ITEM(__pyx_t_8, 1, __pyx_t_2);\n",
       "      __Pyx_GIVEREF(__pyx_t_2);\n",
       "      __pyx_t_1 = 0;\n",
       "      __pyx_t_2 = 0;\n",
       "      __pyx_t_2 = PyObject_GetItem(__pyx_v_malla, __pyx_t_8); if (unlikely(__pyx_t_2 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      __pyx_t_8 = __Pyx_PyInt_From_long((__pyx_v_j - 1)); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __pyx_t_1 = __Pyx_PyInt_From_long((__pyx_v_i - 1)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __pyx_t_9 = PyTuple_New(2); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      PyTuple_SET_ITEM(__pyx_t_9, 0, __pyx_t_8);\n",
       "      __Pyx_GIVEREF(__pyx_t_8);\n",
       "      PyTuple_SET_ITEM(__pyx_t_9, 1, __pyx_t_1);\n",
       "      __Pyx_GIVEREF(__pyx_t_1);\n",
       "      __pyx_t_8 = 0;\n",
       "      __pyx_t_1 = 0;\n",
       "      __pyx_t_1 = PyObject_GetItem(__pyx_v_malla, __pyx_t_9); if (unlikely(__pyx_t_1 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      __pyx_t_9 = PyObject_RichCompare(__pyx_t_2, __pyx_t_1, Py_LT); __Pyx_XGOTREF(__pyx_t_9); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_t_9); if (unlikely(__pyx_t_10 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      if (__pyx_t_10) {\n",
       "      } else {\n",
       "        __pyx_t_7 = __pyx_t_10;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+14:                 malla[j, i] < malla[j-1, i] and
\n", "
      __pyx_t_9 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __pyx_t_1 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_9);\n",
       "      __Pyx_GIVEREF(__pyx_t_9);\n",
       "      PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_1);\n",
       "      __Pyx_GIVEREF(__pyx_t_1);\n",
       "      __pyx_t_9 = 0;\n",
       "      __pyx_t_1 = 0;\n",
       "      __pyx_t_1 = PyObject_GetItem(__pyx_v_malla, __pyx_t_2); if (unlikely(__pyx_t_1 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      __pyx_t_2 = __Pyx_PyInt_From_long((__pyx_v_j - 1)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __pyx_t_9 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __pyx_t_8 = PyTuple_New(2); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_2);\n",
       "      __Pyx_GIVEREF(__pyx_t_2);\n",
       "      PyTuple_SET_ITEM(__pyx_t_8, 1, __pyx_t_9);\n",
       "      __Pyx_GIVEREF(__pyx_t_9);\n",
       "      __pyx_t_2 = 0;\n",
       "      __pyx_t_9 = 0;\n",
       "      __pyx_t_9 = PyObject_GetItem(__pyx_v_malla, __pyx_t_8); if (unlikely(__pyx_t_9 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      __pyx_t_8 = PyObject_RichCompare(__pyx_t_1, __pyx_t_9, Py_LT); __Pyx_XGOTREF(__pyx_t_8); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_t_8); if (unlikely(__pyx_t_10 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      if (__pyx_t_10) {\n",
       "      } else {\n",
       "        __pyx_t_7 = __pyx_t_10;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+15:                 malla[j, i] < malla[j-1, i+1] and
\n", "
      __pyx_t_8 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __pyx_t_9 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_8);\n",
       "      __Pyx_GIVEREF(__pyx_t_8);\n",
       "      PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_9);\n",
       "      __Pyx_GIVEREF(__pyx_t_9);\n",
       "      __pyx_t_8 = 0;\n",
       "      __pyx_t_9 = 0;\n",
       "      __pyx_t_9 = PyObject_GetItem(__pyx_v_malla, __pyx_t_1); if (unlikely(__pyx_t_9 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      __pyx_t_1 = __Pyx_PyInt_From_long((__pyx_v_j - 1)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __pyx_t_8 = __Pyx_PyInt_From_long((__pyx_v_i + 1)); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1);\n",
       "      __Pyx_GIVEREF(__pyx_t_1);\n",
       "      PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_8);\n",
       "      __Pyx_GIVEREF(__pyx_t_8);\n",
       "      __pyx_t_1 = 0;\n",
       "      __pyx_t_8 = 0;\n",
       "      __pyx_t_8 = PyObject_GetItem(__pyx_v_malla, __pyx_t_2); if (unlikely(__pyx_t_8 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      __pyx_t_2 = PyObject_RichCompare(__pyx_t_9, __pyx_t_8, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely(__pyx_t_10 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      if (__pyx_t_10) {\n",
       "      } else {\n",
       "        __pyx_t_7 = __pyx_t_10;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+16:                 malla[j, i] < malla[j, i-1] and
\n", "
      __pyx_t_2 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __pyx_t_8 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __pyx_t_9 = PyTuple_New(2); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      PyTuple_SET_ITEM(__pyx_t_9, 0, __pyx_t_2);\n",
       "      __Pyx_GIVEREF(__pyx_t_2);\n",
       "      PyTuple_SET_ITEM(__pyx_t_9, 1, __pyx_t_8);\n",
       "      __Pyx_GIVEREF(__pyx_t_8);\n",
       "      __pyx_t_2 = 0;\n",
       "      __pyx_t_8 = 0;\n",
       "      __pyx_t_8 = PyObject_GetItem(__pyx_v_malla, __pyx_t_9); if (unlikely(__pyx_t_8 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      __pyx_t_9 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __pyx_t_2 = __Pyx_PyInt_From_long((__pyx_v_i - 1)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_9);\n",
       "      __Pyx_GIVEREF(__pyx_t_9);\n",
       "      PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_2);\n",
       "      __Pyx_GIVEREF(__pyx_t_2);\n",
       "      __pyx_t_9 = 0;\n",
       "      __pyx_t_2 = 0;\n",
       "      __pyx_t_2 = PyObject_GetItem(__pyx_v_malla, __pyx_t_1); if (unlikely(__pyx_t_2 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      __pyx_t_1 = PyObject_RichCompare(__pyx_t_8, __pyx_t_2, Py_LT); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_10 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      if (__pyx_t_10) {\n",
       "      } else {\n",
       "        __pyx_t_7 = __pyx_t_10;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+17:                 malla[j, i] < malla[j, i+1] and
\n", "
      __pyx_t_1 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __pyx_t_2 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __pyx_t_8 = PyTuple_New(2); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_1);\n",
       "      __Pyx_GIVEREF(__pyx_t_1);\n",
       "      PyTuple_SET_ITEM(__pyx_t_8, 1, __pyx_t_2);\n",
       "      __Pyx_GIVEREF(__pyx_t_2);\n",
       "      __pyx_t_1 = 0;\n",
       "      __pyx_t_2 = 0;\n",
       "      __pyx_t_2 = PyObject_GetItem(__pyx_v_malla, __pyx_t_8); if (unlikely(__pyx_t_2 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      __pyx_t_8 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __pyx_t_1 = __Pyx_PyInt_From_long((__pyx_v_i + 1)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __pyx_t_9 = PyTuple_New(2); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      PyTuple_SET_ITEM(__pyx_t_9, 0, __pyx_t_8);\n",
       "      __Pyx_GIVEREF(__pyx_t_8);\n",
       "      PyTuple_SET_ITEM(__pyx_t_9, 1, __pyx_t_1);\n",
       "      __Pyx_GIVEREF(__pyx_t_1);\n",
       "      __pyx_t_8 = 0;\n",
       "      __pyx_t_1 = 0;\n",
       "      __pyx_t_1 = PyObject_GetItem(__pyx_v_malla, __pyx_t_9); if (unlikely(__pyx_t_1 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      __pyx_t_9 = PyObject_RichCompare(__pyx_t_2, __pyx_t_1, Py_LT); __Pyx_XGOTREF(__pyx_t_9); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_t_9); if (unlikely(__pyx_t_10 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      if (__pyx_t_10) {\n",
       "      } else {\n",
       "        __pyx_t_7 = __pyx_t_10;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+18:                 malla[j, i] < malla[j+1, i-1] and
\n", "
      __pyx_t_9 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __pyx_t_1 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_9);\n",
       "      __Pyx_GIVEREF(__pyx_t_9);\n",
       "      PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_1);\n",
       "      __Pyx_GIVEREF(__pyx_t_1);\n",
       "      __pyx_t_9 = 0;\n",
       "      __pyx_t_1 = 0;\n",
       "      __pyx_t_1 = PyObject_GetItem(__pyx_v_malla, __pyx_t_2); if (unlikely(__pyx_t_1 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      __pyx_t_2 = __Pyx_PyInt_From_long((__pyx_v_j + 1)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __pyx_t_9 = __Pyx_PyInt_From_long((__pyx_v_i - 1)); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __pyx_t_8 = PyTuple_New(2); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_2);\n",
       "      __Pyx_GIVEREF(__pyx_t_2);\n",
       "      PyTuple_SET_ITEM(__pyx_t_8, 1, __pyx_t_9);\n",
       "      __Pyx_GIVEREF(__pyx_t_9);\n",
       "      __pyx_t_2 = 0;\n",
       "      __pyx_t_9 = 0;\n",
       "      __pyx_t_9 = PyObject_GetItem(__pyx_v_malla, __pyx_t_8); if (unlikely(__pyx_t_9 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      __pyx_t_8 = PyObject_RichCompare(__pyx_t_1, __pyx_t_9, Py_LT); __Pyx_XGOTREF(__pyx_t_8); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_t_8); if (unlikely(__pyx_t_10 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      if (__pyx_t_10) {\n",
       "      } else {\n",
       "        __pyx_t_7 = __pyx_t_10;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+19:                 malla[j, i] < malla[j+1, i] and
\n", "
      __pyx_t_8 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __pyx_t_9 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_8);\n",
       "      __Pyx_GIVEREF(__pyx_t_8);\n",
       "      PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_9);\n",
       "      __Pyx_GIVEREF(__pyx_t_9);\n",
       "      __pyx_t_8 = 0;\n",
       "      __pyx_t_9 = 0;\n",
       "      __pyx_t_9 = PyObject_GetItem(__pyx_v_malla, __pyx_t_1); if (unlikely(__pyx_t_9 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      __pyx_t_1 = __Pyx_PyInt_From_long((__pyx_v_j + 1)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      __pyx_t_8 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1);\n",
       "      __Pyx_GIVEREF(__pyx_t_1);\n",
       "      PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_8);\n",
       "      __Pyx_GIVEREF(__pyx_t_8);\n",
       "      __pyx_t_1 = 0;\n",
       "      __pyx_t_8 = 0;\n",
       "      __pyx_t_8 = PyObject_GetItem(__pyx_v_malla, __pyx_t_2); if (unlikely(__pyx_t_8 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      __pyx_t_2 = PyObject_RichCompare(__pyx_t_9, __pyx_t_8, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely(__pyx_t_10 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      if (__pyx_t_10) {\n",
       "      } else {\n",
       "        __pyx_t_7 = __pyx_t_10;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+20:                 malla[j, i] < malla[j+1, i+1]):
\n", "
      __pyx_t_2 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __pyx_t_8 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __pyx_t_9 = PyTuple_New(2); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      PyTuple_SET_ITEM(__pyx_t_9, 0, __pyx_t_2);\n",
       "      __Pyx_GIVEREF(__pyx_t_2);\n",
       "      PyTuple_SET_ITEM(__pyx_t_9, 1, __pyx_t_8);\n",
       "      __Pyx_GIVEREF(__pyx_t_8);\n",
       "      __pyx_t_2 = 0;\n",
       "      __pyx_t_8 = 0;\n",
       "      __pyx_t_8 = PyObject_GetItem(__pyx_v_malla, __pyx_t_9); if (unlikely(__pyx_t_8 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_8);\n",
       "      __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "      __pyx_t_9 = __Pyx_PyInt_From_long((__pyx_v_j + 1)); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_9);\n",
       "      __pyx_t_2 = __Pyx_PyInt_From_long((__pyx_v_i + 1)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_GOTREF(__pyx_t_1);\n",
       "      PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_9);\n",
       "      __Pyx_GIVEREF(__pyx_t_9);\n",
       "      PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_2);\n",
       "      __Pyx_GIVEREF(__pyx_t_2);\n",
       "      __pyx_t_9 = 0;\n",
       "      __pyx_t_2 = 0;\n",
       "      __pyx_t_2 = PyObject_GetItem(__pyx_v_malla, __pyx_t_1); if (unlikely(__pyx_t_2 == NULL)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;};\n",
       "      __Pyx_GOTREF(__pyx_t_2);\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      __pyx_t_1 = PyObject_RichCompare(__pyx_t_8, __pyx_t_2, Py_LT); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "      __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_10 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "      __pyx_t_7 = __pyx_t_10;\n",
       "      __pyx_L8_bool_binop_done:;\n",
       "      if (__pyx_t_7) {\n",
       "
+21:                 minimosx.append(i)
\n", "
        __pyx_t_1 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 21; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "        __Pyx_GOTREF(__pyx_t_1);\n",
       "        __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_minimosx, __pyx_t_1); if (unlikely(__pyx_t_11 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 21; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "        __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "
+22:                 minimosy.append(j)
\n", "
        __pyx_t_1 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 22; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "        __Pyx_GOTREF(__pyx_t_1);\n",
       "        __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_minimosy, __pyx_t_1); if (unlikely(__pyx_t_11 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 22; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "        __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "        goto __pyx_L7;\n",
       "      }\n",
       "      __pyx_L7:;\n",
       "    }\n",
       "  }\n",
       "
 23: 
\n", "
+24:     return np.array(minimosx), np.array(minimosy)
\n", "
  __Pyx_XDECREF(__pyx_r);\n",
       "  __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_2);\n",
       "  __pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_array); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_8);\n",
       "  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "  __pyx_t_2 = NULL;\n",
       "  if (CYTHON_COMPILING_IN_CPYTHON && unlikely(PyMethod_Check(__pyx_t_8))) {\n",
       "    __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_8);\n",
       "    if (likely(__pyx_t_2)) {\n",
       "      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_8);\n",
       "      __Pyx_INCREF(__pyx_t_2);\n",
       "      __Pyx_INCREF(function);\n",
       "      __Pyx_DECREF_SET(__pyx_t_8, function);\n",
       "    }\n",
       "  }\n",
       "  if (!__pyx_t_2) {\n",
       "    __pyx_t_1 = __Pyx_PyObject_CallOneArg(__pyx_t_8, __pyx_v_minimosx); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_1);\n",
       "  } else {\n",
       "    __pyx_t_9 = PyTuple_New(1+1); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_9);\n",
       "    PyTuple_SET_ITEM(__pyx_t_9, 0, __pyx_t_2); __Pyx_GIVEREF(__pyx_t_2); __pyx_t_2 = NULL;\n",
       "    __Pyx_INCREF(__pyx_v_minimosx);\n",
       "    PyTuple_SET_ITEM(__pyx_t_9, 0+1, __pyx_v_minimosx);\n",
       "    __Pyx_GIVEREF(__pyx_v_minimosx);\n",
       "    __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_8, __pyx_t_9, NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_1);\n",
       "    __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "  }\n",
       "  __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;\n",
       "  __pyx_t_9 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_9);\n",
       "  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_9, __pyx_n_s_array); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_2);\n",
       "  __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;\n",
       "  __pyx_t_9 = NULL;\n",
       "  if (CYTHON_COMPILING_IN_CPYTHON && unlikely(PyMethod_Check(__pyx_t_2))) {\n",
       "    __pyx_t_9 = PyMethod_GET_SELF(__pyx_t_2);\n",
       "    if (likely(__pyx_t_9)) {\n",
       "      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2);\n",
       "      __Pyx_INCREF(__pyx_t_9);\n",
       "      __Pyx_INCREF(function);\n",
       "      __Pyx_DECREF_SET(__pyx_t_2, function);\n",
       "    }\n",
       "  }\n",
       "  if (!__pyx_t_9) {\n",
       "    __pyx_t_8 = __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_v_minimosy); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_8);\n",
       "  } else {\n",
       "    __pyx_t_12 = PyTuple_New(1+1); if (unlikely(!__pyx_t_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_12);\n",
       "    PyTuple_SET_ITEM(__pyx_t_12, 0, __pyx_t_9); __Pyx_GIVEREF(__pyx_t_9); __pyx_t_9 = NULL;\n",
       "    __Pyx_INCREF(__pyx_v_minimosy);\n",
       "    PyTuple_SET_ITEM(__pyx_t_12, 0+1, __pyx_v_minimosy);\n",
       "    __Pyx_GIVEREF(__pyx_v_minimosy);\n",
       "    __pyx_t_8 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_t_12, NULL); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_8);\n",
       "    __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;\n",
       "  }\n",
       "  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
       "  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_2);\n",
       "  PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1);\n",
       "  __Pyx_GIVEREF(__pyx_t_1);\n",
       "  PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_8);\n",
       "  __Pyx_GIVEREF(__pyx_t_8);\n",
       "  __pyx_t_1 = 0;\n",
       "  __pyx_t_8 = 0;\n",
       "  __pyx_r = ((PyObject*)__pyx_t_2);\n",
       "  __pyx_t_2 = 0;\n",
       "  goto __pyx_L0;\n",
       "
 25: 
\n", "
+26: def busca_min_cython3(malla):
\n", "
/* Python wrapper */\n",
       "static PyObject *__pyx_pw_46_cython_magic_b76d9f95ffc9db5b7e97e92e04623490_1busca_min_cython3(PyObject *__pyx_self, PyObject *__pyx_v_malla); /*proto*/\n",
       "static PyMethodDef __pyx_mdef_46_cython_magic_b76d9f95ffc9db5b7e97e92e04623490_1busca_min_cython3 = {\"busca_min_cython3\", (PyCFunction)__pyx_pw_46_cython_magic_b76d9f95ffc9db5b7e97e92e04623490_1busca_min_cython3, METH_O, 0};\n",
       "static PyObject *__pyx_pw_46_cython_magic_b76d9f95ffc9db5b7e97e92e04623490_1busca_min_cython3(PyObject *__pyx_self, PyObject *__pyx_v_malla) {\n",
       "  PyObject *__pyx_r = 0;\n",
       "  __Pyx_RefNannyDeclarations\n",
       "  __Pyx_RefNannySetupContext(\"busca_min_cython3 (wrapper)\", 0);\n",
       "  __pyx_r = __pyx_pf_46_cython_magic_b76d9f95ffc9db5b7e97e92e04623490_busca_min_cython3(__pyx_self, ((PyObject *)__pyx_v_malla));\n",
       "\n",
       "  /* function exit code */\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return __pyx_r;\n",
       "}\n",
       "\n",
       "static PyObject *__pyx_pf_46_cython_magic_b76d9f95ffc9db5b7e97e92e04623490_busca_min_cython3(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_malla) {\n",
       "  PyObject *__pyx_r = NULL;\n",
       "  __Pyx_RefNannyDeclarations\n",
       "  __Pyx_RefNannySetupContext(\"busca_min_cython3\", 0);\n",
       "/* … */\n",
       "  /* function exit code */\n",
       "  __pyx_L1_error:;\n",
       "  __Pyx_XDECREF(__pyx_t_1);\n",
       "  __Pyx_AddTraceback(\"_cython_magic_b76d9f95ffc9db5b7e97e92e04623490.busca_min_cython3\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
       "  __pyx_r = NULL;\n",
       "  __pyx_L0:;\n",
       "  __Pyx_XGIVEREF(__pyx_r);\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return __pyx_r;\n",
       "}\n",
       "/* … */\n",
       "  __pyx_tuple_ = PyTuple_Pack(1, __pyx_n_s_malla); if (unlikely(!__pyx_tuple_)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_tuple_);\n",
       "  __Pyx_GIVEREF(__pyx_tuple_);\n",
       "/* … */\n",
       "  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_b76d9f95ffc9db5b7e97e92e04623490_1busca_min_cython3, NULL, __pyx_n_s_cython_magic_b76d9f95ffc9db5b7e); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_busca_min_cython3, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "
+27:     return cbusca_min_cython3(malla)
\n", "
  __Pyx_XDECREF(__pyx_r);\n",
       "  __pyx_t_1 = __pyx_f_46_cython_magic_b76d9f95ffc9db5b7e97e92e04623490_cbusca_min_cython3(__pyx_v_malla); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_r = __pyx_t_1;\n",
       "  __pyx_t_1 = 0;\n",
       "  goto __pyx_L0;\n",
       "
" ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%cython --annotate\n", "import numpy as np\n", "\n", "cdef tuple cbusca_min_cython3(malla):\n", " cdef list minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " minimosx = []\n", " minimosy = []\n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)\n", "\n", "def busca_min_cython3(malla):\n", " return cbusca_min_cython3(malla)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "El `if` parece la parte más lenta. Estamos usando el valor de entrada que no tiene un tipo Cython definido.\n", "\n", "Los bucles parece que están optimizados (las variables envueltas en el bucle las hemos declarado como `unsigned int`).\n", "\n", "Pero todas las partes por las que pasa el numpy array parece que no están muy optimizadas..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Cythonizando, que es gerundio (toma 4)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ahora mismo, haciendo `import numpy as np` tenemos acceso a la funcionalidad Python de numpy. Para poder acceder a la funcionalidad C de numpy hemos de hacer un `cimport` de numpy.\n", "\n", "El `cimport` se usa para importar información especial del módulo numpy en el momento de compilación. Esta información se encuentra en el fichero numpy.pxd que es parte de la distribución Cython. El `cimport` también se usa para poder importar desde la *stdlib* de C.\n", "\n", "Vamos a usar esto para declarar el tipo del array de numpy." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --name probandocython4\n", "import numpy as np\n", "cimport numpy as np\n", "\n", "cpdef tuple busca_min_cython4(np.ndarray[double, ndim = 2] malla):\n", " cdef list minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " minimosx = []\n", " minimosy = []\n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 loops, best of 3: 147 ms per loop\n" ] } ], "source": [ "%timeit busca_min_cython4(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Guauuuu!!! Acabamos de obtener un incremento de entre 25x a 30x veces más rápido." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos a comprobar que el resultado sea el mismo que la función original:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 1 1 1 ..., 1998 1998 1998]\n", "[ 1 3 11 ..., 1968 1977 1985]\n" ] } ], "source": [ "a, b = busca_min(data)\n", "print(a)\n", "print(b)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 1 1 1 ..., 1998 1998 1998]\n", "[ 1 3 11 ..., 1968 1977 1985]\n" ] } ], "source": [ "aa, bb = busca_min_cython4(data)\n", "print(aa)\n", "print(bb)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "print(np.array_equal(a, aa))" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "print(np.array_equal(b, bb))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pues parece que sí :-)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos a ver si hemos dejado la mayoría del código anterior en blanco o más clarito usando `--annotate`." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "

Generated by Cython 0.22

\n", "
+01: import numpy as np
\n", "
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_numpy, 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "/* … */\n",
       "  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "
 02: cimport numpy as np
\n", "
 03: 
\n", "
+04: cpdef tuple busca_min_cython4(np.ndarray[double, ndim = 2] malla):
\n", "
static PyObject *__pyx_pw_46_cython_magic_db10c794e43f00f7b90f23a8e05093c1_1busca_min_cython4(PyObject *__pyx_self, PyObject *__pyx_v_malla); /*proto*/\n",
       "static PyObject *__pyx_f_46_cython_magic_db10c794e43f00f7b90f23a8e05093c1_busca_min_cython4(PyArrayObject *__pyx_v_malla, CYTHON_UNUSED int __pyx_skip_dispatch) {\n",
       "  PyObject *__pyx_v_minimosx = 0;\n",
       "  PyObject *__pyx_v_minimosy = 0;\n",
       "  unsigned int __pyx_v_i;\n",
       "  unsigned int __pyx_v_j;\n",
       "  unsigned int __pyx_v_ii;\n",
       "  unsigned int __pyx_v_jj;\n",
       "  unsigned int __pyx_v_start;\n",
       "  __Pyx_LocalBuf_ND __pyx_pybuffernd_malla;\n",
       "  __Pyx_Buffer __pyx_pybuffer_malla;\n",
       "  PyObject *__pyx_r = NULL;\n",
       "  __Pyx_RefNannyDeclarations\n",
       "  __Pyx_RefNannySetupContext(\"busca_min_cython4\", 0);\n",
       "  __pyx_pybuffer_malla.pybuffer.buf = NULL;\n",
       "  __pyx_pybuffer_malla.refcount = 0;\n",
       "  __pyx_pybuffernd_malla.data = NULL;\n",
       "  __pyx_pybuffernd_malla.rcbuffer = &__pyx_pybuffer_malla;\n",
       "  {\n",
       "    __Pyx_BufFmt_StackElem __pyx_stack[1];\n",
       "    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_malla.rcbuffer->pybuffer, (PyObject*)__pyx_v_malla, &__Pyx_TypeInfo_double, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 4; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  }\n",
       "  __pyx_pybuffernd_malla.diminfo[0].strides = __pyx_pybuffernd_malla.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_malla.diminfo[0].shape = __pyx_pybuffernd_malla.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_malla.diminfo[1].strides = __pyx_pybuffernd_malla.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_malla.diminfo[1].shape = __pyx_pybuffernd_malla.rcbuffer->pybuffer.shape[1];\n",
       "/* … */\n",
       "  /* function exit code */\n",
       "  __pyx_L1_error:;\n",
       "  __Pyx_XDECREF(__pyx_t_1);\n",
       "  __Pyx_XDECREF(__pyx_t_42);\n",
       "  __Pyx_XDECREF(__pyx_t_43);\n",
       "  __Pyx_XDECREF(__pyx_t_44);\n",
       "  __Pyx_XDECREF(__pyx_t_45);\n",
       "  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;\n",
       "    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);\n",
       "    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_malla.rcbuffer->pybuffer);\n",
       "  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}\n",
       "  __Pyx_AddTraceback(\"_cython_magic_db10c794e43f00f7b90f23a8e05093c1.busca_min_cython4\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
       "  __pyx_r = 0;\n",
       "  goto __pyx_L2;\n",
       "  __pyx_L0:;\n",
       "  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_malla.rcbuffer->pybuffer);\n",
       "  __pyx_L2:;\n",
       "  __Pyx_XDECREF(__pyx_v_minimosx);\n",
       "  __Pyx_XDECREF(__pyx_v_minimosy);\n",
       "  __Pyx_XGIVEREF(__pyx_r);\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return __pyx_r;\n",
       "}\n",
       "\n",
       "/* Python wrapper */\n",
       "static PyObject *__pyx_pw_46_cython_magic_db10c794e43f00f7b90f23a8e05093c1_1busca_min_cython4(PyObject *__pyx_self, PyObject *__pyx_v_malla); /*proto*/\n",
       "static PyObject *__pyx_pw_46_cython_magic_db10c794e43f00f7b90f23a8e05093c1_1busca_min_cython4(PyObject *__pyx_self, PyObject *__pyx_v_malla) {\n",
       "  PyObject *__pyx_r = 0;\n",
       "  __Pyx_RefNannyDeclarations\n",
       "  __Pyx_RefNannySetupContext(\"busca_min_cython4 (wrapper)\", 0);\n",
       "  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_malla), __pyx_ptype_5numpy_ndarray, 1, \"malla\", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 4; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __pyx_r = __pyx_pf_46_cython_magic_db10c794e43f00f7b90f23a8e05093c1_busca_min_cython4(__pyx_self, ((PyArrayObject *)__pyx_v_malla));\n",
       "  CYTHON_UNUSED int __pyx_lineno = 0;\n",
       "  CYTHON_UNUSED const char *__pyx_filename = NULL;\n",
       "  CYTHON_UNUSED int __pyx_clineno = 0;\n",
       "\n",
       "  /* function exit code */\n",
       "  goto __pyx_L0;\n",
       "  __pyx_L1_error:;\n",
       "  __pyx_r = NULL;\n",
       "  __pyx_L0:;\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return __pyx_r;\n",
       "}\n",
       "\n",
       "static PyObject *__pyx_pf_46_cython_magic_db10c794e43f00f7b90f23a8e05093c1_busca_min_cython4(CYTHON_UNUSED PyObject *__pyx_self, PyArrayObject *__pyx_v_malla) {\n",
       "  __Pyx_LocalBuf_ND __pyx_pybuffernd_malla;\n",
       "  __Pyx_Buffer __pyx_pybuffer_malla;\n",
       "  PyObject *__pyx_r = NULL;\n",
       "  __Pyx_RefNannyDeclarations\n",
       "  __Pyx_RefNannySetupContext(\"busca_min_cython4\", 0);\n",
       "  __pyx_pybuffer_malla.pybuffer.buf = NULL;\n",
       "  __pyx_pybuffer_malla.refcount = 0;\n",
       "  __pyx_pybuffernd_malla.data = NULL;\n",
       "  __pyx_pybuffernd_malla.rcbuffer = &__pyx_pybuffer_malla;\n",
       "  {\n",
       "    __Pyx_BufFmt_StackElem __pyx_stack[1];\n",
       "    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_malla.rcbuffer->pybuffer, (PyObject*)__pyx_v_malla, &__Pyx_TypeInfo_double, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 4; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  }\n",
       "  __pyx_pybuffernd_malla.diminfo[0].strides = __pyx_pybuffernd_malla.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_malla.diminfo[0].shape = __pyx_pybuffernd_malla.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_malla.diminfo[1].strides = __pyx_pybuffernd_malla.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_malla.diminfo[1].shape = __pyx_pybuffernd_malla.rcbuffer->pybuffer.shape[1];\n",
       "  __Pyx_XDECREF(__pyx_r);\n",
       "  __pyx_t_1 = __pyx_f_46_cython_magic_db10c794e43f00f7b90f23a8e05093c1_busca_min_cython4(__pyx_v_malla, 0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 4; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_r = __pyx_t_1;\n",
       "  __pyx_t_1 = 0;\n",
       "  goto __pyx_L0;\n",
       "\n",
       "  /* function exit code */\n",
       "  __pyx_L1_error:;\n",
       "  __Pyx_XDECREF(__pyx_t_1);\n",
       "  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;\n",
       "    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);\n",
       "    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_malla.rcbuffer->pybuffer);\n",
       "  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}\n",
       "  __Pyx_AddTraceback(\"_cython_magic_db10c794e43f00f7b90f23a8e05093c1.busca_min_cython4\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
       "  __pyx_r = NULL;\n",
       "  goto __pyx_L2;\n",
       "  __pyx_L0:;\n",
       "  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_malla.rcbuffer->pybuffer);\n",
       "  __pyx_L2:;\n",
       "  __Pyx_XGIVEREF(__pyx_r);\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return __pyx_r;\n",
       "}\n",
       "
 05:     cdef list minimosx, minimosy
\n", "
 06:     cdef unsigned int i, j
\n", "
+07:     cdef unsigned int ii = malla.shape[1]-1
\n", "
  __pyx_v_ii = ((__pyx_v_malla->dimensions[1]) - 1);\n",
       "
+08:     cdef unsigned int jj = malla.shape[0]-1
\n", "
  __pyx_v_jj = ((__pyx_v_malla->dimensions[0]) - 1);\n",
       "
+09:     cdef unsigned int start = 1
\n", "
  __pyx_v_start = 1;\n",
       "
+10:     minimosx = []
\n", "
  __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_v_minimosx = ((PyObject*)__pyx_t_1);\n",
       "  __pyx_t_1 = 0;\n",
       "
+11:     minimosy = []
\n", "
  __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_v_minimosy = ((PyObject*)__pyx_t_1);\n",
       "  __pyx_t_1 = 0;\n",
       "
+12:     for i in range(start, ii):
\n", "
  __pyx_t_2 = __pyx_v_ii;\n",
       "  for (__pyx_t_3 = __pyx_v_start; __pyx_t_3 < __pyx_t_2; __pyx_t_3+=1) {\n",
       "    __pyx_v_i = __pyx_t_3;\n",
       "
+13:         for j in range(start, jj):
\n", "
    __pyx_t_4 = __pyx_v_jj;\n",
       "    for (__pyx_t_5 = __pyx_v_start; __pyx_t_5 < __pyx_t_4; __pyx_t_5+=1) {\n",
       "      __pyx_v_j = __pyx_t_5;\n",
       "
+14:             if (malla[j, i] < malla[j-1, i-1] and
\n", "
      __pyx_t_7 = __pyx_v_j;\n",
       "      __pyx_t_8 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_7 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_8 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_10 = (__pyx_v_j - 1);\n",
       "      __pyx_t_11 = (__pyx_v_i - 1);\n",
       "      __pyx_t_9 = -1;\n",
       "      if (__pyx_t_10 < 0) {\n",
       "        __pyx_t_10 += __pyx_pybuffernd_malla.diminfo[0].shape;\n",
       "        if (unlikely(__pyx_t_10 < 0)) __pyx_t_9 = 0;\n",
       "      } else if (unlikely(__pyx_t_10 >= __pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (__pyx_t_11 < 0) {\n",
       "        __pyx_t_11 += __pyx_pybuffernd_malla.diminfo[1].shape;\n",
       "        if (unlikely(__pyx_t_11 < 0)) __pyx_t_9 = 1;\n",
       "      } else if (unlikely(__pyx_t_11 >= __pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_12 = (((*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_7, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_8, __pyx_pybuffernd_malla.diminfo[1].strides)) < (*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_10, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_11, __pyx_pybuffernd_malla.diminfo[1].strides))) != 0);\n",
       "      if (__pyx_t_12) {\n",
       "      } else {\n",
       "        __pyx_t_6 = __pyx_t_12;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+15:                 malla[j, i] < malla[j-1, i] and
\n", "
      __pyx_t_13 = __pyx_v_j;\n",
       "      __pyx_t_14 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_13 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_14 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_15 = (__pyx_v_j - 1);\n",
       "      __pyx_t_16 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (__pyx_t_15 < 0) {\n",
       "        __pyx_t_15 += __pyx_pybuffernd_malla.diminfo[0].shape;\n",
       "        if (unlikely(__pyx_t_15 < 0)) __pyx_t_9 = 0;\n",
       "      } else if (unlikely(__pyx_t_15 >= __pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_16 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_12 = (((*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_13, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_14, __pyx_pybuffernd_malla.diminfo[1].strides)) < (*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_15, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_16, __pyx_pybuffernd_malla.diminfo[1].strides))) != 0);\n",
       "      if (__pyx_t_12) {\n",
       "      } else {\n",
       "        __pyx_t_6 = __pyx_t_12;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+16:                 malla[j, i] < malla[j-1, i+1] and
\n", "
      __pyx_t_17 = __pyx_v_j;\n",
       "      __pyx_t_18 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_17 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_18 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_19 = (__pyx_v_j - 1);\n",
       "      __pyx_t_20 = (__pyx_v_i + 1);\n",
       "      __pyx_t_9 = -1;\n",
       "      if (__pyx_t_19 < 0) {\n",
       "        __pyx_t_19 += __pyx_pybuffernd_malla.diminfo[0].shape;\n",
       "        if (unlikely(__pyx_t_19 < 0)) __pyx_t_9 = 0;\n",
       "      } else if (unlikely(__pyx_t_19 >= __pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (__pyx_t_20 < 0) {\n",
       "        __pyx_t_20 += __pyx_pybuffernd_malla.diminfo[1].shape;\n",
       "        if (unlikely(__pyx_t_20 < 0)) __pyx_t_9 = 1;\n",
       "      } else if (unlikely(__pyx_t_20 >= __pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 16; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_12 = (((*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_17, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_18, __pyx_pybuffernd_malla.diminfo[1].strides)) < (*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_19, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_20, __pyx_pybuffernd_malla.diminfo[1].strides))) != 0);\n",
       "      if (__pyx_t_12) {\n",
       "      } else {\n",
       "        __pyx_t_6 = __pyx_t_12;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+17:                 malla[j, i] < malla[j, i-1] and
\n", "
      __pyx_t_21 = __pyx_v_j;\n",
       "      __pyx_t_22 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_21 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_22 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_23 = __pyx_v_j;\n",
       "      __pyx_t_24 = (__pyx_v_i - 1);\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_23 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (__pyx_t_24 < 0) {\n",
       "        __pyx_t_24 += __pyx_pybuffernd_malla.diminfo[1].shape;\n",
       "        if (unlikely(__pyx_t_24 < 0)) __pyx_t_9 = 1;\n",
       "      } else if (unlikely(__pyx_t_24 >= __pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 17; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_12 = (((*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_21, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_22, __pyx_pybuffernd_malla.diminfo[1].strides)) < (*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_23, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_24, __pyx_pybuffernd_malla.diminfo[1].strides))) != 0);\n",
       "      if (__pyx_t_12) {\n",
       "      } else {\n",
       "        __pyx_t_6 = __pyx_t_12;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+18:                 malla[j, i] < malla[j, i+1] and
\n", "
      __pyx_t_25 = __pyx_v_j;\n",
       "      __pyx_t_26 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_25 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_26 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_27 = __pyx_v_j;\n",
       "      __pyx_t_28 = (__pyx_v_i + 1);\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_27 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (__pyx_t_28 < 0) {\n",
       "        __pyx_t_28 += __pyx_pybuffernd_malla.diminfo[1].shape;\n",
       "        if (unlikely(__pyx_t_28 < 0)) __pyx_t_9 = 1;\n",
       "      } else if (unlikely(__pyx_t_28 >= __pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_12 = (((*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_malla.diminfo[1].strides)) < (*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_28, __pyx_pybuffernd_malla.diminfo[1].strides))) != 0);\n",
       "      if (__pyx_t_12) {\n",
       "      } else {\n",
       "        __pyx_t_6 = __pyx_t_12;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+19:                 malla[j, i] < malla[j+1, i-1] and
\n", "
      __pyx_t_29 = __pyx_v_j;\n",
       "      __pyx_t_30 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_29 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_30 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_31 = (__pyx_v_j + 1);\n",
       "      __pyx_t_32 = (__pyx_v_i - 1);\n",
       "      __pyx_t_9 = -1;\n",
       "      if (__pyx_t_31 < 0) {\n",
       "        __pyx_t_31 += __pyx_pybuffernd_malla.diminfo[0].shape;\n",
       "        if (unlikely(__pyx_t_31 < 0)) __pyx_t_9 = 0;\n",
       "      } else if (unlikely(__pyx_t_31 >= __pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (__pyx_t_32 < 0) {\n",
       "        __pyx_t_32 += __pyx_pybuffernd_malla.diminfo[1].shape;\n",
       "        if (unlikely(__pyx_t_32 < 0)) __pyx_t_9 = 1;\n",
       "      } else if (unlikely(__pyx_t_32 >= __pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_12 = (((*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_29, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_30, __pyx_pybuffernd_malla.diminfo[1].strides)) < (*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_31, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_32, __pyx_pybuffernd_malla.diminfo[1].strides))) != 0);\n",
       "      if (__pyx_t_12) {\n",
       "      } else {\n",
       "        __pyx_t_6 = __pyx_t_12;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+20:                 malla[j, i] < malla[j+1, i] and
\n", "
      __pyx_t_33 = __pyx_v_j;\n",
       "      __pyx_t_34 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_33 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_34 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_35 = (__pyx_v_j + 1);\n",
       "      __pyx_t_36 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (__pyx_t_35 < 0) {\n",
       "        __pyx_t_35 += __pyx_pybuffernd_malla.diminfo[0].shape;\n",
       "        if (unlikely(__pyx_t_35 < 0)) __pyx_t_9 = 0;\n",
       "      } else if (unlikely(__pyx_t_35 >= __pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_36 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_12 = (((*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_33, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_34, __pyx_pybuffernd_malla.diminfo[1].strides)) < (*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_35, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_36, __pyx_pybuffernd_malla.diminfo[1].strides))) != 0);\n",
       "      if (__pyx_t_12) {\n",
       "      } else {\n",
       "        __pyx_t_6 = __pyx_t_12;\n",
       "        goto __pyx_L8_bool_binop_done;\n",
       "      }\n",
       "
+21:                 malla[j, i] < malla[j+1, i+1]):
\n", "
      __pyx_t_37 = __pyx_v_j;\n",
       "      __pyx_t_38 = __pyx_v_i;\n",
       "      __pyx_t_9 = -1;\n",
       "      if (unlikely(__pyx_t_37 >= (size_t)__pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (unlikely(__pyx_t_38 >= (size_t)__pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 21; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_39 = (__pyx_v_j + 1);\n",
       "      __pyx_t_40 = (__pyx_v_i + 1);\n",
       "      __pyx_t_9 = -1;\n",
       "      if (__pyx_t_39 < 0) {\n",
       "        __pyx_t_39 += __pyx_pybuffernd_malla.diminfo[0].shape;\n",
       "        if (unlikely(__pyx_t_39 < 0)) __pyx_t_9 = 0;\n",
       "      } else if (unlikely(__pyx_t_39 >= __pyx_pybuffernd_malla.diminfo[0].shape)) __pyx_t_9 = 0;\n",
       "      if (__pyx_t_40 < 0) {\n",
       "        __pyx_t_40 += __pyx_pybuffernd_malla.diminfo[1].shape;\n",
       "        if (unlikely(__pyx_t_40 < 0)) __pyx_t_9 = 1;\n",
       "      } else if (unlikely(__pyx_t_40 >= __pyx_pybuffernd_malla.diminfo[1].shape)) __pyx_t_9 = 1;\n",
       "      if (unlikely(__pyx_t_9 != -1)) {\n",
       "        __Pyx_RaiseBufferIndexError(__pyx_t_9);\n",
       "        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 21; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "      }\n",
       "      __pyx_t_12 = (((*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_37, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_38, __pyx_pybuffernd_malla.diminfo[1].strides)) < (*__Pyx_BufPtrStrided2d(double *, __pyx_pybuffernd_malla.rcbuffer->pybuffer.buf, __pyx_t_39, __pyx_pybuffernd_malla.diminfo[0].strides, __pyx_t_40, __pyx_pybuffernd_malla.diminfo[1].strides))) != 0);\n",
       "      __pyx_t_6 = __pyx_t_12;\n",
       "      __pyx_L8_bool_binop_done:;\n",
       "      if (__pyx_t_6) {\n",
       "
+22:                 minimosx.append(i)
\n", "
        __pyx_t_1 = __Pyx_PyInt_From_unsigned_int(__pyx_v_i); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 22; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "        __Pyx_GOTREF(__pyx_t_1);\n",
       "        __pyx_t_41 = __Pyx_PyList_Append(__pyx_v_minimosx, __pyx_t_1); if (unlikely(__pyx_t_41 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 22; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "        __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "
+23:                 minimosy.append(j)
\n", "
        __pyx_t_1 = __Pyx_PyInt_From_unsigned_int(__pyx_v_j); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "        __Pyx_GOTREF(__pyx_t_1);\n",
       "        __pyx_t_41 = __Pyx_PyList_Append(__pyx_v_minimosy, __pyx_t_1); if (unlikely(__pyx_t_41 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "        __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
       "        goto __pyx_L7;\n",
       "      }\n",
       "      __pyx_L7:;\n",
       "    }\n",
       "  }\n",
       "
 24: 
\n", "
+25:     return np.array(minimosx), np.array(minimosy)
\n", "
  __Pyx_XDECREF(__pyx_r);\n",
       "  __pyx_t_42 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_42)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_42);\n",
       "  __pyx_t_43 = __Pyx_PyObject_GetAttrStr(__pyx_t_42, __pyx_n_s_array); if (unlikely(!__pyx_t_43)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_43);\n",
       "  __Pyx_DECREF(__pyx_t_42); __pyx_t_42 = 0;\n",
       "  __pyx_t_42 = NULL;\n",
       "  if (CYTHON_COMPILING_IN_CPYTHON && unlikely(PyMethod_Check(__pyx_t_43))) {\n",
       "    __pyx_t_42 = PyMethod_GET_SELF(__pyx_t_43);\n",
       "    if (likely(__pyx_t_42)) {\n",
       "      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_43);\n",
       "      __Pyx_INCREF(__pyx_t_42);\n",
       "      __Pyx_INCREF(function);\n",
       "      __Pyx_DECREF_SET(__pyx_t_43, function);\n",
       "    }\n",
       "  }\n",
       "  if (!__pyx_t_42) {\n",
       "    __pyx_t_1 = __Pyx_PyObject_CallOneArg(__pyx_t_43, __pyx_v_minimosx); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_1);\n",
       "  } else {\n",
       "    __pyx_t_44 = PyTuple_New(1+1); if (unlikely(!__pyx_t_44)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_44);\n",
       "    PyTuple_SET_ITEM(__pyx_t_44, 0, __pyx_t_42); __Pyx_GIVEREF(__pyx_t_42); __pyx_t_42 = NULL;\n",
       "    __Pyx_INCREF(__pyx_v_minimosx);\n",
       "    PyTuple_SET_ITEM(__pyx_t_44, 0+1, __pyx_v_minimosx);\n",
       "    __Pyx_GIVEREF(__pyx_v_minimosx);\n",
       "    __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_43, __pyx_t_44, NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_1);\n",
       "    __Pyx_DECREF(__pyx_t_44); __pyx_t_44 = 0;\n",
       "  }\n",
       "  __Pyx_DECREF(__pyx_t_43); __pyx_t_43 = 0;\n",
       "  __pyx_t_44 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_44)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_44);\n",
       "  __pyx_t_42 = __Pyx_PyObject_GetAttrStr(__pyx_t_44, __pyx_n_s_array); if (unlikely(!__pyx_t_42)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_42);\n",
       "  __Pyx_DECREF(__pyx_t_44); __pyx_t_44 = 0;\n",
       "  __pyx_t_44 = NULL;\n",
       "  if (CYTHON_COMPILING_IN_CPYTHON && unlikely(PyMethod_Check(__pyx_t_42))) {\n",
       "    __pyx_t_44 = PyMethod_GET_SELF(__pyx_t_42);\n",
       "    if (likely(__pyx_t_44)) {\n",
       "      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_42);\n",
       "      __Pyx_INCREF(__pyx_t_44);\n",
       "      __Pyx_INCREF(function);\n",
       "      __Pyx_DECREF_SET(__pyx_t_42, function);\n",
       "    }\n",
       "  }\n",
       "  if (!__pyx_t_44) {\n",
       "    __pyx_t_43 = __Pyx_PyObject_CallOneArg(__pyx_t_42, __pyx_v_minimosy); if (unlikely(!__pyx_t_43)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_43);\n",
       "  } else {\n",
       "    __pyx_t_45 = PyTuple_New(1+1); if (unlikely(!__pyx_t_45)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_45);\n",
       "    PyTuple_SET_ITEM(__pyx_t_45, 0, __pyx_t_44); __Pyx_GIVEREF(__pyx_t_44); __pyx_t_44 = NULL;\n",
       "    __Pyx_INCREF(__pyx_v_minimosy);\n",
       "    PyTuple_SET_ITEM(__pyx_t_45, 0+1, __pyx_v_minimosy);\n",
       "    __Pyx_GIVEREF(__pyx_v_minimosy);\n",
       "    __pyx_t_43 = __Pyx_PyObject_Call(__pyx_t_42, __pyx_t_45, NULL); if (unlikely(!__pyx_t_43)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "    __Pyx_GOTREF(__pyx_t_43);\n",
       "    __Pyx_DECREF(__pyx_t_45); __pyx_t_45 = 0;\n",
       "  }\n",
       "  __Pyx_DECREF(__pyx_t_42); __pyx_t_42 = 0;\n",
       "  __pyx_t_42 = PyTuple_New(2); if (unlikely(!__pyx_t_42)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}\n",
       "  __Pyx_GOTREF(__pyx_t_42);\n",
       "  PyTuple_SET_ITEM(__pyx_t_42, 0, __pyx_t_1);\n",
       "  __Pyx_GIVEREF(__pyx_t_1);\n",
       "  PyTuple_SET_ITEM(__pyx_t_42, 1, __pyx_t_43);\n",
       "  __Pyx_GIVEREF(__pyx_t_43);\n",
       "  __pyx_t_1 = 0;\n",
       "  __pyx_t_43 = 0;\n",
       "  __pyx_r = ((PyObject*)__pyx_t_42);\n",
       "  __pyx_t_42 = 0;\n",
       "  goto __pyx_L0;\n",
       "
" ], "text/plain": [ "" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%cython --annotate\n", "import numpy as np\n", "cimport numpy as np\n", "\n", "cpdef tuple busca_min_cython4(np.ndarray[double, ndim = 2] malla):\n", " cdef list minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " minimosx = []\n", " minimosy = []\n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vemos que muchas de las partes oscuras ahora son más claras!!! Pero parece que sigue quedando espacio para la mejora." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Cythonizando, que es gerundio (toma 5)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos a ver si definiendo el tipo del resultado de la función como un numpy array en lugar de como una tupla nos introduce alguna mejora:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --name probandocython5\n", "import numpy as np\n", "cimport numpy as np\n", "\n", "cpdef np.ndarray[int, ndim = 2] busca_min_cython5(np.ndarray[double, ndim = 2] malla):\n", " cdef list minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " minimosx = []\n", " minimosy = []\n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array([minimosx, minimosy])" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 loops, best of 3: 137 ms per loop\n" ] } ], "source": [ "%timeit busca_min_cython5(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vaya, parece que con respecto a la versión anterior solo obtenemos una ganancia de un 2% - 4%." ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Cythonizando, que es gerundio (toma 6)." ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Vamos a dejar de usar listas y vamos a usar numpy arrays vacios que iremos 'rellenando' con `numpy.append`. A ver si usando todo numpy arrays conseguimos algún tipo de mejora:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%%cython --name probandocython6\n", "import numpy as np\n", "cimport numpy as np\n", "\n", "cpdef tuple busca_min_cython6(np.ndarray[double, ndim = 2] malla):\n", " cdef np.ndarray[long, ndim = 1] minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " minimosx = np.array([], dtype = np.int)\n", " minimosy = np.array([], dtype = np.int)\n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " np.append(minimosx, i)\n", " np.append(minimosy, j)\n", "\n", " return minimosx, minimosy" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 5.59 s per loop\n" ] } ], "source": [ "%timeit busca_min_cython6(data)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": true }, "outputs": [], "source": [ "np.append?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En realidad, en la anterior porción de código estoy usando algo muy ineficiente. La función `numpy.append` no funciona como una lista a la que vas anexando elementos. Lo que estamos haciendo en realidad es crear copias del array existente para convertirlo a un nuevo array con un elemento nuevo. Esto no es lo que pretendiamos!!!!" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Cythonizando, que es gerundio (toma 7)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En Python existen [arrays](https://docs.python.org/3.4/library/array.html) eficientes para valores numéricos (según reza la documentación) que también pueden ser usados de la forma en que estoy usando las listas en mi función (arrays vacios a los que les vamos añadiendo elementos). Vamos a usarlos con Cython." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --name probandocython7\n", "import numpy as np\n", "cimport numpy as np\n", "from cpython cimport array as c_array\n", "from array import array\n", "\n", "cpdef tuple busca_min_cython7(np.ndarray[double, ndim = 2] malla):\n", " cdef c_array.array minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " minimosx = array('L', [])\n", " minimosy = array('L', []) \n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 loops, best of 3: 98.1 ms per loop\n" ] } ], "source": [ "%timeit busca_min_cython7(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parece que hemos ganado otro 25% - 30% con respecto a lo anterior más eficiente que habíamos conseguido. Con respecto a la implementación inicial en Python puro tenemos una mejora de 30x - 35x veces la velocidad inicial.\n", "\n", "Vamos a comprobar si seguimos teniendo los mismos resultados." ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 1 1 1 ..., 1998 1998 1998]\n", "[ 1 3 11 ..., 1968 1977 1985]\n", "[ 1 1 1 ..., 1998 1998 1998]\n", "[ 1 3 11 ..., 1968 1977 1985]\n", "True\n", "True\n" ] } ], "source": [ "a, b = busca_min(data)\n", "print(a)\n", "print(b)\n", "aa, bb = busca_min_cython7(data)\n", "print(aa)\n", "print(bb)\n", "print(np.array_equal(a, aa))\n", "print(np.array_equal(b, bb))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "¿Qué pasa si el tamaño del array se incrementa?" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 24.6 s per loop\n", "1 loops, best of 3: 687 ms per loop\n" ] } ], "source": [ "data2 = np.random.randn(5000, 5000)\n", "%timeit busca_min(data2)\n", "%timeit busca_min_cython7(data2)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 1 1 1 ..., 4998 4998 4998]\n", "[ 7 12 18 ..., 4975 4978 4983]\n", "[ 1 1 1 ..., 4998 4998 4998]\n", "[ 7 12 18 ..., 4975 4978 4983]\n", "True\n", "True\n" ] } ], "source": [ "a, b = busca_min(data2)\n", "print(a)\n", "print(b)\n", "aa, bb = busca_min_cython7(data2)\n", "print(aa)\n", "print(bb)\n", "print(np.array_equal(a, aa))\n", "print(np.array_equal(b, bb))" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ "Parece que al ir aumentando el tamaño de los datos de entrada a la función los números son consistentes y el rendimiento se mantiene. En este caso concreto parece que ya hemos llegado a rendimientos de más de ¡¡35x!! con respecto a la implementación inicial." ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Cythonizando, que es gerundio (toma 8)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos usar [directivas de compilación](http://docs.cython.org/src/reference/compilation.html#compiler-directives) que ayuden al compilador a decidir mejor qué es lo que tiene que hacer. Entre ellas se encuentra una opción que es `boundscheck` que evita mirar la posibilidad de obtener `IndexError` asumiendo que el código está libre de estos errores de indexación. Lo vamos a usar conjuntamente con `wraparound`. Esta última opción se encarga de evitar mirar indexaciones relativas al final del iterable (por ejemplo, `mi_iterable[-1]`). En este caso concreto, la segunda opción no aporta nada de mejora de rendimiento pero la dijamos ya que la hemos probado." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --name probandocython8\n", "import numpy as np\n", "cimport numpy as np\n", "from cpython cimport array as c_array\n", "from array import array\n", "cimport cython\n", "\n", "@cython.boundscheck(False) \n", "@cython.wraparound(False)\n", "cpdef tuple busca_min_cython8(np.ndarray[double, ndim = 2] malla):\n", " cdef c_array.array minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " minimosx = array('L', [])\n", " minimosy = array('L', []) \n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 loops, best of 3: 94.3 ms per loop\n" ] } ], "source": [ "%timeit busca_min_cython8(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parece que hemos conseguido arañar otro poquito de rendimiento." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Cythonizando, que es gerundio (toma 9)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En lugar de usar numpy arrays vamos a usar [*memoryviews*](http://docs.cython.org/src/userguide/memoryviews.html#typed-memoryviews). Los *memoryviews* son arrays de acceso rápido. Si solo queremos almacenar cosas y no necesitamos ninguna de las características de un numpy array pueden ser una buena solución. Si necesitamos alguna funcionalidad extra siempre lo podemos convertir en un numpy array usando `numpy.asarray`." ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --name probandocython9\n", "import numpy as np\n", "cimport numpy as np\n", "from cpython cimport array as c_array\n", "from array import array\n", "cimport cython\n", "\n", "@cython.boundscheck(False) \n", "@cython.wraparound(False)\n", "#cpdef tuple busca_min_cython9(np.ndarray[double, ndim = 2] malla):\n", "cpdef tuple busca_min_cython9(double [:,:] malla):\n", " cdef c_array.array minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " #cdef float [:, :] malla_view = malla\n", " minimosx = array('L', [])\n", " minimosy = array('L', []) \n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 loops, best of 3: 97.6 ms per loop\n" ] } ], "source": [ "%timeit busca_min_cython9(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parece que, virtualmente, el rendimiento es parecido a lo que ya teniamos por lo que parece que nos hemos quedado igual." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Bonus track" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Voy a intentar usar pypy (2.4 (CPython 2.7)) conjuntamente con numpypy para ver lo que conseguimos." ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 1.76405235 0.40015721 0.97873798 ..., 0.15843385 -1.14190142\n", " -1.31097037]\n", " [-1.53292105 -1.71197016 0.04613506 ..., -0.03057244 1.57708821\n", " -0.8128021 ]\n", " [ 0.61334917 1.84369998 0.27109098 ..., -0.53788475 0.39344443\n", " 0.28651827]\n", " ..., \n", " [-0.17117027 0.57332063 -0.89516715 ..., -0.01409412 1.28756456\n", " -0.6953778 ]\n", " [-1.53627571 0.57441228 -0.20564476 ..., 0.90499929 0.51428298\n", " 0.72148202]\n", " [ 0.51262101 -0.90758583 1.78121159 ..., -1.12554283 0.95170926\n", " -1.15237806]]\n", "(443641, 443641)\n", "[ 1 1 1 ..., 1998 1998 1998]\n", "[ 1 3 11 ..., 1968 1977 1985]\n", "0.3795211339\n" ] } ], "source": [ "%%pypy\n", "import numpy as np\n", "import time\n", "\n", "np.random.seed(0)\n", "data = np.random.randn(2000,2000)\n", "\n", "def busca_min(malla):\n", " minimosx = []\n", " minimosy = []\n", " for i in range(1, malla.shape[1]-1):\n", " for j in range(1, malla.shape[0]-1):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)\n", "\n", "resx, resy = busca_min(data)\n", "print(data)\n", "print(len(resx), len(resy))\n", "print(resx)\n", "print(resy)\n", "\n", "t = []\n", "for i in range(100):\n", " t0 = time.time()\n", " busca_min(data)\n", " t1 = time.time() - t0\n", " t.append(t1)\n", "print(sum(t) / 100.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "El último valor del output anterior es el tiempo promedio después de repetir el cálculo 100 veces.\n", "\n", "Wow!! Parece que sin hacer modificaciones tenemos que el resultado es 10x - 15x veces más rápido que el obtenido usando la función inicial. Y llega a ser solo 3.5x veces más lento que lo que hemos conseguido con Cython." ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Resumen de resultados." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos a ver los resultados completos en un breve resumen. Primero vamos a ver los tiempos de las diferentes versiones de la función `busca_min_xxx`:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 3.67 s per loop\n", "1 loops, best of 3: 5.34 s per loop\n", "1 loops, best of 3: 3.41 s per loop\n", "1 loops, best of 3: 3.54 s per loop\n", "1 loops, best of 3: 3.65 s per loop\n", "10 loops, best of 3: 139 ms per loop\n", "10 loops, best of 3: 136 ms per loop\n", "1 loops, best of 3: 5.65 s per loop\n", "10 loops, best of 3: 95.4 ms per loop\n", "10 loops, best of 3: 89 ms per loop\n", "10 loops, best of 3: 92.3 ms per loop\n" ] } ], "source": [ "funcs = [busca_min, busca_min_numba, busca_min_cython1,\n", " busca_min_cython2, busca_min_cython3,\n", " busca_min_cython4, busca_min_cython5,\n", " busca_min_cython6, busca_min_cython7,\n", " busca_min_cython8, busca_min_cython9]\n", "t = []\n", "for func in funcs:\n", " res = %timeit -o func(data)\n", " t.append(res.best)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": [ "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\n", "eWqbR2HDjysfq6rfW8/ldya2eRQ2PJMHVNWNVfWh9X3oyRPb3bQNPqZ85/r/x9m3zx+nx9sN/1/5\n", "6fX3oNuq6o1V9ScmtrtpG57Jdet5fKCqrpvYJpdOYF05TvqxoGeSPGzX+51kz1ldLtFrk1w7tK2j\n", "dCabmcnXkvxEd39Xkicn+dGqetTAdo/KmWxgLt391STP7O7HJPnuJM+sqqdd7naPyJls7v6TJNcl\n", "uT3H73HoTDY3l05yursf291PGtrmUTiTzc3kFUl+rbsfldV96Li85vtMNvOY8uH1/4/HJnl8kq8k\n", "+eXL3e4ROpMNzKWqrk7y4iSP6+5HZ3WOgL95uds9ImeymZn8xSR/J8kTk/ylJM+uqj9/udvl0gms\n", "Bamqq9fP3P3i+hmIt1bVPdef++YeqKp6cFV9dH35TFX9SlW9rao+WlU/VlU/WVXvr6rfrqpv3/VX\n", "/K31s2C3VdUT11//pKp69/r6/62qHnnkN/w8quoFVXXreo/AL1XVfavqD9Znt0xV3X/9/nOTPCHJ\n", "G9a3457rTby0qt63fpb4O9df88D1vG5dz+fR649fv95TdVNVfaSqXnp2Hd39ziR/fLS3fn9LmEl3\n", "f7K7b1lf/lJWPwQ9LFu0hLkkSXd/ZX3xHll90//sUc3gXEuZSVU9PMmzkrw6s9F2SZYyl7PLOarb\n", "fSFLmElVfVuSp3f3DcnqJFvd/fmjnsVZS5jJOa5J8pHuvuMIbv55LWQuX8jqib57r//eeyf530c5\n", "h90WMpNHJXlPd3+1u7+R5LeS/LUjHQTfqru9LeQtydVZPWh89/r9/5Dk+evLN2X1bE2SPDjJR9eX\n", "zyT5H0nus/7455O8ZP25n09y3fryTpJ/s7789CS3rS/fL8lV68vXJLlx23NYr+W7svoVAQ9cv/+A\n", "9Z83JPmh9eWXJPln585n/f5Hk/zo+vKPJHnV+vIvJPmH68vPTHLz+vL1Sd6V5O5JHpTkD8/OZde/\n", "zW1mctdMds3lfyW5r7l0snrS6pYkX0zyc2bSSfKfkjw2yTOSvNl96Jtz+YMkNyd5b5IXX+kzSfKY\n", "JO/J6oiB9yd5VZJ7X8kzOWdNNyT5u+4/37z/vCSrx9lPJ3n9lT6TrALrw0kemFVw/naSV2zz/8uV\n", "/mYP1vJ8tLt/b335fVn9AHuQm7r7y939h0k+l+Ts6xxu2/X1neRNyTf3yNy/qu6f5AFJbqzV64t+\n", "PqsHiyX43iT/sbs/myTd/bn1x1+d5EXry2ey+mZ81rnPBv+X9Z/vz11zeGqS16+3eVOSB1XV/bKa\n", "z1u6+2vd/UdZPWg/ZOrGDFnUTKrqvkluzCriv3S5N+4yLGYu3X1nrw4RfHiS76mq0wO371IsYSZ/\n", "uqqeneTT3X3zPtvfhiXM5ex96Gm9OvTrB7I6zPbpl33rLs1SZnIqyeOSvLK7H5fky0l+auD2XYql\n", "zGS14ap7JHlOVk9WbNMi5lKrQ9/+3vrrH5bkvlX1/IHbdykWMZNe/Qqln03ytiS/ntWTN3cO3D4u\n", "kcBanv+76/I3snpmIkm+nrv+ve6Zb7X7a+7c9f6dOeB3nSX5J0ne3qvjmJ+zz7a3pbPPD2Td/e4k\n", "V69/cL2qu28/52t2OzuHb+Rb53C+H/T+367L537NEixmJlV19yT/Ocm/6+5fOewN2JDFzGXX3/35\n", "JG/J6nCQbVjKTJ6S5AdrdUjzm5J8b1W97rA3YgOWMpd09yfWf34mq9fVbOt1WEuZyceTfLy7f3f9\n", "8RuzCq5tWMpMzvqBJO9b/1/ZpiXM5e5ZPa6+u7v/qLu/nlWgPOWwN2LYEmZy9jHlhu5+Qnc/I6sn\n", "2z982BvBPIG1fGfvYB/LXT+sPfciv/bs5b+RJLV64f3nuvsLSe6f5BPr67woy/GOJH+91mfXqW89\n", "y87rkrwhq13wZ30xq9tykHcmef56m6eTfKa7v5hlPLt+kEXMpKoqyWuS3N7dL7/I27AJS5nLg6vq\n", "AevL90ryl7N6FnEbljCT7u5/0N2P6O7vyOpF6O/o7hdc7I0ZtIS5pKruvX42OlV1nyTfn9URB9uw\n", "iJl09yeT3FF3vQ74miQfPPzNGLWImezyvKyPQNmyJcylk/x+kidX1b3W34+uyeokOtuwhJlkfb0/\n", "tf7zzyb5q0neeOhbwTiBtTznPrNx9v1/nuRHqur9WR1327s+3/tc/9zPdZKvrr/+lUn+9vrjP5fk\n", "ZeuPX7XP378V62d7/mmS36qqW7K6/We9Mcm351u/4fzbJP/6nBeOfnNzuet2XZ/k8VV1a5KfSfLC\n", "fa7zLarqTUneneSRVXVHVW0lRBc0k6cm+eGszpJ39vTBWzvL4oLm8tAk71iv4T1Zvd7o7Zd6uy7H\n", "gmayZ2kXcTPGLWguD0nyzl3/V361u992qbfrcixoJkny0qxOAHBrVmcR/JlLuU2Xa0kzWQf4Nbnr\n", "MLKtWcpcuvvWrOLlvUnOvqTiFy/tVl2epcxk7caq+mCS/5rV6/W+cEk3ihHVvYifp+HQanUmnud0\n", "9wsPvPIVwkz2Zy57mcn+zGUvM9nLTPZnLnuZyZVtaa8xgQuqql9I8leyOu0zMZPzMZe9zGR/5rKX\n", "mexlJvszl73MBHuwtqSqHpTkN/f51PedPRvNlcIs9jKT/ZnLXmayP3PZy0z2MpP9mcteZsLFEFgA\n", "AABDnOQCAABgiMACAAAYIrAAAACGCCwAAIAhAgsAAGDI/wdVxu0UqXugmwAAAABJRU5ErkJggg==\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "index = np.arange(len(t))\n", "plt.figure(figsize = (12, 6))\n", "plt.bar(index, t)\n", "plt.xticks(index + 0.4, [func.__name__[9:] for func in funcs])\n", "plt.tight_layout()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En el gráfico anterior, la primera barra corresponde a la función de partida (`busca_min`). Recordemos que la versión de pypy ha tardado unos 0.38 segundos." ] }, { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ "Y ahora vamos a ver los tiempos entre `busca_min` (la versión original) y la última versión de cython que hemos creado, `busca_min_cython9` usando diferentes tamaños de la matriz de entrada:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10000 loops, best of 3: 67.9 µs per loop\n", "The slowest run took 4.77 times longer than the fastest. This could mean that an intermediate result is being cached \n", "100000 loops, best of 3: 5.13 µs per loop\n", "100 loops, best of 3: 8.65 ms per loop\n", "10000 loops, best of 3: 177 µs per loop\n", "1 loops, best of 3: 223 ms per loop\n", "100 loops, best of 3: 5.51 ms per loop\n", "1 loops, best of 3: 890 ms per loop\n", "10 loops, best of 3: 26.6 ms per loop\n", "1 loops, best of 3: 3.64 s per loop\n", "10 loops, best of 3: 92.8 ms per loop\n", "1 loops, best of 3: 22.8 s per loop\n", "1 loops, best of 3: 605 ms per loop\n" ] } ], "source": [ "tamanyos = [10, 100, 500, 1000, 2000, 5000]\n", "t_p = []\n", "t_c = []\n", "for i in tamanyos:\n", " data = np.random.randn(i, i)\n", " res = %timeit -o busca_min(data)\n", " t_p.append(res.best)\n", " res = %timeit -o busca_min_cython9(data)\n", " t_c.append(res.best)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": [ "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\n", "gXmNM6ix6ZarjYGrpzz+DfCgy6acw83r3Xf/xaz11xtlXaYWp42y7nt2ZcFfn8Bts6e+boNstvp8\n", "rjyO6ZWke1liOeImupG2S5fw/C1VLPFvZ3Ny5tw9OXXTL3HF/cdexBV8izXWfMj/tYZFt25iJbov\n", "Rdx3u//xpvCW47o1Yfc7DjbfD95G8qPea2fqNmuGP2/kP/P5sM4/Lhwluf/8vQv+g26R6qDKyZ3A\n", "rQN8f9eZSBop0y1Xy/0vuhO4bfbrWe09Z2bXo4AUyfO5fZ1PcUce/Lp7jj6Y3Y5ambsXzOKeBbNZ\n", "cPfK3L1gNgvuvu/+Ktx198rcffcq3HX3bBbcuAp3Xb8Kd909i3vvpSsL9H6u3rttNOUYi9zPlCMP\n", "Pta7/0r4sw8v8uf6ElfwNngKyQU8sLgsscTMwHOLe919qne7d8r9Au59GqzKYvw57AJc1Pudmbzd\n", "M+D3v7vR5w7kM6/qvl2686Ln7xfwParmLnpckjQY011z9XTg2Kraq/f4GODeqYvak/g3TUmSNDJa\n", "L2hfmW5B+7OB3wLnsciCdkmSpEk0rWnBqro7yZHAt+jWjHzGYiVJkjTATUQlSZIm0UpLf8nyS7JX\n", "ksuS/HeSowfxGVo+ST6bZH6Si6ccWzfJaUl+keTbSeZMee6Y3vm7LMkeU45vk+Ti3nMfmek/x6RK\n", "skmS7ya5NMklSY7qHfccDrkkqyU5N8lFSX6a5B97xz13IyTJrCQXJjm599jzNwKSXJXkJ71zd17v\n", "2ODPXVX19UY3TXg5sCnd18IvAp7c78/xttznZRdga+DiKcfeB/xV7/7RwD/17j+ld95m987j5Swc\n", "5TwP2L53/xS6HW2b//nG/QZsAGzVu78W3ZrHJ3sOR+MGrNH7uTLwA+AZnrvRugFvBf4N+Hrvsedv\n", "BG7AlcC6ixwb+LkbxMjV/RuMVtUC4L4NRtVQVZ1Ft5fXVPsCx/fuHw/s37u/H3BCVS2obqPYy4Ed\n", "kmwIrF1V5/Ve9y9TfkcDVFXXVdVFvfu30W3YuzGew5FQVfdd8moVur+A/gHP3chI8mhgb+DTLNym\n", "x/M3Ohb9BuDAz90gytXiNhjdeACfoxW3flXN792fD6zfu78R3Xm7z33ncNHj1+C5nXFJNqUbhTwX\n", "z+FISLJSkovoztF3q+pSPHej5EPA2+EBm0x7/kZDAacnuSDJYb1jAz93091E9KG4Qn4EVVW5N9nw\n", "S7IW8BXgTVV1a7fpfsdzOLyquy7fVknWAb6V5FmLPO+5G1JJngdcX1UXJpm7uNd4/obazlV1bZJH\n", "AqcluWzqk4M6d4MYuboG2GTK4014YOPT8JifZAOA3rDn9b3ji57DR9Odw2t696cev2YGcgpIMpuu\n", "WH2+qv6jd9hzOEKq6mbgG8A2eO5GxU7AvkmuBE4AdkvyeTx/I6Gqru39/B3dVSy2ZwbO3SDK1QXA\n", "E5JsmmQV4EXA1wfwOVpxXwde0bv/Crpr0N13/JAkqyTZDHgCcF5VXQfckmSHdEMmL5vyOxqg3v/e\n", "nwF+WlVTr8LkORxySda779tISVYHdgcuxHM3EqrqnVW1SVVtBhwCnFFVL8PzN/SSrJFk7d79NYE9\n", "gIuZiXM3oNX5z6X7NtPlwDGtvy3graD7G9dv6S6IezXwKmBd4HTgF8C3gTlTXv/O3vm7DNhzyvFt\n", "ev/nvBz4aOs/16Tc6L5ddi/dN1ku7N328hwO/w3YEvhR79z9BHh777jnbsRuwK4s/Lag52/Ib8Bm\n", "vX/uLgIuua+PzMS5cxNRSZKkPhrIJqKSJEmTynIlSZLUR5YrSZKkPrJcSZIk9ZHlSpIkqY8sV5Ik\n", "SX1kuZIkSeojy5UkSVIf/X+z5bGxZ9Y2dAAAAABJRU5ErkJggg==\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure(figsize = (10,6))\n", "plt.plot(tamanyos, t_p, 'bo-')\n", "plt.plot(tamanyos, t_c, 'ro-')" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": [ "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\n", "nStJkqQOjSS5SgiucyVJklaAUVWudgF+UsVPRzSeJElSL0aVXNnMLkmSVoRRJVc2s0uSpBVhlJUr\n", "kytJkjT1Rlm5clpQkiRNPacFJUmSOmRDuyRJUoesXEmSJHXI5EqSJKlDTgtKkiR1yMqVJElSh6xc\n", "SZIkdcjKlSRJUodMriRJkjrktKAkSVKHrFxJkiR1aMHkKsmBSS5OclmSTUmOb4/vk2R9km8kuTDJ\n", "3vOcxo2bJUnSijBI5eoO4MSqeijwq8CrkjwYOAlYX1UHAxe1r7fHjZslSdKKsGByVVXXVdWl7fNb\n", "gMuBewNHA+vaj60DnjXPaZwWlCRJK8Kieq6S3A84DPgisG9VbWnf2gLsO89XbWiXJEkrwsDJVZI9\n", "gQ8DJ1TVzbPfq6oCap6vW7mSJEkrwk6DfCjJzjSJ1fuq6pz28JYk+1XVdUn2B67fzndPgVc9BP71\n", "2cmXflBVG7oIXJIkaTmSrAZWd37epug078Ch6an6XlWdOOv4G9pjpyU5Cdi7qk7a5rtVVUn4HHBy\n", "FZ/t+geQJEnqwkzestzzDFK5ejzwAuCrSTa2x04GTgXOTvJS4GrgmHnO4bSgJElaERZMrqrqc2y/\n", "N+vIAcexoV2SJK0IrtAuSZLUIZMrSZKkDrlxsyRJUoesXEmSJHVo6MlVQoBdgduHPZYkSVLfRlG5\n", "WgXcXsWdIxhLkiSpV6NIrpwSlCRJK8aoKlc2s0uSpBXBypUkSVKHRlW5MrmSJEkrwqgqV04LSpKk\n", "FcFpQUmSpA7Z0C5JktQhK1eSJEkdsqFdkiSpQza0S5IkdchpQUmSpA7Z0C5JktQhK1eSJEkdMrmS\n", "JEnqkNOCkiRJHbJyJUmS1CErV5IkSR2yciVJktQhkytJkqQOOS0oSZLUIStXkiRJHXLjZkmSpA65\n", "cbMkSVKHnBaUJEnqkA3tkiRJHbJyJUmS1CEb2iVJkjq0YHKV5N1JtiT52qxjpyS5NsnG9nHUPKew\n", "oV2SJK0Yg1Su3gNsmzwVcHpVHdY+PjHP93cCfrzUACVJkibJgslVVX0WuGGOtzLgGLdVUYuKSpIk\n", "aUItp+dqbZKvJHlXkr3n+Zz9VpIkacVYanL1NuAg4FBgM/DGeT5rciVJklaMnZbypaq6fuZ5krOA\n", "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\n", "xyKvAAAAAElFTkSuQmCC\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ratio = np.array(t_p) / np.array(t_c)\n", "plt.figure(figsize = (10,6))\n", "plt.plot(tamanyos, ratio, 'bo-')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parece que conseguimos rendimientos que son 40 veces más rápidos que con Python puro que usa un numpy array de por medio (excepto para tamaños de arrays muy pequeños en los que el rendimiento no sería una gran problema)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Apuntes finales" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ "Después de haber probado Python, Cython, Numba y Pypy:\n", "\n", "**Numba**:\n", "\n", "* Numba no parece fácilmente generalizable a día de hoy (experiencia personal) y no soporta ni parece que soportará todas las características del lenguaje. La idea me parece increible pero creo que le falta todavía un poco de madurez.\n", "\n", "* Me ha costado instalar numba y llvmlite en linux sin usar conda (con conda no lo he probado por lo que no puedo opinar).\n", "\n", "(Creo que JuanLu estaba preparando un post sobre Numba. Habrá que esperar a ver sus conclusiones).\n", "\n", "**Pypy**:\n", "\n", "* Pypy ha funcionado como un titán sin necesidad de hacer modificaciones. \n", "\n", "* Destacar que no tengo excesivas experiencias con el mismo \n", "\n", "* Instalarlo no es tarea fácil (he intentado usar PyPy3 con numpypy y he fallado vilmente). Quería usar numpypy y al final he optado por descargar una versión portable con numpy de serie que quizá afecte al rendimiento ¿?.\n", "\n", "**Cython**:\n", "\n", "* Me ha parecido el más generalizable de todos. Se pueden crear paquetes para CPython, para Pypy,...\n", "\n", "* No lo he probado en Windows por lo que no sé lo doloroso que puede llegar a ser. Mañana lo probaré en el trabajo y ya dejaré un comentario por ahí.\n", "\n", "* El manejo no es tan evidente como con Numba y Pypy. Requiere entender como funcionan los tipos de C y requiere conocer una serie de interioridades de C. Sin duda es el que más esfuerzo requiere de las alternativas aquí expuestas pra este caso concreto y no generalizable.\n", "\n", "* Creo que, una vez hecho el esfuerzo inicial de intentar entender un poco como funciona, se puede sacar un gran rendimiento del mismo en muchas situaciones." ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Y después de haber leído todo esto pensad que, en la mayoría de situaciones, CPython no es tan lento como lo pintan (sobretodo con numpy) y que ¡¡¡LA OPTIMIZACIÓN PREMATURA ES LA RAÍZ DE TODOS LOS MALES!!!" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.4.0" } }, "nbformat": 4, "nbformat_minor": 0 }