【问题标题】:Sparse matrix class with parameterizable "zero"具有可参数化“零”的稀疏矩阵类
【发布时间】:2011-11-07 15:53:38
【问题描述】:

我正在对日志域中的浮点稀疏矩阵进行一些计算,因此“空”条目实际上是 -Inf(使用 -FLT_MAX)。我现在正在使用自定义稀疏矩阵类,但我渴望更换现成的替代品。

这是用 C++ 编写的。我倾向于查看 Eigen 和 Boost uBlas 中的压缩列矩阵。但是,尚不清楚是否支持“零”的自定义值(可能由模板参数提供)。有人有什么建议吗?

澄清

我想要的是:对于之前未“设置”的任何单元格 (i,j),我希望 mat[i,j] 返回 -Inf ...所以这可能更好地描述为稀疏矩阵的“空”条目的“默认”值。

我正在使用它来执行 HMM 递归(Viterbi,sum-product),并将概率保存在日志域中以避免下溢。

我没有做任何矩阵运算……本质上,我只是在填写动态规划表。我想使用稀疏矩阵类,因为我只填充矩阵的一个带,并且我希望有效地使用内存。压缩带矩阵将提供良好的性能,因为我正在“按顺序”填充矩阵。

【问题讨论】:

  • 传统稀疏矩阵的“零”元素不被存储,也不用于任何数值计算。我了解log(0) = -Inf,但我不明白您要做什么-如果您为所有零元素添加-FLT_MAX,您最终会得到一个密集矩阵,无论如何我看不出如何对涉及-Inf 的矩阵进行数值运算是有意义的。也许我错过了重点?
  • 例如,x * 0 == 0,但x * -Inf != -Infx 为负数时(同样x + 0 == x,但x + -Inf != x)。因此,在进行稀疏矩阵乘法时,如果这些值为-Inf,则不能只忽略所谓的空值,而可以直接跳过一堆零。
  • 我认为 OP 想要的是一个稀疏矩阵,其中零单元格实际上是其他值而不是零。所以不是大多数值为零的矩阵(所以它是稀疏的),大多数值是某个值 X(但相对于非 X 值仍然是稀疏的)。
  • 你打算用你的矩阵做什么样的计算?
  • @David:提问者确实解释了 - 矩阵中的值是概率的对数,并且在乘法之前将被反对数。 -Inf 用作log(0) 的近似值。因此,稀疏的东西可能不需要矩阵算术(这在对数域 AFAIK 中没有意义),只需一个具有可调空值的稀疏二维数组即可。

标签: c++ math boost sparse-matrix eigen


【解决方案1】:

我目前制定的解决方案是这样的。定义一个类lfloat

class lfloat {
  float value;
public:
  lfloat(float f=-FLT_MAX)
  {
    value = f;
  }

  lfloat& operator=(float f)
  {
    value = f;
    return *this;
  }

  operator float()   { return value; }
};

并像这样使用它:

compressed_matrix<lfloat> M1(3,3);

这样,我们不会重写 boost 矩阵类中的任何功能,但我们应该得到想要的结果。

【讨论】:

  • 你测试过这个吗?在我看来,压缩矩阵将为缺失值返回零值,这正是您不想要的。
  • 我测试了它并且它有效。我将 value_type() 返回为“零”,在本例中为 lfloat()=-FLT_MAX
  • 这很好。我可以建议一个小的优化。由于每次返回不存在的值都会调用默认构造函数,因此您可以通过编写 lfoat::lfloat() 来加快速度: value( -FLT_MAX ) {}
【解决方案2】:

这样的事情怎么样?

class compressed_matrix_nonzero_default : public boost::numeric::ublas::compressed_matrix<double>
{
    double def;
public:
    compressed_matrix_nonzero_default( int s1, int s2 )
        : boost::numeric::ublas::compressed_matrix<double>(s1,s2)
        , def(0)
    {

    }
    void setDefault( double d ) { def = d; }
    double value( int i, int j )
    {
        typedef boost::numeric::ublas::compressed_matrix<double>::iterator1 it1_t;
        typedef boost::numeric::ublas::compressed_matrix<double>::iterator2 it2_t;
        for (it1_t it1 = begin1(); it1 != end1(); it1++)
        {
            if( it1.index1() <  i )
                continue;
            if( it1.index1() > i ) {
                return def;
            }
            for (it2_t it2 = it1.begin(); it2 != it1.end(); it2++)
            {
                if( it2.index2() < j )
                    continue;
                if( it2.index2() == j )
                    return *it2;
                if( it2.index2() > j )
                    return def;
            }


        }
        return def;
    }

};

用法

compressed_matrix_nonzero_default MNZ(3,3);
MNZ.setDefault(-100);
MNZ (1,1) = 45;

for( int i = 0; i < 3; i++ ) {
    for( int j = 0; j < 3; j++ ) {
        std::cout << MNZ.value(i,j) << ",";
    }
    std::cout << "\n";
}

【讨论】:

  • 感谢 ravenpoint,这绝对是一个可行的解决方案。我唯一的缺点是它总是涉及线性扫描。如果底层的 boostcompressed_matrix 有一些快速缓存方案来加速按顺序访问,我们就不能以这种方式利用它。但我不知道 boost 或 eigen 是否真的实现了这样的方案---我将不得不检查。
  • Boostcompressed_matrix 实际上在这里有很好的功能分离:find_element 定位 (i,j) 元素并返回一个指针,而 operator() 要么取消引用该指针,要么返回 zero_。所以我认为我正在寻找的钩子是以某种方式设置 zero_ 或继承并覆盖 operator()
  • 您可能想要测试性能 - find_element 对 sort 的调用看起来很昂贵!
  • 根据http://www.guwi17.de/ublas/matrix_sparse_usage.html,“按顺序”填充 sparse_matrix 的适当方法是使用 push_back(i,j, val),这是一个常数时间操作。
  • 当然。但这与从随机位置提取值有什么关系?
猜你喜欢
  • 2018-09-18
  • 1970-01-01
  • 2018-01-06
  • 2017-06-07
  • 1970-01-01
  • 2021-06-24
  • 2017-02-12
  • 2017-07-21
  • 2015-06-11
相关资源
最近更新 更多