파이썬의 Ctypes 기능을 활용해 numpy의 sum() 함수를 만들어보자.
기왕 하는 김에, axis 를 지정해 세로축 합, 가로축 합, 전체 합을 구하도록 해보자.
numpy.sum(x, axis=0)
이런 식으로 더러 사용해보셨을 것이다.
이와 같은 기능을 만들어보는 것이다.
C 함수에서 파이썬으로 배열을 return하는 것은 복잡하고 별도의 공부가 필요하므로,
파이썬 numpy에서, C 함수에서 조작할 수 있도록 미리 크기에 맞는 배열을 만들어주자.
그리고 아주 중요한, C 함수에서 사용할 수 있도록 파이썬 자료형을 Ctypes으로 변환해줘야 한다.
그리고 double은 64비트 리눅스에서 8바이트로, 파이썬의 float64 자료형에 해당한다.
넘파이에서 배열을 만들 때 자료형으로 이를 명시해서 혼동이 없도록 한다.
// hsum.c
// This is an implimentation of numpy.sum(), ctypes version.
// ex) [[1, 2, 3],
// [4, 5, 6]]
// shape = (2, 3), height = 2, width = 3
// axis = 0 => sum of row axis
// axis = 1 => sum of column axis
// axis = -1 => sum of all
void hsum(double * src, double * dst, int height, int width, int axis) {
if (axis == -1) { // np.sum(src)
dst[0] = 0;
// 전체 합을 구한다. 1차원 배열 다루듯이.
for (int i = 0; i < height * width ; i++) {
dst[0] += src[i];
}
// 세로축 합을 구한다. 인덱싱에 유의
} else if (axis == 0) { // np.sum(src, axis=0)
for (int i = 0; i < width; i++) {
dst[i] = 0;
for (int j = 0; j < height; j++) {
dst[i] += src[i + width * j];
}
}
// 가로축 합을 구한다. 인덱싱에 유의
} else if (axis == 1) { // np.sum(src, axis=1)
for (int i = 0; i < height; i++) {
dst[i] = 0;
for (int j = 0; j < width; j++) {
dst[i] += src[j + width * i];
}
}
}
}
# numpy_2.py
import ctypes
import numpy as np
import sys
np.set_printoptions(precision=0, suppress=True)
# 데이터 타입을 지정해줘야 안심할 수 있다.
# (3, 4) 타입의 넘파이 행렬을 만든다.
x = np.arange(0, 12, dtype=np.float64)
x = x.reshape(3, 4)
height, width = x.shape
# 동적 라이브러리를 불러온다.
c_lib = ctypes.CDLL("./hsum.so")
#c_lib.hsum.argtypes = [ctypes.POINTER(ctypes.c_double), \
# ctypes.POINTER(ctypes.c_double), ctypes.c_int, ctypes.c_int, ctypes.c_int]
#c_lib.hsum.restype = None
# 원본 배열에 대한 C 타입 자료형을 구한다.
src = x.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
# axis = 0 은 세로축 합으로 numpy.sum(x, axis=0)와 같다
# axis = 1 은 가로축 합으로 numpy.sum(x, axis=1)와 같다
# axis = -1 은 전체 합으로 numpy.sum(x)와 같다
axis = 0
if axis == 0:
y = np.zeros((width,), dtype=np.float64)
elif axis == 1:
y = np.zeros((height,), dtype=np.float64)
elif axis == -1:
y = np.zeros((1,), dtype=np.float64)
else:
sys.exit("axis shoud be -1, 0 or 1.")
# 합을 저장할 배열의 C 타입 자료형을 구한다.
dest = y.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
# Ctype 함수를 호출한다. 각기, (원본 배열, 합을 담을 배열, 원본 height, 원본 width, 합 구할 축)
c_lib.hsum(src, dest, height, width, axis)
print("----------------")
# C에서 포인터를 통해 원본 데이터를 변경했다.
print(x)
print("----------------")
if axis == -1:
# 전체 합을 구할 경우, 브라켓([])을 떼면 좀더 깔끔한 값을 보여준다.
print(*y)
else:
print(y)
[실행 결과] axis = 0 일 때
----------------
[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]
----------------
[12. 15. 18. 21.]
[실행 결과] axis = 1 일 때
----------------
[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]
----------------
[ 6. 22. 38.]
[실행 결과] axis = -1 일 때
----------------
[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]
----------------
66.0