From 3b3eba96fb1d9251f4d0cec9ca2d27ba7f66a65a Mon Sep 17 00:00:00 2001
From: Jakhes <dean.schmitz@schmitzbauer.de>
Date: Sat, 12 Nov 2022 00:15:17 +0100
Subject: [PATCH] Finishing lcc tests

---
 .../local_coordinate_coding.cpp               | 205 ++----------------
 .../local_coordinate_coding.pl                | 131 ++---------
 .../local_coordinate_coding_test.pl           |  44 ++--
 3 files changed, 58 insertions(+), 322 deletions(-)

diff --git a/src/methods/local_coordinate_coding/local_coordinate_coding.cpp b/src/methods/local_coordinate_coding/local_coordinate_coding.cpp
index 8cd6474..231c4c0 100644
--- a/src/methods/local_coordinate_coding/local_coordinate_coding.cpp
+++ b/src/methods/local_coordinate_coding/local_coordinate_coding.cpp
@@ -15,11 +15,6 @@ using namespace mlpack;
 using namespace std;
 using namespace mlpack::lcc;
 
-// Global Variable of the LocalCoordinateCoding object so it can be accessed from all functions
-LocalCoordinateCoding lccGlobObj;
-
-bool normalizeData = false;
-
 
 // input:   const arma::mat & 	            data,
 //          const size_t 	                atoms,
@@ -27,219 +22,47 @@ bool normalizeData = false;
 //          const size_t 	                maxIterations = 0,
 //          const double 	                tolerance = 0.01,
 //          const DictionaryInitializer & 	initializer = DictionaryInitializer()
+//          const arma::mat & 	            toEncodedata,
+//          arma::mat & 	                codes <-
 // output: 
 // description: 
-//          Initializes the model and trains it so encode/6 can be called after
-//
-void initModelWithTrain(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum,
-                            SP_integer normalize, SP_integer atoms, double lambda,  SP_integer maxIterations, double tolerance)
-{
-    normalizeData = normalize == 1;
-
-    // convert the Prolog array to arma::mat
-    mat data = convertArrayToMat(dataMatArr, dataMatSize, dataMatRowNum);
-
-    if (normalizeData)
-    {
-        Log::Info << "Normalizing data before coding..." << endl;
-        for (size_t i = 0; i < data.n_cols; ++i)
-            data.col(i) /= arma::norm(data.col(i), 2);
-    }
-
-    
-    try
-    {
-        lccGlobObj = LocalCoordinateCoding(data, atoms, lambda, maxIterations, tolerance);lccGlobObj = LocalCoordinateCoding(atoms, lambda, maxIterations, tolerance);
-    }
-    catch(const std::exception& e)
-    {
-        raisePrologSystemExeption(e.what());
-    }
-}
-
-
-// input:   const size_t 	atoms = 0,
-//          const double 	lambda = 0.0,
-//          const size_t 	maxIterations = 0,
-//          const double 	tolerance = 0.01
-// output: 
-// description: 
-//          Initializes the model but doesnt train it so train/4 has to be called befor encode/6 can be used.
+//          Initializes the model, trains it and encodes each point of the given data via distance-weighted LARS.
 //
