interconectando Python y Torch7 (Lua) a través de una biblioteca compartida

Estoy tratando de pasar datos (arrays) entre python y lua y quiero manipular los datos en lua usando el marco Torch7. Pensé que esto se puede hacer mejor a través de C, ya que python y lua se interconectan con C. Además, algunas ventajas son que no es necesario copiar datos de esta manera (que solo pasa punteros) y es rápido.

Implementé dos progtwigs, uno donde lua está incrustado en c y otro donde python pasa datos a c. Ambos funcionan cuando se comstackn a binarios ejecutables. Sin embargo, cuando el progtwig c to lua se convierte en una biblioteca compartida, las cosas no funcionan.

Los detalles: Estoy usando ubuntu de 64 bits 14.04 y 12.04. Estoy usando luajit 2.0.2 con lua 5.1 instalado en / usr / local / Las bibliotecas de dependencia están en / usr / local / lib y los encabezados están en / usr / local / include Estoy usando python 2.7

El código para el progtwig c to lua es:

tensor.lua

require 'torch' function hi_tensor(t) print('Hi from lua') torch.setdefaulttensortype('torch.FloatTensor') print(t) return t*2 end 

cluaf.h

 void multiply (float* array, int m, int n, float *result, int m1, int n1); 

cluaf.c

 #include  #include  #include "lua.h" #include "lauxlib.h" #include "lualib.h" #include "luaT.h" #include "TH/TH.h" void multiply (float* array, int m, int n, float *result, int m1, int n1) { lua_State *L = luaL_newstate(); luaL_openlibs( L ); // loading the lua file if (luaL_loadfile(L, "tensor.lua") || lua_pcall(L, 0, 0, 0)) { printf("error: %s \n", lua_tostring(L, -1)); } // convert the c array to Torch7 specific structure representing a tensor THFloatStorage* storage = THFloatStorage_newWithData(array, m*n); THFloatTensor* tensor = THFloatTensor_newWithStorage2d(storage, 0, m, n, n, 1); luaT_newmetatable(L, "torch.FloatTensor", NULL, NULL, NULL, NULL); // load the lua function hi_tensor lua_getglobal(L, "hi_tensor"); if(!lua_isfunction(L,-1)) { lua_pop(L,1); } //this pushes data to the stack to be used as a parameter //to the hi_tensor function call luaT_pushudata(L, (void *)tensor, "torch.FloatTensor"); // call the lua function hi_tensor if (lua_pcall(L, 1, 1, 0) != 0) { printf("error running function `hi_tensor': %s \n", lua_tostring(L, -1)); } // get results returned from the lua function hi_tensor THFloatTensor* z = luaT_toudata(L, -1, "torch.FloatTensor"); lua_pop(L, 1); THFloatStorage *storage_res = z->storage; result = storage_res->data; return ; } 

Entonces para probar lo hago:

 luajit -b tensor.lua tensor.o gcc -w -c -Wall -Wl,-E -fpic cluaf.c -lluajit -lluaT -lTH -lm -ldl -L /usr/local/lib gcc -shared cluaf.o tensor.o -L/usr/local/lib -lluajit -lluaT -lTH -lm -ldl -Wl,-E -o libcluaf.so gcc -L. -Wall -o test main.c -lcluaf ./test 

La salida:

 Hi from lua 1.0000 0.2000 0.2000 5.3000 [torch.FloatTensor of dimension 2x2] c result 2.000000 c result 0.400000 c result 0.400000 c result 10.60000 

Hasta ahora tan bueno. Pero cuando bash usar la biblioteca compartida en Python, se rompe.

test.py

 from ctypes import byref, cdll, c_int import ctypes import numpy as np import cython l = cdll.LoadLibrary('absolute_path_to_so/libcluaf.so') a = np.arange(4, dtype=np.float64).reshape((2,2)) b = np.arange(4, dtype=np.float64).reshape((2,2)) l.multiply.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float), ctypes.c_int, ctypes.c_int] a_list = [] b_list = [] for i in range(a.shape[0]): for j in range(a.shape[1]): a_list.append(a[i][j]) for i in range(b.shape[0]): for j in range(b.shape[1]): b_list.append(b[i][j]) arr_a = (ctypes.c_float * len(a_list))() arr_b = (ctypes.c_float * len(b_list))() l.multiply(arr_a, ctypes.c_int(2), ctypes.c_int(2), arr_b, ctypes.c_int(2), ctypes.c_int(2)) 

Corro:

 python test.py 

y la salida es:

 error: error loading module 'libpaths' from file '/usr/local/lib/lua/5.1/libpaths.so': /usr/local/lib/lua/5.1/libpaths.so: undefined symbol: lua_gettop 

Busqué este error aquí y en todas partes en la web, pero sugieren (1) incluir -Wl, -E para exportar símbolos o (2) para agregar dependencias en los enlaces que hice. (1) Tengo -Wl, -E pero parece que no está haciendo nada. (2) He incluido las dependencias (-L / usr / local / lib -lluajit -lluaT -lTH -lm -ldl)

La prueba de Python no falla cuando se importa la biblioteca compartida, sino cuando se llama el ‘requiere antorcha’ dentro de lua. Eso también es lo diferente en este caso de los otros casos que encontré.

luajit.so define el símbolo lua_gettop (nm /usr/local/lib/luajit.so para ver que) lua.h define LUA_API int (lua_gettop) (lua_State * L);

