jaulib v1.5.0
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
test_math_mat4f_03_inv.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2024 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#include <thread>
25#include <cinttypes>
26#include <cstring>
27
28#include <jau/test/catch2_ext.hpp>
29
30#include <jau/math/mat4f.hpp>
31
32using namespace jau;
33using namespace jau::math;
34
35static float* makeIdentity(float m[]) {
36 m[0+4*0] = 1;
37 m[1+4*0] = 0;
38 m[2+4*0] = 0;
39 m[3+4*0] = 0;
40
41 m[0+4*1] = 0;
42 m[1+4*1] = 1;
43 m[2+4*1] = 0;
44 m[3+4*1] = 0;
45
46 m[0+4*2] = 0;
47 m[1+4*2] = 0;
48 m[2+4*2] = 1;
49 m[3+4*2] = 0;
50
51 m[0+4*3] = 0;
52 m[1+4*3] = 0;
53 m[2+4*3] = 0;
54 m[3+4*3] = 1;
55 return m;
56}
57
58static float* invertMatrix(float msrc[], float mres[], float temp[/*4*4*/]) {
59 int i, j, k, swap;
60 float t;
61 for (i = 0; i < 4; i++) {
62 const int i4 = i*4;
63 for (j = 0; j < 4; j++) {
64 temp[i4+j] = msrc[i4+j];
65 }
66 }
67 makeIdentity(mres);
68
69 for (i = 0; i < 4; i++) {
70 const int i4 = i*4;
71
72 //
73 // Look for largest element in column
74 //
75 swap = i;
76 for (j = i + 1; j < 4; j++) {
77 if (std::abs(temp[j*4+i]) > std::abs(temp[i4+i])) {
78 swap = j;
79 }
80 }
81
82 if (swap != i) {
83 const int swap4 = swap*4;
84 //
85 // Swap rows.
86 //
87 for (k = 0; k < 4; k++) {
88 t = temp[i4+k];
89 temp[i4+k] = temp[swap4+k];
90 temp[swap4+k] = t;
91
92 t = mres[i4+k];
93 mres[i4+k] = mres[swap4+k];
94 mres[swap4+k] = t;
95 }
96 }
97
98 if (temp[i4+i] == 0) {
99 //
100 // No non-zero pivot. The matrix is singular, which shouldn't
101 // happen. This means the user gave us a bad matrix.
102 //
103 return nullptr;
104 }
105
106 t = temp[i4+i];
107 for (k = 0; k < 4; k++) {
108 temp[i4+k] /= t;
109 mres[i4+k] /= t;
110 }
111 for (j = 0; j < 4; j++) {
112 if (j != i) {
113 const int j4 = j*4;
114 t = temp[j4+i];
115 for (k = 0; k < 4; k++) {
116 temp[j4+k] -= temp[i4+k] * t;
117 mres[j4+k] -= mres[i4+k]*t;
118 }
119 }
120 }
121 }
122 return mres;
123}
124
125static void testImpl(float matrix[]) {
126 float inv1_0[16];
127 float inv2_0[16];
128 float temp[16];
129
130 // System.err.println(FloatUtil.matrixToString(null, "orig : ", "%10.7f", matrix, 0, 4, 4, false /* rowMajorOrder */));
131 invertMatrix(matrix, inv1_0, temp);
132 invertMatrix(inv1_0, inv2_0, temp);
133 // System.err.println(FloatUtil.matrixToString(null, "inv1_0: ", "%10.7f", inv1_0, 0, 4, 4, false /* rowMajorOrder */));
134 // System.err.println(FloatUtil.matrixToString(null, "inv2_0: ", "%10.7f", inv2_0, 0, 4, 4, false /* rowMajorOrder */));
135
136 COMPARE_NARRAYS_EPS(matrix, inv2_0, 16, EPSILON<float>);
137
138 //
139 // Mat4f
140 //
141
142 Mat4f matrix_m(matrix);
143 Mat4f inv1_4a(matrix_m);
144 REQUIRE( true == inv1_4a.invert() );
145 Mat4f inv2_4a(inv1_4a);
146 REQUIRE( true == inv2_4a.invert() );
147
148 {
149 Mat4f a(inv1_0), b( inv1_4a.get(temp) );
150 std::cout << "A: " << a << std::endl;
151 std::cout << "B: " << b << std::endl;
152 }
153 COMPARE_NARRAYS_EPS(inv1_0, inv1_4a.get(temp), 16, Mat4f::inv_deviation);
154 COMPARE_NARRAYS_EPS(inv2_0, inv2_4a.get(temp), 16, Mat4f::inv_deviation);
155 REQUIRE_MSG( "I4 failure: "+matrix_m.toString()+" != "+inv2_4a.toString(), matrix_m.equals(inv2_4a, Mat4f::inv_deviation));
156
157 Mat4f inv1_4b;
158 REQUIRE( true == inv1_4b.invert(matrix_m) );
159 Mat4f inv2_4b;
160 REQUIRE( true == inv2_4b.invert(inv1_4b) );
161
162 // Assert.assertEquals(new Mat4f(inv1_2), inv1_4b);
163 // Assert.assertEquals(new Mat4f(inv2_2), inv2_4b);
164 COMPARE_NARRAYS_EPS(inv1_0, inv1_4b.get(temp), 16, Mat4f::inv_deviation);
165 COMPARE_NARRAYS_EPS(inv2_0, inv2_4b.get(temp), 16, Mat4f::inv_deviation);
166 REQUIRE_MSG( "I4 failure: "+matrix_m.toString()+" != "+inv2_4b.toString(), matrix_m.equals(inv2_4b, Mat4f::inv_deviation));
167}
168
169TEST_CASE( "Test 02", "[mat4f][linear_algebra][math]" ) {
170 float p[] = { 2.3464675f, 0, 0, 0,
171 0, 2.4142134f, 0, 0,
172 0, 0, -1.0002f, -1,
173 0, 0, -20.002f, 0 };
174 testImpl(p);
175}
176
177TEST_CASE( "Test 03", "[mat4f][linear_algebra][math]" ) {
178 float mv[] = { 1, 0, 0, 0,
179 0, 1, 0, 0,
180 0, 0, 1, 0,
181 0, 0, -200, 1 } ;
182 testImpl(mv);
183}
184
185TEST_CASE( "Test 04", "[mat4f][linear_algebra][math]" ) {
186 float p[] = { 2.3464675f, 0, 0, 0,
187 0, 2.4142134f, 0, 0,
188 0, 0, -1.0002f, -1,
189 0, 0, -20.002f, 0 };
190
191 testImpl(p);
192}
193
194TEST_CASE( "Test 05 Perf01", "[mat4f][linear_algebra][math]" ) {
195 float p1[] = { 2.3464675f, 0, 0, 0,
196 0, 2.4142134f, 0, 0,
197 0, 0, -1.0002f, -1,
198 0, 0, -20.002f, 0 };
199 Mat4f p1_m(p1);
200
201 float p2[] = { 26, 59, 143, 71,
202 59, 174, 730, 386,
203 143, 730, 9770, 5370,
204 71, 386, 5370, 2954 };
205 Mat4f p2_m(p2);
206
207 Mat4f res_m;
208
209 const size_t warmups = 1000_u64;
210 const size_t loops = 10_u64*1000000_u64;
213
214 // avoid optimizing out unused computation results by simply adding up determinat
215 double dr = 1;
216
217 //
218 // Mat4f
219 //
220
221 // warm-up
222 for(size_t i=0; i<warmups; i++) {
223 res_m.invert(p1_m);
224 dr += res_m.determinant();
225 res_m.invert(p2_m);
226 dr += res_m.determinant();
227 }
229 for(size_t i=0; i<loops; i++) {
230 res_m.invert(p1_m);
231 dr += res_m.determinant();
232 res_m.invert(p2_m);
233 dr += res_m.determinant();
234 }
235 tI4a = (getMonotonicTime() - t_0).to_fraction_i64();
236 REQUIRE( false == jau::is_zero(dr) );
237
238 // warm-up
239 for(size_t i=0; i<warmups; i++) {
240 res_m.load(p1_m).invert();
241 dr += res_m.determinant();
242 res_m.load(p2_m).invert();
243 dr += res_m.determinant();
244 }
245
246 t_0 = jau::getMonotonicTime();
247 for(size_t i=0; i<loops; i++) {
248 res_m.load(p1_m).invert();
249 dr += res_m.determinant();
250 res_m.load(p2_m).invert();
251 dr += res_m.determinant();
252 }
253 tI4b = (getMonotonicTime() - t_0).to_fraction_i64();
254 REQUIRE( false == jau::is_zero(dr) );
255
256 printf("Checkmark %f\n", dr);
257 printf("Summary loops %6zu: I4a %6s ms total (%s us), %f ns/inv, I4a / I4b %f%%\n", loops,
258 jau::to_decstring(tI4a.to_ms()).c_str(), jau::to_decstring(tI4a.to_us()).c_str(),
259 (double)tI4a.to_ns()/2.0/(double)loops, tI4a.to_double()/tI4b.to_double()*100.0);
260 printf("Summary loops %6zu: I4b %6s ms total (%s us), %f ns/inv, I4b / I4a %f%%\n", loops,
261 jau::to_decstring(tI4b.to_ms()).c_str(), jau::to_decstring(tI4b.to_us()).c_str(),
262 (double)tI4b.to_ns()/2.0/(double)loops, tI4b.to_double()/tI4a.to_double()*100.0);
263}
264
265
constexpr double to_double() const noexcept
Returns the converted fraction to lossy double.
constexpr int_type to_us() const noexcept
Convenient shortcut to to_num_of(1_us)
constexpr int_type to_ns() const noexcept
Convenient shortcut to to_num_of(1_ns)
constexpr int_type to_ms() const noexcept
Convenient shortcut to to_num_of(1_ms)
static constexpr const value_type inv_deviation
Definition mat4f.hpp:126
constexpr value_type get(const jau::nsize_t i) const noexcept
Returns the ith component of the given column-major order matrix, 0 <= i < 16, w/o boundary check.
Definition mat4f.hpp:295
bool invert() noexcept
Invert this matrix.
Definition mat4f.hpp:510
value_type determinant() const noexcept
Returns the determinant of this matrix.
Definition mat4f.hpp:430
constexpr bool equals(const Matrix4 &o, const value_type epsilon=std::numeric_limits< value_type >::epsilon()) const noexcept
Definition mat4f.hpp:183
constexpr Matrix4 & load(const_iterator src) noexcept
Load the values of the given matrix src to this matrix w/o boundary check.
Definition mat4f.hpp:253
std::string toString(const std::string &rowPrefix, const std::string_view f) const noexcept
Returns a formatted string representation of this matrix.
Definition mat4f.hpp:1932
void swap(cow_darray< Value_type, Size_type, Alloc_type > &rhs, cow_darray< Value_type, Size_type, Alloc_type > &lhs) noexcept
constexpr T EPSILON
Alias for epsilon constant, i.e. std::numeric_limits<T>::epsilon()
constexpr bool is_zero(const T &a, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if the given value is less than epsilon, w/ epsilon > 0.
FracI64SizeBoolTuple to_fraction_i64(std::string_view str, const fraction_i64 &min_allowed, const fraction_i64 &max_allowed) noexcept
Returns the fraction_i64 of the given character in format <num>/<denom>, which may contain whitespace...
fraction_timespec getMonotonicTime() noexcept
Returns current monotonic time since Unix Epoch 00:00:00 UTC on 1970-01-01.
fraction< int64_t > fraction_i64
fraction using int64_t as integral type
Matrix4< float > Mat4f
Definition mat4f.hpp:1968
std::string to_decstring(const value_type &v, const char separator='\'', const nsize_t min_width=0) noexcept
Produces a decimal integer string representation of an integral integer value with given radix.
constexpr const jau::fraction_i64 zero(0l, 1lu)
zero is 0/1
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
Timespec structure using int64_t for its components in analogy to struct timespec_t on 64-bit platfor...
static void testImpl(float matrix[])
static float * makeIdentity(float m[])
TEST_CASE("Test 02", "[mat4f][linear_algebra][math]")
static float * invertMatrix(float msrc[], float mres[], float temp[])
static int loops
int printf(const char *format,...)
Operating Systems predefined macros.