-void initModelNoTrain(SP_integer normalize, SP_integer atoms, double lambda,  SP_integer maxIterations, double tolerance)
-{
-    normalizeData = normalize == 1;
-
-
-    try
-    {
-        lccGlobObj = LocalCoordinateCoding(atoms, lambda, maxIterations, tolerance);
-    }
-    catch(const std::exception& e)
-    {
-        raisePrologSystemExeption(e.what());
-    }
-}
-
-
-// input:   const arma::mat & 	data,
-//          arma::mat & 	    codes <-
-// output: 
-// description: 
-//          Code each point via distance-weighted LARS.
-//
-void encode(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum,
+void lcc(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum,
+            SP_integer normalize, SP_integer atoms, double lambda,  SP_integer maxIterations, double tolerance,
+            float *toEncodeDataMatArr, SP_integer toEncodeDataMatSize, SP_integer toEncodeDataMatRowNum,
             float **codesMatArr, SP_integer *codesMatColNum, SP_integer *codesMatRowNum)
 {
     // convert the Prolog array to arma::mat
     mat data = convertArrayToMat(dataMatArr, dataMatSize, dataMatRowNum);
+    mat toEncodeData = convertArrayToMat(toEncodeDataMatArr, toEncodeDataMatSize, toEncodeDataMatRowNum);
 
     // create the ReturnMat
     mat codesReturnMat;
 
-    // Normalize each point if the user asked for it.
-    if (IO::HasParam("normalize"))
+    if (normalize == 1)
     {
-        Log::Info << "Normalizing test data before coding..." << endl;
+        Log::Info << "Normalizing data before coding..." << endl;
         for (size_t i = 0; i < data.n_cols; ++i)
             data.col(i) /= arma::norm(data.col(i), 2);
-    }
-
-    
-    try
-    {
-        lccGlobObj.Encode(data, codesReturnMat);
-    }
-    catch(const std::exception& e)
-    {
-        raisePrologSystemExeption(e.what());
-    }
-
-    
-    // return the Matrix
-    returnMatrixInformation(codesReturnMat, codesMatArr, codesMatColNum, codesMatRowNum);
-}
-
-// TODO:    wrong inputs
-// input:   const arma::mat & 	data,
-//          const arma::mat & 	codes,
-//          const arma::uvec & 	adjacencies
-// output: 
-// description: 
-//          Compute objective function given the list of adjacencies.
-//
-void objective(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum,
-                float **codesMatArr, SP_integer *codesMatColNum, SP_integer *codesMatRowNum,
-                float **adjacenciesArr, SP_integer *adjacenciesArrSize)
-{
-    // convert the Prolog arrays to arma::mat
-    mat data = convertArrayToMat(dataMatArr, dataMatSize, dataMatRowNum);
 
-    // create the ReturnMat
-    mat codesReturnMat;
-
-    // create the ReturnVector
-    uvec adjacenciesReturnVector;
-
-    // Normalize each point if the user asked for it.
-    if (IO::HasParam("normalize"))
-    {
         Log::Info << "Normalizing test data before coding..." << endl;
-        for (size_t i = 0; i < data.n_cols; ++i)
-            data.col(i) /= arma::norm(data.col(i), 2);
+        for (size_t i = 0; i < toEncodeData.n_cols; ++i)
+            toEncodeData.col(i) /= arma::norm(toEncodeData.col(i), 2);
     }
 
-
+    
     try
     {
-        lccGlobObj.Objective(data, codesReturnMat, adjacenciesReturnVector);
+        LocalCoordinateCoding(data, atoms, lambda, maxIterations, tolerance)
+            .Encode(toEncodeData, codesReturnMat);
     }
     catch(const std::exception& e)
     {
         raisePrologSystemExeption(e.what());
     }
-    
-    
-    // return the Matrix
-    returnMatrixInformation(codesReturnMat, codesMatArr, codesMatColNum, codesMatRowNum);
-
-    // return the Vector
-    returnVectorInformation(arma::conv_to<vec>::from(adjacenciesReturnVector), adjacenciesArr, adjacenciesArrSize);
-}
-
-// TODO:    wrong inputs
-// input:   const arma::mat & 	data,
-//          const arma::mat & 	codes,
-//          const arma::uvec & 	adjacencies
-// output: 
-// description: 
-//          Learn dictionary by solving linear system.
-//
-void optimizeDictionary(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum,
-                        float **codesMatArr, SP_integer *codesMatColNum, SP_integer *codesMatRowNum,
-                        float **adjacenciesArr, SP_integer *adjacenciesArrSize)
-{
-    // convert the Prolog arrays to arma::mat
-    mat data = convertArrayToMat(dataMatArr, dataMatSize, dataMatRowNum);
-
-    // create the ReturnMat
-    mat codesReturnMat;
-
-    // create the ReturnVector
-    uvec adjacenciesReturnVector;
-
-    // Normalize each point if the user asked for it.
-    if (IO::HasParam("normalize"))
-    {
-        Log::Info << "Normalizing test data before coding..." << endl;
-        for (size_t i = 0; i < data.n_cols; ++i)
-            data.col(i) /= arma::norm(data.col(i), 2);
-    }
 
 
-    try
-    {
-        lccGlobObj.OptimizeDictionary(data, codesReturnMat, adjacenciesReturnVector);
-    }
-    catch(const std::exception& e)
-    {
-        raisePrologSystemExeption(e.what());
-    }
-    
-    
     // return the Matrix
     returnMatrixInformation(codesReturnMat, codesMatArr, codesMatColNum, codesMatRowNum);
-
-    // return the Vector
-    returnVectorInformation(arma::conv_to<vec>::from(adjacenciesReturnVector), adjacenciesArr, adjacenciesArrSize);
-}
-
-
-// input:   const arma::mat & 	            data,
-//          const DictionaryInitializer & 	initializer = DictionaryInitializer()
-// output:  double                          final objective value
-// description: 
-//          Run local coordinate coding and train the model.
-//
-double train(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum)
-{
-    // convert the Prolog arrays to arma::mat
-    mat data = convertArrayToMat(dataMatArr, dataMatSize, dataMatRowNum);
-
-    if (normalizeData)
-    {
-        Log::Info << "Normalizing data before coding..." << endl;
-        for (size_t i = 0; i < data.n_cols; ++i)
-            data.col(i) /= arma::norm(data.col(i), 2);
-    }
-
-
-    try
-    {
-        return lccGlobObj.Train(data);
-    }
-    catch(const std::exception& e)
-    {
-        raisePrologSystemExeption(e.what());
-    }
 }
\ No newline at end of file
diff --git a/src/methods/local_coordinate_coding/local_coordinate_coding.pl b/src/methods/local_coordinate_coding/local_coordinate_coding.pl
index 97c4580..08a1dca 100644
--- a/src/methods/local_coordinate_coding/local_coordinate_coding.pl
+++ b/src/methods/local_coordinate_coding/local_coordinate_coding.pl
@@ -1,10 +1,5 @@
 
-:- module(local_coordinate_coding, [    lcc_initModelWithTrain/7,
-                                        lcc_initModelNoTrain/5,
-                                        lcc_encode/4,
-                                        lcc_objective/5,
-                                        lcc_optimizeDictionary/5,
-                                        lcc_train/3]).
+:- module(local_coordinate_coding, [lcc/11]).
 
 %% requirements of library(struct)
 :- load_files(library(str_decl),
@@ -29,123 +24,33 @@
 %%              float32 lambda          => 0.0,
 %%              int     maxIterations   => 0,
 %%              float32 tolerance       => 0.01
-%%
-%% --Output--
-%%
-%% --Description--
-%%              Initializes the model and trains it so encode/6 can be called after
-%%
-lcc_initModelWithTrain(DataList, DataRows, Normalize, Atoms, Lambda, MaxIterations, Tolerance) :-
-        convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrownum, X)),
-        initModelWithTrainI(X, Xsize, Xrownum, Normalize, Atoms, Lambda, MaxIterations, Tolerance).
-
-foreign(initModelWithTrain, c, initModelWithTrainI(     +pointer(float_array), +integer, +integer, 
-                                                        +integer, 
-                                                        +integer, +float32, +integer, +float32)).
-
-
-%% --Input--
-%%              bool    normalize       => (1)true / (0)false => false,
-%%              int     atoms           => 0,
-%%              float32 lambda          => 0.0,
-%%              int     maxIterations   => 0,
-%%              float32 tolerance       => 0.01
-%%
-%% --Output--
-%%
-%% --Description--
-%%              Initializes the model but doesnt train it so train/4 has to be called befor encode/6 can be used.
-%%
-lcc_initModelNoTrain(Normalize, Atoms, Lambda, MaxIterations, Tolerance) :-
-        initModelNoTrainI(Normalize, Atoms, Lambda, MaxIterations, Tolerance).
-
-foreign(initModelNoTrain, c, initModelNoTrainI( +integer, 
-                                                +integer, +float32, +integer, +float32)).
-
-
-%% --Input--
 %%              mat     data
 %%
 %% --Output--
 %%              mat     codes
 %%
 %% --Description--
