2.2.2.2 练习:从0开始构建一个ufunc

Mandelbrot分形由如下迭代定义:

2.2.2.2 练习:从0开始构建一个ufunc - 图1

C=X+iy是一个复数,只要Z仍然是有限的,无论迭代要跑多久,迭代都会重复。C属于Mandelbrot集。

  • ufunc调用mandel(z0, c)计算:

In [ ]:

  1. z = z0
  2. for k in range(iterations):
  3. z = z*z + c

比如,一百次迭代或者直到z.real2 + z.imag2 > 1000。用它来决定哪个C是在Mandelbrot集中。

  • 我们的函数是很简单的,因此,请利用PyUFunc_*助手。
  • 用Cython来完成

也可以看一下mandel.pyxmandelplot.py

提醒:一些预设Ufunc循环:

PyUfunc_f_ffloat elementwise_func(float input_1)
PyUfunc_ff_ffloat elementwise_func(float input_1, float input_2)
PyUfunc_d_ddouble elementwise_func(double input_1)
PyUfunc_dd_ddouble elementwise_func(double input_1, double input_2)
PyUfunc_D_Delementwise_func(complex_double input, complex_double output)
PyUfunc_DD_Delementwise_func(complex_double in1, complex_double in2, complex_double* out)

打印代码:

NPY_BOOL, NPY_BYTE, NPY_UBYTE, NPY_SHORT, NPY_USHORT, NPY_INT, NPY_UINT, NPY_LONG, NPY_ULONG, NPY_LONGLONG, NPY_ULONGLONG, NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE, NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE, NPY_DATETIME, NPY_TIMEDELTA, NPY_OBJECT, NPY_STRING, NPY_UNICODE, NPY_VOID

2.2.2.3 答案:从0开始创建一个ufunc

In [ ]:

  1. # The elementwise function
  2. #  ------------------------
  3. cdef void mandel_single_point(double complex *z_in,
  4. double complex *c_in,
  5. double complex *z_out) nogil:
  6. #
  7. # The Mandelbrot iteration
  8. #
  9. #
  10. # Some points of note:
  11. #
  12. # - It's *NOT* allowed to call any Python functions here.
  13. #
  14. # The Ufunc loop runs with the Python Global Interpreter Lock released.
  15. # Hence, the ``nogil``.
  16. #
  17. # - And so all local variables must be declared with ``cdef``
  18. #
  19. # - Note also that this function receives *pointers* to the data;
  20. # the "traditional" solution to passing complex variables around
  21. #
  22. cdef double complex z = z_in[0]
  23. cdef double complex c = c_in[0]
  24. cdef int k # the integer we use in the for loop
  25. # Straightforward iteration
  26. for k in range(100):
  27. z = z*z + c
  28. if z.real**2 + z.imag**2 > 1000:
  29. break
  30. # Return the answer for this point
  31. z_out[0] = z
  32. # Boilerplate Cython definitions
  33. #
  34. # You don't really need to read this part, it just pulls in
  35. # stuff from the Numpy C headers.
  36. #  ------------- ------------- ------------- -------------------
  37. cdef extern from "numpy/arrayobject.h":
  38. void import_array()
  39. ctypedef int npy_intp
  40. cdef enum NPY_TYPES:
  41. NPY_CDOUBLE
  42. cdef extern from "numpy/ufuncobject.h":
  43. void import_ufunc()
  44. ctypedef void (*PyUFuncGenericFunction)(char**, npy_intp*, npy_intp*, void*)
  45. object PyUFunc_FromFuncAndData(PyUFuncGenericFunction* func, void** data,
  46. char* types, int ntypes, int nin, int nout,
  47. int identity, char* name, char* doc, int c)
  48. void PyUFunc_DD_D(char**, npy_intp*, npy_intp*, void*)
  49. # Required module initialization
  50. #  ------------- -----------------
  51. import_array()
  52. import_ufunc()
  53. # The actual ufunc declaration
  54. #  ------------- ---------------
  55. cdef PyUFuncGenericFunction loop_func[1]
  56. cdef char input_output_types[3]
  57. cdef void *elementwise_funcs[1]
  58. loop_func[0] = PyUFunc_DD_D
  59. input_output_types[0] = NPY_CDOUBLE
  60. input_output_types[1] = NPY_CDOUBLE
  61. input_output_types[2] = NPY_CDOUBLE
  62. elementwise_funcs[0] = <void*>mandel_single_point
  63. mandel = PyUFunc_FromFuncAndData(
  64. loop_func,
  65. elementwise_funcs,
  66. input_output_types,
  67. 1, # number of supported input types
  68. 2, # number of input args
  69. 1, # number of output args
  70. 0, # `identity` element, never mind this
  71. "mandel", # function name
  72. "mandel(z, c) -> computes iterated z*z + c", # docstring
  73. 0 # unused
  74. )

In [ ]:

  1. import numpy as np
  2. import mandel
  3. x = np.linspace(-1.7, 0.6, 1000)
  4. y = np.linspace(-1.4, 1.4, 1000)
  5. c = x[None,:] + 1j*y[:,None]
  6. z = mandel.mandel(c, c)
  7. import matplotlib.pyplot as plt
  8. plt.imshow(abs(z)**2 < 1000, extent=[-1.7, 0.6, -1.4, 1.4])
  9. plt.gray()
  10. plt.show()

2.2.2.2 练习:从0开始构建一个ufunc - 图2

笔记:大多数模板可以由下列Cython模块来自动完成: http://wiki.cython.org/MarkLodato/CreatingUfuncs

一些可接受的输入类型

例如:支持小数点后一位及两位两个准确度版本

In [ ]:

  1. cdef void mandel_single_point(double complex *z_in,
  2. double complex *c_in,
  3. double complex *z_out) nogil:
  4. ...
  5. cdef void mandel_single_point_singleprec(float complex *z_in,
  6. float complex *c_in,
  7. float complex *z_out) nogil:
  8. ...
  9. cdef PyUFuncGenericFunction loop_funcs[2]
  10. cdef char input_output_types[3*2]
  11. cdef void *elementwise_funcs[1*2]
  12. loop_funcs[0] = PyUFunc_DD_D
  13. input_output_types[0] = NPY_CDOUBLE
  14. input_output_types[1] = NPY_CDOUBLE
  15. input_output_types[2] = NPY_CDOUBLE
  16. elementwise_funcs[0] = <void*>mandel_single_point
  17. loop_funcs[1] = PyUFunc_FF_F
  18. input_output_types[3] = NPY_CFLOAT
  19. input_output_types[4] = NPY_CFLOAT
  20. input_output_types[5] = NPY_CFLOAT
  21. elementwise_funcs[1] = <void*>mandel_single_point_singleprec
  22. mandel = PyUFunc_FromFuncAndData(
  23. loop_func,
  24. elementwise_funcs,
  25. input_output_types,
  26. 2, # number of supported input types < ----------------
  27. 2, # number of input args
  28. 1, # number of output args
  29. 0, # `identity` element, never mind this
  30. "mandel", # function name
  31. "mandel(z, c) -> computes iterated z*z + c", # docstring
  32. 0 # unused
  33. )