[PYTHON] An introduction to Cython that doesn't go deep -2-

This article is a continuation of "Introduction to Cython without Deep".

Let's get started with Cython so as not to go too deep this time. The purpose of this article is not to go into depth, but to have fun and enjoy the delicious parts of Cython.

Cython notation early tour

Cython notation isn't too difficult, I've got some good sample code in the Cython tutorial, so let's borrow it.

Code before optimization


def myfunction(x, y=2):
    a = x - y
    return a + x * y

def _helper(a):
    return a + 1

class A:
    def __init__(self, b=0):
        self.a = 3
        self.b = b
        self._scale = 2.0
        self.read_only = 1.0

    def foo(self, x):
        return x + _helper(1.0)

If you optimize the above code with Cython, it will look like this.

Cython optimization code


%%cython
cpdef int myfunction(int x, int y=2):
    cdef int a = x - y
    return a + x * y

cdef double _helper(double a):
    return a + 1

cdef class A:
    cdef public int x
    cdef public int y
    cdef double _scale
    cdef readonly float read_only

    def __init__(self, int b=0):
        self.a = 3
        self.b = b
        self._scale = 2.0
        self.read_only = 1.0

    cpdef double foo(self, double x):
        return (x + _helper(1.0)) * self._scale

You can see how to type variables, arguments, and return values by looking at them, so it's not necessary to explain them.

Function declaration

If you look closely at the function declarations, the myfunction function is defined by cpdef and the _helper function is defined by cdef. The list of function declarations is as follows.

Declaration Description
def Slow, can be called from Python
cdef Fast, not callable from Python, only available in Cython
cpdef A hybrid of def and cdef, def when called from Python, cdef when called from Cython

cdef class

If you set cdef class A in the class declaration, it becomes a cdef class. The cdef class manages attributes with a structure compared to ordinary classes that manage attributes with a dict, so it has good memory efficiency and fast access, but it is subject to the following restrictions.

--Dynamic methods / members cannot be added --Multiple inheritance not possible with cdef method as parent, single inheritance is possible

Member definitions must be pre-defined as follows:

cdef class A:
    cdef public int x
    cdef public int y
    cdef double _scale
    cdef readonly float read_only
...

References from Python are not possible unless public is added like the _scale member. Also, if you add the readonly attribute like the read_only member, you cannot change it from Python.

a = A()
a._scale            #error
a.read_only = 2.0   #error

List of file extensions used in Cython

extension Description
.pyx Think of it as an implementation file or the program itself
.pxd Definition file
.pxi Include file

If you know the above, you will not be in trouble.

pure python mode

Actually, as a procedure for speeding up a Python program, it is a general procedure to add a type definition to the original program. So, let's dare to propose a different approach, that is "pure Python mode".

Let's rewrite the above sample in pure Python mode.

pure_Example rewritten in Python mode


%%cython
import cython

@cython.ccall
@cython.locals(x=cython.int, y=cython.int)
@cython.returns(cython.int)
def myfunction(x, y=2):
    a = x-y
    return a + x * y

@cython.cfunc
@cython.locals(a=cython.double)
@cython.returns(cython.double)
def _helper(a):
    return a + 1


@cython.cclass
class A:
    a = cython.declare(cython.int, visibility='public')
    b = cython.declare(cython.int, visibility='public')
    _scale = cython.declare(cython.double)
    read_only = cython.declare(cython.double, visibility="readonly")

    @cython.locals(b=cython.int)
    def __init__(self, b=0):
        self.a = 3
        self.b = b
        self._scale = 2.0
        self.read_only = 1.0

    @cython.ccall
    @cython.locals(x=cython.double)
    @cython.returns(cython.double)
    def foo(self, x):
        return x + _helper(1.0) * self._scale

