From 8b5db73eac5ea2b73688df7edce40b5dd9fc0dc9 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 1 Dec 2022 11:52:29 -0500 Subject: [PATCH] Handle transposed matrices for Go. --- src/mlpack/bindings/go/mlpack/arma_util.go | 4 +-- .../bindings/go/mlpack/capi/arma_util.cpp | 7 +++++- .../bindings/go/mlpack/capi/arma_util.h | 3 ++- .../bindings/go/print_input_processing.hpp | 20 ++++++++++++--- .../bindings/go/tests/go_binding_test.go | 25 +++++++++++++++++++ .../go/tests/test_go_binding_main.cpp | 21 ++++++++++++++++ 6 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/mlpack/bindings/go/mlpack/arma_util.go b/src/mlpack/bindings/go/mlpack/arma_util.go index 6657f1ce15..b107117353 100644 --- a/src/mlpack/bindings/go/mlpack/arma_util.go +++ b/src/mlpack/bindings/go/mlpack/arma_util.go @@ -91,7 +91,7 @@ func (m *mlpackArma) allocArmaPtrMatWithInfo(p *params, } // Passes a Gonum matrix to C by using the underlying data from the Gonum matrix. -func gonumToArmaMat(p *params, identifier string, m *mat.Dense) { +func gonumToArmaMat(p *params, identifier string, m *mat.Dense, trans bool) { // Get the number of elements in the Armadillo column. r, c := m.Dims() blas64General := m.RawMatrix() @@ -100,7 +100,7 @@ func gonumToArmaMat(p *params, identifier string, m *mat.Dense) { // Pass pointer of the underlying matrix to mlpack. ptr := unsafe.Pointer(&data[0]) C.mlpackToArmaMat(p.mem, C.CString(identifier), (*C.double)(ptr), - C.size_t(c), C.size_t(r)) + C.size_t(c), C.size_t(r), C.bool(trans)) } // Passes a Gonum matrix to C by using the underlying data from the Gonum matrix. diff --git a/src/mlpack/bindings/go/mlpack/capi/arma_util.cpp b/src/mlpack/bindings/go/mlpack/capi/arma_util.cpp index c8deee547e..81da118f50 100644 --- a/src/mlpack/bindings/go/mlpack/capi/arma_util.cpp +++ b/src/mlpack/bindings/go/mlpack/capi/arma_util.cpp @@ -28,13 +28,18 @@ void mlpackToArmaMat(void* params, const char* identifier, double* mat, const size_t row, - const size_t col) + const size_t col, + bool transpose) { util::Params& p = *((util::Params*) params); // Advanced constructor. arma::mat m(mat, row, col, false, false); + // Transpose if necessary. + if (transpose) + arma::inplace_trans(m); + // Set input parameter with corresponding matrix in IO. SetParam(p, identifier, m); } diff --git a/src/mlpack/bindings/go/mlpack/capi/arma_util.h b/src/mlpack/bindings/go/mlpack/capi/arma_util.h index 8d3da53e2a..dc4dc7d69d 100644 --- a/src/mlpack/bindings/go/mlpack/capi/arma_util.h +++ b/src/mlpack/bindings/go/mlpack/capi/arma_util.h @@ -28,7 +28,8 @@ void mlpackToArmaMat(void* params, const char* identifier, double* mat, const size_t row, - const size_t col); + const size_t col, + bool transpose); /** * Pass Gonum Dense pointer and wrap an Armadillo mat around it. diff --git a/src/mlpack/bindings/go/print_input_processing.hpp b/src/mlpack/bindings/go/print_input_processing.hpp index a337d8b577..8aa2a8997e 100644 --- a/src/mlpack/bindings/go/print_input_processing.hpp +++ b/src/mlpack/bindings/go/print_input_processing.hpp @@ -149,12 +149,26 @@ void PrintInputProcessing( * * // Detect if the parameter was passed; set if so. * if param.Name != nil { - * gonumToArma(params, "paramName", param.Name) + * gonumToArma(params, "paramName", param.Name, false) * setPassed(params, "paramName") * } + * + * where the boolean parameter indicates if the matrix needs to be transposed, + * and is only included for arma::mat type parameters. */ std::cout << prefix << "// Detect if the parameter was passed; set if so." << std::endl; + + // Add extra transpose option, but only for arma::mat types. + std::string transStrExtra = ""; + if (d.cppType == "arma::mat") + { + if (d.noTranspose) + transStrExtra = ", true"; + else + transStrExtra = ", false"; + } + if (!d.required) { std::cout << prefix << "if param." << goParamName @@ -163,7 +177,7 @@ void PrintInputProcessing( // Print function call to set the given parameter into the io. std::cout << prefix << prefix << "gonumToArma" << GetType(d) << "(params, \"" << d.name << "\", param." << goParamName - << ")" << std::endl; + << transStrExtra << ")" << std::endl; // Print function call to set the given parameter as passed. std::cout << prefix << prefix << "setPassed(params, \"" << d.name << "\")" @@ -176,7 +190,7 @@ void PrintInputProcessing( // Print function call to set the given parameter into the io. std::cout << prefix << "gonumToArma" << GetType(d) << "(params, \"" << d.name << "\", " << goParamName - << ")" << std::endl; + << transStrExtra << ")" << std::endl; // Print function call to set the given parameter as passed. std::cout << prefix << "setPassed(params, \"" << d.name << "\")" diff --git a/src/mlpack/bindings/go/tests/go_binding_test.go b/src/mlpack/bindings/go/tests/go_binding_test.go index 65e7f0d248..6ea4eabbf5 100644 --- a/src/mlpack/bindings/go/tests/go_binding_test.go +++ b/src/mlpack/bindings/go/tests/go_binding_test.go @@ -196,6 +196,31 @@ func TestGonumUMatrix(t *testing.T) { } } } + +func TestGonumTransMatrix(t *testing.T) { + t.Log("Test transposed matrix input.") + x := mat.NewDense(3, 5, []float64{ + 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, + }) + x2 := mat.NewDense(3, 5, []float64{ + 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, + }) + + param := mlpack.TestGoBindingOptions() + param.MatrixIn = x + param.TmatrixIn = x2 + d := 4.0 + i := 12 + s := "hello" + // The binding simply needs to run successfully (without exception) to + // succeed. + mlpack.TestGoBinding(d, i, s, param) +} + func TestGonumTransposeRow(t *testing.T) { t.Log("Test a column vector input parameter.") x := mat.NewDense(1, 9, []float64{ diff --git a/src/mlpack/bindings/go/tests/test_go_binding_main.cpp b/src/mlpack/bindings/go/tests/test_go_binding_main.cpp index 83e83190de..1e0f1ca930 100644 --- a/src/mlpack/bindings/go/tests/test_go_binding_main.cpp +++ b/src/mlpack/bindings/go/tests/test_go_binding_main.cpp @@ -41,6 +41,7 @@ PARAM_FLAG("flag1", "Input flag, must be specified.", "f"); PARAM_FLAG("flag2", "Input flag, must not be specified.", "F"); PARAM_MATRIX_IN("matrix_in", "Input matrix.", "m"); PARAM_UMATRIX_IN("umatrix_in", "Input unsigned matrix.", "u"); +PARAM_TMATRIX_IN("tmatrix_in", "Input matrix (transposed).", ""); PARAM_COL_IN("col_in", "Input column.", "c"); PARAM_UCOL_IN("ucol_in", "Input unsigned column.", ""); PARAM_ROW_IN("row_in", "Input row.", ""); @@ -92,6 +93,26 @@ void BINDING_FUNCTION(util::Params& params, util::Timers& /* timer */) params.Get("double_out") = 5.0; } + // If a transposed input matrix is given, it is expected to be the same as the + // (now required) input matrix. + if (params.Has("tmatrix_in")) + { + if (!params.Has("matrix_in")) + { + throw std::runtime_error("If tmatrix_in is specified, matrix_in must be " + "specified!"); + } + + arma::mat tmat = params.Get("tmatrix_in"); + arma::mat mat = params.Get("matrix_in"); + + if (!arma::approx_equal(tmat.t(), mat, "reldiff", 0.001)) + { + throw std::runtime_error("Transposed tmatrix_in and matrix_in are not " + "equal!"); + } + } + // Input matrices should be at least 5 rows; the 5th row will be dropped and // the 3rd row will be multiplied by two. if (params.Has("matrix_in"))