#include <visp3/core/vpConfig.h>
#if defined(VISP_HAVE_CATCH2)
#include <catch_amalgamated.hpp>
#include <visp3/core/vpMatrix.h>
#if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
#include <opencv2/core.hpp>
#endif
#ifdef VISP_HAVE_EIGEN3
#include <Eigen/Dense>
#endif
#ifdef ENABLE_VISP_NAMESPACE
#endif
namespace
{
bool runBenchmark = false;
bool runBenchmarkAll = false;
double getRandomValues(double min, double max) { return (max - min) * ((double)rand() / (double)RAND_MAX) + min; }
vpMatrix generateRandomMatrix(
unsigned int rows,
unsigned int cols,
double min = -1,
double max = 1)
{
for (
unsigned int i = 0; i < M.
getRows(); i++) {
for (
unsigned int j = 0; j < M.
getCols(); j++) {
M[i][j] = getRandomValues(min, max);
}
}
return M;
}
vpColVector generateRandomVector(
unsigned int rows,
double min = -1,
double max = 1)
{
for (
unsigned int i = 0; i < v.
getRows(); i++) {
v[i] = getRandomValues(min, max);
}
return v;
}
{
}
}
unsigned int BcolNum = B.
getCols();
unsigned int BrowNum = B.
getRows();
unsigned int i, j, k;
for (i = 0; i < A.
getRows(); i++) {
double *rowptri = A[i];
double *ci = C[i];
for (j = 0; j < BcolNum; j++) {
double s = 0;
for (k = 0; k < BrowNum; k++) {
s += rowptri[k] * B[k][j];
}
ci[j] = s;
}
}
return C;
}
{
for (
unsigned int i = 0; i < A.
getCols(); i++) {
double *Bi = B[i];
for (unsigned int j = 0; j < i; j++) {
double s = 0;
for (
unsigned int k = 0; k < A.
getRows(); k++) {
s += (*(ptr + i)) * (*(ptr + j));
}
*Bi++ = s;
B[j][i] = s;
}
double s = 0;
for (
unsigned int k = 0; k < A.
getRows(); k++) {
s += (*(ptr + i)) * (*(ptr + i));
}
*Bi = s;
}
return B;
}
{
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = i; j < A.
getRows(); j++) {
double *pi = A[i];
double *pj = A[j];
double ssum = 0;
for (
unsigned int k = 0; k < A.
getCols(); k++)
ssum += *(pi++) * *(pj++);
B[i][j] = ssum;
if (i != j)
B[j][i] = ssum;
}
}
return B;
}
{
}
for (
unsigned int j = 0; j < A.
getCols(); j++) {
double vj = v[j];
for (
unsigned int i = 0; i < A.
getRows(); i++) {
w[i] += A[i][j] * vj;
}
}
return w;
}
{
return false;
}
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
return false;
}
}
}
return true;
}
}
TEST_CASE("Benchmark matrix-matrix multiplication", "[benchmark]")
{
if (runBenchmark || runBenchmarkAll) {
std::vector<std::pair<int, int> > sizes = { {3, 3}, {6, 6}, {8, 8}, {10, 10}, {20, 20}, {6, 200},
{200, 6}, {207, 119}, {83, 201}, {600, 400}, {400, 600} };
for (auto sz : sizes) {
vpMatrix A = generateRandomMatrix(sz.first, sz.second);
vpMatrix B = generateRandomMatrix(sz.second, sz.first);
std::ostringstream oss;
BENCHMARK(oss.str().c_str())
{
C_true = dgemm_regular(A, B);
return C_true;
};
oss.str("");
BENCHMARK(oss.str().c_str())
{
C = A * B;
return C;
};
REQUIRE(equalMatrix(C, C_true));
if (runBenchmarkAll) {
#if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
cv::Mat matA(sz.first, sz.second, CV_64FC1);
cv::Mat matB(sz.second, sz.first, CV_64FC1);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
matA.at<double>(i, j) = A[i][j];
matB.at<double>(j, i) = B[j][i];
}
}
oss.str("");
oss << "(" << matA.rows << "x" << matA.cols << ")x(" << matB.rows << "x" << matB.cols << ") - OpenCV";
BENCHMARK(oss.str().c_str())
{
cv::Mat matC = matA * matB;
return matC;
};
#endif
#ifdef VISP_HAVE_EIGEN3
Eigen::MatrixXd eigenA(sz.first, sz.second);
Eigen::MatrixXd eigenB(sz.second, sz.first);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
eigenA(i, j) = A[i][j];
eigenB(j, i) = B[j][i];
}
}
oss.str("");
oss << "(" << eigenA.rows() << "x" << eigenA.cols() << ")x(" << eigenB.rows() << "x" << eigenB.cols()
<< ") - Eigen";
BENCHMARK(oss.str().c_str())
{
Eigen::MatrixXd eigenC = eigenA * eigenB;
return eigenC;
};
#endif
}
}
}
{
const unsigned int rows = 47, cols = 63;
vpMatrix A = generateRandomMatrix(rows, cols);
vpMatrix B = generateRandomMatrix(cols, rows);
REQUIRE(equalMatrix(C, C_true));
}
}
TEST_CASE("Benchmark matrix-rotation matrix multiplication", "[benchmark]")
{
if (runBenchmark || runBenchmarkAll) {
std::vector<std::pair<int, int> > sizes = { {3, 3} };
for (auto sz : sizes) {
vpMatrix A = generateRandomMatrix(sz.first, sz.second);
std::ostringstream oss;
BENCHMARK(oss.str().c_str())
{
AB_true = dgemm_regular(A,
static_cast<vpMatrix>(B));
return AB_true;
};
oss.str("");
BENCHMARK(oss.str().c_str())
{
AB = A * B;
return AB;
};
REQUIRE(equalMatrix(AB, AB_true));
if (runBenchmarkAll) {
#if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
cv::Mat matA(sz.first, sz.second, CV_64FC1);
cv::Mat matB(3, 3, CV_64FC1);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
matA.at<double>(i, j) = A[i][j];
}
}
for (
unsigned int i = 0; i < B.
getRows(); i++) {
for (
unsigned int j = 0; j < B.
getCols(); j++) {
matB.at<double>(j, i) = B[j][i];
}
}
oss.str("");
oss << "(" << matA.rows << "x" << matA.cols << ")x(" << matB.rows << "x" << matB.cols << ") - OpenCV";
BENCHMARK(oss.str().c_str())
{
cv::Mat matC = matA * matB;
return matC;
};
#endif
#ifdef VISP_HAVE_EIGEN3
Eigen::MatrixXd eigenA(sz.first, sz.second);
Eigen::MatrixXd eigenB(3, 3);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
eigenA(i, j) = A[i][j];
}
}
for (
unsigned int i = 0; i < B.
getRows(); i++) {
for (
unsigned int j = 0; j < B.
getCols(); j++) {
eigenB(j, i) = B[j][i];
}
}
oss.str("");
oss << "(" << eigenA.rows() << "x" << eigenA.cols() << ")x(" << eigenB.rows() << "x" << eigenB.cols()
<< ") - Eigen";
BENCHMARK(oss.str().c_str())
{
Eigen::MatrixXd eigenC = eigenA * eigenB;
return eigenC;
};
#endif
}
}
}
{
const unsigned int rows = 3, cols = 3;
vpMatrix A = generateRandomMatrix(rows, cols);
REQUIRE(equalMatrix(AB, AB_true));
}
}
TEST_CASE("Benchmark matrix-homogeneous matrix multiplication", "[benchmark]")
{
if (runBenchmark || runBenchmarkAll) {
std::vector<std::pair<int, int> > sizes = { {4, 4} };
for (auto sz : sizes) {
vpMatrix A = generateRandomMatrix(sz.first, sz.second);
std::ostringstream oss;
BENCHMARK(oss.str().c_str())
{
AB_true = dgemm_regular(A,
static_cast<vpMatrix>(B));
return AB_true;
};
oss.str("");
BENCHMARK(oss.str().c_str())
{
AB = A * B;
return AB;
};
REQUIRE(equalMatrix(AB, AB_true));
if (runBenchmarkAll) {
#if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
cv::Mat matA(sz.first, sz.second, CV_64FC1);
cv::Mat matB(4, 4, CV_64FC1);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
matA.at<double>(i, j) = A[i][j];
}
}
for (
unsigned int i = 0; i < B.
getRows(); i++) {
for (
unsigned int j = 0; j < B.
getCols(); j++) {
matB.at<double>(j, i) = B[j][i];
}
}
oss.str("");
oss << "(" << matA.rows << "x" << matA.cols << ")x(" << matB.rows << "x" << matB.cols << ") - OpenCV";
BENCHMARK(oss.str().c_str())
{
cv::Mat matC = matA * matB;
return matC;
};
#endif
#ifdef VISP_HAVE_EIGEN3
Eigen::MatrixXd eigenA(sz.first, sz.second);
Eigen::MatrixXd eigenB(4, 4);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
eigenA(i, j) = A[i][j];
}
}
for (
unsigned int i = 0; i < B.
getRows(); i++) {
for (
unsigned int j = 0; j < B.
getCols(); j++) {
eigenB(j, i) = B[j][i];
}
}
oss.str("");
oss << "(" << eigenA.rows() << "x" << eigenA.cols() << ")x(" << eigenB.rows() << "x" << eigenB.cols()
<< ") - Eigen";
BENCHMARK(oss.str().c_str())
{
Eigen::MatrixXd eigenC = eigenA * eigenB;
return eigenC;
};
#endif
}
}
}
{
const unsigned int rows = 4, cols = 4;
vpMatrix A = generateRandomMatrix(rows, cols);
REQUIRE(equalMatrix(AB, AB_true));
}
}
TEST_CASE("Benchmark matrix-vector multiplication", "[benchmark]")
{
if (runBenchmark || runBenchmarkAll) {
std::vector<std::pair<int, int> > sizes = { {3, 3}, {6, 6}, {8, 8}, {10, 10}, {20, 20}, {6, 200},
{200, 6}, {207, 119}, {83, 201}, {600, 400}, {400, 600} };
for (auto sz : sizes) {
vpMatrix A = generateRandomMatrix(sz.first, sz.second);
std::ostringstream oss;
BENCHMARK(oss.str().c_str())
{
C_true = dgemv_regular(A,
static_cast<vpColVector>(B));
return C_true;
};
oss.str("");
BENCHMARK(oss.str().c_str())
{
C = A * B;
return C;
};
if (runBenchmarkAll) {
#if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
cv::Mat matA(sz.first, sz.second, CV_64FC1);
cv::Mat matB(sz.second, 1, CV_64FC1);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
matA.at<double>(i, j) = A[i][j];
if (i == 0) {
matB.at<double>(j, 0) = B[j];
}
}
}
oss.str("");
oss << "(" << matA.rows << "x" << matA.cols << ")x(" << matB.rows << "x" << matB.cols << ") - OpenCV";
BENCHMARK(oss.str().c_str())
{
cv::Mat matC = matA * matB;
return matC;
};
#endif
#ifdef VISP_HAVE_EIGEN3
Eigen::MatrixXd eigenA(sz.first, sz.second);
Eigen::MatrixXd eigenB(sz.second, 1);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
eigenA(i, j) = A[i][j];
if (i == 0) {
eigenB(j, 0) = B[j];
}
}
}
oss.str("");
oss << "(" << eigenA.rows() << "x" << eigenA.cols() << ")x(" << eigenB.rows() << "x" << eigenB.cols()
<< ") - Eigen";
BENCHMARK(oss.str().c_str())
{
Eigen::MatrixXd eigenC = eigenA * eigenB;
return eigenC;
};
#endif
}
}
}
{
const unsigned int rows = 47, cols = 63;
vpMatrix A = generateRandomMatrix(rows, cols);
}
}
TEST_CASE("Benchmark AtA", "[benchmark]")
{
if (runBenchmark || runBenchmarkAll) {
std::vector<std::pair<int, int> > sizes = { {3, 3}, {6, 6}, {8, 8}, {10, 10}, {20, 20}, {6, 200},
{200, 6}, {207, 119}, {83, 201}, {600, 400}, {400, 600} };
for (auto sz : sizes) {
vpMatrix A = generateRandomMatrix(sz.first, sz.second);
std::ostringstream oss;
BENCHMARK(oss.str().c_str())
{
AtA_true = AtA_regular(A);
return AtA_true;
};
oss.str("");
BENCHMARK(oss.str().c_str())
{
return AtA;
};
REQUIRE(equalMatrix(AtA, AtA_true));
if (runBenchmarkAll) {
#if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
cv::Mat matA(sz.first, sz.second, CV_64FC1);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
matA.at<double>(i, j) = A[i][j];
}
}
oss.str("");
oss << "(" << matA.rows << "x" << matA.cols << ") - OpenCV";
BENCHMARK(oss.str().c_str())
{
cv::Mat matAtA = matA.t() * matA;
return matAtA;
};
#endif
#ifdef VISP_HAVE_EIGEN3
Eigen::MatrixXd eigenA(sz.first, sz.second);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
eigenA(i, j) = A[i][j];
}
}
oss.str("");
oss << "(" << eigenA.rows() << "x" << eigenA.cols() << ") - Eigen";
BENCHMARK(oss.str().c_str())
{
Eigen::MatrixXd eigenAtA = eigenA.
transpose() * eigenA;
return eigenAtA;
};
#endif
}
}
}
{
const unsigned int rows = 47, cols = 63;
vpMatrix A = generateRandomMatrix(rows, cols);
REQUIRE(equalMatrix(AtA, AtA_true));
}
}
TEST_CASE("Benchmark AAt", "[benchmark]")
{
if (runBenchmark || runBenchmarkAll) {
std::vector<std::pair<int, int> > sizes = {
{3, 3}, {6, 6}, {8, 8}, {10, 10},
{20, 20}, {6, 200}, {200, 6} };
for (auto sz : sizes) {
vpMatrix A = generateRandomMatrix(sz.first, sz.second);
std::ostringstream oss;
BENCHMARK(oss.str().c_str())
{
AAt_true = AAt_regular(A);
return AAt_true;
};
oss.str("");
BENCHMARK(oss.str().c_str())
{
return AAt;
};
REQUIRE(equalMatrix(AAt, AAt_true));
if (runBenchmarkAll) {
#if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
cv::Mat matA(sz.first, sz.second, CV_64FC1);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
matA.at<double>(i, j) = A[i][j];
}
}
oss.str("");
oss << "(" << matA.rows << "x" << matA.cols << ") - OpenCV";
BENCHMARK(oss.str().c_str())
{
cv::Mat matAAt = matA * matA.t();
return matAAt;
};
#endif
#ifdef VISP_HAVE_EIGEN3
Eigen::MatrixXd eigenA(sz.first, sz.second);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
eigenA(i, j) = A[i][j];
}
}
oss.str("");
oss << "(" << eigenA.rows() << "x" << eigenA.cols() << ") - Eigen";
BENCHMARK(oss.str().c_str())
{
Eigen::MatrixXd eigenAAt = eigenA * eigenA.
transpose();
return eigenAAt;
};
#endif
}
}
}
{
const unsigned int rows = 47, cols = 63;
vpMatrix A = generateRandomMatrix(rows, cols);
REQUIRE(equalMatrix(AAt, AAt_true));
}
}
TEST_CASE("Benchmark matrix-velocity twist multiplication", "[benchmark]")
{
if (runBenchmark || runBenchmarkAll) {
std::vector<std::pair<int, int> > sizes = { {6, 6}, {20, 6}, {207, 6}, {600, 6}, {1201, 6} };
for (auto sz : sizes) {
vpMatrix A = generateRandomMatrix(sz.first, sz.second);
std::ostringstream oss;
oss <<
"(" << A.
getRows() <<
"x" << A.
getCols() <<
")x(6x6) - Naive code";
BENCHMARK(oss.str().c_str())
{
AV_true = dgemm_regular(A,
static_cast<vpMatrix>(V));
return AV_true;
};
oss.str("");
BENCHMARK(oss.str().c_str())
{
AV = A * V;
return AV;
};
REQUIRE(equalMatrix(AV, AV_true));
if (runBenchmarkAll) {
#if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
cv::Mat matA(sz.first, sz.second, CV_64FC1);
cv::Mat matV(6, 6, CV_64FC1);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
matA.at<double>(i, j) = A[i][j];
}
}
for (unsigned int i = 0; i < V.getRows(); i++) {
for (unsigned int j = 0; j < V.getCols(); j++) {
matV.at<double>(i, j) = V[i][j];
}
}
oss.str("");
oss << "(" << matA.rows << "x" << matA.cols << ")x(6x6) - OpenCV";
BENCHMARK(oss.str().c_str())
{
cv::Mat matAV = matA * matV;
return matAV;
};
#endif
#ifdef VISP_HAVE_EIGEN3
Eigen::MatrixXd eigenA(sz.first, sz.second);
Eigen::MatrixXd eigenV(6, 6);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
eigenA(i, j) = A[i][j];
}
}
for (unsigned int i = 0; i < V.getRows(); i++) {
for (unsigned int j = 0; j < V.getCols(); j++) {
eigenV(i, j) = V[i][j];
}
}
oss.str("");
oss << "(" << eigenA.rows() << "x" << eigenA.cols() << ")x(6x6) - Eigen";
BENCHMARK(oss.str().c_str())
{
Eigen::MatrixXd eigenAV = eigenA * eigenV;
return eigenAV;
};
#endif
}
}
}
{
const unsigned int rows = 47, cols = 6;
vpMatrix A = generateRandomMatrix(rows, cols);
REQUIRE(equalMatrix(AV, AV_true));
}
}
TEST_CASE("Benchmark matrix-force twist multiplication", "[benchmark]")
{
if (runBenchmark || runBenchmarkAll) {
std::vector<std::pair<int, int> > sizes = { {6, 6}, {20, 6}, {207, 6}, {600, 6}, {1201, 6} };
for (auto sz : sizes) {
vpMatrix A = generateRandomMatrix(sz.first, sz.second);
std::ostringstream oss;
oss <<
"(" << A.
getRows() <<
"x" << A.
getCols() <<
")x(6x6) - Naive code";
BENCHMARK(oss.str().c_str())
{
AV_true = dgemm_regular(A,
static_cast<vpMatrix>(V));
return AV_true;
};
oss.str("");
BENCHMARK(oss.str().c_str())
{
AV = A * V;
return AV;
};
REQUIRE(equalMatrix(AV, AV_true));
if (runBenchmarkAll) {
#if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
cv::Mat matA(sz.first, sz.second, CV_64FC1);
cv::Mat matV(6, 6, CV_64FC1);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
matA.at<double>(i, j) = A[i][j];
}
}
for (unsigned int i = 0; i < V.getRows(); i++) {
for (unsigned int j = 0; j < V.getCols(); j++) {
matV.at<double>(i, j) = V[i][j];
}
}
oss.str("");
oss << "(" << matA.rows << "x" << matA.cols << ")x(6x6) - OpenCV";
BENCHMARK(oss.str().c_str())
{
cv::Mat matAV = matA * matV;
return matAV;
};
#endif
#ifdef VISP_HAVE_EIGEN3
Eigen::MatrixXd eigenA(sz.first, sz.second);
Eigen::MatrixXd eigenV(6, 6);
for (
unsigned int i = 0; i < A.
getRows(); i++) {
for (
unsigned int j = 0; j < A.
getCols(); j++) {
eigenA(i, j) = A[i][j];
}
}
for (unsigned int i = 0; i < V.getRows(); i++) {
for (unsigned int j = 0; j < V.getCols(); j++) {
eigenV(i, j) = V[i][j];
}
}
oss.str("");
oss << "(" << eigenA.rows() << "x" << eigenA.cols() << ")x(6x6) - Eigen";
BENCHMARK(oss.str().c_str())
{
Eigen::MatrixXd eigenAV = eigenA * eigenV;
return eigenAV;
};
#endif
}
}
}
{
const unsigned int rows = 47, cols = 6;
vpMatrix A = generateRandomMatrix(rows, cols);
REQUIRE(equalMatrix(AV, AV_true));
}
}
int main(int argc, char *argv[])
{
srand(1);
Catch::Session session;
std::cout << "Default matrix/vector min size to enable Blas/Lapack optimization: " << lapackMinSize << std::endl;
auto cli = session.cli()
| Catch::Clara::Opt(runBenchmark)["--benchmark"]("run benchmark comparing naive code with ViSP implementation")
| Catch::Clara::Opt(runBenchmarkAll)["--benchmark-all"]("run benchmark comparing naive code with ViSP, OpenCV, Eigen implementation")
| Catch::Clara::Opt(lapackMinSize, "min size")["--lapack-min-size"]("matrix/vector min size to enable blas/lapack usage");
session.cli(cli);
session.applyCommandLine(argc, argv);
<< std::endl;
int numFailed = session.run();
return numFailed;
}
#else
#include <iostream>
int main() { return EXIT_SUCCESS; }
#endif
unsigned int getCols() const
Type * data
Address of the first element of the data array.
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
unsigned int getRows() const
Implementation of column vector and the associated operations.
void resize(unsigned int i, bool flagNullify=true)
error that can be emitted by ViSP classes.
@ dimensionError
Bad dimension.
Implementation of an homogeneous matrix and operations on such kind of matrices.
static bool equal(double x, double y, double threshold=0.001)
static double deg(double rad)
Implementation of a matrix and operations on matrices.
static void setLapackMatrixMinSize(unsigned int min_size)
static unsigned int getLapackMatrixMinSize()
vpMatrix transpose() const
static void mult2Matrices(const vpMatrix &A, const vpMatrix &B, vpMatrix &C)
Implementation of a rotation matrix and operations on such kind of matrices.
Implementation of a rotation vector as axis-angle minimal representation.
Class that consider the case of a translation vector.