Current section
Files
Jump to
Current section
Files
c_src/Tensorflex.c
#include "erl_nif.h"
#include "c_api.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <assert.h>
#include <jpeglib.h>
#include <stdint.h>
typedef struct
{
unsigned nrows;
unsigned ncols;
double* data;
} Matrix;
typedef union
{
void* vp;
Matrix* p;
} mx_t;
#define POS(MX, ROW, COL) ((MX)->data[(ROW)* (MX)->ncols + (COL)])
#define BUF_SIZE 500000
static int get_number(ErlNifEnv* env, ERL_NIF_TERM term, double* dp);
static Matrix* alloc_matrix(ErlNifEnv* env, unsigned nrows, unsigned ncols);
static void matrix_destr(ErlNifEnv* env, void* obj);
static ErlNifResourceType* resource_type = NULL;
void free_buffer(void* data, size_t length) {
free(data);
}
ErlNifResourceType *graph_resource, *op_desc_resource, *tensor_resource, *session_resource, *op_resource, *buffer_resource, *status_resource, *graph_opts_resource;
void graph_destr(ErlNifEnv *env, void *res) {
TF_DeleteGraph(*(TF_Graph **)res);
}
void graph_opts_destr(ErlNifEnv *env, void *res) {
TF_DeleteImportGraphDefOptions(*(TF_ImportGraphDefOptions **)res);
}
void tensor_destr(ErlNifEnv *env, void *res) {
TF_DeleteTensor(*(TF_Tensor **)res);
}
void status_destr(ErlNifEnv *env, void *res) {
TF_DeleteStatus(*(TF_Status**)res);
}
void buffer_destr(ErlNifEnv *env, void *res) {
TF_DeleteBuffer(*(TF_Buffer **)res);
}
void session_destr(ErlNifEnv *env, void *res) {
TF_Status *status = TF_NewStatus();
TF_DeleteSession(*(TF_Session **)res, status);
if (TF_GetCode(status) != TF_OK) {
fprintf(stderr, "Error: Cannot delete session!: %s\r\n", TF_Message(status));
}
TF_DeleteStatus(status);
}
void op_destr(ErlNifEnv *env, void *res) {}
void op_desc_destr(ErlNifEnv *env, void *res) {}
void tensor_deallocator(void* data, size_t len, void* arg) {
enif_free(data);
}
static ERL_NIF_TERM version(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
return enif_make_string(env, TF_Version() , ERL_NIF_LATIN1);
}
int res_loader(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;
graph_resource = enif_open_resource_type(env, NULL, "graph", graph_destr, flags, NULL);
op_desc_resource = enif_open_resource_type(env, NULL, "op_desc", op_desc_destr, flags, NULL);
op_resource = enif_open_resource_type(env, NULL, "op", op_destr, flags, NULL);
status_resource = enif_open_resource_type(env, NULL, "status", status_destr, flags, NULL);
tensor_resource = enif_open_resource_type(env, NULL, "tensor", tensor_destr, flags, NULL);
session_resource = enif_open_resource_type(env, NULL, "session", session_destr, flags, NULL);
buffer_resource = enif_open_resource_type(env, NULL, "buffer", buffer_destr, flags, NULL);
graph_opts_resource = enif_open_resource_type(env, NULL, "graph_opts",graph_opts_destr, flags, NULL);
ErlNifResourceType* rt = enif_open_resource_type(env, NULL, "matrix", matrix_destr, ERL_NIF_RT_CREATE, NULL);
if (rt == NULL) {
return -1;
}
assert(resource_type == NULL);
resource_type = rt;
return 0;
}
static ERL_NIF_TERM create_matrix(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
unsigned nrows, ncols;
unsigned i, j;
ERL_NIF_TERM list, row, ret;
Matrix* mx = NULL;
if (!enif_get_uint(env, argv[0], &nrows) || nrows < 1 ||
!enif_get_uint(env, argv[1], &ncols) || ncols < 1) {
goto badarg;
}
mx = alloc_matrix(env, nrows, ncols);
list = argv[2];
for (i = 0; i<nrows; i++) {
if (!enif_get_list_cell(env, list, &row, &list)) {
goto badarg;
}
for (j = 0; j<ncols; j++) {
ERL_NIF_TERM v;
if (!enif_get_list_cell(env, row, &v, &row) ||
!get_number(env, v, &POS(mx,i,j))) {
goto badarg;
}
}
if (!enif_is_empty_list(env, row)) {
goto badarg;
}
}
if (!enif_is_empty_list(env, list)) {
goto badarg;
}
ret = enif_make_resource(env, mx);
enif_release_resource(mx);
return ret;
badarg:
if (mx != NULL) {
enif_release_resource(mx);
}
return enif_make_badarg(env);
}
static ERL_NIF_TERM matrix_pos(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
mx_t mx;
unsigned i, j;
if (!enif_get_resource(env, argv[0], resource_type, &mx.vp) ||
!enif_get_uint(env, argv[1], &i) || (--i >= mx.p->nrows) ||
!enif_get_uint(env, argv[2], &j) || (--j >= mx.p->ncols)) {
return enif_make_badarg(env);
}
return enif_make_double(env, POS(mx.p, i,j));
}
static ERL_NIF_TERM size_of_matrix(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
mx_t mx;
if (!enif_get_resource(env, argv[0], resource_type, &mx.vp)) {
return enif_make_badarg(env);
}
return enif_make_tuple2(env, enif_make_uint(env, mx.p->nrows),
enif_make_uint(env, mx.p->ncols));
}
static ERL_NIF_TERM matrix_to_lists(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
unsigned i, j;
ERL_NIF_TERM res;
mx_t mx;
mx.p = NULL;
if (!enif_get_resource(env, argv[0], resource_type, &mx.vp)) {
return enif_make_badarg(env);
}
res = enif_make_list(env, 0);
for (i = mx.p->nrows; i-- > 0; ) {
ERL_NIF_TERM row = enif_make_list(env, 0);
for (j = mx.p->ncols; j-- > 0; ) {
row = enif_make_list_cell(env, enif_make_double(env, POS(mx.p,i,j)),
row);
}
res = enif_make_list_cell(env, row, res);
}
return res;
}
static ERL_NIF_TERM append_to_matrix(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Matrix* mx_ret = NULL;
unsigned j, it_i, it_j;
unsigned nrows, ncols;
ERL_NIF_TERM list, row, ret;
mx_t mx;
if (!enif_get_resource(env, argv[0], resource_type, &mx.vp)) return enif_make_badarg(env);
nrows = mx.p->nrows;
ncols = mx.p->ncols;
mx_ret = alloc_matrix(env, nrows+1, ncols);
for(it_i = 0; it_i < nrows; it_i++) {
for(it_j = 0; it_j < ncols; it_j++){
POS(mx_ret, it_i, it_j) = POS(mx.p, it_i, it_j);
}
}
list = argv[1];
if (!enif_get_list_cell(env, list, &row, &list)) {
goto badarg;
}
for (j = 0; j<ncols; j++) {
ERL_NIF_TERM v;
if (!enif_get_list_cell(env, row, &v, &row) ||
!get_number(env, v, &POS(mx_ret,nrows,j))) {
goto badarg;
}
}
if (!enif_is_empty_list(env, row)) {
goto badarg;
}
if (!enif_is_empty_list(env, list)) {
goto badarg;
}
ret = enif_make_resource(env, mx_ret);
enif_release_resource(mx_ret);
return ret;
badarg:
if (mx.p != NULL) {
enif_release_resource(mx.p);
}
return enif_make_badarg(env);
}
static int get_number(ErlNifEnv* env, ERL_NIF_TERM term, double* dp)
{
long i;
return enif_get_double(env, term, dp) ||
(enif_get_long(env, term, &i) && (*dp=(double)i, 1));
}
static Matrix* alloc_matrix(ErlNifEnv* env, unsigned nrows, unsigned ncols)
{
Matrix* mx = enif_alloc_resource(resource_type, sizeof(Matrix));
mx->nrows = nrows;
mx->ncols = ncols;
mx->data = enif_alloc(nrows*ncols*sizeof(double));
return mx;
}
static void matrix_destr(ErlNifEnv* env, void* obj)
{
Matrix* mx = (Matrix*) obj;
enif_free(mx->data);
mx->data = NULL;
}
static ERL_NIF_TERM error_to_atom(ErlNifEnv *env, TF_Status* status)
{
switch(TF_GetCode(status))
{
case TF_CANCELLED: return enif_make_atom(env,"cancelled");
break;
case TF_UNKNOWN: return enif_make_atom(env,"unknown");
break;
case TF_INVALID_ARGUMENT: return enif_make_atom(env,"invalid_argument");
break;
case TF_DEADLINE_EXCEEDED: return enif_make_atom(env,"deadline_exceeded");
break;
case TF_NOT_FOUND: return enif_make_atom(env,"not_found");
break;
case TF_ALREADY_EXISTS: return enif_make_atom(env, "already_exists");
break;
case TF_PERMISSION_DENIED: return enif_make_atom(env,"permission_denied");
break;
case TF_UNAUTHENTICATED: return enif_make_atom(env,"unauthenticated");
break;
case TF_RESOURCE_EXHAUSTED: return enif_make_atom(env,"resource_exhausted");
break;
case TF_FAILED_PRECONDITION: return enif_make_atom(env,"failed_precondition");
break;
case TF_ABORTED: return enif_make_atom(env,"aborted");
break;
case TF_OUT_OF_RANGE: return enif_make_atom(env,"out_of_range");
break;
case TF_UNIMPLEMENTED:return enif_make_atom(env,"unimplemented");
break;
case TF_INTERNAL: return enif_make_atom(env,"internal");
break;
case TF_UNAVAILABLE: return enif_make_atom(env,"unavailable");
break;
case TF_DATA_LOSS: return enif_make_atom(env,"data_loss");
break;
default: return enif_make_atom(env,"unlisted_code");
}
}
static ERL_NIF_TERM datatype_to_atom(ErlNifEnv *env, TF_DataType type)
{
switch(type)
{
case TF_FLOAT: return enif_make_atom(env,"tf_float");
break;
case TF_DOUBLE: return enif_make_atom(env,"tf_double");
break;
case TF_INT32: return enif_make_atom(env,"tf_int32");
break;
case TF_UINT8: return enif_make_atom(env,"tf_uint8");
break;
case TF_INT16: return enif_make_atom(env,"tf_int16");
break;
case TF_INT8: return enif_make_atom(env, "tf_int8");
break;
case TF_STRING: return enif_make_atom(env,"tf_string");
break;
case TF_COMPLEX64: return enif_make_atom(env,"tf_complex64");
break;
case TF_INT64: return enif_make_atom(env,"tf_int64");
break;
case TF_BOOL: return enif_make_atom(env,"tf_bool");
break;
case TF_QINT8: return enif_make_atom(env,"tf_qint8");
break;
case TF_QUINT8: return enif_make_atom(env,"tf_quint8");
break;
case TF_QINT32:return enif_make_atom(env,"tf_qint32");
break;
case TF_BFLOAT16: return enif_make_atom(env,"tf_bfloat16");
break;
case TF_QINT16: return enif_make_atom(env,"tf_qint16");
break;
case TF_QUINT16: return enif_make_atom(env,"tf_quint16");
break;
case TF_UINT16: return enif_make_atom(env,"tf_uint16");
break;
case TF_COMPLEX128: return enif_make_atom(env,"tf_complex128");
break;
case TF_HALF: return enif_make_atom(env,"tf_half");
break;
case TF_RESOURCE: return enif_make_atom(env,"tf_resource");
break;
case TF_VARIANT: return enif_make_atom(env,"tf_variant");
break;
default: return enif_make_atom(env,"unlisted_datatype");
}
}
static ERL_NIF_TERM read_graph(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary filepath;
enif_inspect_binary(env,argv[0], &filepath);
char* file = enif_alloc(filepath.size+1);
memset(file, 0, filepath.size+1);
memcpy(file, (void *) filepath.data, filepath.size);
const char *dot = strrchr(file, '.');
if(!dot || dot == file) return enif_make_badarg(env);
if(strcmp((dot + 1),"pb") != 0) return enif_make_badarg(env);
FILE *f = fopen(file, "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
void* data = malloc(fsize);
fread(data, fsize, 1, f);
fclose(f);
TF_Buffer* buf = TF_NewBuffer();
buf->data = data;
buf->length = fsize;
buf->data_deallocator = free_buffer;
TF_Status* status = TF_NewStatus();
TF_ImportGraphDefOptions *graph_opts = TF_NewImportGraphDefOptions();
TF_Graph *graph = TF_NewGraph();
TF_GraphImportGraphDef(graph, buf, graph_opts, status);
if (TF_GetCode(status) != TF_OK) {
return enif_make_tuple2(env, enif_make_atom(env,"error"), error_to_atom(env,status));
}
TF_Graph **graph_resource_alloc = enif_alloc_resource(graph_resource, sizeof(TF_Graph *));
memcpy((void *) graph_resource_alloc, (void *) &graph, sizeof(TF_Graph *));
ERL_NIF_TERM loaded_graph = enif_make_resource(env, graph_resource_alloc);
enif_release_resource(graph_resource_alloc);
return enif_make_tuple2(env, enif_make_atom(env,"ok"), loaded_graph);
}
static ERL_NIF_TERM get_graph_ops(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
TF_Graph **graph;
enif_get_resource(env, argv[0], graph_resource, (void *) &graph);
int n_ops = 0;
size_t pos = 0;
TF_Operation *op_count;
while ((op_count = TF_GraphNextOperation(*graph, &pos)) != NULL) {
n_ops++;
}
ERL_NIF_TERM *op_list;
ERL_NIF_TERM op_list_eterm;
TF_Operation *op_temp;
ErlNifBinary erl_str;
op_list = malloc(sizeof(ERL_NIF_TERM)*n_ops);
pos = 0;
for(int i=0; i<n_ops; i++) {
op_temp = TF_GraphNextOperation(*graph, &pos);
enif_alloc_binary(strlen((char*) TF_OperationName(op_temp)), &erl_str);
memcpy(erl_str.data, (char*) TF_OperationName(op_temp), strlen((char*) TF_OperationName(op_temp)));
op_list[i] = enif_make_binary(env, &erl_str);
}
op_list_eterm = enif_make_list_from_array(env, op_list, n_ops);
free(op_list);
return op_list_eterm;
}
static ERL_NIF_TERM tensor_datatype(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
TF_Tensor **tensor;
enif_get_resource(env, argv[0], tensor_resource, (void *) &tensor);
TF_DataType type = TF_TensorType(*tensor);
return enif_make_tuple2(env, enif_make_atom(env,"ok"), datatype_to_atom(env, type));
}
static ERL_NIF_TERM string_tensor(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
TF_Tensor *tensor;
TF_Tensor **tensor_resource_alloc = enif_alloc_resource(tensor_resource, sizeof(TF_Tensor *));
if (!(enif_is_binary(env, argv[0]))) {
return enif_make_badarg(env);
}
ErlNifBinary str;
enif_inspect_binary(env, argv[0], &str);
TF_Status *status = TF_NewStatus();
void *val = enif_alloc(str.size+1);
memset(val, 0, str.size+1);
TF_StringEncode((void *) str.data, str.size, val, str.size+1, status);
if (TF_GetCode(status) != TF_OK) {
return enif_make_tuple2(env, enif_make_atom(env,"error"), error_to_atom(env,status));
}
tensor = TF_NewTensor(TF_STRING, 0, 0, val, str.size, tensor_deallocator, 0);
memcpy((void *) tensor_resource_alloc, (void *) &tensor, sizeof(TF_Tensor *));
ERL_NIF_TERM new_tensor = enif_make_resource(env, tensor_resource_alloc);
enif_release_resource(tensor_resource_alloc);
return enif_make_tuple2(env, enif_make_atom(env,"ok"), new_tensor);
}
static ERL_NIF_TERM float64_tensor(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
TF_Tensor *tensor;
TF_Tensor **tensor_resource_alloc = enif_alloc_resource(tensor_resource, sizeof(TF_Tensor *));
if (enif_is_number(env, argv[0])) {
void *val = enif_alloc(sizeof(double));
if (enif_get_double(env, argv[0], val)) {
tensor = TF_NewTensor(TF_DOUBLE, 0, 0, val, sizeof(double), tensor_deallocator, 0);
} else return enif_make_badarg(env);
}
else {
mx_t mx1, mx2;
if (!enif_get_resource(env, argv[0], resource_type, &mx1.vp) || !enif_get_resource(env, argv[1], resource_type, &mx2.vp) || mx2.p->nrows > 1) {
return enif_make_badarg(env);
}
int ndims = (int)(mx2.p->ncols);
unsigned i,j;
int64_t dims[mx2.p->ncols];
int size_alloc = 1;
for (i = 0; i < mx2.p->nrows; i++) {
for (j = 0; j < mx2.p->ncols; j++) {
size_alloc = size_alloc * POS(mx2.p, i, j);
dims[j] = POS(mx2.p, i, j);
}
}
double *data = enif_alloc((mx1.p->nrows)*(mx1.p->ncols)*sizeof(double));
for (i = 0; i < mx1.p->nrows; i++) {
for (j = 0; j < mx1.p->ncols; j++) {
data[(i)*(mx1.p->ncols) + (j)] = (double) POS(mx1.p, i, j);
}
}
tensor = TF_NewTensor(TF_DOUBLE, dims, ndims, data, (size_alloc) * sizeof(double), tensor_deallocator, 0);
}
memcpy((void *) tensor_resource_alloc, (void *) &tensor, sizeof(TF_Tensor *));
ERL_NIF_TERM new_tensor = enif_make_resource(env, tensor_resource_alloc);
enif_release_resource(tensor_resource_alloc);
return enif_make_tuple2(env, enif_make_atom(env,"ok"), new_tensor);
}
static ERL_NIF_TERM float32_tensor(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
TF_Tensor *tensor;
TF_Tensor **tensor_resource_alloc = enif_alloc_resource(tensor_resource, sizeof(TF_Tensor *));
if (enif_is_number(env, argv[0])) {
void *val = enif_alloc(sizeof(float));
if (enif_get_double(env, argv[0], val)) {
tensor = TF_NewTensor(TF_FLOAT, 0, 0, val, sizeof(float), tensor_deallocator, 0);
} else return enif_make_badarg(env);
}
else {
mx_t mx1, mx2;
if (!enif_get_resource(env, argv[0], resource_type, &mx1.vp) || !enif_get_resource(env, argv[1], resource_type, &mx2.vp) || mx2.p->nrows > 1) {
return enif_make_badarg(env);
}
int ndims = (int)(mx2.p->ncols);
unsigned i,j;
int64_t dims[mx2.p->ncols];
int size_alloc = 1;
for (i = 0; i < mx2.p->nrows; i++) {
for (j = 0; j < mx2.p->ncols; j++) {
size_alloc = size_alloc * POS(mx2.p, i, j);
dims[j] = POS(mx2.p, i, j);
}
}
float *data = enif_alloc((mx1.p->nrows)*(mx1.p->ncols)*sizeof(float));
for (i = 0; i < mx1.p->nrows; i++) {
for (j = 0; j < mx1.p->ncols; j++) {
data[(i)*(mx1.p->ncols) + (j)] = (float) POS(mx1.p, i, j);
}
}
tensor = TF_NewTensor(TF_FLOAT, dims, ndims, data, (size_alloc) * sizeof(float), tensor_deallocator, 0);
}
memcpy((void *) tensor_resource_alloc, (void *) &tensor, sizeof(TF_Tensor *));
ERL_NIF_TERM new_tensor = enif_make_resource(env, tensor_resource_alloc);
enif_release_resource(tensor_resource_alloc);
return enif_make_tuple2(env, enif_make_atom(env,"ok"), new_tensor);
}
static ERL_NIF_TERM int32_tensor(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
TF_Tensor *tensor;
TF_Tensor **tensor_resource_alloc = enif_alloc_resource(tensor_resource, sizeof(TF_Tensor *));
if (enif_is_number(env, argv[0])) {
void *val = enif_alloc(sizeof(int32_t));
if (enif_get_int(env, argv[0], val)) {
tensor = TF_NewTensor(TF_INT32, 0, 0, val, sizeof(int32_t), tensor_deallocator, 0);
} else return enif_make_badarg(env);
}
else {
mx_t mx1, mx2;
if (!enif_get_resource(env, argv[0], resource_type, &mx1.vp) || !enif_get_resource(env, argv[1], resource_type, &mx2.vp) || mx2.p->nrows > 1) {
return enif_make_badarg(env);
}
int ndims = (int)(mx2.p->ncols);
unsigned i,j;
int64_t dims[mx2.p->ncols];
int size_alloc = 1;
for (i = 0; i < mx2.p->nrows; i++) {
for (j = 0; j < mx2.p->ncols; j++) {
size_alloc = size_alloc * POS(mx2.p, i, j);
dims[j] = POS(mx2.p, i, j);
}
}
int32_t *data = enif_alloc((mx1.p->nrows)*(mx1.p->ncols)*sizeof(int32_t));
for (i = 0; i < mx1.p->nrows; i++) {
for (j = 0; j < mx1.p->ncols; j++) {
data[(i)*(mx1.p->ncols) + (j)] = (int32_t) POS(mx1.p, i, j);
}
}
tensor = TF_NewTensor(TF_INT32, dims, ndims, data, (size_alloc) * sizeof(int32_t), tensor_deallocator, 0);
}
memcpy((void *) tensor_resource_alloc, (void *) &tensor, sizeof(TF_Tensor *));
ERL_NIF_TERM new_tensor = enif_make_resource(env, tensor_resource_alloc);
enif_release_resource(tensor_resource_alloc);
return enif_make_tuple2(env, enif_make_atom(env,"ok"), new_tensor);
}
static ERL_NIF_TERM allocate_tensor(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[], char* datatype)
{
TF_Tensor *tensor;
TF_Tensor **tensor_resource_alloc = enif_alloc_resource(tensor_resource, sizeof(TF_Tensor *));
mx_t mx;
if (!enif_get_resource(env, argv[0], resource_type, &mx.vp) || mx.p->nrows > 1) {
return enif_make_badarg(env);
}
int ndims = (int)(mx.p->ncols);
unsigned i,j;
int64_t dims[mx.p->ncols];
int size_alloc = 1;
for (i = 0; i < mx.p->nrows; i++) {
for (j = 0; j < mx.p->ncols; j++) {
size_alloc = size_alloc * POS(mx.p, i, j);
dims[j] = POS(mx.p, i, j);
}
}
if(strcmp(datatype, "TF_FLOAT") == 0) {
tensor = TF_AllocateTensor(TF_FLOAT, dims, ndims, (size_alloc)* sizeof(float));
}
else if(strcmp(datatype, "TF_DOUBLE") == 0) {
tensor = TF_AllocateTensor(TF_DOUBLE, dims, ndims, (size_alloc)* sizeof(double));
}
else if(strcmp(datatype, "TF_INT32") == 0) {
tensor = TF_AllocateTensor(TF_INT32, dims, ndims, (size_alloc)* sizeof(int32_t));
} else return enif_make_badarg(env);
memcpy((void *) tensor_resource_alloc, (void *) &tensor, sizeof(TF_Tensor *));
ERL_NIF_TERM new_tensor = enif_make_resource(env, tensor_resource_alloc);
enif_release_resource(tensor_resource_alloc);
return enif_make_tuple2(env, enif_make_atom(env,"ok"), new_tensor);
}
static ERL_NIF_TERM int32_tensor_alloc(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
return allocate_tensor(env, argc, argv, "TF_INT32");
}
static ERL_NIF_TERM float32_tensor_alloc(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
return allocate_tensor(env, argc, argv, "TF_FLOAT");
}
static ERL_NIF_TERM float64_tensor_alloc(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
return allocate_tensor(env, argc, argv, "TF_DOUBLE");
}
static ERL_NIF_TERM run_session(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
TF_Graph **graph;
enif_get_resource(env, argv[0], graph_resource, (void *) &graph);
TF_Tensor **input_tensor;
enif_get_resource(env, argv[1], tensor_resource, (void *) &input_tensor);
TF_Tensor **output_tensor;
enif_get_resource(env, argv[2], tensor_resource, (void *) &output_tensor);
ErlNifBinary input_opname_bin;
enif_inspect_binary(env,argv[3], &input_opname_bin);
char* input_opname = enif_alloc(input_opname_bin.size+1);
memset(input_opname, 0, input_opname_bin.size+1);
memcpy(input_opname, (void *) input_opname_bin.data, input_opname_bin.size);
ErlNifBinary output_opname_bin;
enif_inspect_binary(env,argv[4], &output_opname_bin);
char* output_opname = enif_alloc(output_opname_bin.size+1);
memset(output_opname, 0, output_opname_bin.size+1);
memcpy(output_opname, (void *) output_opname_bin.data, output_opname_bin.size);
TF_Operation* input_op = TF_GraphOperationByName(*graph, input_opname);
TF_Output input_op_o = {input_op, 0};
TF_Operation* output_op = TF_GraphOperationByName(*graph, output_opname);
TF_Output output_op_o = {output_op, 0};
TF_Status* status = TF_NewStatus();
TF_SessionOptions* sess_opts = TF_NewSessionOptions();
TF_Session* session = TF_NewSession(*graph, sess_opts, status);
assert(TF_GetCode(status) == TF_OK);
TF_SessionRun(session, NULL, &input_op_o, &(*input_tensor), 1, &output_op_o, &(*output_tensor), 1, NULL, 0, NULL, status);
ERL_NIF_TERM *data_list, *data_list_eterm, data_list_of_lists;
data_list = malloc(sizeof(ERL_NIF_TERM)*TF_Dim(*output_tensor,(TF_NumDims(*output_tensor)-1)));
data_list_eterm = malloc(sizeof(ERL_NIF_TERM)*((int)(TF_Dim(*output_tensor,0))));
float* data = (float*)(TF_TensorData(*output_tensor));
for(int j=0; j<(int)(TF_Dim(*output_tensor,0)); j++)
{
for(int i=0; i<TF_Dim(*output_tensor,(TF_NumDims(*output_tensor)-1)); i++)
{
data_list[i] = enif_make_double(env, *data++);
}
data_list_eterm[j] = enif_make_list_from_array(env, data_list, TF_Dim(*output_tensor,(TF_NumDims(*output_tensor)-1)));
}
data_list_of_lists = enif_make_list_from_array(env, data_list_eterm, (int)(TF_Dim(*output_tensor,0)));
free(data_list);
free(data_list_eterm);
TF_CloseSession(session, status);
TF_DeleteSession(session, status);
TF_DeleteSessionOptions(sess_opts);
TF_DeleteStatus(status);
return data_list_of_lists;
}
static ERL_NIF_TERM load_image_as_tensor(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
TF_Tensor *tensor;
TF_Tensor **tensor_resource_alloc = enif_alloc_resource(tensor_resource, sizeof(TF_Tensor *));
int error_check;
unsigned long input_size;
unsigned char *input_img;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
unsigned long output_size;
unsigned char *output;
int row_stride, width, height, num_pixels;
ErlNifBinary filepath;
enif_inspect_binary(env,argv[0], &filepath);
char* file = enif_alloc(filepath.size+1);
memset(file, 0, filepath.size+1);
memcpy(file, (void *) filepath.data, filepath.size);
const char *dot = strrchr(file, '.');
if(!dot || dot == file) return enif_make_badarg(env);
if(!((strcmp((dot + 1),"jpg") == 0) || (strcmp((dot + 1),"jpeg") == 0))) return enif_make_badarg(env);
FILE* f = fopen(file, "rb");
fseek(f, 0, SEEK_END);
input_size = ftell(f);
fseek(f, 0, SEEK_SET);
input_img = (unsigned char*) malloc(input_size);
fread(input_img, input_size, 1, f);
fclose(f);
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, input_img, input_size);
error_check = jpeg_read_header(&cinfo, TRUE);
if (error_check != 1) return enif_make_badarg(env);
jpeg_start_decompress(&cinfo);
width = cinfo.output_width;
height = cinfo.output_height;
num_pixels = cinfo.output_components;
output_size = width * height * num_pixels;
output = (unsigned char*) malloc(output_size);
row_stride = width * num_pixels;
while (cinfo.output_scanline < cinfo.output_height) {
unsigned char *buf[1];
buf[0] = output + (cinfo.output_scanline) * row_stride;
jpeg_read_scanlines(&cinfo, buf, 1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
free(input_img);
const int size_alloc = output_size * sizeof(unsigned char);
int64_t dims[3] = {width, height, num_pixels};
uint8_t *data = enif_alloc(output_size*sizeof(uint8_t));
for(int it = 0; it < output_size; it++){
data[it] = (uint8_t)output[it];
}
tensor = TF_NewTensor(TF_UINT8, dims, 3, data, size_alloc, tensor_deallocator, 0);
memcpy((void *) tensor_resource_alloc, (void *) &tensor, sizeof(TF_Tensor *));
ERL_NIF_TERM new_tensor = enif_make_resource(env, tensor_resource_alloc);
enif_release_resource(tensor_resource_alloc);
return enif_make_tuple2(env, enif_make_atom(env,"ok"), new_tensor);
}
static ERL_NIF_TERM load_csv_as_matrix(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM mat_ret;
ErlNifBinary filepath;
enif_inspect_binary(env,argv[0], &filepath);
char* file = enif_alloc(filepath.size+1);
memset(file, 0, filepath.size+1);
memcpy(file, (void *) filepath.data, filepath.size);
char buf_init[BUF_SIZE], buf[BUF_SIZE];
char *val_init, *line_init, *val, *line;
unsigned int header_atom_len;
enif_get_atom_length(env, argv[1], &header_atom_len, ERL_NIF_LATIN1);
char* header_atom = (char*)enif_alloc(header_atom_len + 1);
enif_get_atom(env, argv[1], header_atom, header_atom_len + 1, ERL_NIF_LATIN1);
ErlNifBinary delimiter;
enif_inspect_binary(env,argv[2], &delimiter);
char* delimiter_str = enif_alloc(delimiter.size+1);
memset(delimiter_str, 0, delimiter.size+1);
memcpy(delimiter_str, (void *) delimiter.data, delimiter.size);
FILE *f_init = fopen(file, "rb");
unsigned i = 0, j = 0;
while((line_init=fgets(buf_init,sizeof(buf_init),f_init))!=NULL) {
j = 0;
val_init = strtok(line_init,delimiter_str);
while(val_init != NULL) {
val_init = strtok(NULL,delimiter_str);
j++;
}
i++;
}
fclose(f_init);
int flag = 0;
if(strcmp(header_atom, "true") == 0) {
i--;
flag = 1;
}
mx_t mx;
mx.p = alloc_matrix(env, i, j);
FILE *f = fopen(file, "rb");
i = 0;
while((line=fgets(buf,sizeof(buf),f))!=NULL) {
j = 0;
val = strtok(line,delimiter_str);
while(val != NULL) {
if(flag == 0) {
POS(mx.p, i, j) = atof(val);
j++;
}
val = strtok(NULL,delimiter_str);
}
if(flag == 1){
flag = 0;
i--;
}
i++;
}
fclose(f);
mat_ret = enif_make_resource(env, mx.p);
enif_release_resource(mx.p);
return mat_ret;
}
static ERL_NIF_TERM add_scalar_to_matrix(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM ret;
unsigned i, j;
mx_t mx, mx_ret;
mx.p = NULL;
mx_ret.p = NULL;
if (!enif_get_resource(env, argv[0], resource_type, &mx.vp)) {
return enif_make_badarg(env);
}
double scalar = 0.0;
if (!enif_get_double(env, argv[1], &scalar)) {
return enif_make_badarg(env);
}
mx_ret.p = alloc_matrix(env, mx.p->nrows, mx.p->ncols);
for (i = 0; i < mx.p->nrows; i++) {
for (j = 0; j < mx.p->ncols; j++) {
POS(mx_ret.p, i, j) = POS(mx.p, i, j) + scalar;
}
}
ret = enif_make_resource(env, mx_ret.p);
enif_release_resource(mx_ret.p);
return ret;
}
static ERL_NIF_TERM subtract_scalar_from_matrix(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM ret;
unsigned i, j;
mx_t mx, mx_ret;
mx.p = NULL;
mx_ret.p = NULL;
if (!enif_get_resource(env, argv[0], resource_type, &mx.vp)) {
return enif_make_badarg(env);
}
double scalar = 0.0;
if (!enif_get_double(env, argv[1], &scalar)) {
return enif_make_badarg(env);
}
mx_ret.p = alloc_matrix(env, mx.p->nrows, mx.p->ncols);
for (i = 0; i < mx.p->nrows; i++) {
for (j = 0; j < mx.p->ncols; j++) {
POS(mx_ret.p, i, j) = POS(mx.p, i, j) - scalar;
}
}
ret = enif_make_resource(env, mx_ret.p);
enif_release_resource(mx_ret.p);
return ret;
}
static ERL_NIF_TERM multiply_matrix_with_scalar(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM ret;
unsigned i, j;
mx_t mx, mx_ret;
mx.p = NULL;
mx_ret.p = NULL;
if (!enif_get_resource(env, argv[0], resource_type, &mx.vp)) {
return enif_make_badarg(env);
}
double scalar = 1.0;
if (!enif_get_double(env, argv[1], &scalar)) {
return enif_make_badarg(env);
}
mx_ret.p = alloc_matrix(env, mx.p->nrows, mx.p->ncols);
for (i = 0; i < mx.p->nrows; i++) {
for (j = 0; j < mx.p->ncols; j++) {
POS(mx_ret.p, i, j) = POS(mx.p, i, j) * scalar;
}
}
ret = enif_make_resource(env, mx_ret.p);
enif_release_resource(mx_ret.p);
return ret;
}
static ERL_NIF_TERM divide_matrix_by_scalar(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM ret;
unsigned i, j;
mx_t mx, mx_ret;
mx.p = NULL;
mx_ret.p = NULL;
if (!enif_get_resource(env, argv[0], resource_type, &mx.vp)) {
return enif_make_badarg(env);
}
double scalar = 1.0;;
if ((!enif_get_double(env, argv[1], &scalar)) || (scalar == 0.0)) {
return enif_make_badarg(env);
}
mx_ret.p = alloc_matrix(env, mx.p->nrows, mx.p->ncols);
for (i = 0; i < mx.p->nrows; i++) {
for (j = 0; j < mx.p->ncols; j++) {
POS(mx_ret.p, i, j) = POS(mx.p, i, j) / scalar;
}
}
ret = enif_make_resource(env, mx_ret.p);
enif_release_resource(mx_ret.p);
return ret;
}
static ERL_NIF_TERM add_matrices(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
unsigned i, j;
ERL_NIF_TERM ret;
mx_t mx1, mx2, mx;
mx1.p = NULL;
mx2.p = NULL;
mx.p = NULL;
if (!enif_get_resource(env, argv[0], resource_type, &mx1.vp) || !enif_get_resource(env, argv[1], resource_type, &mx2.vp) || mx1.p->nrows != mx2.p->nrows || mx1.p->ncols != mx2.p->ncols) {
return enif_make_badarg(env);
}
mx.p = alloc_matrix(env, mx1.p->nrows, mx2.p->ncols);
for (i = 0; i < mx1.p->nrows; i++) {
for (j = 0; j < mx2.p->ncols; j++) {
POS(mx.p, i, j) = POS(mx1.p, i, j) + POS(mx2.p, i, j);
}
}
ret = enif_make_resource(env, mx.p);
enif_release_resource(mx.p);
return ret;
}
static ERL_NIF_TERM subtract_matrices(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
unsigned i, j;
ERL_NIF_TERM ret;
mx_t mx1, mx2, mx;
mx1.p = NULL;
mx2.p = NULL;
mx.p = NULL;
if (!enif_get_resource(env, argv[0], resource_type, &mx1.vp) || !enif_get_resource(env, argv[1], resource_type, &mx2.vp) || mx1.p->nrows != mx2.p->nrows || mx1.p->ncols != mx2.p->ncols) {
return enif_make_badarg(env);
}
mx.p = alloc_matrix(env, mx1.p->nrows, mx2.p->ncols);
for (i = 0; i < mx1.p->nrows; i++) {
for (j = 0; j < mx2.p->ncols; j++) {
POS(mx.p, i, j) = POS(mx1.p, i, j) - POS(mx2.p, i, j);
}
}
ret = enif_make_resource(env, mx.p);
enif_release_resource(mx.p);
return ret;
}
static ERL_NIF_TERM tensor_to_matrix(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
unsigned i,j;
ERL_NIF_TERM ret;
TF_Tensor **tensor;
mx_t mx;
mx.p = NULL;
enif_get_resource(env, argv[0], tensor_resource, (void *) &tensor);
TF_DataType type = TF_TensorType(*tensor);
if(TF_NumDims(*tensor) == 2) {
mx.p = alloc_matrix(env, (unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-2))), (unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-1))));
if(type == TF_FLOAT){
float* float_tensor_data = (float*)TF_TensorData(*tensor);
for(j=0; j<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-2))); j++) {
for(i=0; i<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-1))); i++)
{
POS(mx.p,j,i) = (double)*float_tensor_data++;
}
}
}
else if(type == TF_INT32){
int32_t* int32_tensor_data = (int32_t*)TF_TensorData(*tensor);
for(j=0; j<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-2))); j++) {
for(i=0; i<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-1))); i++)
{
POS(mx.p,j,i) = (double)*int32_tensor_data++;
}
}
}
else if(type == TF_DOUBLE){
double* double_tensor_data = (double*)TF_TensorData(*tensor);
for(j=0; j<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-2))); j++) {
for(i=0; i<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-1))); i++)
{
POS(mx.p,j,i) = *double_tensor_data++;
}
}
}
} else return enif_make_badarg(env);
ret = enif_make_resource(env, mx.p);
enif_release_resource(mx.p);
return ret;
}
static ErlNifFunc nif_funcs[] =
{
{"create_matrix", 3, create_matrix},
{"matrix_pos", 3, matrix_pos},
{"append_to_matrix", 2, append_to_matrix},
{"size_of_matrix", 1, size_of_matrix},
{"matrix_to_lists", 1, matrix_to_lists},
{ "version", 0, version },
{ "read_graph", 1, read_graph },
{ "get_graph_ops", 1, get_graph_ops },
{ "float64_tensor", 2, float64_tensor },
{ "float64_tensor", 1, float64_tensor },
{ "float32_tensor", 2, float32_tensor },
{ "float32_tensor", 1, float32_tensor },
{ "int32_tensor", 2, int32_tensor },
{ "int32_tensor", 1, int32_tensor },
{ "string_tensor", 1, string_tensor },
{ "tensor_datatype", 1, tensor_datatype },
{ "float64_tensor_alloc", 1, float64_tensor_alloc },
{ "float32_tensor_alloc", 1, float32_tensor_alloc },
{ "int32_tensor_alloc", 1, int32_tensor_alloc },
{ "run_session", 5, run_session },
{ "load_image_as_tensor", 1, load_image_as_tensor },
{ "load_csv_as_matrix", 3, load_csv_as_matrix },
{ "add_scalar_to_matrix", 2, add_scalar_to_matrix },
{ "subtract_scalar_from_matrix", 2, subtract_scalar_from_matrix },
{ "multiply_matrix_with_scalar", 2, multiply_matrix_with_scalar },
{ "divide_matrix_by_scalar", 2, divide_matrix_by_scalar },
{ "add_matrices", 2, add_matrices },
{ "subtract_matrices", 2, subtract_matrices },
{ "tensor_to_matrix", 1, tensor_to_matrix },
};
ERL_NIF_INIT(Elixir.Tensorflex.NIFs, nif_funcs, res_loader, NULL, NULL, NULL)