Skip to content

Commit

Permalink
Merge pull request #175 from leondavi/leondavi/nerltensor
Browse files Browse the repository at this point in the history
Leondavi/nerltensor
  • Loading branch information
leondavi committed May 12, 2023
2 parents 90f26d6 + b7b428b commit f6b1dd4
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 39 deletions.
66 changes: 64 additions & 2 deletions src_cpp/opennnBridge/openNNnif.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,68 @@ static ERL_NIF_TERM decode_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
}
}
return nifpp::make(env, return_val);
}
}

/**
* Multiply a tensor by scalar
* Args: nerltensor binary, type, scalar (regular erl_float)
*/
static ERL_NIF_TERM nerltensor_scalar_multiplication_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
std::tuple<nifpp::TERM, nifpp::TERM> return_tuple;
enum {ARG_BINARY, ARG_TYPE, ARG_SCALAR};
double scalard;
nifpp::str_atom nerltensors_type;

nifpp::get_throws(env, argv[ARG_SCALAR], scalard);
nifpp::get_throws(env, argv[ARG_TYPE], nerltensors_type);
int enc_type_num = atom_str_to_enum(nerltensors_type);
int dims;
nifpp::TERM nerltensor_bin;

switch (enc_type_num)
{
case ATOM_FLOAT:
{
std::shared_ptr<fTensor2D> eigen_tensor;
dims = nifpp::get_tensor_2d<float,fTensor2DPtr, fTensor2D>(env, argv[ARG_BINARY], eigen_tensor); // TODO - upgrade to 3d

fTensor2DPtr eigen_tensor_res = make_shared<fTensor2D>(eigen_tensor->dimension(0), eigen_tensor->dimension(1));
float scalarf = static_cast<float>(scalard);
(*eigen_tensor_res) = (*eigen_tensor) * scalarf;

nifpp::make_tensor<float, fTensor2D>(env, nerltensor_bin, dims, eigen_tensor_res);

return_tuple = { nerltensor_bin , nifpp::make(env, nerltensors_type) };
break;
}
case ATOM_DOUBLE:
{
std::shared_ptr<dTensor2D> eigen_tensor;
dims = nifpp::get_tensor_2d<double,dTensor2DPtr, dTensor2D>(env, argv[ARG_BINARY], eigen_tensor); // TODO - upgrade to 3d

dTensor2DPtr eigen_tensor_res = make_shared<dTensor2D>(eigen_tensor->dimension(0), eigen_tensor->dimension(1));
(*eigen_tensor_res) = (*eigen_tensor) * scalard;

nifpp::make_tensor<double, dTensor2D>(env, nerltensor_bin, dims, eigen_tensor_res);

return_tuple = { nerltensor_bin , nifpp::make(env, nerltensors_type) };
break;
}
case ATOM_INT32:
{
throw("unsuported type");
break;
}
case ATOM_INT16:
{
throw("unsuported type");
break;
}
}
return nifpp::make(env, return_tuple);
}


template<typename EigenTypePtr> inline void sum_eigen(EigenTypePtr A, EigenTypePtr B, EigenTypePtr &C)
{
Expand Down Expand Up @@ -399,7 +460,8 @@ static ErlNifFunc nif_funcs[] =
{"set_weights_nif",2, set_weights_nif},
{"encode_nif",2, encode_nif},
{"decode_nif",2, decode_nif},
{"nerltensor_sum_nif",3, nerltensor_sum_nif}
{"nerltensor_sum_nif",3, nerltensor_sum_nif},
{"nerltensor_scalar_multiplication_nif",3,nerltensor_scalar_multiplication_nif}
};


Expand Down
22 changes: 17 additions & 5 deletions src_erl/erlBridge/nerlNIF.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
%nerltensor
-define(NUMOF_DIMS,3).

-export([nerltensor_sum_nif/3]).
-export([nerltensor_sum_erl/2]).
-export([nerltensor_sum_nif/3, nerltensor_sum_erl/2]).
-export([nerltensor_scalar_multiplication_nif/3, nerltensor_scalar_multiplication_erl/2]).

