forked from AprilRobotics/apriltag
-
Notifications
You must be signed in to change notification settings - Fork 0
/
matd.h
446 lines (385 loc) · 16.2 KB
/
matd.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
/* Copyright (C) 2013-2016, The Regents of The University of Michigan.
All rights reserved.
This software was developed in the APRIL Robotics Lab under the
direction of Edwin Olson, [email protected]. This software may be
available under alternative licensing terms; contact the address above.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the Regents of The University of Michigan.
*/
#pragma once
#include <assert.h>
#include <stddef.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Defines a matrix structure for holding double-precision values with
* data in row-major order (i.e. index = row*ncols + col).
*
* nrows and ncols are 1-based counts with the exception that a scalar (non-matrix)
* is represented with nrows=0 and/or ncols=0.
*/
typedef struct
{
unsigned int nrows, ncols;
double data[];
// double *data;
} matd_t;
#define MATD_ALLOC(name, nrows, ncols) double name ## _storage [nrows*ncols]; matd_t name = { .nrows = nrows, .ncols = ncols, .data = &name ## _storage };
/**
* Defines a small value which can be used in place of zero for approximating
* calculations which are singular at zero values (i.e. inverting a matrix with
* a zero or near-zero determinant).
*/
#define MATD_EPS 1e-8
/**
* A macro to reference a specific matd_t data element given it's zero-based
* row and column indexes. Suitable for both retrieval and assignment.
*/
#define MATD_EL(m, row, col) (m)->data[((row)*(m)->ncols + (col))]
/**
* Creates a double matrix with the given number of rows and columns (or a scalar
* in the case where rows=0 and/or cols=0). All data elements will be initialized
* to zero. It is the caller's responsibility to call matd_destroy() on the
* returned matrix.
*/
matd_t *matd_create(int rows, int cols);
/**
* Creates a double matrix with the given number of rows and columns (or a scalar
* in the case where rows=0 and/or cols=0). All data elements will be initialized
* using the supplied array of data, which must contain at least rows*cols elements,
* arranged in row-major order (i.e. index = row*ncols + col). It is the caller's
* responsibility to call matd_destroy() on the returned matrix.
*/
matd_t *matd_create_data(int rows, int cols, const double *data);
/**
* Creates a double matrix with the given number of rows and columns (or a scalar
* in the case where rows=0 and/or cols=0). All data elements will be initialized
* using the supplied array of float data, which must contain at least rows*cols elements,
* arranged in row-major order (i.e. index = row*ncols + col). It is the caller's
* responsibility to call matd_destroy() on the returned matrix.
*/
matd_t *matd_create_dataf(int rows, int cols, const float *data);
/**
* Creates a square identity matrix with the given number of rows (and
* therefore columns), or a scalar with value 1 in the case where dim=0.
* It is the caller's responsibility to call matd_destroy() on the
* returned matrix.
*/
matd_t *matd_identity(int dim);
/**
* Creates a scalar with the supplied value 'v'. It is the caller's responsibility
* to call matd_destroy() on the returned matrix.
*
* NOTE: Scalars are different than 1x1 matrices (implementation note:
* they are encoded as 0x0 matrices). For example: for matrices A*B, A
* and B must both have specific dimensions. However, if A is a
* scalar, there are no restrictions on the size of B.
*/
matd_t *matd_create_scalar(double v);
/**
* Retrieves the cell value for matrix 'm' at the given zero-based row and column index.
* Performs more thorough validation checking than MATD_EL().
*/
double matd_get(const matd_t *m, unsigned int row, unsigned int col);
/**
* Assigns the given value to the matrix cell at the given zero-based row and
* column index. Performs more thorough validation checking than MATD_EL().
*/
void matd_put(matd_t *m, unsigned int row, unsigned int col, double value);
/**
* Retrieves the scalar value of the given element ('m' must be a scalar).
* Performs more thorough validation checking than MATD_EL().
*/
double matd_get_scalar(const matd_t *m);
/**
* Assigns the given value to the supplied scalar element ('m' must be a scalar).
* Performs more thorough validation checking than MATD_EL().
*/
void matd_put_scalar(matd_t *m, double value);
/**
* Creates an exact copy of the supplied matrix 'm'. It is the caller's
* responsibility to call matd_destroy() on the returned matrix.
*/
matd_t *matd_copy(const matd_t *m);
/**
* Creates a copy of a subset of the supplied matrix 'a'. The subset will include
* rows 'r0' through 'r1', inclusive ('r1' >= 'r0'), and columns 'c0' through 'c1',
* inclusive ('c1' >= 'c0'). All parameters are zero-based (i.e. matd_select(a, 0, 0, 0, 0)
* will return only the first cell). Cannot be used on scalars or to extend
* beyond the number of rows/columns of 'a'. It is the caller's responsibility to
* call matd_destroy() on the returned matrix.
*/
matd_t *matd_select(const matd_t *a, unsigned int r0, int r1, unsigned int c0, int c1);
/**
* Prints the supplied matrix 'm' to standard output by applying the supplied
* printf format specifier 'fmt' for each individual element. Each row will
* be printed on a separate newline.
*/
void matd_print(const matd_t *m, const char *fmt);
/**
* Prints the transpose of the supplied matrix 'm' to standard output by applying
* the supplied printf format specifier 'fmt' for each individual element. Each
* row will be printed on a separate newline.
*/
void matd_print_transpose(const matd_t *m, const char *fmt);
/**
* Adds the two supplied matrices together, cell-by-cell, and returns the results
* as a new matrix of the same dimensions. The supplied matrices must have
* identical dimensions. It is the caller's responsibility to call matd_destroy()
* on the returned matrix.
*/
matd_t *matd_add(const matd_t *a, const matd_t *b);
/**
* Adds the values of 'b' to matrix 'a', cell-by-cell, and overwrites the
* contents of 'a' with the results. The supplied matrices must have
* identical dimensions.
*/
void matd_add_inplace(matd_t *a, const matd_t *b);
/**
* Subtracts matrix 'b' from matrix 'a', cell-by-cell, and returns the results
* as a new matrix of the same dimensions. The supplied matrices must have
* identical dimensions. It is the caller's responsibility to call matd_destroy()
* on the returned matrix.
*/
matd_t *matd_subtract(const matd_t *a, const matd_t *b);
/**
* Subtracts the values of 'b' from matrix 'a', cell-by-cell, and overwrites the
* contents of 'a' with the results. The supplied matrices must have
* identical dimensions.
*/
void matd_subtract_inplace(matd_t *a, const matd_t *b);
/**
* Scales all cell values of matrix 'a' by the given scale factor 's' and
* returns the result as a new matrix of the same dimensions. It is the caller's
* responsibility to call matd_destroy() on the returned matrix.
*/
matd_t *matd_scale(const matd_t *a, double s);
/**
* Scales all cell values of matrix 'a' by the given scale factor 's' and
* overwrites the contents of 'a' with the results.
*/
void matd_scale_inplace(matd_t *a, double s);
/**
* Multiplies the two supplied matrices together (matrix product), and returns the
* results as a new matrix. The supplied matrices must have dimensions such that
* columns(a) = rows(b). The returned matrix will have a row count of rows(a)
* and a column count of columns(b). It is the caller's responsibility to call
* matd_destroy() on the returned matrix.
*/
matd_t *matd_multiply(const matd_t *a, const matd_t *b);
/**
* Creates a matrix which is the transpose of the supplied matrix 'a'. It is the
* caller's responsibility to call matd_destroy() on the returned matrix.
*/
matd_t *matd_transpose(const matd_t *a);
/**
* Calculates the determinant of the supplied matrix 'a'.
*/
double matd_det(const matd_t *a);
/**
* Attempts to compute an inverse of the supplied matrix 'a' and return it as
* a new matrix. This is strictly only possible if the determinant of 'a' is
* non-zero (matd_det(a) != 0).
*
* If the determinant is zero, NULL is returned. It is otherwise the
* caller's responsibility to cope with the results caused by poorly
* conditioned matrices. (E.g.., if such a situation is likely to arise, compute
* the pseudo-inverse from the SVD.)
**/
matd_t *matd_inverse(const matd_t *a);
static inline void matd_set_data(matd_t *m, const double *data)
{
memcpy(m->data, data, m->nrows * m->ncols * sizeof(double));
}
/**
* Determines whether the supplied matrix 'a' is a scalar (positive return) or
* not (zero return, indicating a matrix of dimensions at least 1x1).
*/
static inline int matd_is_scalar(const matd_t *a)
{
assert(a != NULL);
return a->ncols <= 1 && a->nrows <= 1;
}
/**
* Determines whether the supplied matrix 'a' is a row or column vector
* (positive return) or not (zero return, indicating either 'a' is a scalar or a
* matrix with at least one dimension > 1).
*/
static inline int matd_is_vector(const matd_t *a)
{
assert(a != NULL);
return a->ncols == 1 || a->nrows == 1;
}
/**
* Determines whether the supplied matrix 'a' is a row or column vector
* with a dimension of 'len' (positive return) or not (zero return).
*/
static inline int matd_is_vector_len(const matd_t *a, int len)
{
assert(a != NULL);
return (a->ncols == 1 && a->nrows == (unsigned int)len) || (a->ncols == (unsigned int)len && a->nrows == 1);
}
/**
* Calculates the magnitude of the supplied matrix 'a'.
*/
double matd_vec_mag(const matd_t *a);
/**
* Calculates the magnitude of the distance between the points represented by
* matrices 'a' and 'b'. Both 'a' and 'b' must be vectors and have the same
* dimension (although one may be a row vector and one may be a column vector).
*/
double matd_vec_dist(const matd_t *a, const matd_t *b);
/**
* Same as matd_vec_dist, but only uses the first 'n' terms to compute distance
*/
double matd_vec_dist_n(const matd_t *a, const matd_t *b, int n);
/**
* Calculates the dot product of two vectors. Both 'a' and 'b' must be vectors
* and have the same dimension (although one may be a row vector and one may be
* a column vector).
*/
double matd_vec_dot_product(const matd_t *a, const matd_t *b);
/**
* Calculates the normalization of the supplied vector 'a' (i.e. a unit vector
* of the same dimension and orientation as 'a' with a magnitude of 1) and returns
* it as a new vector. 'a' must be a vector of any dimension and must have a
* non-zero magnitude. It is the caller's responsibility to call matd_destroy()
* on the returned matrix.
*/
matd_t *matd_vec_normalize(const matd_t *a);
/**
* Calculates the cross product of supplied matrices 'a' and 'b' (i.e. a x b)
* and returns it as a new matrix. Both 'a' and 'b' must be vectors of dimension
* 3, but can be either row or column vectors. It is the caller's responsibility
* to call matd_destroy() on the returned matrix.
*/
matd_t *matd_crossproduct(const matd_t *a, const matd_t *b);
double matd_err_inf(const matd_t *a, const matd_t *b);
/**
* Creates a new matrix by applying a series of matrix operations, as expressed
* in 'expr', to the supplied list of matrices. Each matrix to be operated upon
* must be represented in the expression by a separate matrix placeholder, 'M',
* and there must be one matrix supplied as an argument for each matrix
* placeholder in the expression. All rules and caveats of the corresponding
* matrix operations apply to the operated-on matrices. It is the caller's
* responsibility to call matd_destroy() on the returned matrix.
*
* Available operators (in order of increasing precedence):
* M+M add two matrices together
* M-M subtract one matrix from another
* M*M multiply two matrices together (matrix product)
* MM multiply two matrices together (matrix product)
* -M negate a matrix
* M^-1 take the inverse of a matrix
* M' take the transpose of a matrix
*
* Expressions can be combined together and grouped by enclosing them in
* parenthesis, i.e.:
* -M(M+M+M)-(M*M)^-1
*
* Scalar values can be generated on-the-fly, i.e.:
* M*2.2 scales M by 2.2
* -2+M adds -2 to all elements of M
*
* All whitespace in the expression is ignored.
*/
matd_t *matd_op(const char *expr, ...);
/**
* Frees the memory associated with matrix 'm', being the result of an earlier
* call to a matd_*() function, after which 'm' will no longer be usable.
*/
void matd_destroy(matd_t *m);
typedef struct
{
matd_t *U;
matd_t *S;
matd_t *V;
} matd_svd_t;
/** Compute a complete SVD of a matrix. The SVD exists for all
* matrices. For a matrix MxN, we will have:
*
* A = U*S*V'
*
* where A is MxN, U is MxM (and is an orthonormal basis), S is MxN
* (and is diagonal up to machine precision), and V is NxN (and is an
* orthonormal basis).
*
* The caller is responsible for destroying U, S, and V.
**/
matd_svd_t matd_svd(matd_t *A);
#define MATD_SVD_NO_WARNINGS 1
matd_svd_t matd_svd_flags(matd_t *A, int flags);
////////////////////////////////
// PLU Decomposition
// All square matrices (even singular ones) have a partially-pivoted
// LU decomposition such that A = PLU, where P is a permutation
// matrix, L is a lower triangular matrix, and U is an upper
// triangular matrix.
//
typedef struct
{
// was the input matrix singular? When a zero pivot is found, this
// flag is set to indicate that this has happened.
int singular;
unsigned int *piv; // permutation indices
int pivsign; // either +1 or -1
// The matd_plu_t object returned "owns" the enclosed LU matrix. It
// is not expected that the returned object is itself useful to
// users: it contains the L and U information all smushed
// together.
matd_t *lu; // combined L and U matrices, permuted so they can be triangular.
} matd_plu_t;
matd_plu_t *matd_plu(const matd_t *a);
void matd_plu_destroy(matd_plu_t *mlu);
double matd_plu_det(const matd_plu_t *lu);
matd_t *matd_plu_p(const matd_plu_t *lu);
matd_t *matd_plu_l(const matd_plu_t *lu);
matd_t *matd_plu_u(const matd_plu_t *lu);
matd_t *matd_plu_solve(const matd_plu_t *mlu, const matd_t *b);
// uses LU decomposition internally.
matd_t *matd_solve(matd_t *A, matd_t *b);
////////////////////////////////
// Cholesky Factorization
/**
* Creates a double matrix with the Cholesky lower triangular matrix
* of A. A must be symmetric, positive definite. It is the caller's
* responsibility to call matd_destroy() on the returned matrix.
*/
//matd_t *matd_cholesky(const matd_t *A);
typedef struct
{
int is_spd;
matd_t *u;
} matd_chol_t;
matd_chol_t *matd_chol(matd_t *A);
matd_t *matd_chol_solve(const matd_chol_t *chol, const matd_t *b);
void matd_chol_destroy(matd_chol_t *chol);
// only sensible on PSD matrices
matd_t *matd_chol_inverse(matd_t *a);
void matd_ltransposetriangle_solve(matd_t *u, const double *b, double *x);
void matd_ltriangle_solve(matd_t *u, const double *b, double *x);
void matd_utriangle_solve(matd_t *u, const double *b, double *x);
double matd_max(matd_t *m);
#ifdef __cplusplus
}
#endif