Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 357b9ab

Browse files
authored
Merge pull request #25450 from savuor:rv/svd_perf
Perf tests for SVD and solve() created #25450 fixes #25336 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
1 parent 8af7669 commit 357b9ab

File tree

1 file changed

+228
-0
lines changed

1 file changed

+228
-0
lines changed

modules/core/perf/perf_math.cpp

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,234 @@ PERF_TEST_P(VectorLength, phase64f, testing::Values(128, 1000, 128*1024, 512*102
3636
SANITY_CHECK(angle, 5e-5);
3737
}
3838

39+
// generates random vectors, performs Gram-Schmidt orthogonalization on them
40+
Mat randomOrtho(int rows, int ftype, RNG& rng)
41+
{
42+
Mat result(rows, rows, ftype);
43+
rng.fill(result, RNG::UNIFORM, cv::Scalar(-1), cv::Scalar(1));
44+
45+
for (int i = 0; i < rows; i++)
46+
{
47+
Mat v = result.row(i);
48+
49+
for (int j = 0; j < i; j++)
50+
{
51+
Mat p = result.row(j);
52+
v -= p.dot(v) * p;
53+
}
54+
55+
v = v * (1. / cv::norm(v));
56+
}
57+
58+
return result;
59+
}
60+
61+
template<typename FType>
62+
Mat buildRandomMat(int rows, int cols, RNG& rng, int rank, bool symmetrical)
63+
{
64+
int mtype = cv::traits::Depth<FType>::value;
65+
Mat u = randomOrtho(rows, mtype, rng);
66+
Mat v = randomOrtho(cols, mtype, rng);
67+
Mat s(rows, cols, mtype, Scalar(0));
68+
69+
std::vector<FType> singVals(rank);
70+
rng.fill(singVals, RNG::UNIFORM, Scalar(0), Scalar(10));
71+
std::sort(singVals.begin(), singVals.end());
72+
auto singIter = singVals.rbegin();
73+
for (int i = 0; i < rank; i++)
74+
{
75+
s.at<FType>(i, i) = *singIter++;
76+
}
77+
78+
if (symmetrical)
79+
return u * s * u.t();
80+
else
81+
return u * s * v.t();
82+
}
83+
84+
Mat buildRandomMat(int rows, int cols, int mtype, RNG& rng, int rank, bool symmetrical)
85+
{
86+
if (mtype == CV_32F)
87+
{
88+
return buildRandomMat<float>(rows, cols, rng, rank, symmetrical);
89+
}
90+
else if (mtype == CV_64F)
91+
{
92+
return buildRandomMat<double>(rows, cols, rng, rank, symmetrical);
93+
}
94+
else
95+
{
96+
CV_Error(cv::Error::StsBadArg, "This type is not supported");
97+
}
98+
}
99+
100+
CV_ENUM(SolveDecompEnum, DECOMP_LU, DECOMP_SVD, DECOMP_EIG, DECOMP_CHOLESKY, DECOMP_QR)
101+
102+
enum RankMatrixOptions
103+
{
104+
RANK_HALF, RANK_MINUS_1, RANK_FULL
105+
};
106+
107+
CV_ENUM(RankEnum, RANK_HALF, RANK_MINUS_1, RANK_FULL)
108+
109+
enum SolutionsOptions
110+
{
111+
NO_SOLUTIONS, ONE_SOLUTION, MANY_SOLUTIONS
112+
};
113+
114+
CV_ENUM(SolutionsEnum, NO_SOLUTIONS, ONE_SOLUTION, MANY_SOLUTIONS)
115+
116+
typedef perf::TestBaseWithParam<std::tuple<int, RankEnum, MatDepth, SolveDecompEnum, bool, SolutionsEnum>> SolveTest;
117+
118+
PERF_TEST_P(SolveTest, randomMat, ::testing::Combine(
119+
::testing::Values(31, 64, 100),
120+
::testing::Values(RANK_HALF, RANK_MINUS_1, RANK_FULL),
121+
::testing::Values(CV_32F, CV_64F),
122+
::testing::Values(DECOMP_LU, DECOMP_SVD, DECOMP_EIG, DECOMP_CHOLESKY, DECOMP_QR),
123+
::testing::Bool(), // normal
124+
::testing::Values(NO_SOLUTIONS, ONE_SOLUTION, MANY_SOLUTIONS)
125+
))
126+
{
127+
auto t = GetParam();
128+
int size = std::get<0>(t);
129+
auto rankEnum = std::get<1>(t);
130+
int mtype = std::get<2>(t);
131+
int method = std::get<3>(t);
132+
bool normal = std::get<4>(t);
133+
auto solutions = std::get<5>(t);
134+
135+
bool symmetrical = (method == DECOMP_CHOLESKY || method == DECOMP_LU);
136+
137+
if (normal)
138+
{
139+
method |= DECOMP_NORMAL;
140+
}
141+
142+
int rank = size;
143+
switch (rankEnum)
144+
{
145+
case RANK_HALF: rank /= 2; break;
146+
case RANK_MINUS_1: rank -= 1; break;
147+
default: break;
148+
}
149+
150+
RNG& rng = theRNG();
151+
Mat A = buildRandomMat(size, size, mtype, rng, rank, symmetrical);
152+
Mat x(size, 1, mtype);
153+
Mat b(size, 1, mtype);
154+
155+
switch (solutions)
156+
{
157+
// no solutions, let's make b random
158+
case NO_SOLUTIONS:
159+
{
160+
rng.fill(b, RNG::UNIFORM, Scalar(-1), Scalar(1));
161+
}
162+
break;
163+
// exactly 1 solution, let's combine b from A and x
164+
case ONE_SOLUTION:
165+
{
166+
rng.fill(x, RNG::UNIFORM, Scalar(-10), Scalar(10));
167+
b = A * x;
168+
}
169+
break;
170+
// infinitely many solutions, let's make b zero
171+
default:
172+
{
173+
b = 0;
174+
}
175+
break;
176+
}
177+
178+
TEST_CYCLE() cv::solve(A, b, x, method);
179+
180+
SANITY_CHECK_NOTHING();
181+
}
182+
183+
typedef perf::TestBaseWithParam<std::tuple<std::tuple<int, int>, RankEnum, MatDepth, bool, bool>> SvdTest;
184+
185+
PERF_TEST_P(SvdTest, decompose, ::testing::Combine(
186+
::testing::Values(std::make_tuple(5, 15), std::make_tuple(32, 32), std::make_tuple(100, 100)),
187+
::testing::Values(RANK_HALF, RANK_MINUS_1, RANK_FULL),
188+
::testing::Values(CV_32F, CV_64F),
189+
::testing::Bool(), // symmetrical
190+
::testing::Bool() // needUV
191+
))
192+
{
193+
auto t = GetParam();
194+
auto rc = std::get<0>(t);
195+
auto rankEnum = std::get<1>(t);
196+
int mtype = std::get<2>(t);
197+
bool symmetrical = std::get<3>(t);
198+
bool needUV = std::get<4>(t);
199+
200+
int rows = std::get<0>(rc);
201+
int cols = std::get<1>(rc);
202+
203+
if (symmetrical)
204+
{
205+
rows = max(rows, cols);
206+
cols = rows;
207+
}
208+
209+
int rank = std::min(rows, cols);
210+
switch (rankEnum)
211+
{
212+
case RANK_HALF: rank /= 2; break;
213+
case RANK_MINUS_1: rank -= 1; break;
214+
default: break;
215+
}
216+
217+
int flags = needUV ? 0 : SVD::NO_UV;
218+
219+
RNG& rng = theRNG();
220+
Mat A = buildRandomMat(rows, cols, mtype, rng, rank, symmetrical);
221+
TEST_CYCLE() cv::SVD svd(A, flags);
222+
223+
SANITY_CHECK_NOTHING();
224+
}
225+
226+
227+
PERF_TEST_P(SvdTest, backSubst, ::testing::Combine(
228+
::testing::Values(std::make_tuple(5, 15), std::make_tuple(32, 32), std::make_tuple(100, 100)),
229+
::testing::Values(RANK_HALF, RANK_MINUS_1, RANK_FULL),
230+
::testing::Values(CV_32F, CV_64F),
231+
// back substitution works the same regardless of source matrix properties
232+
::testing::Values(true),
233+
// back substitution has no sense without u and v
234+
::testing::Values(true) // needUV
235+
))
236+
{
237+
auto t = GetParam();
238+
auto rc = std::get<0>(t);
239+
auto rankEnum = std::get<1>(t);
240+
int mtype = std::get<2>(t);
241+
242+
int rows = std::get<0>(rc);
243+
int cols = std::get<1>(rc);
244+
245+
int rank = std::min(rows, cols);
246+
switch (rankEnum)
247+
{
248+
case RANK_HALF: rank /= 2; break;
249+
case RANK_MINUS_1: rank -= 1; break;
250+
default: break;
251+
}
252+
253+
RNG& rng = theRNG();
254+
Mat A = buildRandomMat(rows, cols, mtype, rng, rank, /* symmetrical */ false);
255+
cv::SVD svd(A);
256+
// preallocate to not spend time on it during backSubst()
257+
Mat dst(cols, 1, mtype);
258+
Mat rhs(rows, 1, mtype);
259+
rng.fill(rhs, RNG::UNIFORM, Scalar(-10), Scalar(10));
260+
261+
TEST_CYCLE() svd.backSubst(rhs, dst);
262+
263+
SANITY_CHECK_NOTHING();
264+
}
265+
266+
39267
typedef perf::TestBaseWithParam< testing::tuple<int, int, int> > KMeans;
40268

41269
PERF_TEST_P_(KMeans, single_iter)

0 commit comments

Comments
 (0)