-%%              Code each point via distance-weighted LARS.
-%%
-lcc_encode(DataList, DataRows, CodesList, YCols) :-
-        convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrows, X)),
-        encodeI(X, Xsize, Xrows, Y, YCols, YRows),
-        convert_float_array_to_2d_list(Y, YCols, YRows, CodesList).
-
-foreign(encode, c, encodeI(     +pointer(float_array), +integer, +integer, 
-                                -pointer(float_array), -integer, -integer)).
-
-
-%% --Input--
-%%              mat     data
-%%
-%% --Output--
-%%              mat     codes,
-%%              vec     adjacencies
-%%
-%% --Description--
-%%              Compute objective function given the list of adjacencies.
-%%
-lcc_objective(DataList, DataRows, CodesList, YCols, AdjacenciesList) :-
-        convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrows, X)),
-        objectiveI(X, Xsize, Xrows, Y, YCols, YRows, Z, Zsize),
-        convert_float_array_to_2d_list(Y, YCols, YRows, CodesList),
-        convert_float_array_to_list(Z, Zsize, AdjacenciesList).
-
-foreign(objective, c, objectiveI(       +pointer(float_array), +integer, +integer, 
-                                        -pointer(float_array), -integer, -integer, 
-                                        -pointer(float_array), -integer)).
-
-
-%% --Input--
-%%              mat     data
-%%
-%% --Output--
-%%              mat     codes,
-%%              vec     adjacencies
-%%
-%% --Description--
-%%              Learn dictionary by solving linear system.
-%%
-lcc_optimizeDictionary(DataList, DataRows, CodesList, YCols, AdjacenciesList) :-
-        convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrows, X)),
-        optimizeDictionaryI(X, Xsize, Xrows, Y, YCols, YRows, Z, Zsize),
-        convert_float_array_to_2d_list(Y, YCols, YRows, CodesList),
-        convert_float_array_to_list(Z, Zsize, AdjacenciesList).
-
-foreign(optimizeDictionary, c, optimizeDictionaryI(     +pointer(float_array), +integer, +integer, 
-                                                        -pointer(float_array), -integer, -integer, 
-                                                        -pointer(float_array), -integer)).
-
-
-%% --Input--
-%%              mat     data
-%%
-%% --Output--
-%%              float32 final objective value
-%%
-%% --Description--
-%%              Run local coordinate coding and train the model.
+%%              Initializes the model and trains it so encode/6 can be called after
 %%