init() ->
NELNET_LIB_PATH = ?NERLNET_PATH++?BUILD_TYPE_RELEASE++"/"++?NERLNET_LIB,
Expand Down Expand Up @@ -117,6 +117,10 @@ decode_nif(_Binary, _BinaryType) when erlang:is_binary(_Binary) and erlang:is_at
nerltensor_sum_nif(_BinaryA, _BinaryB, _Mutual_Binary_Type) ->
exit(nif_library_not_loaded). % returns {Binary, Type}

% Only float/double types are supported
nerltensor_scalar_multiplication_nif(_NerlTensorBinary, _BinaryType, _ScalarValue) ->
exit(nif_library_not_loaded). % returns {Binary, Type}

%---------- nerlTensor -----------%
nerltensor_binary_decode(Binary, Type) when erlang:is_binary(Binary) and erlang:is_atom(Type) ->
NerlTensorListForm = decode_nif(Binary, Type),
Expand Down Expand Up @@ -158,15 +162,23 @@ nerltensor_conversion({NerlTensor, Type}, ResType) ->
nerltensor_sum_erl({NerlTensorErlA, Type}, {NerlTensorErlB, Type}) ->
ListGroup = lists:member(Type, get_all_nerltensor_list_types()),
if ListGroup ->
DIMS = lists:sublist(NerlTensorErlA, 1, ?NUMOF_DIMS),
Dims = lists:sublist(NerlTensorErlA, 1, ?NUMOF_DIMS),
NerlTensorErlA_NODIMS = lists:sublist(NerlTensorErlA, ?NUMOF_DIMS + 1, length(NerlTensorErlA) - ?NUMOF_DIMS),
%io:format("nerltensorA nodims: ~p~n", [NerlTensorErlA_NODIMS]),
NerlTensorErlB_NODIMS = lists:sublist(NerlTensorErlB, ?NUMOF_DIMS + 1, length(NerlTensorErlB) - ?NUMOF_DIMS),
% io:format("nerltensorB nodims: ~p~n", [NerlTensorErlB_NODIMS]),

DIMS ++ lists:zipwith(fun(X,Y) -> X + Y end, NerlTensorErlA_NODIMS, NerlTensorErlB_NODIMS);
Dims ++ lists:zipwith(fun(X,Y) -> X + Y end, NerlTensorErlA_NODIMS, NerlTensorErlB_NODIMS);
true -> throw("Bad Type")
end.


nerltensor_scalar_multiplication_erl({NerlTensorErl, Type}, ScalarValue) ->
ListGroup = lists:member(Type, get_all_nerltensor_list_types()),
if
ListGroup ->
Dims = lists:sublist(NerlTensorErl, 1, ?NUMOF_DIMS),
NerlTensorErl_NODIMS = lists:sublist(NerlTensorErl, ?NUMOF_DIMS + 1, length(NerlTensorErl) - ?NUMOF_DIMS),
Dims ++ lists:map(fun(X) -> X * ScalarValue end, NerlTensorErl_NODIMS);
true -> throw("Bad Type")
end.

86 changes: 54 additions & 32 deletions src_erl/erlBridge/nerlTests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
-import(nerlNIF,[call_to_get_weights/1,call_to_set_weights/2]).
-import(nerlNIF,[decode_nif/2, nerltensor_binary_decode/2]).
-import(nerlNIF,[encode_nif/2, nerltensor_encode/5, nerltensor_conversion/2, get_all_binary_types/0, get_all_nerltensor_list_types/0]).
-import(nerlNIF,[nerltensor_sum_nif/3]).
-import(nerlNIF,[nerltensor_sum_nif/3, nerltensor_sum_erl/2]).
-import(nerlNIF,[nerltensor_scalar_multiplication_nif/3, nerltensor_scalar_multiplication_erl/2]).
-import(nerl,[compare_floats_L/3, string_format/2, logger_settings/1]).

