From 40d1dfc8026fbaea3eba422ae55faa37fcace2f8 Mon Sep 17 00:00:00 2001
From: Jakhes <dean.schmitz@schmitzbauer.de>
Date: Tue, 11 Oct 2022 22:42:00 +0200
Subject: [PATCH] Adding Sparse_coding tests

---
 src/helper_files/helper.pl                    |   1 +
 src/methods/new_method/new_method_test.pl     |   4 +-
 src/methods/sparse_coding/sparse_coding.cpp   |  27 ++-
 src/methods/sparse_coding/sparse_coding.pl    |  49 +++-
 .../sparse_coding/sparse_coding_test.pl       | 221 ++++++++++++++----
 test_all.pl                                   |   4 +-
 6 files changed, 236 insertions(+), 70 deletions(-)

diff --git a/src/helper_files/helper.pl b/src/helper_files/helper.pl
index dd56e21..85a9f77 100644
--- a/src/helper_files/helper.pl
+++ b/src/helper_files/helper.pl
@@ -46,6 +46,7 @@ convert_float_array_to_list(Mem, Index, Count, [Val|Rest]) :-
         get_contents(Mem, Index , Val).
 
 %% takes library(struct) array and fills a 2D [[a], [b]] with the content of the array from 0 to to given Index
+%% The data gets transposed since in most mlpack matrices is every col a data point 
 convert_float_array_to_2d_list(Mem, ColNum, RowNum, Out) :-
         convert_float_array_to_2d_list(Mem, RowNum, ColNum, 0, Out).
 
diff --git a/src/methods/new_method/new_method_test.pl b/src/methods/new_method/new_method_test.pl
index 1aee96d..7f0a5ab 100644
--- a/src/methods/new_method/new_method_test.pl
+++ b/src/methods/new_method/new_method_test.pl
@@ -32,8 +32,8 @@ test(testDescription3, [true(Error =:= 1)]) :-
 test(testDescription4, [true(Error =:= 0.9797958971132711)]) :-
         reset_Model_No_Train(perceptron),
         open('/home/afkjakhes/eclipse-workspace/prolog-mlpack-libary/src/data_csv/iris2.csv', read, File),
-        take_csv_row(File, skipFirstRow,10, Records),
-        train(Records, 4, [0,1,0,1,1,0,1,1,1,0], 2, perceptron, 50, 0.0001, Error).
+        take_csv_row(File, skipFirstRow,10, Data),
+        train(Data, 4, [0,1,0,1,1,0,1,1,1,0], 2, perceptron, 50, 0.0001, Error).
 
 :- end_tests(predicate).
 