-lcc_train(DataList, DataRows, ReturnValue) :-
-        convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrows, X)),
-        trainI(X, Xsize, Xrows, ReturnValue).
+lcc(DataList, DataRows, Normalize, Atoms, Lambda, MaxIterations, Tolerance, TestDataList, TestDataRows, CodesList, ZRows) :-
+        Atoms > 0,
+        Lambda >= 0,
+        MaxIterations >= 0,
+        Tolerance > 0,
+        convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrownum, X)),
+        Atoms < (Xsize / Xrownum),
+        convert_list_to_float_array(TestDataList, TestDataRows, array(Ysize, Yrownum, Y)),
+        lccI(X, Xsize, Xrownum, Normalize, Atoms, Lambda, MaxIterations, Tolerance, Y, Ysize, Yrownum, Z, ZCols, ZRows),
+        convert_float_array_to_2d_list(Z, ZCols, ZRows, CodesList).
 
-foreign(train, c, trainI(       +pointer(float_array), +integer, +integer, 
-                                [-float32])).
+foreign(lcc, c, lccI(   +pointer(float_array), +integer, +integer, 
+                        +integer, 
+                        +integer, +float32, +integer, +float32,
+                        +pointer(float_array), +integer, +integer,
+                        -pointer(float_array), -integer, -integer)).
 
 
 %% Defines the functions that get connected from main.cpp
