c++ 如何比较Eigen:Arrays rowwise,but undordered?

ou6hu8tu  于 2024-01-09  发布在  其他
关注(0)|答案(1)|浏览(128)

我正在为一个函数编写一个单元测试,该函数计算一个由4个大小为2的行向量组成的特征数组。我想检查它是否近似等于预期的结果,但行不一定必须以相同的顺序出现。因此,对于实际输出中的每一行,我想看看它是否与预期输出中的 some 行匹配。
所以这是我现在的测试

  1. Eigen::Array<float, 4, 2> expectedResult {{-14.1421356F, -14.1421356F},
  2. {-28.2842712F, 28.2841712F},
  3. {14.1421356F, 14.1421356F},
  4. {28.2842712F, -28.284172F}};
  5. Eigen::Array<float, 4, 2> result = myFunction();
  6. for (int rowIndex = 0; rowIndex < 4; rowIndex++)
  7. {
  8. // Check whether each row of result matches some other row of expectedResult,
  9. // not necessarily in order.
  10. // @todo find less clumsy way
  11. bool foundMatch = false;
  12. for (int comparisonRowIndex = 0; comparisonRowIndex < 4; comparisonRowIndex++)
  13. {
  14. if (result.row(rowIndex).isApprox(expectedResult.row(comparisonRowIndex), TOLERANCE))
  15. {
  16. foundMatch = true;
  17. }
  18. }
  19. EXPECT_TRUE(foundMatch);
  20. }

字符串
这感觉并不令人满意,我想我肯定能够以某种方式使用Eigen的any()结构,但我仍然没有找到原因。

  1. for (int rowIndex = 0; rowIndex < 4; rowIndex++)
  2. {
  3. EXPECT_TRUE(expectedResult.rowwise().isApprox(result.row(rowIndex), TOLERANCE).any()); // WRONG
  4. }

hjqgdpho

hjqgdpho1#

你的代码不能编译的原因是因为expectedResult.rowwise的返回值不是一个数组,因此没有成员isApprox。它是一个VectorwiseOp,参见rowwise的文档 (下次,请在你的问题中包括你的编译器错误)
我不是Eigen方面的Maven,但你的问题似乎是如此具体,我有一种预感,在Eigen中没有专门的功能来解决你的问题。
你没有说明为什么你认为你的方法是“笨拙的”。我认为你的方法总体上是好的,你需要检查源数组中的每一行与目标数组中的每一行,没有办法绕过它。如果你可以为行提供一个部分排序比较器函数,情况就会有所不同:然后你可以手动排序行,或者潜在地使用std::set
但是,您可以通过减少比较次数来提高效率:
1.如果源数组中的一行在目标数组中找不到匹配的行,可以停止函数并返回false。
1.如果对于源数组中的一行,您在目标数组中找到了匹配的行,则可以停止在源数组中搜索与该行匹配的行:您已经找到了匹配项。
1.如果对于源数组中的一行,您在目标数组中找到了匹配的行,则在对源数组行的后续迭代中不必考虑此目标行。

  1. template <typename T, int nrows, int ncols>
  2. auto compare_by_rows_unordered(
  3. Eigen::Array<T, nrows, ncols> const& source,
  4. Eigen::Array<T, nrows, ncols> const& target,
  5. T tolerance
  6. ) -> bool
  7. {
  8. bool rows_match_approx = true;
  9. // keep a set of target rows to check against. At the beginning this set contains all rows
  10. std::set<int> target_rows;
  11. for (int i = 0; i < nrows; ++i) {
  12. target_rows.insert(target_rows.end(), i);
  13. }
  14. for (int rowIndex = 0; rowIndex < nrows; rowIndex++)
  15. {
  16. bool foundMatch = false;
  17. for (int comparisonRowIndex : target_rows)
  18. {
  19. std::cout << "i = " << rowIndex << ", j = " << comparisonRowIndex << "\n";
  20. if (source.row(rowIndex).isApprox(target.row(comparisonRowIndex), tolerance))
  21. {
  22. std::cout << "MATCH!\n";
  23. target_rows.erase(comparisonRowIndex); // 3. target row has been found and does not need to be considered in future comparisons
  24. foundMatch = true;
  25. break; // 2. no need to check if the source row matches more than one target row
  26. }
  27. }
  28. if (!foundMatch) {
  29. rows_match_approx = false;
  30. break; // 1. source row matches no row in target. The arrays don't have the same rows! We can exit now
  31. }
  32. }
  33. return rows_match_approx;
  34. }

字符串
Godbolt链接:https://godbolt.org/z/6oTacj87j

附录

正如@chtz在他的评论中指出的那样,可能会有这种检查失败的情况。原因是“近似相等”不是等价关系。更具体地说,它不是传递的,即x.isApprox(y,tol)y.isApprox(z,tol)并不意味着x.isApprox(z,tol)
作为一个例子,让我们考虑你的问题的简化版本,我们想比较两个长度为1的行的数组,即两个二元向量s和t。
| S|不|
| --|--|
| 1.0 |1.0版|
| 0.91 |一点零九|
规则2和规则3将s[0]t[0]匹配,由于t[0]将从候选行集合中删除,因此s[1]没有匹配对象,函数将返回false,这可能是意外的。
那么,不排除任何比较是否更安全呢?
不,不是这样的。让我们放弃规则1,2和3,并严格比较以下输入的每个源行和每个目标行:
| S|不|
| --|--|
| 1.0 |1.0版|
| 0.91 |42.0|
然后,给定0.1的容差,您提出的算法将s[0]s[1]t[0]匹配,并且由于每个源行在目标数组中都有匹配,因此您的算法将返回true,这可能也不是您所期望的。
长话短说,浮点比较有复杂的陷阱,如果可能的话,拥有一个尊重所有边缘情况的比较函数并不是一个微不足道的任务。

展开查看全部

相关问题