-define(NERLTEST_PRINT_STR, "[NERLTEST] ").
Expand All @@ -20,41 +21,44 @@ nerltest_print(String) ->
% encode_decode test macros
-define(DIMX_RAND_MAX, 200).
-define(DIMY_RAND_MAX, 200).
-define(SUM_NIF_ROUNDS, 100).
-define(ENCODE_DECODE_ROUNDS, 100).
-define(SUM_NIF_ROUNDS, 50).
-define(ENCODE_DECODE_ROUNDS, 50).
-define(NERLTENSOR_CONVERSION_ROUNDS, 50).
-define(NERLTENSOR_SCALAR_MULTIPLICATION_ROUNDS, 50).

test_envelope(Func, TestName, Rounds) ->
nerltest_print(nerl:string_format("~p test starts for ~p rounds",[TestName, Rounds])),
Tic = nerl:tic(),
Func(Rounds),
{Diff, TimeUnit} = nerl:toc(Tic),
nerltest_print(nerl:string_format("Elapsed: ~p~p",[Diff,TimeUnit])), ok.

run_tests()->
nerl:logger_settings(nerlTests),
DimXStr = integer_to_list(?DIMX_RAND_MAX),
DimYStr = integer_to_list(?DIMY_RAND_MAX),
nerltest_print("encode decode test starts "++integer_to_list(?ENCODE_DECODE_ROUNDS)++" tests up to ("++DimXStr++","++DimYStr++")"),
Tic_niftest_encode_decode = nerl:tic(),
encode_decode_nifs_test(?ENCODE_DECODE_ROUNDS,[]), % throws if a test fails
{TDiff_niftest_encode_decode, TimeUnit} = nerl:toc(Tic_niftest_encode_decode),
nerltest_print(nerl:string_format("Elapsed: ~p~p",[TDiff_niftest_encode_decode,TimeUnit])),

nerltest_print("nerltensor sum_nif float test starts "++integer_to_list(?SUM_NIF_ROUNDS)++" tests"),
nerltest_print("Performance is measured for the following operations:"),
nerltest_print("encode_nif x2 and nerltensor sum_nif"),

Tic_nerltensor_sum_nif_test_float = nerl:tic(),
Perfromance = 0,
PerformanceSumNifFloat = nerltensor_sum_nif_test(float, ?SUM_NIF_ROUNDS, Perfromance) / ?SUM_NIF_ROUNDS,
{TDiff_nerltensor_sum_nif_test_float, _} = nerl:toc(Tic_nerltensor_sum_nif_test_float),
nerltest_print(nerl:string_format("Elapsed: ~p~p, Avg nif operations: ~.4f~p",[TDiff_nerltensor_sum_nif_test_float,TimeUnit,PerformanceSumNifFloat,TimeUnit])),

nerltest_print("nerltensor sum_nif double test starts "++integer_to_list(?SUM_NIF_ROUNDS)++" tests"),
nerltest_print("Performance is measured for the following operations:"),
nerltest_print("encode_nif x2 and nerltensor sum_nif"),
Tic_nerltensor_sum_nif_test_double = nerl:tic(),
Perfromance = 0,
PerformanceSumNifDouble = nerltensor_sum_nif_test(double, ?SUM_NIF_ROUNDS, Perfromance) / ?SUM_NIF_ROUNDS,
{TDiff_nerltensor_sum_nif_test_double, _} = nerl:toc(Tic_nerltensor_sum_nif_test_double),
nerltest_print(nerl:string_format("Elapsed: ~p~p, Avg nif operations: ~.4f~p",[TDiff_nerltensor_sum_nif_test_double,TimeUnit,PerformanceSumNifDouble,TimeUnit])),

nerltest_print("nerltensor_conversion_test starts "++integer_to_list(?NERLTENSOR_CONVERSION_ROUNDS)++" tests"),
nerltensor_conversion_test(?NERLTENSOR_CONVERSION_ROUNDS),

EncodeDecodeTestFunc = fun(Rounds) -> encode_decode_nifs_test(Rounds,[]) end,
EncodeDecodeTestName = "encode_decode_nifs",
test_envelope(EncodeDecodeTestFunc, EncodeDecodeTestName, ?ENCODE_DECODE_ROUNDS ),