diff --git a/src/methods/sparse_coding/sparse_coding.cpp b/src/methods/sparse_coding/sparse_coding.cpp
index dda7851..be067ef 100644
--- a/src/methods/sparse_coding/sparse_coding.cpp
+++ b/src/methods/sparse_coding/sparse_coding.cpp
@@ -32,6 +32,13 @@ SparseCoding sparseCoding;
 void initModelWithTrain(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum,
                         SP_integer atoms, double lambda1, double lambda2, SP_integer maxIterations, double objTolerance, double newtonTolerance)
 {
+    if (dataMatSize/ dataMatRowNum < atoms)
+    {
+        raisePrologSystemExeption("There have to be more DataPoints than Atoms!");
+        return;
+    }
+    
+
     // convert the Prolog array to arma::mat
     mat data = convertArrayToMat(dataMatArr, dataMatSize, dataMatRowNum);
 
@@ -68,8 +75,17 @@ void encode(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum,
     mat codesReturnMat;
 
 
-    sparseCoding.Encode(data, codesReturnMat);
-    
+    try
+    {
+        sparseCoding.Encode(data, codesReturnMat);
+    }
+    catch(const std::exception& e)
+    {
+        std::cerr << e.what() << '\n';
+        raisePrologSystemExeption("Matrix has not the correct dimensions!");
+        return;
+    }
+
 
     // return the Matrix
     returnMatrixInformation(codesReturnMat, codesMatArr, codesMatColNum, codesMatRowNum);
@@ -83,9 +99,14 @@ void encode(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum,
 // description: 
 double train(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum)
 {
+    if (dataMatSize/ dataMatRowNum < sparseCoding.Atoms())
+    {
+        raisePrologSystemExeption("There have to be more DataPoints than Atoms!");
+        return 0.0;
+    }
     // convert the Prolog array to arma::mat
     mat data = convertArrayToMat(dataMatArr, dataMatSize, dataMatRowNum);
 
 
     return sparseCoding.Train(data, RandomInitializer());
-}
\ No newline at end of file
+}
diff --git a/src/methods/sparse_coding/sparse_coding.pl b/src/methods/sparse_coding/sparse_coding.pl
index 624eeb9..c7f4b4e 100644
--- a/src/methods/sparse_coding/sparse_coding.pl
+++ b/src/methods/sparse_coding/sparse_coding.pl
@@ -1,8 +1,8 @@
 
-:- module(sparse_coding, [      initModelWithTrain/9,
+:- module(sparse_coding, [      initModelWithTrain/8,
                                 initModelNoTrain/6,
-                                encode/6,
-                                train/4]).
+                                encode/4,
+                                train/3]).
 
 %% requirements of library(struct)
 :- load_files(library(str_decl),
@@ -20,6 +20,9 @@
 %% definitions for the connected function
 
 
+
+foreign(initModelWithTrain, c, initModelWithTrainI(      +pointer(float_array), +integer, +integer,
+                                                        +integer, +float32, +float32, +integer, +float32, +float32)).
 %% --Input--
 %%              mat     data,
 %%              int     atoms                   => 15,
@@ -34,10 +37,18 @@
 %% --Description--
 %%              Initializes sparse_coding model and trains it.
 %%
-foreign(initModelWithTrain, c, initModelWithTrain(      +pointer(float_array), +integer, +integer,
-                                                        +integer, +float32, +float32, +integer, +float32, +float32)).
-
-
+initModelWithTrain(MatList, MatRows, Atoms, Lambda1, Lambda2, MaxIterations, ObjTolerance, NewtonTolerance) :-
+        Atoms >= 0,
+        Lambda1 > 0,
+        Lambda2 >= 0,
+        MaxIterations >= 0,
+        ObjTolerance > 0,
+        NewtonTolerance > 0,
+        convert_list_to_float_array(MatList, MatRows, array(Xsize, Xrownum, X)),
+        initModelWithTrainI(X, Xsize, Xrownum, Atoms, Lambda1, Lambda2, MaxIterations, ObjTolerance, NewtonTolerance).
+
+
+foreign(initModelNoTrain, c, initModelNoTrainI(+integer, +float32, +float32, +integer, +float32, +float32)).
 %% --Input--
 %%              int     atoms                   => 15,
 %%              float32 lambda1                 => 0,
@@ -51,9 +62,18 @@ foreign(initModelWithTrain, c, initModelWithTrain(      +pointer(float_array), +
 %% --Description--
 %%              Initializes sparse_coding model but will not train the model, and a subsequent call to Train will be required before the model can encode points with Encode.
 %%
-foreign(initModelNoTrain, c, initModelNoTrain(+integer, +float32, +float32, +integer, +float32, +float32)).
+initModelNoTrain(Atoms, Lambda1, Lambda2, MaxIterations, ObjTolerance, NewtonTolerance) :-
+        Atoms >= 0,
+        Lambda1 > 0,
+        Lambda2 >= 0,
+        MaxIterations >= 0,
+        ObjTolerance > 0,
+        NewtonTolerance > 0,
+        initModelNoTrainI(Atoms, Lambda1, Lambda2, MaxIterations, ObjTolerance, NewtonTolerance).
 
 
+foreign(encode, c, encodeI(      +pointer(float_array), +integer, +integer, 
+                                -pointer(float_array), -integer, -integer)).
 %% --Input--
 %%              mat     data
 %%
@@ -63,10 +83,14 @@ foreign(initModelNoTrain, c, initModelNoTrain(+integer, +float32, +float32, +int
 %% --Description--
 %%              Sparse code each point in the given dataset via LARS, using the current dictionary and store the encoded data in the codes matrix.
 %%
-foreign(encode, c, encode(      +pointer(float_array), +integer, +integer, 
-                                -pointer(float_array), -integer, -integer)).
+encode(DataList, DataRows, CodesList, YCols) :-
+        convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrownum, X)),
+        encodeI(X, Xsize, Xrownum, Y, YCols, YRows),
+        convert_float_array_to_2d_list(Y, YCols, YRows, CodesList).
 
 
+foreign(train, c, trainI(+pointer(float_array), +integer, +integer, 
+                        [-float32])).
 %% --Input--
 %%              mat     data
 %%
@@ -76,8 +100,9 @@ foreign(encode, c, encode(      +pointer(float_array), +integer, +integer,
 %% --Description--
 %%              Train the sparse coding model on the given dataset.
 %%
-foreign(train, c, train(+pointer(float_array), +integer, +integer, 
-                        [-float32])).
+train(DataList, DataRows, ReturnValue) :-
+        convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrownum, X)),
+        trainI(X, Xsize, Xrownum, ReturnValue).
 
 
 %% Defines the functions that get connected from main.cpp
diff --git a/src/methods/sparse_coding/sparse_coding_test.pl b/src/methods/sparse_coding/sparse_coding_test.pl
index e8f2663..8cbb930 100644
--- a/src/methods/sparse_coding/sparse_coding_test.pl
+++ b/src/methods/sparse_coding/sparse_coding_test.pl
@@ -1,56 +1,175 @@
+
 :- use_module(library(plunit)).
 
 :- use_module(sparse_coding).
 :- use_module('../../helper_files/helper.pl').
 
-reset_Model :-
-        initModel(1,0,50,0.0001).
-
-:- begin_tests(lists).
-
-%% alpha tests
-test(alpha_std_init) :-
-        reset_Model,
-        alpha(0).
-test(alpha_wrong_input, fail) :-
-        reset_Model,
-        alpha(1).
-test(alpha_after_train, A =:= 9223372036854775808) :-
-        reset_Model,
-        convert_list_to_float_array([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5],3, array(Xsize, Xrownum, X)),
-        convert_list_to_float_array([0.2,0.2,0.2,0.2], array(Ysize, Y)),
-        train(X,Xsize, Xrownum,Y, Ysize),
-        alpha(A).
-
-%% train tests
-test(correct_train) :-
-        reset_Model,
-        convert_list_to_float_array([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5],3, array(Xsize, Xrownum, X)),
-        convert_list_to_float_array([0.2,0.2,0.2,0.2], array(Ysize, Y)),
-        train(X,Xsize, Xrownum,Y, Ysize).
-test(false_train, fail) :-
-        reset_Model,
-        convert_list_to_float_array([],3, array(Xsize, Xrownum, X)),
-        convert_list_to_float_array([0.2,0.2,0.2,0.2], array(Ysize, Y)),
-        train(X,Xsize, Xrownum,Y, Ysize).
-test(false_train2, fail) :-
-        reset_Model,
-        convert_list_to_float_array([],0, array(Xsize, Xrownum, X)),
-        convert_list_to_float_array([0.2,0.2,0.2,0.2], array(Ysize, Y)),
-        train(X,Xsize, Xrownum,Y, Ysize).
-test(false_train3, fail) :-
-        reset_Model,
-        convert_list_to_float_array([1,2],0, array(Xsize, Xrownum, X)),
-        convert_list_to_float_array([0.2,0.2,0.2,0.2], array(Ysize, Y)),
-        train(X,Xsize, Xrownum,Y, Ysize).
-test(false_train3, fail) :-
-        reset_Model,
-        convert_list_to_float_array([1,2,44,3],3, array(Xsize, Xrownum, X)),
-        convert_list_to_float_array([0.2,0.2,0.2,0.2], array(Ysize, Y)),
-        train(X,Xsize, Xrownum,Y, Ysize).
-test(false_train4) :-
-        reset_Model,
-        convert_list_to_float_array([1,2,44,3],2, array(Xsize, Xrownum, X)),
-        convert_list_to_float_array([0.2,0.2,0.2,0.2], array(Ysize, Y)),
-        train(X,Xsize, Xrownum,Y, Ysize).
-:- end_tests(lists).
\ No newline at end of file
+
+reset_Model_With_Train_A :-
+        open('/home/afkjakhes/eclipse-workspace/prolog-mlpack-libary/src/data_csv/iris2.csv', read, File),
+        take_csv_row(File, skipFirstRow,20, Data),
+        initModelWithTrain(Data,4,15,0.1,0.0,100,0.01,0.000001).
+reset_Model_No_Train_A :-
+        initModelNoTrain(2,0.1,0.0,100,0.01,0.000001).
+
+
+%%
+%% TESTING predicate initModelWithTrain/8
+%%
+:- begin_tests(initModelWithTrain).      
+
+%% Failure Tests
+                                            
+test(initModelWithTrain_Negative_RowNum, fail) :-
+        initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], -3,15,0.1,0.0,100,0.01,0.000001).
+
+test(initModelWithTrain_Negative_Atoms, fail) :-
+        initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3,-15,0.1,0.0,100,0.01,0.000001).
+
+test(initModelWithTrain_Negative_MaxIterations, fail) :-
+        initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3,15,0.1,0.0,-100,0.01,0.000001).
+
+test(initModelWithTrain_Negative_ObjTolerance, fail) :-
+        initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3,15,0.1,0.0,100,-0.01,0.000001).
+
+test(initModelWithTrain_Negative_NewtonTolerance, fail) :-
+        initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3,15,0.1,0.0,100,0.01,-0.000001).
+
+test(initModelWithTrain_Empty_List, fail) :-
+        initModelWithTrain([], 3,15,0.1,0.0,100,0.01,-0.000001).
+
+test(initModelWithTrain_Data_Amount_Smaller_Than_Atoms, [error(_,system_error('There have to be more DataPoints than Atoms!'))]) :-
+        initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 4,10,0.1,0.0,2,0.01,0.000001).
+        
+
+%% Successful Tests
+
+test(initModelWithTrain_Direct_Input_Use) :-
+        initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, 3,0.1,0.0,2,0.01,0.000001).
+
+test(initModelWithTrain_Changed_Input) :-
+        initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 6,2,2.3,0.1,30,0.002,0.0001).
+
+test(initModelWithTrain_CSV_Use) :-
+        open('/home/afkjakhes/eclipse-workspace/prolog-mlpack-libary/src/data_csv/iris2.csv', read, File),
+        take_csv_row(File, skipFirstRow,20, Data),
+        initModelWithTrain(Data,4,15,0.1,0.0,100,0.01,0.000001).
+
+:- end_tests(initModelWithTrain).
+
+
+%%
+%% TESTING predicate initModelNoTrain/6
+%%
+:- begin_tests(initModelNoTrain).      
+
+%% Failure Tests
+
+test(initModelNoTrain_Negative_Atoms, fail) :-
+        initModelNoTrain(-15,0.1,0.0,100,0.01,0.000001).
+
+test(initModelNoTrain_Negative_MaxIterations, fail) :-
+        initModelNoTrain(15,0.1,0.0,-100,0.01,0.000001).
+
+test(initModelNoTrain_Negative_ObjTolerance, fail) :-
+        initModelNoTrain(15,0.1,0.0,100,-0.01,0.000001).
+
+test(initModelNoTrain_Negative_NewtonTolerance, fail) :-
+        initModelNoTrain(15,0.1,0.0,100,0.01,-0.000001).
+
+test(initModelNoTrain_Empty_List, fail) :-
+        initModelNoTrain(15,0.1,0.0,100,0.01,-0.000001).
+        
+
+%% Successful Tests
+
+test(initModelNoTrain_Normal_Input_Use) :-
+        initModelNoTrain(15,0.1,0.0,100,0.01,0.000001).
+
+test(initModelNoTrain_Changed_Input) :-
+        initModelNoTrain(5,2.3,0.2,200,0.002,0.0001).
+
+:- end_tests(initModelNoTrain).
+
+
+%%
+%% TESTING predicate encode/4
+%%
+:- begin_tests(encode).      
+
+%% Failure Tests
+
+test(encode_With_No_Trained_Model, [error(_,system_error('Matrix has not the correct dimensions!'))]) :-
+        reset_Model_No_Train_A,
+        encode([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 4, CodesList, CodesRows),
+        print(CodesList),
+        print('\n'),
+        print(CodesRows).
+
+test(encode_With_Not_Fitting_Data, [error(_,system_error('Matrix has not the correct dimensions!'))]) :-
+        reset_Model_No_Train_A,
+        encode([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 2, CodesList, CodesRows),
+        print(CodesList),
+        print('\n'),
+        print(CodesRows).
+        
+
+%% Successful Tests
+
+test(encode_Normal_Use1) :-
+        reset_Model_With_Train_A,
+        encode([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 4, CodesList, CodesRows),
+        print(CodesList),
+        print('\n'),
+        print(CodesRows).
+
+
+test(encode_Normal_Use2) :-
+        reset_Model_No_Train_A,
+        open('/home/afkjakhes/eclipse-workspace/prolog-mlpack-libary/src/data_csv/iris2.csv', read, File),
+        take_csv_row(File, skipFirstRow,10, Data),
+        train(Data, 4, _),
+        encode([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 4, CodesList, CodesRows),
+        print(CodesList),
+        print('\n'),
+        print(CodesRows).
+
+:- end_tests(encode).
+
+
+%%
+%% TESTING predicate train/3
+%%
+:- begin_tests(train).      
+
+%% Failure Tests
+                                            
+test(train_With_Negative_RowNum, fail) :-
+        reset_Model_No_Train_A,
+        train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], -3, _).
+
+test(train_With_Empty_Data_List1, fail) :-
+        reset_Model_No_Train_A,
+        train([], 1, _).
+
+test(train_With_Empty_Data_List2, fail) :-
+        reset_Model_No_Train_A,
+        train([], 0, _).
+        
+
+%% Successful Tests
+
+test(train_With_Direkt_Input) :-
+        reset_Model_No_Train_A,
+        train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, ObjectiveValue),
+        print(ObjectiveValue).
+
+test(train_With_CSV_Input) :-
+        reset_Model_No_Train_A,
+        open('/home/afkjakhes/eclipse-workspace/prolog-mlpack-libary/src/data_csv/iris2.csv', read, File),
+        take_csv_row(File, skipFirstRow,20, Data),
+        train(Data, 4, ObjectiveValue),
+        print(ObjectiveValue).
+
+:- end_tests(train).
+
diff --git a/test_all.pl b/test_all.pl
index ec3331c..e6c45d1 100644
--- a/test_all.pl
+++ b/test_all.pl
@@ -1,4 +1,4 @@
 
-:- use_module(library(plunit)).
-
 :- ['src/methods/adaboost/adaboost_test.pl'].
+
+:- ['src/methods/sparse_coding/sparse_coding_test.pl'].
\ No newline at end of file
-- 
GitLab