Supongo que al comstackr c en binario, todo funciona porque encuentra todos los símbolos en lua.h, pero al usar la biblioteca compartida no elige lua_gettop de luajit.so (no sé por qué).

www.luajit.org/running.html dice: ‘En la mayoría de los sistemas basados ​​en ELF (por ejemplo, Linux) necesita exportar explícitamente los símbolos globales al vincular su aplicación, por ejemplo, con: -Wl, -E require () intenta cargar incrustado Datos de bytecode de los símbolos exportados (en * .exe o lua51.dll en Windows) y de bibliotecas compartidas en package.cpath. ‘

package.cpath y package.path son:

 ./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so ./?.lua;/usr/local/share/luajit-2.0.2/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua 

Esto es lo que nm libcluaf.so devuelve:

 00000000002020a0 B __bss_start 00000000002020a0 b completed.6972 w __cxa_finalize@@GLIBC_2.2.5 0000000000000a50 t deregister_tm_clones 0000000000000ac0 t __do_global_dtors_aux 0000000000201dd8 t __do_global_dtors_aux_fini_array_entry 0000000000202098 d __dso_handle 0000000000201de8 d _DYNAMIC 00000000002020a0 D _edata 00000000002020a8 B _end 0000000000000d28 T _fini 0000000000000b00 t frame_dummy 0000000000201dd0 t __frame_dummy_init_array_entry 0000000000000ed0 r __FRAME_END__ 0000000000202000 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 0000000000000918 T _init w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000201de0 d __JCR_END__ 0000000000201de0 d __JCR_LIST__ w _Jv_RegisterClasses U lua_getfield 0000000000000d99 R luaJIT_BC_tensor U luaL_loadfile U luaL_newstate U luaL_openlibs U lua_pcall U lua_settop U luaT_newmetatable U lua_tolstring U luaT_pushudata U luaT_toudata U lua_type 0000000000000b35 T multiply U printf@@GLIBC_2.2.5 0000000000000a80 t register_tm_clones U THFloatStorage_newWithData U THFloatTensor_newWithStorage2d 00000000002020a0 d __TMC_END__ 

Gracias por adelantado

En Linux, los módulos Lua no se vinculan directamente con la biblioteca de Lua, sino que esperan encontrar las funciones de la API de Lua ya cargadas. Esto generalmente se hace exportándolos desde el intérprete usando el -Wl,-E enlazador -Wl,-E . Esta bandera solo funciona para símbolos en ejecutables , no en bibliotecas compartidas. Para las bibliotecas compartidas existe algo similar: el indicador RTLD_GLOBAL para la función dlopen . De forma predeterminada, todas las bibliotecas compartidas que figuran en la línea de comandos del comstackdor se cargan con RTLD_LOCAL en RTLD_LOCAL lugar, pero afortunadamente Linux reutiliza los manejadores de biblioteca ya abiertos. Así que puedes:

RTLD_GLOBAL previamente la biblioteca Lua (JIT) usando RTLD_GLOBAL antes de que se cargue automáticamente (lo que sucede cuando carga libcluaf.so ):

 from ctypes import byref, cdll, c_int import ctypes lualib = ctypes.CDLL("libluajit-5.1.so", mode=ctypes.RTLD_GLOBAL) l = cdll.LoadLibrary('absolute_path_to_so/libcluaf.so') # ... 

O bien, cambie las banderas del identificador de la biblioteca Lua (JIT) posteriormente utilizando la bandera dlopen para dlopen . Sin embargo, este indicador no está en POSIX, y probablemente tenga que usar C para hacerlo. Ver por ejemplo aquí .

Para intercambiar datos entre python / numpy y lua / torch, puede probar una biblioteca llamada ” lutorpy “. Hace exactamente lo que intenta hacer, comparte la memoria y solo pasa el puntero con el método “asNumpyArray ()”.

 import lutorpy as lua import numpy as np ## run lua code in python with minimal modification: replace ":" to "._" t = torch.DoubleTensor(10,3) print(t._size()) # the corresponding lua version is t:size() ## convert torch tensor to numpy array ### Note: the underlying object are sharing the same memory, so the conversion is instant arr = t.asNumpyArray() print(arr.shape) ## or, you can convert numpy array to torch tensor xn = np.random.randn(100) ## convert the numpy array into torch tensor xt = torch.fromNumpyArray(xn) 

Lua_gettop es una función definida en Lua .so, que en su caso debe ser luajit.so. Parece que vinculas tu lib a eso, eso es bueno, y luego vinculas main a tu lib, así que, presumiblemente, el comstackdor c encuentra las funciones de Lua utilizadas por main en el luajit. Hasta ahora tan bueno.

Ahora, cuando carga su biblioteca a través de ctypes en Python, ¿se carga automáticamente luajit lib? Usted esperaría que lo hiciera, pero debería confirmar, tal vez tenga que decirle a ctypes que cargue las librerías vinculadas. Otra posibilidad es que ctypes o el cargador de lib no encuentre luajit, tal vez porque busca en lugares donde no se encuentra luajit. Para asegurarse de que podría querer intentar colocar todas las librerías en la misma carpeta desde donde llama a Python.

Si eso no ayuda, intente una variante de lo que intentó: no cargue su módulo en Python, simplemente cargue luajit directamente usando ctypes e intente llamar a algunos de sus métodos.