-foreign_resource(local_coordinate_coding, [     initModelWithTrain,
-                                                initModelNoTrain,
-                                                encode,
-                                                objective,
-                                                optimizeDictionary,
-                                                train]).
+foreign_resource(local_coordinate_coding, [lcc]).
 
 :- load_foreign_resource(local_coordinate_coding).
diff --git a/src/methods/local_coordinate_coding/local_coordinate_coding_test.pl b/src/methods/local_coordinate_coding/local_coordinate_coding_test.pl
index 0bdb5de..d4682db 100644
--- a/src/methods/local_coordinate_coding/local_coordinate_coding_test.pl
+++ b/src/methods/local_coordinate_coding/local_coordinate_coding_test.pl
@@ -6,38 +6,46 @@
 :- use_module(local_coordinate_coding).
 :- use_module('../../helper_files/helper.pl').
 
-reset_Model :-
-        lcc_initModel(1,0,50,0.0001).
 
 %%
-%% TESTING predicate predicate/10
+%% TESTING predicate lcc/11
 %%
-:- begin_tests(predicate).      
+:- begin_tests(lcc).      
 
 %% Failure Tests
                                             
-test(testDescription, [error(domain_error('expectation' , culprit), _)]) :-
-        reset_Model_No_Train(perceptron),
-        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, [0,0,0,0], 2, culprit, 50, 0.0001, _).
+test(lcc_Bad_Atom_Input, fail) :-
+        lcc([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, 0, -2, 0.0, 0, 0.01, [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, _, _),
+        lcc([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, 0, 4, 0.0, 0, 0.01, [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(lcc_Negative_Lambda, fail) :-
+        lcc([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, 0, 2, -1.0, 0, 0.01, [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(lcc_Negative_MaxIterations, fail) :-
+        lcc([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, 0, 2, 0.0, -1, 0.01, [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(lcc_Negative_Tolerance, fail) :-
+        lcc([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, 0, 2, 0.0, 0, -0.01, [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(lcc_Different_Dims, [error(_,system_error('matrix multiplication: incompatible matrix dimensions: 2x4 and 3x4'))]) :-
+        lcc([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, 0, 2, 0.0, 0, 0.01, [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(testDescription2, [error(_,system_error('The values of the Label have to start at 0 and be >= 0 and < the given numClass!'))]) :-
-        reset_Model_No_Train(perceptron),
-        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, [0,1,0,2], 2, perceptron, 50, 0.0001, _).
-        
 
 %% Successful Tests
 
-test(testDescription3, [true(Error =:= 1)]) :-
-        reset_Model_No_Train(perceptron),
-        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, [0,0,0,0], 2, perceptron, 50, 0.0001, Error).
+test(lcc_Normal_Use) :-
+        lcc([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, 0, 2, 0.0, 0, 0.01, [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, CodesList, _),
+        print('\nCodes: '),
+        print(CodesList).
 
-test(testDescription4, [true(Error =:= 0.9797958971132711)]) :-
-        reset_Model_No_Train(perceptron),
+test(lcc_CSV_Input) :-
         open('src/data_csv/iris2.csv', read, File),
         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).
+        lcc(Data, 4, 1, 5, 1.5, 50, 0.042, [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, _),
+        print('\nCodes: '),
+        print(CodesList).
 
-:- end_tests(predicate).
+:- end_tests(lcc).
 
 run_local_coordinate_coding_tests :-
         run_tests.
-- 
GitLab