C++中犰狳矩阵维度的动态参数化

Posted

技术标签:

【中文标题】C++中犰狳矩阵维度的动态参数化【英文标题】:Dynamic parameterization of Armadillo matrix dimensions in C++ 【发布时间】:2021-01-24 07:38:04 【问题描述】:

标题总结了更准确地动态检索传递给犰狳矩阵的 MATLAB 数组的维数的目标。

我想将 mY() 和 mD() 的第二个和第三个参数更改为下面的参数。

// mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false)
arma::mat mY(&dY[0], 2, 168, false);
arma::mat mD(&dD[0], 2, 168, false);

这绝对是一个常见的用例,但是当从 MATLAB 提供的数组的维数可以是任意的 (n > 2) 时,我仍然找不到一个很好的方法来实现它。

对于矩阵(二维)情况,我可能会破解我的方式,但我觉得这不够优雅(可能也没有效率)。

恕我直言,必须走的路是:

matlab::data::TypedArray<double> 具有 getDimensions() 成员函数,它检索基本上是 std::vector<size_t>matlab::data::ArrayDimensions

索引getDimensions()检索到的向量的第一个和第二个元素可以检索行数和列数,如下所示。

unsigned int mYrows = matrixY.getDimensions()[0];
unsigned int mYcols = matrixY.getDimensions()[1];

但是,以我当前的设置,我无法通过 sub.cpp 的 foo() 函数中的指针/引用调用 getDimensions()。如果可行,我既不想创建额外的临时对象,也不想将其他参数传递给foo()。这怎么可能?

直觉一直告诉我,这样也必须有一个优雅的解决方案。也许使用多重间接?

我非常感谢知识渊博的 SO 成员提供的任何帮助、提示或建设性的 cmets。提前谢谢你。

设置:

两个 C++ 源文件和一个头文件:

ma​​in.cpp

包含 MATLAB 和 C++ 之间的通用 IO 接口 将两个 double 数组和两个 double const double 提供给 C++ 它通过调用 foo() 执行一些基于 Armadillo 的循环(这部分并不那么重要,因此省略) 返回 outp,它是一个“普通的”双精度标量 没有什么花哨或复杂的。

sub.cpp

这仅适用于 foo() 循环部分。

sub.hpp

只是一个简单的头文件。
// main.cpp
// MATLAB API Header Files
#include "mex.hpp"
#include "mexAdapter.hpp"

// Custom header
#include "sub.hpp"

// Overloading the function call operator, thus class acts as a functor
class MexFunction : public matlab::mex::Function 
    public:
        void operator()(matlab::mex::ArgumentList outputs,
                        matlab::mex::ArgumentList inputs)
            
            matlab::data::ArrayFactory factory;
            // Validate arguments
            checkArguments(outputs, inputs);

            matlab::data::TypedArray<double> matrixY = std::move(inputs[0]);
            matlab::data::TypedArray<double> matrixD = std::move(inputs[1]);
            const double csT = inputs[2][0];
            const double csKy = inputs[3][0];

            buffer_ptr_t<double> mY = matrixY.release();
            buffer_ptr_t<double> mD = matrixD.release();

            double* darrY = mY.get();
            double* darrD = mD.get();

            // data type of outp is "just" a plain double, NOT a double array
            double outp = foo(darrY, darrD, csT, csKy);

            outputs[0] = factory.createScalar(outp);

            void checkArguments(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs)
            // Create pointer to MATLAB engine
            std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr = getEngine();
            // Create array factory, allows us to create MATLAB arrays in C++
            matlab::data::ArrayFactory factory;
            // Check input size and types
            if (inputs[0].getType() != ArrayType::DOUBLE ||
                inputs[0].getType() == ArrayType::COMPLEX_DOUBLE)
            
                // Throw error directly into MATLAB if type does not match
                matlabPtr->feval(u"error", 0,
                    std::vector<Array>( factory.createScalar("Input must be double array.") ));
            
            // Check output size
            if (outputs.size() > 1) 
                matlabPtr->feval(u"error", 0, 
                    std::vector<Array>( factory.createScalar("Only one output is returned.") ));
                
        
;

// sub.cpp

#include "sub.hpp"
#include "armadillo"

double foo(double* dY, double* dD, const double T, const double Ky) 
    
    double sum = 0;

    // Conversion of input parameters to Armadillo types
    // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false)
    arma::mat mY(&dY[0], 2, 168, false);
    arma::mat mD(&dD[0], 2, 168, false);

    // Armadillo calculations

    for(int t=0; t<int(T); t++)

        // some armadillo based calculation
        // each for cycle increments sum by its return value 
    

    return sum;


// sub.hpp

#ifndef SUB_H_INCLUDED
#define SUB_H_INCLUDED

double foo(double* dY, double* dD, const double T, const double Ky);

#endif // SUB_H_INCLUDED

【问题讨论】:

您无法从double* 中检索尺寸,该信息在那时就丢失了。因此,您必须将调用更改为foo 才能直接传递矩阵或类似的东西。 @n314159 感谢您的评论/回答。是的你是对的;直到那时,当问题以我的方式提出时,信息就会丢失。 【参考方案1】:

一种方法是使用函数将其转换为 arma 矩阵

template<class T>
arma::Mat<T> getMat( matlab::data::TypedArray<T> A)

  matlab::data::TypedIterator<T> it = A.begin();
  matlab::data::ArrayDimensions nDim = A.getDimensions();
  return arma::Mat<T>(it.operator->(), nDim[0], nDim[1]);

并调用它

 arma::mat Y = getMat<double>(inputs[0]);
 arma::mat D = getMat<double>(inputs[1]);
 ...
 double outp = foo(Y,D, csT, csKy);

并将foo() 更改为

double foo( arma::mat& dY, arma::mat& dD, const double T, const double Ky) 

【讨论】:

@RolenClaes 谢谢。这是一个非常优雅的解决方案。回答接受。当维度信息以整数保存并且 foo() 被新参数扩展时,我什至检查了与我不复杂的解决方案相比的性能。它与您的模板化函数 + 迭代器实现非常相似。

以上是关于C++中犰狳矩阵维度的动态参数化的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中存储大矩阵(犰狳)

C++ Armadillo 和 OpenMp:外积求和的并行化 - 定义 Armadillo 矩阵的约简

犰狳中的并行化

C ++犰狳重塑只有一维大小的矩阵

正则化处理

参数化模型(parametric model)和非参数化模型non-parametric model)的区别?哪些模型是参数化模型,哪些模型是非参数化模型?