SumNifTestFloatFunc = fun(Rounds) -> Performance = 0, nerltensor_sum_nif_test(float, Rounds, Performance) end,
SumNifTestFloatTestName = "nerltensor_sum_nif float",
test_envelope(SumNifTestFloatFunc, SumNifTestFloatTestName, ?SUM_NIF_ROUNDS ),

SumNifTestDoubleFunc = fun(Rounds) -> Performance = 0, nerltensor_sum_nif_test(double, Rounds, Performance) end,
SumNifTestDoubleTestName = "nerltensor_sum_nif double",
test_envelope(SumNifTestDoubleFunc, SumNifTestDoubleTestName, ?SUM_NIF_ROUNDS ),

ScalarMultiplicationNifFloatFunc = fun(Rounds) -> Performance = 0, nerltensor_scalar_multiplication_nif_test(float, Rounds, Performance) end,
ScalarMultiplicationNifFloatTestName = "nerltensor_scalar_multiplication_nif float",
test_envelope(ScalarMultiplicationNifFloatFunc, ScalarMultiplicationNifFloatTestName, ?NERLTENSOR_SCALAR_MULTIPLICATION_ROUNDS ),

ScalarMultiplicationNifDoubleFunc = fun(Rounds) -> Performance = 0, nerltensor_scalar_multiplication_nif_test(double, Rounds, Performance) end,
ScalarMultiplicationNifDoubleTestName = "nerltensor_scalar_multiplication_nif double",
test_envelope(ScalarMultiplicationNifDoubleFunc, ScalarMultiplicationNifDoubleTestName, ?NERLTENSOR_SCALAR_MULTIPLICATION_ROUNDS ),

ConversionTestFunc = fun(Rounds) -> nerltensor_conversion_test(Rounds) end,
ConversionTestName = "nerltensor_conversion",
test_envelope(ConversionTestFunc, ConversionTestName, ?NERLTENSOR_CONVERSION_ROUNDS ),

nerltest_print("Tests Completed"),
ok.
Expand Down Expand Up @@ -83,6 +87,24 @@ generate_nerltensor(Type,DimX,DimY,DimZ) ->
true -> wrong_type
end.

nerltensor_scalar_multiplication_nif_test(_Type,0, Performance) -> Performance;
nerltensor_scalar_multiplication_nif_test(Type, N, Performance) ->
ScalarRand = rand:uniform() * rand:uniform(100),
NerlTensor = generate_nerltensor_rand_dims(Type),
ExpectedResult = nerlNIF:nerltensor_scalar_multiplication_erl({NerlTensor, erl_float}, ScalarRand),
Tic = nerl:tic(),
{NerlTensorEnc, Type} = nerlNIF:nerltensor_conversion({NerlTensor, erl_float}, Type), % encode
NerlTensorEncRes = nerlNIF:nerltensor_scalar_multiplication_nif(NerlTensorEnc, Type, ScalarRand),
{NerlTensorEncResDec, erl_float} = nerlNIF:nerltensor_conversion(NerlTensorEncRes, erl_float), % encode
{TocRes, _} = nerl:toc(Tic),
PerformanceNew = TocRes + Performance,
%io:format("ExpectedResult: ~p~n",[ExpectedResult]),
%io:format("NerlTensorEncResDec: ~p~n~n",[NerlTensorEncResDec]),
CompareFloats = nerl:compare_floats_L(NerlTensorEncResDec, ExpectedResult, 3), % Erlang accuracy is double
if
CompareFloats -> nerltensor_scalar_multiplication_nif_test(Type, N-1, PerformanceNew);
true -> throw(ner:string_format("test failed - not equal ~n ExpectedResult: ~p ~n NerlTensorEncResDec: ~p",[ExpectedResult, NerlTensorEncResDec]))
end.


nerltensor_sum_nif_test(_Type,0, Performance) -> Performance;
Expand Down

0 comments on commit f6b1dd4

Please sign in to comment.