It is a style of adding ʻimport cython` to Python code and adding type information by decorating it. Now you can run the same file in Python and compile it in Cython.

Since the type information is defined outside the function, the Python code part inside the function does not need to be changed at all. The readability of the processing part remains the same, so it's surprisingly comfortable if you get used to the decorator storm.

For details on Cython's pure Python mode, see the official documentation. It's a concise tutorial, so it's easy to read because there is almost no English text.

http://cython.readthedocs.io/en/latest/src/tutorial/pure.html

Agumenting .pxd file

In pure Python mode, you can speed up the code in the function as it is, but you can use the .pxd file to speed up the .py file without changing it entirely.

This explanation is a brief excerpt from the official manual.

http://omake.accense.com/static/doc-ja/cython/src/tutorial/pure.html

If it finds a .pxd with the same name as the> .py file, Cython scans for cdefed classes and cdef / cpdefed functions and methods. Then convert the corresponding classes / functions / methods in the .py to the appropriate types. Therefore, if there is a.pxd like below:

cdef class A:
    cpdef foo(self, int i)

At the same time, if you have a file called a.py like the one below:

class A:
    def foo(self, i):
        print "Big" if i > 1000 else "Small"

The code is interpreted as follows:

cdef class A:
    cpdef foo(self, int i):
        print "Big" if i > 1000 else "Small"

Cooperation between type hints and Cython (wishful thinking)

If you look closely at the code in pure Python mode, you can see [PyCharm Type Hinting](http://qiita.com/pashango2/items/de342abc10722ed7a569#%E5%BC%B7%E5%8A%9B%E3%81%AA%" E3% 82% BF% E3% 82% A4% E3% 83% 97% E3% 83% 92% E3% 83% B3% E3% 83% 86% E3% 82% A3% E3% 83% B3% E3% Similar to 82% B0). I use PyCharm type hints a lot, so I found pure Python mode easy to use.

PyCharm Type Hinting Examples


class A:
    """
    :type a: int
    :type b: int
    """

    def __init__(self, b=0):
        """
        :type b: int
        """
        self.a = 3
        self.b = b

    def foo(self, x):
        """
        :type x: float
        :rtype: float
        """
        return x * float(self.a)

Also, Python has a stub file (.pyi) that contains only type information.

https://www.python.org/dev/peps/pep-0484/#stub-files

The stub file is very similar to the "agumenting.pxd file" described at the end.

In the future, Python code with type hints or Python code with type annotations will be automatically accelerated by Cython without any modification of the code (although it will be difficult to completely accelerate it, of course). I'm personally happy when it comes to this.

However, I haven't found such information in my research, and if you have any information about type hints and Cython automation, please teach me.

Summary

It's pretty quick, but I introduced the features of Cython. There are many more features of Cython, but for the time being, it should be enough to speed up with Cython.

If you want to get an overview of Cython, I recommend the slides below, which are well organized.

http://www.behnel.de/cython200910/talk.html

Recommended Posts

An introduction to Cython that doesn't go deep
An introduction to Cython that doesn't go deep -2-
An introduction to Word2Vec that even cats can understand
An introduction to machine learning
Introduction to Deep Learning ~ Learning Rules ~
An introduction to Python Programming
An introduction to Bayesian optimization
Deep Reinforcement Learning 1 Introduction to Reinforcement Learning
Introduction to Deep Learning ~ Backpropagation ~
[Introduction to StyleGAN] That "man who doesn't laugh" smiled unintentionally ♬
An introduction to Python that even monkeys can understand (Part 3)
An introduction to Python that even monkeys can understand (Part 1)
An introduction to Python that even monkeys can understand (Part 2)
Introduction to Deep Learning ~ Function Approximation ~
Introduction to Deep Learning ~ Forward Propagation ~
Introduction to Deep Learning ~ CNN Experiment ~
An introduction to Python for non-engineers
[Python Tutorial] An Easy Introduction to Python
An introduction to Pandas that you can learn while suffering [Part 1]
Introduction to Deep Learning ~ Convolution and Pooling ~
An introduction to OpenCV for machine learning
Recurrent Neural Networks: An Introduction to RNN
An introduction to Python for machine learning
Introduction to Deep Learning (1) --Chainer is explained in an easy-to-understand manner for beginners-
[What is an algorithm? Introduction to Search Algorithm] ~ Python ~
An introduction to object-oriented programming for beginners by beginners
An amateur tried Deep Learning using Caffe (Introduction)
An introduction to statistical modeling for data analysis
Introduction to Deep Learning ~ Localization and Loss Function ~
Introduction to Python "Re" 1 Building an execution environment
An introduction to voice analysis for music apps
Introduction to MQTT (Introduction)
Introduction to Scrapy (1)
Introduction to Scrapy (3)
Introduction to Supervisor
Introduction to Tkinter 1: Introduction
Introduction to PyQt
Introduction to Scrapy (2)
[Linux] Introduction to Linux
Introduction to Scrapy (4)
Introduction to discord.py (2)
Introduction to discord.py
An introduction to Python distributed parallel processing with Ray
Reading Note: An Introduction to Data Analysis with Python
How to make an HTTPS server with Go / Gin
How to deploy a Go application to an ECS instance
An introduction to machine learning from a simple perceptron
An introduction to Web API development for those who have completed the Progate Go course