blob: cc78492f784e0ad63f017bfbca03717b399609c9 [file] [log] [blame]
Pekka Paalanen4520d5c2012-01-16 15:04:28 +02001/*
2 * Copyright © 2012 Collabora, Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
Pekka Paalanen4520d5c2012-01-16 15:04:28 +020025#include <math.h>
26#include <unistd.h>
27#include <signal.h>
28#include <time.h>
29
30#include "matrix.h"
31
Pekka Paalanend1f0ab62012-01-20 10:47:57 +020032struct inverse_matrix {
33 double LU[16]; /* column-major */
34 unsigned perm[4]; /* permutation */
35};
36
Pekka Paalanen4520d5c2012-01-16 15:04:28 +020037static struct timespec begin_time;
38
39static void
40reset_timer(void)
41{
42 clock_gettime(CLOCK_MONOTONIC, &begin_time);
43}
44
45static double
46read_timer(void)
47{
48 struct timespec t;
49
50 clock_gettime(CLOCK_MONOTONIC, &t);
51 return (double)(t.tv_sec - begin_time.tv_sec) +
52 1e-9 * (t.tv_nsec - begin_time.tv_nsec);
53}
54
55static double
John Kåre Alsaker71c47442012-10-03 17:30:05 +020056det3x3(const float *c0, const float *c1, const float *c2)
Pekka Paalanen4520d5c2012-01-16 15:04:28 +020057{
58 return (double)
59 c0[0] * c1[1] * c2[2] +
60 c1[0] * c2[1] * c0[2] +
61 c2[0] * c0[1] * c1[2] -
62 c0[2] * c1[1] * c2[0] -
63 c1[2] * c2[1] * c0[0] -
64 c2[2] * c0[1] * c1[0];
65}
66
67static double
68determinant(const struct weston_matrix *m)
69{
70 double det = 0;
71#if 1
72 /* develop on last row */
73 det -= m->d[3 + 0 * 4] * det3x3(&m->d[4], &m->d[8], &m->d[12]);
74 det += m->d[3 + 1 * 4] * det3x3(&m->d[0], &m->d[8], &m->d[12]);
75 det -= m->d[3 + 2 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[12]);
76 det += m->d[3 + 3 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[8]);
77#else
78 /* develop on first row */
79 det += m->d[0 + 0 * 4] * det3x3(&m->d[5], &m->d[9], &m->d[13]);
80 det -= m->d[0 + 1 * 4] * det3x3(&m->d[1], &m->d[9], &m->d[13]);
81 det += m->d[0 + 2 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[13]);
82 det -= m->d[0 + 3 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[9]);
83#endif
84 return det;
85}
86
87static void
Pekka Paalanend1f0ab62012-01-20 10:47:57 +020088print_permutation_matrix(const struct inverse_matrix *m)
Pekka Paalanen4520d5c2012-01-16 15:04:28 +020089{
Pekka Paalanend1f0ab62012-01-20 10:47:57 +020090 const unsigned *p = m->perm;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +020091 const char *row[4] = {
92 "1 0 0 0\n",
93 "0 1 0 0\n",
94 "0 0 1 0\n",
95 "0 0 0 1\n"
96 };
97
98 printf(" P =\n%s%s%s%s", row[p[0]], row[p[1]], row[p[2]], row[p[3]]);
99}
100
101static void
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200102print_LU_decomposition(const struct inverse_matrix *m)
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200103{
104 unsigned r, c;
105
106 printf(" L "
107 " U\n");
108 for (r = 0; r < 4; ++r) {
109 double v;
110
111 for (c = 0; c < 4; ++c) {
112 if (c < r)
113 v = m->LU[r + c * 4];
114 else if (c == r)
115 v = 1.0;
116 else
117 v = 0.0;
118 printf(" %12.6f", v);
119 }
120
121 printf(" | ");
122
123 for (c = 0; c < 4; ++c) {
124 if (c >= r)
125 v = m->LU[r + c * 4];
126 else
127 v = 0.0;
128 printf(" %12.6f", v);
129 }
130 printf("\n");
131 }
132}
133
134static void
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200135print_inverse_data_matrix(const struct inverse_matrix *m)
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200136{
137 unsigned r, c;
138
139 for (r = 0; r < 4; ++r) {
140 for (c = 0; c < 4; ++c)
141 printf(" %12.6f", m->LU[r + c * 4]);
142 printf("\n");
143 }
144
145 printf("permutation: ");
146 for (r = 0; r < 4; ++r)
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200147 printf(" %u", m->perm[r]);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200148 printf("\n");
149}
150
151static void
152print_matrix(const struct weston_matrix *m)
153{
154 unsigned r, c;
155
156 for (r = 0; r < 4; ++r) {
157 for (c = 0; c < 4; ++c)
158 printf(" %14.6e", m->d[r + c * 4]);
159 printf("\n");
160 }
161}
162
163static double
164frand(void)
165{
166 double r = random();
167 return r / (double)(RAND_MAX / 2) - 1.0f;
168}
169
170static void
171randomize_matrix(struct weston_matrix *m)
172{
173 unsigned i;
174 for (i = 0; i < 16; ++i)
175#if 1
176 m->d[i] = frand() * exp(10.0 * frand());
177#else
178 m->d[i] = frand();
179#endif
180}
181
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200182/* Take a matrix, compute inverse, multiply together
183 * and subtract the identity matrix to get the error matrix.
184 * Return the largest absolute value from the error matrix.
185 */
186static double
187test_inverse(struct weston_matrix *m)
188{
189 unsigned i;
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200190 struct inverse_matrix q;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200191 double errsup = 0.0;
192
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200193 if (matrix_invert(q.LU, q.perm, m) != 0)
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200194 return INFINITY;
195
196 for (i = 0; i < 4; ++i)
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200197 inverse_transform(q.LU, q.perm, &m->d[i * 4]);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200198
199 m->d[0] -= 1.0f;
200 m->d[5] -= 1.0f;
201 m->d[10] -= 1.0f;
202 m->d[15] -= 1.0f;
203
204 for (i = 0; i < 16; ++i) {
205 double err = fabs(m->d[i]);
206 if (err > errsup)
207 errsup = err;
208 }
209
210 return errsup;
211}
212
213enum {
214 TEST_OK,
215 TEST_NOT_INVERTIBLE_OK,
216 TEST_FAIL,
217 TEST_COUNT
218};
219
220static int
221test(void)
222{
223 struct weston_matrix m;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200224 double det, errsup;
225
226 randomize_matrix(&m);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200227 det = determinant(&m);
228
229 errsup = test_inverse(&m);
230 if (errsup < 1e-6)
231 return TEST_OK;
232
233 if (fabs(det) < 1e-5 && isinf(errsup))
234 return TEST_NOT_INVERTIBLE_OK;
235
236 printf("test fail, det: %g, error sup: %g\n", det, errsup);
Kristian Høgsberg010f98b2012-02-23 17:30:45 -0500237
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200238 return TEST_FAIL;
239}
240
241static int running;
242static void
243stopme(int n)
244{
245 running = 0;
246}
247
248static void
249test_loop_precision(void)
250{
251 int counts[TEST_COUNT] = { 0 };
252
253 printf("\nRunning a test loop for 10 seconds...\n");
254 running = 1;
255 alarm(10);
256 while (running) {
257 counts[test()]++;
258 }
259
260 printf("tests: %d ok, %d not invertible but ok, %d failed.\n"
261 "Total: %d iterations.\n",
262 counts[TEST_OK], counts[TEST_NOT_INVERTIBLE_OK],
263 counts[TEST_FAIL],
264 counts[TEST_OK] + counts[TEST_NOT_INVERTIBLE_OK] +
265 counts[TEST_FAIL]);
266}
267
268static void __attribute__((noinline))
269test_loop_speed_matrixvector(void)
270{
271 struct weston_matrix m;
272 struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } };
273 unsigned long count = 0;
274 double t;
275
276 printf("\nRunning 3 s test on weston_matrix_transform()...\n");
277
278 weston_matrix_init(&m);
279
280 running = 1;
281 alarm(3);
282 reset_timer();
283 while (running) {
284 weston_matrix_transform(&m, &v);
285 count++;
286 }
287 t = read_timer();
288
289 printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n",
290 count, t, 1e9 * t / count);
291}
292
293static void __attribute__((noinline))
294test_loop_speed_inversetransform(void)
295{
296 struct weston_matrix m;
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200297 struct inverse_matrix inv;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200298 struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } };
299 unsigned long count = 0;
300 double t;
301
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200302 printf("\nRunning 3 s test on inverse_transform()...\n");
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200303
304 weston_matrix_init(&m);
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200305 matrix_invert(inv.LU, inv.perm, &m);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200306
307 running = 1;
308 alarm(3);
309 reset_timer();
310 while (running) {
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200311 inverse_transform(inv.LU, inv.perm, v.f);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200312 count++;
313 }
314 t = read_timer();
315
316 printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n",
317 count, t, 1e9 * t / count);
318}
319
320static void __attribute__((noinline))
321test_loop_speed_invert(void)
322{
323 struct weston_matrix m;
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200324 struct inverse_matrix inv;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200325 unsigned long count = 0;
326 double t;
327
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200328 printf("\nRunning 3 s test on matrix_invert()...\n");
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200329
330 weston_matrix_init(&m);
331
332 running = 1;
333 alarm(3);
334 reset_timer();
335 while (running) {
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200336 matrix_invert(inv.LU, inv.perm, &m);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200337 count++;
338 }
339 t = read_timer();
340
341 printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n",
342 count, t, 1e9 * t / count);
343}
344
345static void __attribute__((noinline))
346test_loop_speed_invert_explicit(void)
347{
348 struct weston_matrix m;
349 unsigned long count = 0;
350 double t;
351
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200352 printf("\nRunning 3 s test on weston_matrix_invert()...\n");
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200353
354 weston_matrix_init(&m);
355
356 running = 1;
357 alarm(3);
358 reset_timer();
359 while (running) {
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200360 weston_matrix_invert(&m, &m);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200361 count++;
362 }
363 t = read_timer();
364
365 printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n",
366 count, t, 1e9 * t / count);
367}
368
369int main(void)
370{
371 struct sigaction ding;
372 struct weston_matrix M;
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200373 struct inverse_matrix Q;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200374 int ret;
375 double errsup;
376 double det;
377
378 ding.sa_handler = stopme;
379 sigemptyset(&ding.sa_mask);
380 ding.sa_flags = 0;
381 sigaction(SIGALRM, &ding, NULL);
382
383 srandom(13);
384
385 M.d[0] = 3.0; M.d[4] = 17.0; M.d[8] = 10.0; M.d[12] = 0.0;
386 M.d[1] = 2.0; M.d[5] = 4.0; M.d[9] = -2.0; M.d[13] = 0.0;
387 M.d[2] = 6.0; M.d[6] = 18.0; M.d[10] = -12; M.d[14] = 0.0;
388 M.d[3] = 0.0; M.d[7] = 0.0; M.d[11] = 0.0; M.d[15] = 1.0;
389
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200390 ret = matrix_invert(Q.LU, Q.perm, &M);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200391 printf("ret = %d\n", ret);
392 printf("det = %g\n\n", determinant(&M));
393
394 if (ret != 0)
395 return 1;
396
397 print_inverse_data_matrix(&Q);
398 printf("P * A = L * U\n");
399 print_permutation_matrix(&Q);
400 print_LU_decomposition(&Q);
401
402
403 printf("a random matrix:\n");
404 randomize_matrix(&M);
405 det = determinant(&M);
406 print_matrix(&M);
407 errsup = test_inverse(&M);
408 printf("\nThe matrix multiplied by its inverse, error:\n");
409 print_matrix(&M);
410 printf("max abs error: %g, original determinant %g\n", errsup, det);
411
412 test_loop_precision();
413 test_loop_speed_matrixvector();
414 test_loop_speed_inversetransform();
415 test_loop_speed_invert();
416 test_loop_speed_invert_explicit();
417
418 return 0;
419}