├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── Makefile ├── fitcomp.png ├── pca.c └── polyfit.c ├── src ├── ulapack.c ├── ulapack.h ├── ulapack_options.h └── ulapack_type.h └── test ├── Makefile └── unit_tests.c /.gitignore: -------------------------------------------------------------------------------- 1 | # MacOS Ignore 2 | *.DS_Store 3 | 4 | # C 5 | *.o 6 | 7 | # Valgrind symbols 8 | *.dSYM 9 | 10 | # Test Executables 11 | ulapack_test_static 12 | ulapack_test_dynamic 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: false 3 | 4 | before_script: 5 | - cd test/ 6 | - make 7 | 8 | script: 9 | - ./ulapack_test_static 10 | - ./ulapack_test_dynamic 11 | 12 | after_script: 13 | - make clean 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | μLAPack License v1.0 2 | Copyright (c) 2019 Sargis S Yonan 3 | Contact: sargis@yonan.org 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 11 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "You" (or "Your") shall mean an individual or Legal Entity 16 | exercising permissions granted by this License. 17 | 18 | "Source" form shall mean the preferred form for making modifications, 19 | including but not limited to software source code, documentation 20 | source, unit testing code, instructions, and configuration files. 21 | 22 | "Object" form shall mean any form resulting from mechanical 23 | transformation or translation of a Source form, including but 24 | not limited to compiled object code, generated documentation, 25 | and conversions to other media types, including but not limited to 26 | medical devices, aircraft, robotic applications, home automation 27 | equipment, calibration software, machine learning applications, 28 | real-time control, feedback control, data processing, sensor 29 | calibration, and state estimation. 30 | 31 | "Work" shall mean the work of authorship, whether in Source or 32 | Object form, made available under the License. The Definition 33 | of Work will be used interchangeably with μLAPack in this License. 34 | 35 | "Derivative Works" shall mean any work, whether in Source or Object 36 | form, that is based on (or derived from) the Work and for which the 37 | editorial revisions, annotations, elaborations, or other modifications 38 | represent, as a whole, an original work of authorship. For the purposes 39 | of this License, Derivative Works shall not include works that remain 40 | separable from, or merely link (or bind by name) to the interfaces of, 41 | the Work and Derivative Works thereof. 42 | 43 | "Contribution" shall mean any work of authorship, including 44 | the original version of the Work and any modifications or additions 45 | to that Work or Derivative Works thereof, that is intentionally 46 | submitted to Licensor for inclusion in the Work by the copyright owner 47 | or by an individual authorized to submit on behalf of the copyright 48 | owner. For the purposes of this definition, "submitted" means any form 49 | of electronic, verbal, or written communication sent to the Licensor 50 | or its representatives, including but not limited to communication on 51 | electronic mailing lists, source code control systems, and issue 52 | tracking systems that are managed by, or on behalf of, the Licensor 53 | for the purpose of discussing and improving the Work if and only if 54 | the copyright owner designates in writing "This is a Contribution to 55 | the Work." 56 | 57 | "Contributor" shall mean Licensor, copywriter owner and any individual, 58 | or Legal Entity on behalf of whom a Contribution has been received by 59 | Licensor, subsequently incorporated within the Work, and explicitly 60 | designated by the copyright owner in writing, stating verbatim, 61 | "This is a Contribution to the Work." 62 | 63 | "Personal Use" shall mean the use of this Work for non-profitable 64 | means, exercises, and personal education. Personal/Private Use will 65 | explicitly exclude the use of this Work for any commercial, corporate, 66 | for-cost, and for-profit purpose, or the intentions to do so. 67 | 68 | "Private Use" will be defined identically to the definition for Personal 69 | Use. 70 | 71 | "Free" shall refer to the absence of monetary compensation for the Work. 72 | 73 | "Developer" an individual who uses this Work or Source with the 74 | intention of making a commercially available product in any medium, with 75 | or without modifications, to this Work and/or Derivative 76 | Work they create or help create. 77 | 78 | 2. Grant of Copyright License. 79 | Sargis S Yonan is the copyright holder, and the Licensor of this Work. The 80 | Licensor of this Work must explicitly declare You to be a Contributor to 81 | this Work by the terms defined in the definition for Contribution and 82 | Contributor. 83 | 84 | 3. Redistribution. 85 | You may only reproduce and distribute copies of the Work or Derivative 86 | Works in any medium, with or without modifications, and in Source or 87 | Object form, provided that You meet the following conditions: 88 | 89 | (a) You must cause any modified files to carry prominent notices 90 | stating that You changed the files, and who the original authors 91 | were. A copy of this License must be attached to the source code 92 | if the source code is redistributed; and 93 | 94 | (b) You must not redistribute the direct Work or Source form with 95 | a different License agreement. This License must be included in 96 | the code base of any redistribution unless otherwise specified in 97 | a written agreement between You and the Licensor; and 98 | 99 | (c) Subject to the terms and conditions of this License, the Licensor, can 100 | grant to You redistribution right if and only if a signed agreement 101 | (contract) between You and the Licensor, including at least, but not 102 | limited to the following terms and conditions: 103 | 104 | (I) A description of the Object form where the Works and/or 105 | Derivative Works are used; and 106 | 107 | (II) How the Work and/or Derivative Works contribute to the use of 108 | the final Object form; and 109 | 110 | (III) The estimated number of Object units sold, to-be sold, rented 111 | out, distributed and, manufactured. Evidence supporting this 112 | claim must also be included in the agreement; and 113 | 114 | (IV) The manufacturing cost of each Object form; and 115 | 116 | (V) The average selling price of each Object form in The United 117 | States of America. If the object form is not sold in The 118 | United States of America, the estimated average sell price in 119 | USD will be included instead; and 120 | 121 | (VI) The regions of the world in which the Object form will be on 122 | sale; and 123 | 124 | (VII) The largest expected sales country of the world in which the 125 | object form will be sold, and the average expected selling 126 | price in that country converted to USD; and 127 | 128 | (VIII) The agreed upon monetary compensation, and means of 129 | compensation, to the Licensor for the use of Works and/or 130 | Derivative Works. The payment must be made to the Licensor 131 | only in US Dollar currency. The monetary payment will include 132 | any combination, and at least one, of the following possible 133 | agreement conditions: 134 | 135 | (i) A single payment, agreed upon by both You and the 136 | Licensor, paid to the Licensor within 3 months 137 | (120 days) with no more than four installations of 138 | payment over the course, where each installation 139 | includes at least the total agreed upon payment in 140 | USD divided by five (5); and/or 141 | 142 | (ii) An agreed upon royalty fee, where the Licensor will be 143 | compensated per instance of Works and/or Derivative 144 | Works, including, but not limited to Object forms either 145 | sold, rented out, manufactured, or distributed in any 146 | way by any party. The agreement may also include an 147 | agreed upon initial payment for the use of these Works 148 | and/or Derivative Works, if a Developer License was not 149 | previously granted. 150 | 151 | The agreement may also include a predetermined milestone 152 | royalty percentage increase, where at each predetermined 153 | milestone of units either distributed, sold, rented out, 154 | or manufactured exceeds a predetermined quantity. 155 | 156 | The time period at which each royalty fee will be given 157 | to the Licensor will be included in the agreement. 158 | 159 | A minimum payment threshold may also be set if a 160 | predetermined minimum royalty fee could not be met for a 161 | predetermined period of time. 162 | 163 | 5. Developer License. 164 | Subject to the terms and conditions of this License, a Developer License 165 | can be granted on an individual basis. The purpose of the Developer 166 | License is to allow the use of this Work in a development environment to 167 | decide weather this Work can assist in the creation of a commercial 168 | product. A Developer using the Developer License may use this Work and 169 | Source form for development purposes within a corporation, company, or 170 | individually with the intent of redistributing an Object form or 171 | Derivative Works. 172 | 173 | The Developer License is a permission granted to a Developer to use the 174 | Source or Work in a development environment, and not for commercial use by 175 | any medium. A redistribution agreement must be made in order to grant the 176 | ability to redistribute the Work, Derivative Work, or Object form. The 177 | Developer may not make a profit by redistributing this Work, Derivative 178 | Work, or Object form in any way, without a redistribution agreement. 179 | 180 | The license does however allow a Developer to develop Derivative Work with 181 | the intention, to commercially produce Derivative Work and Object forms 182 | of this Work in any medium. 183 | 184 | A single Developer License may only be granted on a single individual 185 | basis for a single payment to the Licensor agreed upon by both You and the 186 | Licensor. 187 | 188 | 6. Free License. 189 | Subject to the terms and conditions of this License, a Free License is 190 | granted to an individual using this Work for the creation of Object forms 191 | and Derivative Works for Personal and Private Use. A Developer may use the 192 | Free License within a 24 hour grace period before deciding to purchase a 193 | Developer License. Besides the grace period, under no circumstances may a 194 | corporation, company, academic institution, an individual developing a 195 | product, or an affiliate with the intention of redistributing on behalf of 196 | a corporation, company, academic institution use the Free License. A 197 | Developer License must be granted for such cases. The Free License will 198 | only grant the use of this Work for academic purposes, including but not 199 | limited to research and publication (only by citing the creator of the 200 | Source), and self-learning purposes. The Free License is not allowed for 201 | any product development purposes. 202 | 203 | This License shall be included via text, or linked to, anytime the Work 204 | or Derivitive Work is published publicly, including but not limited to 205 | publishing on the world wide web. 206 | 207 | 7. Submission of Contributions. 208 | Subject to the terms and conditions of this License, a Contributor may 209 | have rights to monetary compensation subject to a contractual agreement 210 | between the Licensor and Contributor decided upon, and signed by both 211 | parties, within no more than one week (7 days) upon Contribution to 212 | this Work. A Contributor will not be granted the Copyright License, unless 213 | explicitly defined in a mutually signed agreement (contract) between both 214 | the Licensor and Contributor. 215 | 216 | 8. Trademarks. 217 | This License does not grant permission to use the trade names, trademarks, 218 | service marks, or product names of the Licensor, except as required for 219 | reasonable and customary use in describing the origin of the Work and 220 | reproducing the content of the NOTICE file. 221 | 222 | 9. Disclaimer of Warranty. 223 | Unless required by applicable law or agreed to in writing, Licensor 224 | provides the Work (and each Contributor provides its Contributions) on an 225 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 226 | express or implied, including, without limitation, any warranties or 227 | conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 228 | PARTICULAR PURPOSE. You are solely responsible for determining the 229 | appropriateness of using or redistributing the Work and assume any risks 230 | associated with Your exercise of permissions under this License. 231 | 232 | 10. Limitation of Liability. In no event and under no legal theory, 233 | whether in tort (including negligence), contract, or otherwise, 234 | unless required by applicable law (such as deliberate and grossly 235 | negligent acts) or agreed to in writing, shall any Contributor be 236 | liable to You for damages, including any direct, indirect, special, 237 | incidental, or consequential damages of any character arising as a 238 | result of this License or out of the use or inability to use the 239 | Work (including but not limited to damages for loss of goodwill, 240 | work stoppage, computer failure or malfunction, or any and all 241 | other commercial damages or losses), even if such Contributor 242 | has been advised of the possibility of such damages. 243 | 244 | 11. Accepting Warranty or Additional Liability. While redistributing 245 | the Work or Derivative Works thereof, You may choose to offer, 246 | and charge a fee for, acceptance of support, warranty, indemnity, 247 | or other liability obligations and/or rights consistent with this 248 | License. However, in accepting such obligations, You may act only 249 | on Your own behalf and on Your sole responsibility, not on behalf 250 | of any other Contributor, and only if You agree to indemnify, 251 | defend, and hold each Contributor harmless for any liability 252 | incurred by, or claims asserted against, such Contributor by reason 253 | of your accepting any such warranty or additional liability. 254 | 255 | END OF TERMS AND CONDITIONS 256 | 257 | Unless required by applicable law or agreed to in writing, software 258 | distributed under the License is distributed on an "AS IS" BASIS, 259 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 260 | See the License for the specific language governing permissions and 261 | limitations under the License. 262 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # μLAPack 2 | Micro Linear Algebra Package 3 | 4 | Pronounced: mu la pack 5 | 6 | A small linear algebra package to compute various matrix/vector operations. 7 | The library is optimized to work on microcontrollers and embedded environments, but can be used anywhere C can be compiled. 8 | 9 | 10 | [![Build Status](https://travis-ci.com/SargisYonan/uLAPack.svg?branch=master)](https://travis-ci.com/SargisYonan/uLAPack) [![DOI](https://zenodo.org/badge/164747669.svg)](https://zenodo.org/badge/latestdoi/164747669) 11 | 12 | 13 | ## Features 14 | All μLAPack functions are "safe" in that the matrix/vector operations are checked for initialization and mathematic dimensional legality before an operation takes place. The library contains various operations and manipulations that can be configured to your needs. The library can be configured to run safely in even the most constrained environments where memory allocation is a concern. 15 | 16 | ### Included Functionality 17 | μLAPack contains functionality to assist in your matrix math needs for feedback control, statistical analysis, data processing, filtering, estimation, basic algebraic computation, and machine learning. 18 | 19 | #### Library Specific Functions 20 | * Initialize a matrix/vector object - `ulapack_initialize` 21 | * Print a matrix/vector to an output file stream - `ulapack_print` 22 | * Free dynamically allocated matrix/vector memory - `ulapack_destroy` 23 | * Safely edit a matrix/vector element - `ulapack_edit_entry` 24 | * Safely modify a matrix/vector element - `ulapack_get_entry` 25 | * Query an object to see if it is a vector - `ulapack_is_vector` 26 | * Copy the contents of a matrix or vector - `ulapack_copy` 27 | * Copy data from an array to a matrix column - `ulapack_array_col_copy` 28 | * Copy data from an array to a matrix row - `ulapack_array_row_copy` 29 | 30 | #### Basic Matrix/Vector Operations 31 | * Get the size of a matrix or vector - `ulapack_size` 32 | * Check if two matrices or vectors are equal in value - `ulapack_is_equal` 33 | 34 | ##### Arithmetic 35 | * Sum the elements of a matrix or vector - `ulapack_sum` 36 | * Add two matrices or vectors - `ulapack_add` 37 | * Subtract two matrices or vectors - `ulapack_subtract` 38 | * Take the trace of a matrix - `ulapack_trace` 39 | * Take the product of two matrices or vectors - `ulapack_product` 40 | * Take the inner product of two vectors - `ulapack_dot` 41 | * Take the norm of a vector or Frobenius norm of a matrix - `ulapack_norm` 42 | * Find the determinant of a Matrix - `ulapack_det` 43 | * Scale a matrix or vector by a scalar - `ulapack_scale` 44 | 45 | ##### Manipulations 46 | * Put a vector on the diagonal of a matrix - `ulapack_diag` 47 | * Set a square matrix to the identity matrix - `ulapack_eye` 48 | * Safely set the entirety of matrix/vector to a single value - `ulapack_set` 49 | * Take the transpose of a matrix or vector - `ulapack_transpose` 50 | * Take the inverse of a matrix - `ulapack_inverse` 51 | * Take the pseudo inverse of a matrix - `ulapack_pinverse` 52 | 53 | #### Advanced Operations 54 | * Decompose a matrix into LU triangular matrices - `ulapack_lu` 55 | * Create a Vandermonde matrix from a vector - `ulapack_vandermonde` 56 | * Compute the least squares of a matrix/system - `ulapack_least_squares` 57 | * Fit an nth degree polynomial - `ulapack_polyfit` 58 | * Decompose a matrix via Singular Value Decomposition (SVD) - `ulapack_svd` 59 | * Compute a score matrix via Principle Component Analysis (PCA) - `ulapack_pca` 60 | 61 | ### Options 62 | Compile time options can be set in either the file `ulapack_options.h` or in a build/make system. The user of μLAPack can make the following considerations while using the library. 63 | 64 | #### Static Memory Allocation 65 | The user of this library has the option of only using static memory allocation. This option is most suitable in embedded environments that can not dynamically allocate memory because of safety concerns with calls to alloc and because of time critical requirements. The macro `ULAPACK_USE_STATIC_ALLOC` must be `#define`d at compile time in order to use the static memory allocation feature. The static allocation technique relies on using overhead memory in the static matrix memory allocation. Due to this technique, the maximum possible row and column dimensions of any matrix object must be explicitly defined at compile time via the macros: `ULAPACK_MAX_MATRIX_N_ROWS` and `ULAPACK_MAX_MATRIX_N_COLS`. Any matrix initialized after compile time can not exceed the dimensions defined by the macros, and an error will be returned upon initialization if this is attempted. 66 | 67 | #### Dynamic Memory Allocation 68 | Define `ULAPACK_USE_DYNAMIC_ALLOC` to specify using dynamic memory allocation for all objects. 69 | 70 | #### Choose Your Entry Container Type 71 | The user of this library has the option of setting the matrix/vector element data type to a desired type via the `ULAPACK_MATRIX_ENTRY_TYPE` `#define`. It is recommended that a primitive floating point type is used. `ULAPACK_MATRIX_ENTRY_TYPE` is set to `double` by default, and is `typedef`ed to `MatrixEntry_t` in the source code. 72 | `MINIMUM_THRESHOLD_TOLERANCE` defines the threshold floating point limit for a 0. 73 | 74 | #### Clear Upon Initialization 75 | All new matrix/vector objects made can be initialized to zeros if `ULAPACK_INITIALIZE_MEMORY` is defined at compile time. 76 | 77 | #### Matrix Inversion Options 78 | Define `ULAPACK_INVERSE_VIA_LU` to use LU decomposition for matrix inversions. 79 | 80 | #### Allocator/Freer Options 81 | If dynamic memory allocation is used, the memory allocator and freer can be configured via the macros `ULAPACK_ALLOC` and `ULAPACK_FREE`. 82 | 83 | ### Unit Tests 84 | μLAPack was developed using test-driven practices. The unit tests and `Makefile` for building and running the unit tests are in the `test/` directory. To test static memory allocation run `make ulapack_test_static`. To unit test the dynamically allocated methods, run `make ulapack_test_dynamic`. To run both tests, use `make`. `valgrind ./ulapack_test_dynamic` returns no memory leaks while using dynamic memory allocation. 85 | 86 | ### Error Codes 87 | The following error codes can be returned from the library functions. See the in-file documentation for the function to check its return codes of type `MatrixError_t`. 88 | 89 | * `ulapack_error` - general error code 90 | * `ulapack_oom` - out of memory error code 91 | * `ulapack_invalid_argument` - bad argument given to function 92 | * `ulapack_uninit_obj` - uninitialized object passed into function 93 | * `ulapack_success` - general success code 94 | 95 | # Usage 96 | 97 | ## Include The Library 98 | ```C 99 | #include "ulapack.h" 100 | 101 | ``` 102 | 103 | ## Statically Allocated Matrix 104 | An example of setting up and using μLAPack with static memory allocation turned on. 105 | 106 | File: `ulapack_options.h` 107 | ```C 108 | #define ULAPACK_USE_STATIC_ALLOC // switch on static allocation 109 | #define ULAPACK_MAX_MATRIX_N_ROWS (10u) // set maximum matrix rows possible to 10 110 | #define ULAPACK_MAX_MATRIX_N_COLS (10u) // set maximum matrix cols possible to 10 111 | 112 | ``` 113 | File: `another_c_file.c` 114 | ```C 115 | #include "ulapack.h" 116 | 117 | ... 118 | 119 | Matrix_t A; // declare a matrix object with name A 120 | ulapack_initialize_matrix(&A, 3u, 3u); // initialize the matrix with size 3X3 121 | ulapack_eye(&A); // set the matrix A equal to the 3X3 identity matrix 122 | ulapack_scale(&A, 3, &A); // scale the matrix A by 3 123 | 124 | ulapack_print(&A, stdout); 125 | 126 | ``` 127 | 128 | Output: 129 | ``` 130 | [ 3 0 0 ] 131 | [ 0 3 0 ] 132 | [ 0 0 3 ] 133 | ``` 134 | 135 | ## Dynamically Allocated Matrix 136 | An example of setting up and using μLAPack with static memory allocation turned on. 137 | 138 | File: `ulapack_options.h` 139 | ```C 140 | #define ULAPACK_USE_DYNAMIC_ALLOC // switch on dynamic allocation 141 | 142 | ``` 143 | 144 | File: `a_c_file.c` 145 | ```C 146 | #include "ulapack.h" 147 | 148 | ... 149 | 150 | Matrix_t *A; // declare a matrix object pointer with name A 151 | ulapack_initialize_matrix(&A, 3u, 3u); // initialize the matrix with size 3X3 152 | ulapack_eye(A); // set the matrix A equal to the 3X3 identity matrix 153 | ulapack_scale(A, 3, A); // scale the matrix A by 3 154 | 155 | ulapack_print(A, stdout); 156 | 157 | ``` 158 | 159 | Output: 160 | ``` 161 | [ 3 0 0 ] 162 | [ 0 3 0 ] 163 | [ 0 0 3 ] 164 | ``` 165 | ## Examples 166 | Navigate to the `examples/` directory for code samples using μLAPack. 167 | 168 | An example of using μLAPack for Principle Component Analysis (PCA). 169 | ```C 170 | #include "ulapack.h" 171 | 172 | int main(void) { 173 | 174 | const double Adata[10][8] = {{0.4170, 0.4192, 0.8007, 0.0983, 0.9889, 0.0194, 0.1023, 0.9034}, 175 | {0.7203, 0.6852, 0.9683, 0.4211, 0.7482, 0.6788, 0.4141, 0.1375}, 176 | {0.0001, 0.2045, 0.3134, 0.9579, 0.2804, 0.2116, 0.6944, 0.1393}, 177 | {0.3023, 0.8781, 0.6923, 0.5332, 0.7893, 0.2655, 0.4142, 0.8074}, 178 | {0.1468, 0.0274, 0.8764, 0.6919, 0.1032, 0.4916, 0.0500, 0.3977}, 179 | {0.0923, 0.6705, 0.8946, 0.3155, 0.4479, 0.0534, 0.5359, 0.1654}, 180 | {0.1863, 0.4173, 0.0850, 0.6865, 0.9086, 0.5741, 0.6638, 0.9275}, 181 | {0.3456, 0.5587, 0.0391, 0.8346, 0.2936, 0.1467, 0.5149, 0.3478}, 182 | {0.3968, 0.1404, 0.1698, 0.0183, 0.2878, 0.5893, 0.9446, 0.7508}, 183 | {0.5388, 0.1981, 0.8781, 0.7501, 0.1300, 0.6998, 0.5866, 0.7260}}; 184 | 185 | Matrix_t A; 186 | Matrix_t T; 187 | 188 | ulapack_init(&A, 10, 8); 189 | ulapack_init(&T, 10, 10); 190 | 191 | /* 192 | * Copy data points into vector objects. 193 | */ 194 | for (uint64_t row_itor = 0; row_itor < 10; row_itor++) { 195 | for (uint64_t col_itor = 0; col_itor < 8; col_itor++) { 196 | ulapack_edit_entry(&A, 197 | row_itor, col_itor, 198 | Adata[row_itor][col_itor]); 199 | } 200 | } 201 | 202 | ulapack_pca(&A, &T); 203 | } 204 | ``` 205 | 206 | An example using μLAPack for polynomial regression. 207 | 208 | ```C 209 | #include "ulapack.h" 210 | 211 | int main(void) { 212 | /* 213 | * Fit 10 data points. 214 | */ 215 | const uint64_t data_points = 10; 216 | 217 | /* 218 | * Fit to the 2nd degree. 219 | */ 220 | const uint64_t fit_degree = 2; 221 | 222 | const double xdata[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 223 | /* 224 | * x^2 + noise 225 | */ 226 | const double ydata[] = {0.679196, 3.215585, 227 | 8.635037, 16.117271, 228 | 25.174340, 35.784344, 229 | 48.847389, 64.033688, 230 | 81.458282, 101.281631}; 231 | 232 | /* 233 | * Declare vector objects for data points. 234 | */ 235 | Matrix_t x; 236 | Matrix_t y; 237 | 238 | /* 239 | * Declare vector object for coefficients. 240 | */ 241 | Matrix_t p; 242 | 243 | /* 244 | * Initialize vector objects. 245 | */ 246 | ulapack_init(&x, data_points, 1); 247 | ulapack_init(&y, data_points, 1); 248 | 249 | ulapack_init(&p, fit_degree + 1, 1); 250 | 251 | /* 252 | * Copy data points into vector objects. 253 | */ 254 | for (uint64_t row_itor = 0; row_itor < data_points; row_itor++) { 255 | ulapack_edit_entry(&x, 256 | row_itor, 0, 257 | xdata[row_itor]); 258 | 259 | ulapack_edit_entry(&y, 260 | row_itor, 0, 261 | ydata[row_itor]); 262 | } 263 | 264 | /* 265 | * Run the regression. 266 | */ 267 | ulapack_polyfit(&x, &y, fit_degree, &p); 268 | } 269 | ``` 270 | 271 | ![Polynomial Fit Comparison](examples/fitcomp.png) 272 | 273 | # Requirements 274 | The libc math.h library is required for a square root operation in the `ulapack_norm` and `ulapack_svd` functions. To avoid the use of additional cmath library calls, the `math.h` `pow()` function was rewritten as a private function. 275 | 276 | # Licensing & Support 277 | μLAPack and any derivative works of μLAPack (and embedded_lapack) are free to use for personal and/or educational use without profit and for development purposes in your project(s) to verify if μLAPack is right for you. 278 | 279 | For commercial use (with redistribution rights): 280 | Fill out the [licensing inquiry form](https://goo.gl/forms/8QpSDgC3JthAGoTG2), and I will promptly reply with a response and proposed license agreement. 281 | 282 | Refer to the [LICENSE](LICENSE) document for licensing details. 283 | 284 | # Legacy 285 | μLAPack is a fork of my now deprecated embedded_lapack library. μLAPack includes definitions for both static and dynamic memory allocation, safer type definitions, more explicit and safer error code status, Doxygen, more functionality, and more compiler support when compared to embedded_lapack. 286 | 287 | # TODO 288 | * find occurrences and indices of a value in a matrix/vector - `ulapack_find` 289 | * take the pseudo inverse of a matrix via SVD (good for poorly conditioned matrix inversions) 290 | * grab a sub-matrix from a matrix/vector (similar to the `:` operator in MATLAB) - `ulapack_copy_submatrix` 291 | * take the cross product of two vector - `ulapack_cross` 292 | * write an element square root function to avoid any math libc calls 293 | * make a skew symmetric matrix - `ulapack_skew` 294 | * make a Kalman extension package `μKalman` with: 295 | - model propagation 296 | - Kalman gain matrix calculations using μLAPack 297 | 298 | Have a suggestion for a new feature/function? File an issue in this repository with your requests. 299 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | cc=cc 2 | cc_options=-Wall -Werror -Wextra -Os 3 | 4 | ulap_src_dir=../src 5 | ulap_opt=-DULAPACK_USE_STATIC_ALLOC=1 -DULAPACK_MAX_MATRIX_N_ROWS=20 -DULAPACK_MAX_MATRIX_N_COLS=20 6 | ulap_headers=$(ulap_src_dir)/ulapack.h $(ulap_src_dir)/ulapack_options.h 7 | 8 | all: pca polyfit 9 | 10 | pca : ulapack.o pca.c 11 | cc -I$(ulap_src_dir) $(cc_options) $(ulap_opt) ulapack.o pca.c -o pca 12 | 13 | polyfit : ulapack.o polyfit.c 14 | cc -I$(ulap_src_dir) $(cc_options) $(ulap_opt) ulapack.o polyfit.c -o polyfit 15 | 16 | ulapack.o : $(ulap_src_dir)/ulapack.c $(ulap_headers) 17 | cc -I$(ulap_src_dir) $(cc_options) $(ulap_opt) -c $(ulap_src_dir)/ulapack.c -o ulapack.o 18 | 19 | clean : 20 | rm *.o polyfit pca -------------------------------------------------------------------------------- /examples/fitcomp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SargisYonan/uLAPack/b80ddb45c757b090df9c7213cddda25fa8b3131f/examples/fitcomp.png -------------------------------------------------------------------------------- /examples/pca.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @name pca.c 3 | * An example of using uLAPAck for principle component analysis. 4 | * 5 | * @note In this example, static memory allocation is used 6 | * via the -D compiler options. 7 | * @note Build this example using: $ make pca 8 | * @note Run this example using: $ ./pca 9 | */ 10 | 11 | #include "ulapack.h" 12 | 13 | #include 14 | 15 | static void run_pca(double Amatrix[10][8]) { 16 | Matrix_t A; 17 | Matrix_t T; 18 | 19 | ulapack_init(&A, 10, 8); 20 | ulapack_init(&T, 10, 10); 21 | 22 | /* 23 | * Copy data points into vector objects. 24 | */ 25 | for (Index_t row_itor = 0; row_itor < 10; row_itor++) { 26 | for (Index_t col_itor = 0; col_itor < 8; col_itor++) { 27 | ulapack_edit_entry(&A, 28 | row_itor, col_itor, 29 | Amatrix[row_itor][col_itor]); 30 | } 31 | } 32 | 33 | ulapack_pca(&A, &T); 34 | 35 | printf("\nA = \n"); 36 | ulapack_print(&A, stdout); 37 | 38 | printf("\nT = \n"); 39 | ulapack_print(&T, stdout); 40 | } 41 | 42 | 43 | 44 | int main(void) { 45 | double Adata[10][8] = { {0.4170, 0.4192, 0.8007, 0.0983, 0.9889, 0.0194, 0.1023, 0.9034}, 46 | {0.7203, 0.6852, 0.9683, 0.4211, 0.7482, 0.6788, 0.4141, 0.1375}, 47 | {0.0001, 0.2045, 0.3134, 0.9579, 0.2804, 0.2116, 0.6944, 0.1393}, 48 | {0.3023, 0.8781, 0.6923, 0.5332, 0.7893, 0.2655, 0.4142, 0.8074}, 49 | {0.1468, 0.0274, 0.8764, 0.6919, 0.1032, 0.4916, 0.0500, 0.3977}, 50 | {0.0923, 0.6705, 0.8946, 0.3155, 0.4479, 0.0534, 0.5359, 0.1654}, 51 | {0.1863, 0.4173, 0.0850, 0.6865, 0.9086, 0.5741, 0.6638, 0.9275}, 52 | {0.3456, 0.5587, 0.0391, 0.8346, 0.2936, 0.1467, 0.5149, 0.3478}, 53 | {0.3968, 0.1404, 0.1698, 0.0183, 0.2878, 0.5893, 0.9446, 0.7508}, 54 | {0.5388, 0.1981, 0.8781, 0.7501, 0.1300, 0.6998, 0.5866, 0.7260}}; 55 | run_pca(Adata); 56 | 57 | return 1; 58 | } 59 | 60 | /* 61 | * Corresponding MATLAB code for comparison. 62 | */ 63 | 64 | /* 65 | 66 | clear all; 67 | close all; 68 | clc; 69 | 70 | rng(1); 71 | 72 | A = rand(10, 8); 73 | 74 | [U,S,V] = svd(A * A'); 75 | T = U * S 76 | 77 | */ -------------------------------------------------------------------------------- /examples/polyfit.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @name polyfit.c 3 | * An example of using uLAPAck for polynomial regression. 4 | * 5 | * @note In this example, static memory allocation is used 6 | * via the -D compiler options. 7 | * @note Build this example using: $ make polyfit 8 | * @note Run this example using: $ ./polyfit 9 | */ 10 | 11 | #include "ulapack.h" 12 | 13 | #include 14 | 15 | /** 16 | * @name run_regression 17 | * A helper function to regress some data. 18 | * 19 | * @param[in] xdata The x coordinate data. 20 | * @param[in] ydata The y coordinate data. 21 | * @param The number of x and y points. 22 | * @param nth_degree The number of degrees to fit a polynomial to. 23 | * @param[out] polynomial_coefs The returning polynomial fit coefficients. 24 | */ 25 | static void run_regression(const MatrixEntry_t * xdata, 26 | const MatrixEntry_t * ydata, 27 | const Index_t data_points, 28 | const Index_t nth_degree, 29 | Matrix_t * const polynomial_coefs) { 30 | /* 31 | * Declare matrix objects. 32 | */ 33 | Matrix_t x; 34 | Matrix_t y; 35 | 36 | /* 37 | * Initialize matrix objects. 38 | */ 39 | ulapack_init(&x, data_points, 1); 40 | ulapack_init(&y, data_points, 1); 41 | 42 | /* 43 | * Copy data points into vector objects. 44 | */ 45 | ulapack_array_col_copy(ydata, &y, 0, data_points); 46 | ulapack_array_col_copy(xdata, &x, 0, data_points); 47 | 48 | /* 49 | * Run the regression. 50 | */ 51 | ulapack_polyfit(&x, &y, nth_degree, polynomial_coefs); 52 | } 53 | 54 | int main(void) { 55 | 56 | MatrixEntry_t xdata[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 57 | /* 58 | * x^2 + noise 59 | */ 60 | MatrixEntry_t ydata[] = {0.679196, 3.215585, 61 | 8.635037, 16.117271, 62 | 25.174340, 35.784344, 63 | 48.847389, 64.033688, 64 | 81.458282, 101.281631}; 65 | 66 | const Index_t degree = 2; 67 | Matrix_t p; 68 | 69 | ulapack_init(&p, degree + 1, 1); 70 | 71 | run_regression(xdata, ydata, 10, 2, &p); 72 | 73 | printf("\nFit coefficients: \n"); 74 | ulapack_print(&p, stdout); 75 | 76 | } 77 | 78 | /* 79 | * Corresponding MATLAB code for comparison. 80 | */ 81 | 82 | /* 83 | 84 | clear all; 85 | close all; 86 | clc; 87 | 88 | x = [1:1:10]'; 89 | 90 | %% Noisy x^2 91 | y = [0.679196; 92 | 3.215585; 93 | 8.635037; 94 | 16.117271; 95 | 25.174340; 96 | 35.784344; 97 | 48.847389; 98 | 64.033688; 99 | 81.458282, 100 | 101.281631]; 101 | 102 | p = polyfit(x, y, 2)'; 103 | 104 | ulap_p = [1.02164869318180340229673674912191927433013916015625; 105 | -0.09319982499999923675204627215862274169921875; 106 | -0.2981993499998480956492130644619464874267578125]; 107 | 108 | error = norm(p - ulap_p); 109 | 110 | figure() 111 | plot(x, y, 'o'); 112 | hold on; 113 | plot(linspace(1, 10), polyval(p, linspace(1,10))) 114 | hold on; 115 | plot(linspace(1, 10), polyval(ulap_p, linspace(1,10))) 116 | 117 | leg = legend('Actual Data', 'MATLAB', '$\mu$LAPack polyfit'); 118 | set(leg, 'FontSize', 12, 'Interpreter', 'Latex'); 119 | 120 | title('Comparing MATLAB and $\mu$LAPack Polynomial Fitting (Noisy $x^2$)', ... 121 | 'Interpreter', 'Latex', 'FontSize', 16) 122 | xlabel('$x$', 'Interpreter', 'Latex', 'FontSize', 20) 123 | ylabel('$y$', 'Interpreter', 'Latex', 'FontSize', 20) 124 | 125 | grid on 126 | 127 | fprintf('\nResidual Error = %f\n\n', error) 128 | 129 | */ 130 | -------------------------------------------------------------------------------- /src/ulapack.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ulapack.c 3 | * @brief Definitions of uLAPack matrix manipulation functions. 4 | * 5 | * @author Sargis Yonan 6 | * @date July 8, 2017 7 | * 8 | * @version 1.0.1 9 | **/ 10 | 11 | /* 12 | * CLib math required for sqrt() function. 13 | */ 14 | #include 15 | 16 | #include "ulapack.h" 17 | 18 | /** 19 | * @name _ulapack_is_valid_memory 20 | * Static subroutine to determine if a passed in object has been initialized. 21 | * 22 | * @param[in] mat A pointer to an object. 23 | * 24 | * @return ulapack_error if the object has not yet been initialized. 25 | * ulapack_success if the object passed in has been initialized. 26 | */ 27 | static MatrixError_t _ulapack_is_valid_memory(const Matrix_t *mat) { 28 | if (!mat) { 29 | return ulapack_error; 30 | } 31 | 32 | return ulapack_success; 33 | } 34 | 35 | /** 36 | * @name ulapack_entry_abs 37 | * Take the absolute value of a scalar value. 38 | * 39 | * @param element The value to take the absolute value of. 40 | * 41 | * @return The absolute value of element. 42 | */ 43 | static MatrixEntry_t ulapack_abs(const MatrixEntry_t element) { 44 | if (element < 0) { 45 | return -1 * element; 46 | } else { 47 | return element; 48 | } 49 | } 50 | 51 | /** 52 | * @name _ulapack_elem_pow 53 | * Take the power of a matrix element. 54 | * 55 | * @param element The element value to take to the power of p. 56 | * @param p The power to take the element to. 57 | * 58 | * @return element^p. 59 | */ 60 | static MatrixEntry_t _ulapack_elem_pow(const MatrixEntry_t elem, 61 | const MatrixEntry_t p) { 62 | Index_t pitor = 0; 63 | 64 | MatrixEntry_t power = 0; 65 | 66 | if (p == 0) { 67 | return 1; 68 | } else { 69 | 70 | if (elem == 0) { 71 | return 0; 72 | } 73 | 74 | power = 1; 75 | for (pitor = 0; pitor < p; pitor++) { 76 | power *= elem; 77 | } 78 | } 79 | 80 | return power; 81 | } 82 | 83 | /** 84 | * @name ulapack_hypot 85 | * Get the value of the Pythagorean theorem from two sides of triangle. 86 | * 87 | * @param a The first side length of the triangle. 88 | * @param b Another side length of the triangle. 89 | * 90 | * @return The hypotenuse of the triangle, c = sqrt(a^2 + b^2). 91 | */ 92 | static MatrixEntry_t ulapack_hypot(MatrixEntry_t a, 93 | MatrixEntry_t b) { 94 | MatrixEntry_t c = 0; 95 | 96 | a = ulapack_abs(a); 97 | b = ulapack_abs(b); 98 | 99 | if (a > b) { 100 | c = b / a; 101 | c = a * sqrt(1 + c * c); 102 | } else if (b > 0) { 103 | c = a / b; 104 | c = b * sqrt(1 + c * c); 105 | } 106 | 107 | return(c); 108 | } 109 | 110 | /** 111 | * @ulapack_sign 112 | * Conditional sign function. 113 | * 114 | * @param a The value to modify based on the condition variable. 115 | * @param b The condition variable. 116 | * 117 | * @return If the sign of b is positive, return the absolute value of a, |a|. 118 | * If the sign of b is zero or negative, return the -|a|. 119 | */ 120 | static MatrixEntry_t ulapack_sign(const MatrixEntry_t a, 121 | const MatrixEntry_t b) { 122 | if (b > 0) { 123 | return ulapack_abs(a); 124 | } else { 125 | return -1 * ulapack_abs(a); 126 | } 127 | } 128 | 129 | /** 130 | * @ulapack_max 131 | * Get the sign of a value. 132 | * 133 | * @param a The first operand. 134 | * @param b The second operand. 135 | * 136 | * @return If a > b, a is returned; else b. 137 | */ 138 | static MatrixEntry_t ulapack_max(const MatrixEntry_t a, const MatrixEntry_t b) { 139 | if (a > b) { 140 | return a; 141 | } else { 142 | return b; 143 | } 144 | } 145 | 146 | MatrixError_t ulapack_init( 147 | #ifdef ULAPACK_USE_STATIC_ALLOC 148 | Matrix_t *matrix, 149 | #else 150 | Matrix_t **matrix, 151 | #endif 152 | const Index_t n_rows, const Index_t n_cols) { 153 | 154 | /* 155 | * Case when using static memory allocation. 156 | */ 157 | #ifdef ULAPACK_USE_STATIC_ALLOC 158 | /* 159 | * Check to make sure a legitimate matrix object block was given. 160 | */ 161 | if (!matrix) { 162 | return ulapack_invalid_argument; 163 | } 164 | 165 | /* 166 | * Return an error if a dimension given exceeds the preset maximum 167 | * dimensions. 168 | */ 169 | if (n_rows > ULAPACK_MAX_MATRIX_N_ROWS || 170 | n_cols > ULAPACK_MAX_MATRIX_N_COLS) { 171 | 172 | matrix->n_rows = 0; 173 | matrix->n_cols = 0; 174 | 175 | return ulapack_invalid_argument; 176 | } 177 | 178 | /* 179 | * Set the matrix dimensions. 180 | */ 181 | matrix->n_rows = n_rows; 182 | matrix->n_cols = n_cols; 183 | 184 | /* 185 | * If specified, initialize all memory to zeros. 186 | */ 187 | #ifdef ULAPACK_INITIALIZE_MEMORY 188 | ulapack_set(matrix, 0); 189 | #endif 190 | 191 | return ulapack_success; 192 | /* 193 | * Case when using dynamic memory allocation. 194 | */ 195 | #else 196 | /* 197 | * Declare loop counters here in case C99 is not used. 198 | */ 199 | Index_t row_itor = 0; 200 | 201 | #ifdef ULAPACK_INITIALIZE_MEMORY 202 | Index_t col_itor = 0; 203 | #endif 204 | 205 | Index_t free_rows_itor = 0; 206 | 207 | /* 208 | * Check to make sure the matrix object pointer given is not allocated. 209 | */ 210 | if (*matrix) { 211 | return ulapack_invalid_argument; 212 | } 213 | 214 | /* 215 | * Allocate memory for overall object and check for successful 216 | * allocation. 217 | */ 218 | *matrix = ULAPACK_ALLOC(sizeof(Matrix_t)); 219 | if (!matrix) { 220 | return ulapack_oom; 221 | } 222 | /* 223 | * Allocate memory for the matrix rows. 224 | */ 225 | (*matrix)->entry = ULAPACK_ALLOC(sizeof(MatrixEntry_t) * n_rows); 226 | 227 | /* 228 | * Check for successful alloc. 229 | */ 230 | if (!((*matrix)->entry)) 231 | { 232 | /* 233 | * Free the last unusable memory allocated. 234 | */ 235 | ULAPACK_FREE(*matrix); 236 | *matrix = NULL; 237 | 238 | return ulapack_oom; 239 | } 240 | 241 | /* 242 | * For each row in the matrix, allocate enough columns. 243 | */ 244 | for(row_itor = 0; row_itor < n_rows; row_itor++) { 245 | /* 246 | * Allocate memory for each row. 247 | */ 248 | (*matrix)->entry[row_itor] = ULAPACK_ALLOC( 249 | sizeof(MatrixEntry_t) * n_cols); 250 | 251 | /* 252 | * Check for error in alloc. If an error occurred, free all memory 253 | * allocated up to this point, and then return with error. 254 | */ 255 | if (!((*matrix)->entry[row_itor])) { 256 | for(free_rows_itor = 0; 257 | free_rows_itor < row_itor; 258 | free_rows_itor++) { 259 | ULAPACK_FREE((*matrix)->entry[row_itor]); 260 | } 261 | 262 | ULAPACK_FREE(*matrix); 263 | *matrix = NULL; 264 | 265 | return ulapack_oom; 266 | } 267 | 268 | /* 269 | * If specified, initialize all allocated memory to zeros. 270 | */ 271 | #ifdef ULAPACK_INITIALIZE_MEMORY 272 | for (col_itor = 0; col_itor < n_cols; col_itor++) { 273 | (*matrix)->entry[row_itor][col_itor] = 0; 274 | } 275 | #endif 276 | } 277 | 278 | /* 279 | * Set the matrix dimensions. 280 | */ 281 | (*matrix)->n_rows = n_rows; 282 | (*matrix)->n_cols = n_cols; 283 | 284 | return ulapack_success; 285 | 286 | #endif 287 | } 288 | 289 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 290 | MatrixError_t ulapack_destroy(Matrix_t *obj) { 291 | Index_t row_itor = 0; 292 | 293 | if (obj) { 294 | if (obj->entry) { 295 | /* 296 | * First free and NULL out all dynamically allocated matrix rows. 297 | */ 298 | for (row_itor = 0; row_itor < obj->n_rows; row_itor++) { 299 | ULAPACK_FREE(obj->entry[row_itor]); 300 | obj->entry[row_itor] = NULL; 301 | } 302 | 303 | /* 304 | * Free and NULL out the matrix entry pointer. 305 | */ 306 | if (obj->entry) { 307 | ULAPACK_FREE(obj->entry); 308 | obj->entry = NULL; 309 | } 310 | } 311 | 312 | /* 313 | * Free and NULLify the actual object. 314 | */ 315 | ULAPACK_FREE(obj); 316 | obj = NULL; 317 | 318 | return ulapack_success; 319 | } 320 | 321 | return ulapack_uninit_obj; 322 | } 323 | #endif 324 | 325 | #ifdef ULAPACK_USE_PRINT 326 | MatrixError_t ulapack_print(const Matrix_t * const matrix, 327 | FILE *stream) { 328 | 329 | Index_t row_itor = 0; 330 | Index_t col_itor = 0; 331 | 332 | /* 333 | * Verify that a valid operand object have been passed in. 334 | */ 335 | if (!_ulapack_is_valid_memory(matrix)) { 336 | return ulapack_uninit_obj; 337 | } 338 | 339 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 340 | 341 | fprintf(stream, "[ "); 342 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 343 | fprintf(stream, ULAPACK_PRINT_DELIMITER " ", 344 | matrix->entry[row_itor][col_itor]); 345 | } 346 | 347 | fprintf(stream, "]\n"); 348 | } 349 | 350 | fprintf(stream, "\n"); 351 | 352 | return ulapack_success; 353 | } 354 | #endif 355 | 356 | MatrixError_t ulapack_edit_entry(Matrix_t * const matrix, 357 | const Index_t row, const Index_t col, 358 | const MatrixEntry_t value) { 359 | /* 360 | * Verify that a valid operand object have been passed in. 361 | */ 362 | if (!_ulapack_is_valid_memory(matrix)) { 363 | return ulapack_uninit_obj; 364 | } 365 | 366 | /* 367 | * Verify that the coordinates to edit are within the initialized dimensions 368 | * of the matrix object. 369 | */ 370 | if (row > matrix->n_rows || col > matrix->n_cols) { 371 | return ulapack_invalid_argument; 372 | } 373 | 374 | /* 375 | * Edit the value of the matrix at (row, col). 376 | */ 377 | matrix->entry[row][col] = value; 378 | 379 | return ulapack_success; 380 | } 381 | 382 | MatrixError_t ulapack_get_entry(const Matrix_t * const matrix, 383 | const Index_t row, const Index_t col, 384 | MatrixEntry_t * const value) { 385 | /* 386 | * Verify that a valid operand object have been passed in. 387 | */ 388 | if (!_ulapack_is_valid_memory(matrix)) { 389 | return ulapack_uninit_obj; 390 | } 391 | 392 | /* 393 | * Verify that the coordinates to edit are within the initialized dimensions 394 | * of the matrix object. 395 | */ 396 | if (row > matrix->n_rows || col > matrix->n_cols) { 397 | return ulapack_invalid_argument; 398 | } 399 | 400 | /* 401 | * The value pointer should not be NULL because it should be written to. 402 | */ 403 | if (!value) { 404 | return ulapack_invalid_argument; 405 | } 406 | 407 | /* 408 | * Store the value of the matrix at (row, col). 409 | */ 410 | *value = matrix->entry[row][col]; 411 | return ulapack_success; 412 | } 413 | 414 | MatrixEntry_t ulapack_array_col_copy(const MatrixEntry_t * const data, 415 | Matrix_t * const dest_obj, 416 | const Index_t col, 417 | const Index_t data_points) { 418 | Index_t row_itor = 0; 419 | 420 | if (!data) { 421 | return ulapack_invalid_argument; 422 | } 423 | 424 | /* 425 | * Verify that a valid operand object have been passed in. 426 | */ 427 | if (!_ulapack_is_valid_memory(dest_obj)) { 428 | return ulapack_uninit_obj; 429 | } 430 | 431 | if (dest_obj->n_cols <= col) { 432 | return ulapack_invalid_argument; 433 | } 434 | 435 | if (dest_obj->n_rows < data_points) { 436 | return ulapack_invalid_argument; 437 | } 438 | 439 | for (row_itor = 0; row_itor < data_points; row_itor++) { 440 | dest_obj->entry[row_itor][col] = data[row_itor]; 441 | } 442 | 443 | return ulapack_success; 444 | } 445 | 446 | MatrixEntry_t ulapack_array_row_copy(const MatrixEntry_t * const data, 447 | Matrix_t * const dest_obj, 448 | const Index_t row, 449 | const Index_t data_points) { 450 | Index_t col_itor = 0; 451 | 452 | if (!data) { 453 | return ulapack_invalid_argument; 454 | } 455 | 456 | /* 457 | * Verify that a valid operand object have been passed in. 458 | */ 459 | if (!_ulapack_is_valid_memory(dest_obj)) { 460 | return ulapack_uninit_obj; 461 | } 462 | 463 | if (dest_obj->n_rows <= row) { 464 | return ulapack_invalid_argument; 465 | } 466 | 467 | if (dest_obj->n_cols < data_points) { 468 | return ulapack_invalid_argument; 469 | } 470 | 471 | for (col_itor = 0; col_itor < data_points; col_itor++) { 472 | dest_obj->entry[row][col_itor] = data[col_itor]; 473 | } 474 | 475 | return ulapack_success; 476 | } 477 | 478 | MatrixError_t ulapack_size(const Matrix_t * const matrix, 479 | Index_t * const rows, Index_t * const cols) { 480 | /* 481 | * Verify that a valid operand object have been passed in. 482 | */ 483 | if (!_ulapack_is_valid_memory(matrix)) { 484 | return ulapack_uninit_obj; 485 | } 486 | 487 | if (!rows || !cols) { 488 | return ulapack_invalid_argument; 489 | } 490 | 491 | *rows = matrix->n_rows; 492 | *cols = matrix->n_cols; 493 | 494 | return ulapack_success; 495 | } 496 | 497 | MatrixError_t ulapack_set(Matrix_t * const matrix, 498 | const MatrixEntry_t value) { 499 | /* 500 | * Matrix iterators. 501 | * Declared at top of function for legacy compiler comparability. 502 | */ 503 | Index_t row_itor = 0; 504 | Index_t col_itor = 0; 505 | 506 | /* 507 | * Verify that a valid operand object have been passed in. 508 | */ 509 | if (!_ulapack_is_valid_memory(matrix)) { 510 | return ulapack_uninit_obj; 511 | } 512 | 513 | /* 514 | * Set the elements of the matrix. 515 | */ 516 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 517 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 518 | matrix->entry[row_itor][col_itor] = value; 519 | } 520 | } 521 | 522 | return ulapack_success; 523 | } 524 | 525 | MatrixError_t ulapack_is_equal(const Matrix_t * const A, 526 | const Matrix_t * const B, 527 | MatrixError_t * const is_equal) { 528 | /* 529 | * Matrix iterators. 530 | * Declared at top of function for legacy compiler comparability. 531 | */ 532 | Index_t row_itor = 0; 533 | Index_t col_itor = 0; 534 | 535 | /* 536 | * Verify that valid operand objects have been passed in. 537 | */ 538 | if (!_ulapack_is_valid_memory(A) || !_ulapack_is_valid_memory(B)) { 539 | return ulapack_uninit_obj; 540 | } 541 | 542 | /* 543 | * Check if the dimensions of the operands are identical. 544 | */ 545 | if (A->n_rows != B->n_rows || A->n_cols != B->n_cols) { 546 | *is_equal = ulapack_error; 547 | return ulapack_success; 548 | } 549 | 550 | /* 551 | * The is_equal pointer should not be NULL because it should be written to. 552 | */ 553 | if (!is_equal) { 554 | return ulapack_invalid_argument; 555 | } 556 | 557 | /* 558 | * Compare the entries of the operands. Check if A == B. 559 | */ 560 | for (row_itor = 0; row_itor < A->n_rows; row_itor++) { 561 | for (col_itor = 0; col_itor < A->n_cols; col_itor++) { 562 | /* 563 | * If a mismatch occurs, set is_equal to ulapack_error, and return 564 | * ulapack_success to indicate a successful comparison. 565 | */ 566 | if (ulapack_abs(A->entry[row_itor][col_itor] - 567 | B->entry[row_itor][col_itor]) > 568 | MINIMUM_THRESHOLD_TOLERANCE) { 569 | *is_equal = ulapack_error; 570 | return ulapack_success; 571 | } 572 | } 573 | } 574 | 575 | /* 576 | * The matrices are assumed to be identical at this point. 577 | */ 578 | *is_equal = ulapack_success; 579 | return ulapack_success; 580 | } 581 | 582 | /** 583 | * @name ulapack_is_vector 584 | * Check if the input operand is a vector: 1xN or Nx1. 585 | * 586 | * @param[in] vector An initialized vector object operand. 587 | * @param[out] is_vector Pass back if the input is a vector. ulapack_success if 588 | * the input is a vector, and ulapack_error if the input is not a 589 | * vector. 590 | * 591 | * @return ULAPack success code ulapack_success is returned if the input vector 592 | * is a initialized and checked for vector dimensions successful. 593 | */ 594 | MatrixError_t ulapack_is_vector(const Matrix_t * const vector, 595 | MatrixError_t * const is_vector) { 596 | /* 597 | * Verify that valid operand objects have been passed in. 598 | */ 599 | if (!_ulapack_is_valid_memory(vector)) { 600 | return ulapack_uninit_obj; 601 | } 602 | 603 | if ((vector->n_rows == 1 && vector->n_cols >= 1) || 604 | (vector->n_cols == 1 && vector->n_rows >= 1)) { 605 | *is_vector = ulapack_success; 606 | } else { 607 | *is_vector = ulapack_error; 608 | } 609 | 610 | return ulapack_success; 611 | } 612 | 613 | MatrixError_t ulapack_eye(Matrix_t * const matrix) { 614 | /* 615 | * Matrix iterators. 616 | * Declared at top of function for legacy compiler comparability. 617 | */ 618 | Index_t row_itor = 0; 619 | Index_t col_itor = 0; 620 | 621 | /* 622 | * Verify that a valid operand object have been passed in. 623 | */ 624 | if (!_ulapack_is_valid_memory(matrix)) { 625 | return ulapack_uninit_obj; 626 | } 627 | 628 | /* 629 | * Check to ensure that the dimensions of the operands are identical. 630 | */ 631 | if (matrix->n_rows != matrix->n_cols) { 632 | return ulapack_invalid_argument; 633 | } 634 | 635 | /* 636 | * Set the diagonal elements to ones, and everything else to zeros. 637 | */ 638 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 639 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 640 | 641 | if (row_itor != col_itor){ 642 | matrix->entry[row_itor][col_itor] = 0; 643 | } else { 644 | matrix->entry[row_itor][col_itor] = 1; 645 | } 646 | 647 | } 648 | } 649 | 650 | return ulapack_success; 651 | } 652 | 653 | MatrixError_t ulapack_sum(const Matrix_t * const matrix, 654 | MatrixEntry_t * const result) { 655 | /* 656 | * Matrix iterators. 657 | * Declared at top of function for legacy compiler comparability. 658 | */ 659 | Index_t row_itor = 0; 660 | Index_t col_itor = 0; 661 | 662 | /* 663 | * Verify that a valid operand object have been passed in. 664 | */ 665 | if (!_ulapack_is_valid_memory(matrix)) { 666 | return ulapack_uninit_obj; 667 | } 668 | 669 | /* 670 | * The result pointer should not be NULL because it should be written to. 671 | */ 672 | if (!result) { 673 | return ulapack_invalid_argument; 674 | } 675 | 676 | /* 677 | * Reset result. 678 | */ 679 | *result = 0; 680 | 681 | /* 682 | * Add the elements of the matrix. 683 | */ 684 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 685 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 686 | *result += matrix->entry[row_itor][col_itor]; 687 | } 688 | } 689 | 690 | return ulapack_success; 691 | } 692 | 693 | MatrixError_t ulapack_add(const Matrix_t * const A, 694 | const Matrix_t * const B, 695 | Matrix_t * const result) { 696 | /* 697 | * Matrix iterators. 698 | * Declared at top of function for legacy compiler comparability. 699 | */ 700 | Index_t row_itor = 0; 701 | Index_t col_itor = 0; 702 | 703 | /* 704 | * Verify that valid operand objects have been passed in. 705 | */ 706 | if (!_ulapack_is_valid_memory(A) || !_ulapack_is_valid_memory(B)) { 707 | return ulapack_uninit_obj; 708 | } 709 | 710 | /* 711 | * Verify that the result matrix object has been initialized. 712 | */ 713 | if (!_ulapack_is_valid_memory(result)) { 714 | return ulapack_uninit_obj; 715 | } 716 | 717 | /* 718 | * Check to ensure that the dimensions of the operands are identical. 719 | */ 720 | if (A->n_rows != B->n_rows || A->n_cols != B->n_cols) { 721 | return ulapack_invalid_argument; 722 | } 723 | 724 | /* 725 | * The dimensions of the result pointer are changed to equal that of the 726 | * operands if static allocation is used. 727 | */ 728 | #ifdef ULAPACK_USE_STATIC_ALLOC 729 | result->n_rows = A->n_rows; 730 | result->n_cols = A->n_cols; 731 | 732 | /* 733 | * If dynamic memory allocation is specified, the dimensions of result 734 | * should match that of A and B. 735 | */ 736 | #else 737 | if (A->n_rows != result->n_rows || A->n_cols != result->n_cols) { 738 | return ulapack_invalid_argument; 739 | } 740 | #endif 741 | 742 | /* 743 | * Compute the sum of the operands: A + B, and store the result into the 744 | * result object. 745 | */ 746 | for (row_itor = 0; row_itor < A->n_rows; row_itor++) { 747 | for (col_itor = 0; col_itor < A->n_cols; col_itor++) { 748 | result->entry[row_itor][col_itor] = 749 | A->entry[row_itor][col_itor] + B->entry[row_itor][col_itor]; 750 | } 751 | } 752 | 753 | return ulapack_success; 754 | } 755 | 756 | MatrixError_t ulapack_subtract(const Matrix_t * const A, 757 | const Matrix_t * const B, 758 | Matrix_t * const result) { 759 | /* 760 | * Matrix iterators. 761 | * Declared at top of function for legacy compiler comparability. 762 | */ 763 | Index_t row_itor = 0; 764 | Index_t col_itor = 0; 765 | 766 | /* 767 | * Verify that valid operand objects have been passed in. 768 | */ 769 | if (!_ulapack_is_valid_memory(A) || !_ulapack_is_valid_memory(B)) { 770 | return ulapack_uninit_obj; 771 | } 772 | 773 | /* 774 | * Verify that the result matrix object has been initialized. 775 | */ 776 | if (!_ulapack_is_valid_memory(result)) { 777 | return ulapack_uninit_obj; 778 | } 779 | 780 | /* 781 | * Check to ensure that the dimensions of the operands are identical. 782 | */ 783 | if (A->n_rows != B->n_rows || A->n_cols != B->n_cols) { 784 | return ulapack_invalid_argument; 785 | } 786 | 787 | /* 788 | * The dimensions of the result pointer are changed to equal that of the 789 | * operands if static allocation is used. 790 | */ 791 | #ifdef ULAPACK_USE_STATIC_ALLOC 792 | result->n_rows = A->n_rows; 793 | result->n_cols = A->n_cols; 794 | 795 | /* 796 | * If dynamic memory allocation is specified, the dimensions of result 797 | * should match that of A and B. 798 | */ 799 | #else 800 | if (A->n_rows != result->n_rows || A->n_cols != result->n_cols) { 801 | return ulapack_invalid_argument; 802 | } 803 | #endif 804 | 805 | /* 806 | * Compute the difference of the operands: A - B, and store the result into 807 | * the result object. 808 | */ 809 | for (row_itor = 0; row_itor < A->n_rows; row_itor++) { 810 | for (col_itor = 0; col_itor < A->n_cols; col_itor++) { 811 | result->entry[row_itor][col_itor] = 812 | A->entry[row_itor][col_itor] - B->entry[row_itor][col_itor]; 813 | } 814 | } 815 | 816 | return ulapack_success; 817 | } 818 | 819 | MatrixError_t ulapack_scale(Matrix_t * const matrix, 820 | const MatrixEntry_t scalar, 821 | Matrix_t * const result) { 822 | /* 823 | * Matrix iterators. 824 | * Declared at top of function for legacy compiler comparability. 825 | */ 826 | Index_t row_itor = 0; 827 | Index_t col_itor = 0; 828 | 829 | /* 830 | * Verify valid operand objects have been passed in. 831 | */ 832 | if (!_ulapack_is_valid_memory(matrix)) { 833 | return ulapack_uninit_obj; 834 | } 835 | 836 | /* 837 | * Verify that the result matrix object has been initialized. 838 | */ 839 | if (!_ulapack_is_valid_memory(result)) { 840 | return ulapack_uninit_obj; 841 | } 842 | 843 | /* 844 | * The dimensions of the result pointer are changed to equal that of the 845 | * operands if static allocation is used. 846 | */ 847 | #ifdef ULAPACK_USE_STATIC_ALLOC 848 | result->n_rows = matrix->n_rows; 849 | result->n_cols = matrix->n_cols; 850 | 851 | /* 852 | * If dynamic memory allocation is specified, the dimensions of result 853 | * should match that of A and B. 854 | */ 855 | #else 856 | if (matrix->n_rows != result->n_rows || 857 | matrix->n_cols != result->n_cols) { 858 | return ulapack_invalid_argument; 859 | } 860 | #endif 861 | 862 | /* 863 | * Scale the elements of the matrix by the value specified. 864 | */ 865 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 866 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 867 | result->entry[row_itor][col_itor] = 868 | matrix->entry[row_itor][col_itor] * scalar; 869 | } 870 | } 871 | 872 | return ulapack_success; 873 | } 874 | 875 | MatrixError_t ulapack_norm(const Matrix_t * const matrix, 876 | MatrixEntry_t * const norm) { 877 | /* 878 | * Matrix iterators. 879 | * Declared at top of function for legacy compiler comparability. 880 | */ 881 | Index_t row_itor = 0; 882 | Index_t col_itor = 0; 883 | 884 | /* 885 | * Verify that valid operand objects have been passed in. 886 | */ 887 | if (!_ulapack_is_valid_memory(matrix)) { 888 | return ulapack_uninit_obj; 889 | } 890 | 891 | /* 892 | * The norm pointer should not be NULL because it should be written to. 893 | */ 894 | if (!norm) { 895 | return ulapack_invalid_argument; 896 | } 897 | 898 | /* 899 | * Reset norm variable. 900 | */ 901 | *norm = 0; 902 | 903 | /* 904 | * Scale the elements of the matrix by the value specified. 905 | */ 906 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 907 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 908 | *norm += matrix->entry[row_itor][col_itor] * 909 | matrix->entry[row_itor][col_itor]; 910 | } 911 | } 912 | 913 | *norm = sqrt(*norm); 914 | 915 | return ulapack_success; 916 | } 917 | 918 | MatrixError_t ulapack_trace(const Matrix_t * const matrix, 919 | MatrixEntry_t * const trace) { 920 | /* 921 | * Matrix iterators. 922 | * Declared at top of function for legacy compiler comparability. 923 | */ 924 | Index_t row_itor = 0; 925 | Index_t col_itor = 0; 926 | 927 | /* 928 | * Verify that valid operand objects have been passed in. 929 | */ 930 | if (!_ulapack_is_valid_memory(matrix)) { 931 | return ulapack_uninit_obj; 932 | } 933 | 934 | /* 935 | * The trace pointer should not be NULL because it should be written to. 936 | */ 937 | if (!trace) { 938 | return ulapack_invalid_argument; 939 | } 940 | 941 | /* 942 | * Reset trace variable. 943 | */ 944 | *trace = 0; 945 | 946 | /* 947 | * Scale the elements of the matrix by the value specified. 948 | */ 949 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 950 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 951 | *trace += matrix->entry[row_itor][col_itor]; 952 | } 953 | } 954 | 955 | return ulapack_success; 956 | } 957 | 958 | MatrixError_t ulapack_dot(const Matrix_t * const vector_a, 959 | const Matrix_t * const vector_b, 960 | MatrixEntry_t * const dot) { 961 | /* 962 | * Vector element iterator. 963 | * Declared at top of function for legacy compiler comparability. 964 | */ 965 | Index_t elem_itor = 0; 966 | 967 | /* 968 | * is_vector return value. 969 | */ 970 | MatrixError_t isvect = ulapack_error; 971 | 972 | /* 973 | * Verify that valid operand objects have been passed in. 974 | */ 975 | if (!_ulapack_is_valid_memory(vector_a) || 976 | !_ulapack_is_valid_memory(vector_b)) { 977 | return ulapack_uninit_obj; 978 | } 979 | 980 | /* 981 | * Check if input a is a vector. Only need to check one because the 982 | * dimensions must be equal in the next check. 983 | */ 984 | ulapack_is_vector(vector_a, &isvect); 985 | if (!isvect) { 986 | return ulapack_invalid_argument; 987 | } 988 | 989 | /* 990 | * Check that dimensions match. 991 | */ 992 | if ((vector_a->n_rows != vector_b->n_rows) || 993 | (vector_a->n_cols != vector_b->n_cols)) { 994 | return ulapack_invalid_argument; 995 | } 996 | 997 | /* 998 | * Reset the result variable. 999 | */ 1000 | *dot = 0; 1001 | 1002 | /* 1003 | * Check if row vector. 1004 | */ 1005 | if (vector_a->n_cols > vector_a->n_rows) { 1006 | /* 1007 | * Perform the dot product operation on the columns if a row vector. 1008 | */ 1009 | for (elem_itor = 0; elem_itor < vector_a->n_cols; elem_itor++) { 1010 | *dot += vector_a->entry[0][elem_itor] * 1011 | vector_b->entry[0][elem_itor]; 1012 | } 1013 | } else { 1014 | /* 1015 | * Assume the vector is a column vector, and operate on the rows. 1016 | */ 1017 | for (elem_itor = 0; elem_itor < vector_a->n_rows; elem_itor++) { 1018 | *dot += vector_a->entry[elem_itor][0] * 1019 | vector_b->entry[elem_itor][0]; 1020 | } 1021 | } 1022 | 1023 | return ulapack_success; 1024 | } 1025 | 1026 | MatrixError_t ulapack_product(const Matrix_t * const A, 1027 | const Matrix_t * const B, 1028 | Matrix_t * const result) { 1029 | /* 1030 | * Matrix iterators. 1031 | * Declared at top of function for legacy compiler comparability. 1032 | */ 1033 | Index_t row_itor = 0; 1034 | Index_t col_itor = 0; 1035 | Index_t swap_itor = 0; 1036 | 1037 | /* 1038 | * Verify that valid operand objects have been passed in. 1039 | */ 1040 | if (!_ulapack_is_valid_memory(A) || !_ulapack_is_valid_memory(B)) { 1041 | return ulapack_uninit_obj; 1042 | } 1043 | 1044 | /* 1045 | * The result pointer should not be NULL because it should be written to. 1046 | */ 1047 | if (!_ulapack_is_valid_memory(result)) { 1048 | return ulapack_invalid_argument; 1049 | } 1050 | 1051 | /* 1052 | * Check for legal dimensions. 1053 | */ 1054 | if (A->n_cols != B->n_rows) { 1055 | return ulapack_invalid_argument; 1056 | } 1057 | 1058 | /* 1059 | * The dimensions of the result pointer are changed to equal that of the 1060 | * operands if static allocation is used. 1061 | */ 1062 | #ifdef ULAPACK_USE_STATIC_ALLOC 1063 | result->n_rows = A->n_rows; 1064 | result->n_cols = B->n_cols; 1065 | 1066 | /* 1067 | * If dynamic memory allocation is specified, the dimensions of result 1068 | * should match that of the dimensions of the product of A and B. 1069 | */ 1070 | #else 1071 | if (A->n_rows != result->n_rows || 1072 | B->n_cols != result->n_cols) { 1073 | return ulapack_invalid_argument; 1074 | } 1075 | #endif 1076 | 1077 | /* 1078 | * Compute and store product in the result matrix. 1079 | */ 1080 | for(row_itor = 0; row_itor < A->n_rows; row_itor++) { 1081 | for(col_itor = 0; col_itor < B->n_cols; col_itor++) { 1082 | for(swap_itor = 0; swap_itor < A->n_cols; swap_itor++) { 1083 | result->entry[row_itor][col_itor] += 1084 | A->entry[row_itor][swap_itor] * B->entry[swap_itor][col_itor]; 1085 | } 1086 | } 1087 | } 1088 | 1089 | return ulapack_success; 1090 | } 1091 | 1092 | MatrixError_t ulapack_transpose(const Matrix_t * const matrix, 1093 | Matrix_t * const result) { 1094 | /* 1095 | * Matrix iterators. 1096 | * Declared at top of function for legacy compiler comparability. 1097 | */ 1098 | Index_t row_itor = 0; 1099 | Index_t col_itor = 0; 1100 | 1101 | /* 1102 | * Verify that a valid operand object have been passed in. 1103 | */ 1104 | if (!_ulapack_is_valid_memory(matrix)) { 1105 | return ulapack_uninit_obj; 1106 | } 1107 | 1108 | /* 1109 | * The result pointer should not be NULL because it should be written to. 1110 | */ 1111 | if (!_ulapack_is_valid_memory(result)) { 1112 | return ulapack_invalid_argument; 1113 | } 1114 | 1115 | /* 1116 | * The dimensions of the result pointer are changed to equal that of the 1117 | * transpose of the operands if static allocation is used. 1118 | */ 1119 | #ifdef ULAPACK_USE_STATIC_ALLOC 1120 | result->n_rows = matrix->n_cols; 1121 | result->n_cols = matrix->n_rows; 1122 | 1123 | /* 1124 | * If dynamic memory allocation is specified, the dimensions of result 1125 | * should match that of the dimensions of the transpose of the matrix. 1126 | */ 1127 | #else 1128 | if (matrix->n_rows != result->n_cols || 1129 | matrix->n_cols != result->n_rows) { 1130 | return ulapack_invalid_argument; 1131 | } 1132 | #endif 1133 | 1134 | /* 1135 | * Compute and store transpose in the result matrix. 1136 | */ 1137 | for(row_itor = 0; row_itor < result->n_rows; row_itor++) { 1138 | for(col_itor = 0; col_itor < result->n_cols; col_itor++) { 1139 | result->entry[row_itor][col_itor] = 1140 | matrix->entry[col_itor][row_itor]; 1141 | } 1142 | } 1143 | 1144 | return ulapack_success; 1145 | } 1146 | 1147 | MatrixError_t ulapack_det(const Matrix_t * const matrix, 1148 | MatrixEntry_t * const det) { 1149 | /* 1150 | * Matrix element iterator. 1151 | * Declared at top of function for legacy compiler comparability. 1152 | */ 1153 | Index_t elem_itor = 0; 1154 | 1155 | /* 1156 | * Make a copy of the input matrix to put it into upper triangular form. 1157 | */ 1158 | #ifdef ULAPACK_USE_STATIC_ALLOC 1159 | Matrix_t matrix_upper_tri; 1160 | Matrix_t matrix_lower_tri; 1161 | #endif 1162 | 1163 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1164 | Matrix_t *matrix_upper_tri = NULL; 1165 | Matrix_t *matrix_lower_tri = NULL; 1166 | #endif 1167 | 1168 | /* 1169 | * Verify that a valid operand object have been passed in. 1170 | */ 1171 | if (!_ulapack_is_valid_memory(matrix)) { 1172 | return ulapack_uninit_obj; 1173 | } 1174 | 1175 | /* 1176 | * Verify if the matrix is square. 1177 | */ 1178 | if (matrix->n_rows != matrix->n_cols) { 1179 | return ulapack_invalid_argument; 1180 | } 1181 | 1182 | /* 1183 | * Reset determinant result. 1184 | */ 1185 | *det = 0; 1186 | 1187 | /* 1188 | * Switch on the square dimension of the matrix. 1189 | */ 1190 | switch (matrix->n_rows) { 1191 | /* 1192 | * No matrix left. 1193 | */ 1194 | case 0: 1195 | break; 1196 | /* 1197 | * Scalar case. Determinant is itself. 1198 | */ 1199 | case 1: 1200 | *det = matrix->entry[0][0]; 1201 | break; 1202 | /* 1203 | * For the 2x2 case, just use the closed form solution for the matrix 1204 | * determinant. 1205 | */ 1206 | case 2: 1207 | *det = (matrix->entry[0][0] * matrix->entry[1][1]) - 1208 | (matrix->entry[1][0] * matrix->entry[0][1]); 1209 | break; 1210 | 1211 | case 3: 1212 | for(elem_itor = 0; elem_itor < 3; elem_itor++) { 1213 | *det += (matrix->entry[0][elem_itor] * 1214 | (matrix->entry[1][(elem_itor + 1) % 3] * 1215 | matrix->entry[2][(elem_itor + 2) % 3] - 1216 | matrix->entry[1][(elem_itor + 2) % 3] * 1217 | matrix->entry[2][(elem_itor + 1) % 3])); 1218 | } 1219 | break; 1220 | /* 1221 | * For all cases where the dimensions of the matrix are 4x4 or more. 1222 | */ 1223 | default: 1224 | *det = 1; 1225 | 1226 | /* 1227 | * Put the new matrix into upper triangular form. 1228 | */ 1229 | #ifdef ULAPACK_USE_STATIC_ALLOC 1230 | /* 1231 | * No need to compile in this code since the matrices won't 1232 | * exceed the closed form switch cases. 1233 | */ 1234 | #if ULAPACK_MAX_MATRIX_N_ROWS <= 3 1235 | return ulapack_invalid_argument; 1236 | #else 1237 | 1238 | ulapack_init(&matrix_upper_tri, matrix->n_rows, matrix->n_cols); 1239 | ulapack_init(&matrix_lower_tri, matrix->n_rows, matrix->n_cols); 1240 | 1241 | if (ulapack_lu(matrix, &matrix_upper_tri, &matrix_lower_tri) != 1242 | ulapack_success) { 1243 | return ulapack_error; 1244 | } 1245 | 1246 | for (elem_itor = 0; 1247 | elem_itor < matrix_upper_tri.n_rows && 1248 | elem_itor < matrix_upper_tri.n_cols; 1249 | elem_itor++) { 1250 | 1251 | *det *= 1252 | isnormal(matrix_upper_tri.entry[elem_itor][elem_itor]) ? 1253 | matrix_upper_tri.entry[elem_itor][elem_itor] : 0; 1254 | } 1255 | #endif 1256 | #endif 1257 | 1258 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1259 | if (ulapack_init(&matrix_upper_tri, matrix->n_rows, 1260 | matrix->n_cols) != 1261 | ulapack_success) { 1262 | 1263 | return ulapack_error; 1264 | } 1265 | 1266 | if (ulapack_init(&matrix_lower_tri, 1267 | matrix->n_rows, matrix->n_cols) != ulapack_success) { 1268 | return ulapack_error; 1269 | } 1270 | 1271 | if (ulapack_lu(matrix, matrix_upper_tri, matrix_lower_tri) != 1272 | ulapack_success) { 1273 | ulapack_destroy(matrix_upper_tri); 1274 | ulapack_destroy(matrix_lower_tri); 1275 | 1276 | return ulapack_error; 1277 | } 1278 | 1279 | for (elem_itor = 0; 1280 | elem_itor < matrix_upper_tri->n_rows && 1281 | elem_itor < matrix_upper_tri->n_cols; 1282 | elem_itor++) { 1283 | 1284 | *det *= 1285 | isnormal(matrix_upper_tri->entry[elem_itor][elem_itor]) ? 1286 | matrix_upper_tri->entry[elem_itor][elem_itor] : 0; 1287 | } 1288 | 1289 | /* 1290 | * Free the copy matrix memory. 1291 | */ 1292 | ulapack_destroy(matrix_upper_tri); 1293 | ulapack_destroy(matrix_lower_tri); 1294 | 1295 | #endif 1296 | 1297 | break; 1298 | } 1299 | 1300 | return ulapack_success; 1301 | 1302 | } 1303 | 1304 | MatrixError_t ulapack_lu(const Matrix_t * const matrix, 1305 | Matrix_t * const upper_matrix, 1306 | Matrix_t * const lower_matrix) { 1307 | 1308 | /* 1309 | * Matrix iterators. 1310 | * Declared at top of function for legacy compiler comparability. 1311 | */ 1312 | Index_t row_itor = 0; 1313 | Index_t col_itor = 0; 1314 | 1315 | Index_t sub_row = 0; 1316 | Index_t sub_col = 0; 1317 | 1318 | /* 1319 | * A copy of the input matrix. 1320 | */ 1321 | #ifdef ULAPACK_USE_STATIC_ALLOC 1322 | Matrix_t matrix_copy_mem; 1323 | Matrix_t *matrix_copy = &matrix_copy_mem; 1324 | #endif 1325 | 1326 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1327 | Matrix_t *matrix_copy = NULL; 1328 | #endif 1329 | 1330 | /* 1331 | * Verify that a valid operand object have been passed in. 1332 | */ 1333 | if (!_ulapack_is_valid_memory(matrix)) { 1334 | return ulapack_uninit_obj; 1335 | } 1336 | 1337 | /* 1338 | * The result pointer should not be NULL because it should be written to. 1339 | */ 1340 | if (!_ulapack_is_valid_memory(upper_matrix)) { 1341 | return ulapack_invalid_argument; 1342 | } 1343 | if (!_ulapack_is_valid_memory(lower_matrix)) { 1344 | return ulapack_invalid_argument; 1345 | } 1346 | /* 1347 | * The dimensions of the result pointer are changed to equal that of the 1348 | * transpose of the operands if static allocation is used. 1349 | */ 1350 | #ifdef ULAPACK_USE_STATIC_ALLOC 1351 | upper_matrix->n_rows = matrix->n_rows; 1352 | upper_matrix->n_cols = matrix->n_cols; 1353 | 1354 | lower_matrix->n_rows = matrix->n_rows; 1355 | lower_matrix->n_cols = matrix->n_cols; 1356 | #endif 1357 | /* 1358 | * If dynamic memory allocation is specified, the dimensions of result 1359 | * should match that of the dimensions of the transpose of the matrix. 1360 | */ 1361 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1362 | if (matrix->n_rows != upper_matrix->n_rows || 1363 | matrix->n_cols != upper_matrix->n_cols || 1364 | matrix->n_rows != lower_matrix->n_rows || 1365 | matrix->n_cols != lower_matrix->n_cols) { 1366 | return ulapack_invalid_argument; 1367 | } 1368 | #endif 1369 | 1370 | #ifdef ULAPACK_USE_STATIC_ALLOC 1371 | ulapack_init(matrix_copy, matrix->n_rows, matrix->n_cols); 1372 | #endif 1373 | 1374 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1375 | ulapack_init(&matrix_copy, matrix->n_rows, matrix->n_cols); 1376 | #endif 1377 | 1378 | ulapack_copy(matrix, matrix_copy); 1379 | 1380 | /* 1381 | * Using Cormen's algorithm from Introduction to Algorithms. 1382 | */ 1383 | 1384 | /* 1385 | * Initialize memory to zero. 1386 | */ 1387 | ulapack_set(upper_matrix, 0); 1388 | ulapack_set(lower_matrix, 0); 1389 | 1390 | /* 1391 | * Initialize lu components. 1392 | */ 1393 | for (row_itor = 0; row_itor < upper_matrix->n_rows; row_itor++) { 1394 | for(col_itor = 0; col_itor < upper_matrix->n_cols; col_itor++) { 1395 | if (row_itor > col_itor) { 1396 | upper_matrix->entry[row_itor][col_itor] = 0; 1397 | } else if (row_itor < col_itor) { 1398 | lower_matrix->entry[row_itor][col_itor] = 0; 1399 | } else if (row_itor == col_itor) { 1400 | lower_matrix->entry[row_itor][col_itor] = 1; 1401 | } 1402 | } 1403 | } 1404 | 1405 | /* 1406 | * Decompose. 1407 | */ 1408 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 1409 | upper_matrix->entry[row_itor][row_itor] = 1410 | matrix_copy->entry[row_itor][row_itor]; 1411 | 1412 | for (col_itor = row_itor + 1; col_itor < matrix->n_cols; col_itor++) { 1413 | upper_matrix->entry[row_itor][col_itor] = 1414 | matrix_copy->entry[row_itor][col_itor]; 1415 | 1416 | lower_matrix->entry[col_itor][row_itor] = 1417 | matrix_copy->entry[col_itor][row_itor] / 1418 | upper_matrix->entry[row_itor][row_itor]; 1419 | } 1420 | for(sub_row = row_itor + 1; sub_row < matrix->n_rows; sub_row++){ 1421 | for(sub_col = row_itor + 1; sub_col < matrix->n_cols; sub_col++) { 1422 | matrix_copy->entry[sub_row][sub_col] = 1423 | matrix_copy->entry[sub_row][sub_col] - 1424 | (lower_matrix->entry[sub_row][row_itor] * 1425 | upper_matrix->entry[row_itor][sub_col]); 1426 | } 1427 | } 1428 | } 1429 | 1430 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1431 | ulapack_destroy(matrix_copy); 1432 | #endif 1433 | 1434 | return ulapack_success; 1435 | } 1436 | 1437 | /** 1438 | * @name _ulapack_swap_rows 1439 | * A private function to swap two rows in a matrix. 1440 | * 1441 | * @note The input matrix must be square. 1442 | * 1443 | * @param[in/out] matrix The matrix to manipulate. 1444 | * @param row_a One of the operand rows. 1445 | * @param row_b The other operand row. 1446 | */ 1447 | #ifndef ULAPACK_INVERSE_VIA_LU 1448 | static void _ulapack_swap_rows(Matrix_t * const matrix, 1449 | const Index_t row_a, 1450 | const Index_t row_b) { 1451 | MatrixEntry_t swap_element = 0; 1452 | 1453 | Index_t elem_itor = 0; 1454 | 1455 | for (elem_itor = 0; elem_itor < matrix->n_rows; elem_itor++) { 1456 | swap_element = matrix->entry[row_a][elem_itor]; 1457 | matrix->entry[row_a][elem_itor] = matrix->entry[row_b][elem_itor]; 1458 | matrix->entry[row_b][elem_itor] = swap_element; 1459 | } 1460 | } 1461 | #endif 1462 | 1463 | /** 1464 | * @name ulapack_backsub 1465 | * Perform a backwards substitution between two matrices. 1466 | * 1467 | * @note Both matrices must be square and equal in size. 1468 | * 1469 | * @param[in/out] matrix_a The first matrix. 1470 | * @param[in/out] matrix_b The second matrix. 1471 | */ 1472 | static void _ulapack_backsub(Matrix_t * const matrix_a, 1473 | Matrix_t * const matrix_b) { 1474 | Index_t row_itor = 0; 1475 | Index_t col_itor = 0; 1476 | Index_t sub_itor = 0; 1477 | 1478 | MatrixEntry_t pivot = 0; 1479 | MatrixEntry_t ratio = 0; 1480 | 1481 | for(int row_itor = matrix_a->n_rows - 1; 1482 | (SIndex_t)row_itor >= 0; 1483 | row_itor--) { 1484 | 1485 | pivot = matrix_a->entry[row_itor][row_itor]; 1486 | for (col_itor = row_itor - 1; (SIndex_t)col_itor >= 0; col_itor--) { 1487 | ratio = -1 * matrix_a->entry[col_itor][row_itor] / pivot; 1488 | 1489 | for(sub_itor = 0; sub_itor < matrix_a->n_rows; sub_itor++) { 1490 | 1491 | matrix_a->entry[col_itor][sub_itor] += ratio * 1492 | matrix_a->entry[row_itor][sub_itor]; 1493 | 1494 | matrix_b->entry[col_itor][sub_itor] += ratio * 1495 | matrix_b->entry[row_itor][sub_itor]; 1496 | } 1497 | } 1498 | } 1499 | 1500 | for (row_itor = 0; row_itor < matrix_a->n_rows; row_itor++) { 1501 | 1502 | for (col_itor = 0; col_itor < matrix_a->n_rows; col_itor++) { 1503 | matrix_b->entry[row_itor][col_itor] /= 1504 | matrix_a->entry[row_itor][row_itor]; 1505 | } 1506 | 1507 | matrix_a->entry[row_itor][row_itor] = 1; 1508 | } 1509 | } 1510 | 1511 | /** 1512 | * @name ulapack_forwardsub 1513 | * Perform a forward substitution between two matrices. 1514 | * 1515 | * @note Both matrices must be square and equal in size. 1516 | * 1517 | * @param[in/out] matrix_a The first matrix (must be upper triangular). 1518 | * @param[in/out] matrix_b The second matrix. 1519 | */ 1520 | #ifdef ULAPACK_INVERSE_VIA_LU 1521 | static void _ulapack_forwardsub(Matrix_t * const matrix_a, 1522 | Matrix_t * const matrix_b) { 1523 | Index_t row_itor = 0; 1524 | Index_t col_itor = 0; 1525 | Index_t sub_itor = 0; 1526 | 1527 | MatrixEntry_t pivot = 0; 1528 | MatrixEntry_t ratio = 0; 1529 | 1530 | for (row_itor = 0; row_itor < matrix_a->n_rows; row_itor++) { 1531 | pivot = matrix_a->entry[row_itor][row_itor]; 1532 | 1533 | for (col_itor = row_itor + 1; col_itor < matrix_a->n_rows; col_itor++) { 1534 | ratio = -1 * matrix_a->entry[col_itor][row_itor]/pivot; 1535 | 1536 | for (sub_itor = 0; sub_itor < matrix_a->n_rows; sub_itor++) { 1537 | matrix_a->entry[col_itor][sub_itor] += 1538 | ratio * matrix_a->entry[row_itor][sub_itor]; 1539 | 1540 | matrix_b->entry[col_itor][sub_itor] += 1541 | ratio * matrix_b->entry[row_itor][sub_itor]; 1542 | } 1543 | } 1544 | } 1545 | } 1546 | #endif 1547 | 1548 | MatrixError_t ulapack_inverse(const Matrix_t * const matrix, 1549 | Matrix_t * const inverse) { 1550 | 1551 | /* 1552 | * Matrix iterators. 1553 | * Declared at top of function for legacy compiler comparability. 1554 | */ 1555 | #ifndef ULAPACK_INVERSE_VIA_LU 1556 | Index_t row_itor = 0; 1557 | Index_t col_itor = 0; 1558 | 1559 | Index_t pivot_index = 0; 1560 | 1561 | MatrixEntry_t pivot_element = 0; 1562 | MatrixEntry_t ratio = 0; 1563 | #endif 1564 | 1565 | /* 1566 | * A copy of the input matrix. 1567 | */ 1568 | #ifdef ULAPACK_USE_STATIC_ALLOC 1569 | Matrix_t matrix_copy_mem; 1570 | Matrix_t *matrix_copy = &matrix_copy_mem; 1571 | 1572 | #ifdef ULAPACK_INVERSE_VIA_LU 1573 | Matrix_t upper_matrix_mem; 1574 | Matrix_t lower_matrix_mem; 1575 | 1576 | Matrix_t *upper_matrix = &upper_matrix_mem; 1577 | Matrix_t *lower_matrix = &lower_matrix_mem; 1578 | #endif 1579 | #endif 1580 | 1581 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1582 | Matrix_t *matrix_copy = NULL; 1583 | 1584 | #ifdef ULAPACK_INVERSE_VIA_LU 1585 | Matrix_t *upper_matrix = NULL; 1586 | Matrix_t *lower_matrix = NULL; 1587 | #endif 1588 | #endif 1589 | 1590 | /* 1591 | * Verify that a valid operand object have been passed in. 1592 | */ 1593 | if (!_ulapack_is_valid_memory(matrix)) { 1594 | return ulapack_uninit_obj; 1595 | } 1596 | 1597 | /* 1598 | * The result pointer should not be NULL because it should be written to. 1599 | */ 1600 | if (!_ulapack_is_valid_memory(inverse)) { 1601 | return ulapack_uninit_obj; 1602 | } 1603 | 1604 | /* 1605 | * The dimensions of the result pointer are changed to equal that of the 1606 | * transpose of the operands if static allocation is used. 1607 | */ 1608 | #ifdef ULAPACK_USE_STATIC_ALLOC 1609 | inverse->n_rows = matrix->n_rows; 1610 | inverse->n_cols = matrix->n_cols; 1611 | #endif 1612 | 1613 | /* 1614 | * If dynamic memory allocation is specified, the dimensions of result 1615 | * should match that of the dimensions of the transpose of the matrix. 1616 | */ 1617 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1618 | if (matrix->n_rows != inverse->n_rows || 1619 | matrix->n_cols != inverse->n_cols) { 1620 | return ulapack_invalid_argument; 1621 | } 1622 | #endif 1623 | 1624 | #ifdef ULAPACK_USE_STATIC_ALLOC 1625 | ulapack_init(matrix_copy, matrix->n_rows, matrix->n_cols); 1626 | 1627 | #ifdef ULAPACK_INVERSE_VIA_LU 1628 | ulapack_init(upper_matrix, matrix->n_rows, matrix->n_cols); 1629 | ulapack_init(lower_matrix, matrix->n_rows, matrix->n_cols); 1630 | #endif 1631 | #endif 1632 | 1633 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1634 | ulapack_init(&matrix_copy, matrix->n_rows, matrix->n_cols); 1635 | 1636 | #ifdef ULAPACK_INVERSE_VIA_LU 1637 | ulapack_init(&upper_matrix, matrix->n_rows, matrix->n_cols); 1638 | ulapack_init(&lower_matrix, matrix->n_rows, matrix->n_cols); 1639 | #endif 1640 | #endif 1641 | 1642 | ulapack_copy(matrix, matrix_copy); 1643 | 1644 | /* 1645 | * Start by setting the inverse result to the identity matrix. 1646 | */ 1647 | ulapack_eye(inverse); 1648 | 1649 | #ifdef ULAPACK_INVERSE_VIA_LU 1650 | 1651 | ulapack_lu(matrix, upper_matrix, lower_matrix); 1652 | _ulapack_forwardsub(lower_matrix, inverse); 1653 | _ulapack_backsub(upper_matrix, inverse); 1654 | 1655 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1656 | ulapack_destroy(upper_matrix); 1657 | ulapack_destroy(lower_matrix); 1658 | #endif 1659 | 1660 | /* 1661 | * Method not using LU decomposition. 1662 | */ 1663 | #else 1664 | for (row_itor = 0; row_itor < matrix_copy->n_rows; row_itor++) { 1665 | 1666 | pivot_index = row_itor; 1667 | while (pivot_element < MINIMUM_THRESHOLD_TOLERANCE && 1668 | pivot_index < matrix_copy->n_rows) { 1669 | 1670 | pivot_element = matrix_copy->entry[pivot_index][row_itor]; 1671 | pivot_index++; 1672 | } 1673 | 1674 | if (pivot_element < MINIMUM_THRESHOLD_TOLERANCE) { 1675 | /* 1676 | * The matrix is singular. 1677 | */ 1678 | return ulapack_error; 1679 | } 1680 | 1681 | if (row_itor != pivot_index - 1) { 1682 | _ulapack_swap_rows(matrix_copy, row_itor, pivot_index); 1683 | _ulapack_swap_rows(inverse, row_itor, pivot_index); 1684 | } 1685 | 1686 | for (col_itor = row_itor + 1; 1687 | col_itor < matrix_copy->n_rows; 1688 | col_itor++) { 1689 | 1690 | ratio = -1 * matrix_copy->entry[col_itor][row_itor] / 1691 | pivot_element; 1692 | 1693 | for(pivot_index = 0; 1694 | pivot_index < matrix_copy->n_rows; 1695 | pivot_index++) { 1696 | 1697 | matrix_copy->entry[col_itor][pivot_index] += 1698 | ratio * matrix_copy->entry[row_itor][pivot_index]; 1699 | 1700 | inverse->entry[col_itor][pivot_index] += 1701 | ratio * inverse->entry[row_itor][pivot_index]; 1702 | } 1703 | } 1704 | } 1705 | 1706 | _ulapack_backsub(matrix_copy, inverse); 1707 | 1708 | #endif // ULAPACK_INVERSE_VIA_LU 1709 | 1710 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1711 | ulapack_destroy(matrix_copy); 1712 | #endif 1713 | 1714 | return ulapack_success; 1715 | } 1716 | 1717 | MatrixError_t ulapack_pinverse(const Matrix_t * const matrix, 1718 | Matrix_t * const pinverse) { 1719 | MatrixError_t ret_code; 1720 | 1721 | #ifdef ULAPACK_USE_STATIC_ALLOC 1722 | Matrix_t matrix_transpose_mem; 1723 | Matrix_t matrix_transpose_term_product_mem; 1724 | Matrix_t matrix_transpose_term_inverse_mem; 1725 | 1726 | Matrix_t *matrix_transpose = &matrix_transpose_mem; 1727 | 1728 | Matrix_t *matrix_transpose_term_product = 1729 | &matrix_transpose_term_product_mem; 1730 | 1731 | Matrix_t *matrix_transpose_term_inverse = 1732 | &matrix_transpose_term_inverse_mem; 1733 | #endif 1734 | 1735 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1736 | Matrix_t *matrix_transpose = NULL; 1737 | Matrix_t *matrix_transpose_term_product = NULL; 1738 | Matrix_t *matrix_transpose_term_inverse = NULL; 1739 | #endif 1740 | 1741 | /* 1742 | * Verify that a valid operand object have been passed in. 1743 | */ 1744 | if (!_ulapack_is_valid_memory(matrix)) { 1745 | return ulapack_uninit_obj; 1746 | } 1747 | 1748 | /* 1749 | * The result pointer should not be NULL because it should be written to. 1750 | */ 1751 | if (!_ulapack_is_valid_memory(pinverse)) { 1752 | return ulapack_uninit_obj; 1753 | } 1754 | 1755 | /* 1756 | * The dimensions of the result pointer are changed to equal that of the 1757 | * pseudo inverse of the operands if static allocation is used. 1758 | */ 1759 | #ifdef ULAPACK_USE_STATIC_ALLOC 1760 | pinverse->n_rows = matrix->n_cols; 1761 | pinverse->n_cols = matrix->n_rows; 1762 | #endif 1763 | 1764 | /* 1765 | * If dynamic memory allocation is specified, the dimensions of result 1766 | * should match that of the dimensions of the pseudo inverse of the matrix. 1767 | */ 1768 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1769 | if (matrix->n_rows != pinverse->n_cols || 1770 | matrix->n_cols != pinverse->n_rows) { 1771 | return ulapack_invalid_argument; 1772 | } 1773 | #endif 1774 | 1775 | #ifdef ULAPACK_USE_STATIC_ALLOC 1776 | /* 1777 | * Memory for matrix^T. 1778 | */ 1779 | if (ulapack_init(matrix_transpose, 1780 | matrix->n_cols, matrix->n_rows) != ulapack_success) { 1781 | return ulapack_error; 1782 | } 1783 | 1784 | /* 1785 | * Memory for (matrix^T * matrix). 1786 | */ 1787 | if (ulapack_init(matrix_transpose_term_product, 1788 | matrix->n_cols, matrix->n_cols) != ulapack_success) { 1789 | return ulapack_error; 1790 | } 1791 | 1792 | /* 1793 | * Memory for (matrix^T * matrix)^-1. 1794 | */ 1795 | if (ulapack_init(matrix_transpose_term_inverse, 1796 | matrix->n_cols, matrix->n_cols) != ulapack_success) { 1797 | return ulapack_error; 1798 | } 1799 | #endif 1800 | 1801 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1802 | /* 1803 | * Memory for matrix^T. 1804 | */ 1805 | ret_code = ulapack_init(&matrix_transpose, 1806 | matrix->n_cols, matrix->n_rows); 1807 | 1808 | if (ret_code != ulapack_success) { 1809 | return ret_code; 1810 | } 1811 | 1812 | /* 1813 | * Memory for (matrix^T * matrix). 1814 | */ 1815 | ret_code = ulapack_init(&matrix_transpose_term_product, 1816 | matrix->n_cols, matrix->n_cols); 1817 | 1818 | if (ret_code != ulapack_success) { 1819 | ulapack_destroy(matrix_transpose); 1820 | return ret_code; 1821 | } 1822 | 1823 | /* 1824 | * Memory for (matrix^T * matrix)^-1. 1825 | */ 1826 | ret_code = ulapack_init(&matrix_transpose_term_inverse, 1827 | matrix->n_cols, matrix->n_cols); 1828 | 1829 | if (ret_code != ulapack_success) { 1830 | ulapack_destroy(matrix_transpose); 1831 | ulapack_destroy(matrix_transpose_term_product); 1832 | return ret_code; 1833 | } 1834 | #endif 1835 | 1836 | ulapack_set(pinverse, 0); 1837 | ulapack_set(matrix_transpose, 0); 1838 | ulapack_set(matrix_transpose_term_product, 0); 1839 | ulapack_set(matrix_transpose_term_inverse, 0); 1840 | 1841 | /* 1842 | * A^T. 1843 | */ 1844 | ret_code = ulapack_transpose(matrix, matrix_transpose); 1845 | 1846 | if (ret_code != ulapack_success) { 1847 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1848 | ulapack_destroy(matrix_transpose); 1849 | ulapack_destroy(matrix_transpose_term_product); 1850 | ulapack_destroy(matrix_transpose_term_inverse); 1851 | #endif 1852 | 1853 | return ret_code; 1854 | } 1855 | 1856 | /* 1857 | * (A^T * A). 1858 | */ 1859 | ret_code = ulapack_product(matrix_transpose, matrix, 1860 | matrix_transpose_term_product); 1861 | 1862 | if (ret_code != ulapack_success) { 1863 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1864 | ulapack_destroy(matrix_transpose); 1865 | ulapack_destroy(matrix_transpose_term_product); 1866 | ulapack_destroy(matrix_transpose_term_inverse); 1867 | #endif 1868 | 1869 | return ret_code; 1870 | } 1871 | 1872 | /* 1873 | * (A^T * A)^-1. 1874 | */ 1875 | ret_code = ulapack_inverse(matrix_transpose_term_product, 1876 | matrix_transpose_term_inverse); 1877 | 1878 | if (ret_code != ulapack_success) { 1879 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1880 | ulapack_destroy(matrix_transpose); 1881 | ulapack_destroy(matrix_transpose_term_product); 1882 | ulapack_destroy(matrix_transpose_term_inverse); 1883 | #endif 1884 | 1885 | return ret_code; 1886 | } 1887 | 1888 | /* 1889 | * A^* = (A^T * A)^-1 * A^T. 1890 | */ 1891 | ret_code = ulapack_product(matrix_transpose_term_inverse, matrix_transpose, 1892 | pinverse); 1893 | 1894 | if (ret_code != ulapack_success) { 1895 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1896 | ulapack_destroy(matrix_transpose); 1897 | ulapack_destroy(matrix_transpose_term_product); 1898 | ulapack_destroy(matrix_transpose_term_inverse); 1899 | #endif 1900 | 1901 | return ret_code; 1902 | } 1903 | 1904 | 1905 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1906 | ulapack_destroy(matrix_transpose); 1907 | ulapack_destroy(matrix_transpose_term_product); 1908 | ulapack_destroy(matrix_transpose_term_inverse); 1909 | #endif 1910 | 1911 | return ulapack_success; 1912 | } 1913 | 1914 | MatrixError_t ulapack_least_squares(const Matrix_t * const A, 1915 | const Matrix_t * const y, 1916 | Matrix_t * const x) { 1917 | 1918 | MatrixError_t ret_code; 1919 | 1920 | #ifdef ULAPACK_USE_STATIC_ALLOC 1921 | Matrix_t pinv_mem; 1922 | Matrix_t *pinv = &pinv_mem; 1923 | #endif 1924 | 1925 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1926 | Matrix_t *pinv = NULL; 1927 | #endif 1928 | 1929 | /* 1930 | * Verify that a valid operand object have been passed in. 1931 | */ 1932 | if (!_ulapack_is_valid_memory(A) || !_ulapack_is_valid_memory(y)) { 1933 | return ulapack_uninit_obj; 1934 | } 1935 | 1936 | /* 1937 | * The result pointer should not be NULL because it should be written to. 1938 | */ 1939 | if (!_ulapack_is_valid_memory(x)) { 1940 | return ulapack_uninit_obj; 1941 | } 1942 | 1943 | /* 1944 | * The dimensions of the result pointer are changed to equal that of the 1945 | * pseudo inverse of the operands if static allocation is used. 1946 | */ 1947 | #ifdef ULAPACK_USE_STATIC_ALLOC 1948 | x->n_rows = A->n_cols; 1949 | x->n_cols = 1; 1950 | #endif 1951 | 1952 | /* 1953 | * If dynamic memory allocation is specified, the dimensions of result 1954 | * should match that of the expected dimensions. 1955 | */ 1956 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1957 | if (x->n_rows != A->n_cols || 1958 | x->n_cols != 1) { 1959 | return ulapack_invalid_argument; 1960 | } 1961 | #endif 1962 | 1963 | #ifdef ULAPACK_USE_STATIC_ALLOC 1964 | /* 1965 | * Memory for A^*. 1966 | */ 1967 | if (ulapack_init(pinv, A->n_cols, A->n_rows) != ulapack_success) { 1968 | return ulapack_error; 1969 | } 1970 | #endif 1971 | 1972 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1973 | /* 1974 | * Memory for A^*. 1975 | */ 1976 | ret_code = ulapack_init(&pinv, A->n_cols, A->n_rows); 1977 | 1978 | if (ret_code != ulapack_success) { 1979 | return ret_code; 1980 | } 1981 | #endif 1982 | 1983 | /* 1984 | * A^*. 1985 | */ 1986 | ret_code = ulapack_pinverse(A, pinv); 1987 | 1988 | if (ret_code != ulapack_success) { 1989 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 1990 | ulapack_destroy(pinv); 1991 | #endif 1992 | 1993 | return ret_code; 1994 | } 1995 | 1996 | /* 1997 | * x ~= (A^* * y). 1998 | */ 1999 | ret_code = ulapack_product(pinv, y, x); 2000 | if (ret_code != ulapack_success) { 2001 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2002 | ulapack_destroy(pinv); 2003 | #endif 2004 | 2005 | return ret_code; 2006 | } 2007 | 2008 | 2009 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2010 | ulapack_destroy(pinv); 2011 | #endif 2012 | 2013 | return ulapack_success; 2014 | } 2015 | 2016 | MatrixError_t ulapack_vandermonde(const Matrix_t * const x, 2017 | const Index_t order, 2018 | Matrix_t * const V) { 2019 | Index_t col_itor = 0; 2020 | Index_t v_itor = 0; 2021 | 2022 | /* 2023 | * Verify that a valid operand object have been passed in. 2024 | */ 2025 | if (!_ulapack_is_valid_memory(x)) { 2026 | return ulapack_uninit_obj; 2027 | } 2028 | 2029 | /* 2030 | * The result pointer should not be NULL because it should be written to. 2031 | */ 2032 | if (!_ulapack_is_valid_memory(V)) { 2033 | return ulapack_uninit_obj; 2034 | } 2035 | 2036 | if (order > x->n_rows) { 2037 | return ulapack_invalid_argument; 2038 | } 2039 | 2040 | /* 2041 | * The dimensions of the result pointer are changed to equal that of the 2042 | * Vandermonde of the operands if static allocation is used. 2043 | */ 2044 | #ifdef ULAPACK_USE_STATIC_ALLOC 2045 | V->n_rows = x->n_rows; 2046 | V->n_cols = order; 2047 | #endif 2048 | 2049 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2050 | if (V->n_rows != x->n_rows || 2051 | V->n_cols != order) { 2052 | return ulapack_invalid_argument; 2053 | } 2054 | #endif 2055 | 2056 | for (v_itor = 0; v_itor < x->n_rows; v_itor++) { 2057 | for (col_itor = 0; col_itor < order; col_itor++) { 2058 | V->entry[v_itor][order - 1 - col_itor] = 2059 | _ulapack_elem_pow(x->entry[v_itor][0], col_itor); 2060 | } 2061 | } 2062 | 2063 | return ulapack_success; 2064 | } 2065 | 2066 | MatrixError_t ulapack_power(const Matrix_t * const matrix, 2067 | const Index_t power, 2068 | Matrix_t * const result) { 2069 | /* 2070 | * Matrix iterators. 2071 | * Declared at top of function for legacy compiler comparability. 2072 | */ 2073 | Index_t row_itor = 0; 2074 | Index_t col_itor = 0; 2075 | 2076 | /* 2077 | * Verify that a valid operand object have been passed in. 2078 | */ 2079 | if (!_ulapack_is_valid_memory(matrix)) { 2080 | return ulapack_uninit_obj; 2081 | } 2082 | 2083 | /* 2084 | * The result pointer should not be NULL because it should be written to. 2085 | */ 2086 | if (!_ulapack_is_valid_memory(result)) { 2087 | return ulapack_invalid_argument; 2088 | } 2089 | 2090 | #ifdef ULAPACK_USE_STATIC_ALLOC 2091 | result->n_rows = matrix->n_rows; 2092 | result->n_cols = matrix->n_cols; 2093 | #endif 2094 | 2095 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2096 | if (result->n_rows != matrix->n_rows || 2097 | result->n_cols != matrix->n_cols) { 2098 | return ulapack_invalid_argument; 2099 | } 2100 | #endif 2101 | 2102 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 2103 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 2104 | result->entry[row_itor][col_itor] = 2105 | _ulapack_elem_pow(matrix->entry[row_itor][col_itor], power); 2106 | } 2107 | } 2108 | 2109 | return ulapack_success; 2110 | } 2111 | 2112 | MatrixError_t ulapack_polyfit(const Matrix_t * const x, 2113 | const Matrix_t * const y, 2114 | const Index_t n, 2115 | Matrix_t * const p) { 2116 | MatrixError_t ret_code; 2117 | 2118 | #ifdef ULAPACK_USE_STATIC_ALLOC 2119 | Matrix_t V_mem; 2120 | 2121 | Matrix_t *V = &V_mem; 2122 | #endif 2123 | 2124 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2125 | Matrix_t *V = NULL; 2126 | #endif 2127 | 2128 | /* 2129 | * Verify that a valid operand object have been passed in. 2130 | */ 2131 | if (!_ulapack_is_valid_memory(x) || !_ulapack_is_valid_memory(y)) { 2132 | return ulapack_uninit_obj; 2133 | } 2134 | 2135 | /* 2136 | * The result pointer should not be NULL because it should be written to. 2137 | */ 2138 | if (!_ulapack_is_valid_memory(p)) { 2139 | return ulapack_uninit_obj; 2140 | } 2141 | 2142 | /* 2143 | * x and y must have equal dimensions. 2144 | */ 2145 | if (x->n_rows != y->n_rows || x->n_cols != y->n_cols) { 2146 | return ulapack_invalid_argument; 2147 | } 2148 | 2149 | #ifdef ULAPACK_USE_STATIC_ALLOC 2150 | p->n_rows = n + 1; 2151 | p->n_cols = 1; 2152 | #endif 2153 | 2154 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2155 | if (p->n_rows != n + 1 || p->n_cols != 1) { 2156 | return ulapack_invalid_argument; 2157 | } 2158 | #endif 2159 | 2160 | #ifdef ULAPACK_USE_STATIC_ALLOC 2161 | ret_code = ulapack_init(V, x->n_rows, n + 1); 2162 | if (ret_code != ulapack_success) { 2163 | return ret_code; 2164 | } 2165 | #endif 2166 | 2167 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2168 | ret_code = ulapack_init(&V, x->n_rows, n + 1); 2169 | if (ret_code != ulapack_success) { 2170 | return ret_code; 2171 | } 2172 | 2173 | #endif 2174 | 2175 | /* 2176 | * Compute the Vandermonde matrix. 2177 | */ 2178 | ret_code = ulapack_vandermonde(x, n + 1, V); 2179 | if (ret_code != ulapack_success) { 2180 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2181 | ulapack_destroy(V); 2182 | #endif 2183 | 2184 | return ret_code; 2185 | } 2186 | 2187 | ret_code = ulapack_least_squares(V, y, p); 2188 | if (ret_code != ulapack_success) { 2189 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2190 | ulapack_destroy(V); 2191 | #endif 2192 | 2193 | return ret_code; 2194 | } 2195 | 2196 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2197 | ulapack_destroy(V); 2198 | #endif 2199 | 2200 | return ulapack_success; 2201 | } 2202 | 2203 | MatrixError_t ulapack_copy(const Matrix_t * const matrix, 2204 | Matrix_t * const result) { 2205 | /* 2206 | * Matrix iterators. 2207 | * Declared at top of function for legacy compiler comparability. 2208 | */ 2209 | Index_t row_itor = 0; 2210 | Index_t col_itor = 0; 2211 | 2212 | /* 2213 | * Verify that a valid operand object have been passed in. 2214 | */ 2215 | if (!_ulapack_is_valid_memory(matrix)) { 2216 | return ulapack_uninit_obj; 2217 | } 2218 | 2219 | /* 2220 | * The result pointer should not be NULL because it should be written to. 2221 | */ 2222 | if (!_ulapack_is_valid_memory(result)) { 2223 | return ulapack_uninit_obj; 2224 | } 2225 | 2226 | if (result->n_rows < matrix->n_rows || 2227 | result->n_cols < matrix->n_cols) { 2228 | return ulapack_invalid_argument; 2229 | } 2230 | 2231 | /* 2232 | * Copy the matrix elements. 2233 | */ 2234 | for(row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 2235 | for(col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 2236 | result->entry[row_itor][col_itor] = 2237 | matrix->entry[row_itor][col_itor]; 2238 | } 2239 | } 2240 | 2241 | return ulapack_success; 2242 | } 2243 | 2244 | MatrixError_t ulapack_diag(const Matrix_t * const vector, 2245 | Matrix_t * const matrix) { 2246 | /* 2247 | * Matrix iterators. 2248 | * Declared at top of function for legacy compiler comparability. 2249 | */ 2250 | Index_t row_itor = 0; 2251 | Index_t col_itor = 0; 2252 | 2253 | /* 2254 | * Verify that a valid operand object have been passed in. 2255 | */ 2256 | if (!_ulapack_is_valid_memory(vector)) { 2257 | return ulapack_uninit_obj; 2258 | } 2259 | 2260 | /* 2261 | * The result pointer should not be NULL because it should be written to. 2262 | */ 2263 | if (!_ulapack_is_valid_memory(matrix)) { 2264 | return ulapack_uninit_obj; 2265 | } 2266 | 2267 | MatrixError_t isv; 2268 | ulapack_is_vector(vector, &isv); 2269 | if (isv != ulapack_success) { 2270 | return ulapack_invalid_argument; 2271 | } 2272 | 2273 | if (vector->n_rows >= vector->n_cols) { 2274 | 2275 | #ifdef ULAPACK_USE_STATIC_ALLOC 2276 | matrix->n_rows = vector->n_rows; 2277 | matrix->n_cols = vector->n_rows; 2278 | #endif 2279 | 2280 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2281 | if (matrix->n_rows != vector->n_rows || 2282 | matrix->n_cols != vector->n_rows) { 2283 | return ulapack_invalid_argument; 2284 | } 2285 | #endif 2286 | 2287 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 2288 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 2289 | if (row_itor == col_itor) { 2290 | matrix->entry[row_itor][col_itor] = 2291 | vector->entry[row_itor][0]; 2292 | } else { 2293 | matrix->entry[row_itor][col_itor] = 0; 2294 | } 2295 | } 2296 | } 2297 | } else { 2298 | #ifdef ULAPACK_USE_STATIC_ALLOC 2299 | matrix->n_rows = vector->n_cols; 2300 | matrix->n_cols = vector->n_cols; 2301 | #endif 2302 | 2303 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2304 | if (matrix->n_rows != vector->n_cols || 2305 | matrix->n_cols != vector->n_cols) { 2306 | return ulapack_invalid_argument; 2307 | } 2308 | #endif 2309 | 2310 | for (row_itor = 0; row_itor < matrix->n_rows; row_itor++) { 2311 | for (col_itor = 0; col_itor < matrix->n_cols; col_itor++) { 2312 | if (row_itor == col_itor) { 2313 | matrix->entry[row_itor][col_itor] = 2314 | vector->entry[0][row_itor]; 2315 | } else { 2316 | matrix->entry[row_itor][col_itor] = 0; 2317 | } 2318 | } 2319 | } 2320 | } 2321 | 2322 | return ulapack_success; 2323 | } 2324 | 2325 | MatrixError_t ulapack_svd(const Matrix_t * const matrix, 2326 | Matrix_t * const U, Matrix_t * const S, Matrix_t * const V) { 2327 | 2328 | Index_t flag = 0; 2329 | Index_t i = 0; 2330 | Index_t its = 0; 2331 | Index_t j = 0; 2332 | Index_t jj = 0; 2333 | Index_t k = 0; 2334 | Index_t l = 0; 2335 | Index_t nm = 0; 2336 | 2337 | /* 2338 | * todo: use better names for these variables. 2339 | */ 2340 | MatrixEntry_t c = 0; 2341 | MatrixEntry_t f = 0; 2342 | MatrixEntry_t h = 0; 2343 | MatrixEntry_t s = 0; 2344 | MatrixEntry_t x = 0; 2345 | MatrixEntry_t y = 0; 2346 | MatrixEntry_t z = 0; 2347 | 2348 | MatrixEntry_t anorm = 0; 2349 | MatrixEntry_t g = 0; 2350 | MatrixEntry_t scale = 0; 2351 | 2352 | Matrix_t *temp_vector = NULL; 2353 | 2354 | #ifdef ULAPACK_USE_STATIC_ALLOC 2355 | Matrix_t temp_vector_mem; 2356 | temp_vector = &temp_vector_mem; 2357 | #endif 2358 | 2359 | /* 2360 | * Verify that a valid operand object have been passed in. 2361 | */ 2362 | if (!_ulapack_is_valid_memory(matrix)) { 2363 | return ulapack_uninit_obj; 2364 | } 2365 | 2366 | /* 2367 | * The input must have more rows than columns. 2368 | */ 2369 | if (matrix->n_rows < matrix->n_cols) { 2370 | return ulapack_invalid_argument; 2371 | } 2372 | 2373 | /* 2374 | * The result pointers should not be NULL because they will be written to. 2375 | */ 2376 | if (!_ulapack_is_valid_memory(U) | 2377 | !_ulapack_is_valid_memory(S) | 2378 | !_ulapack_is_valid_memory(V)) { 2379 | return ulapack_uninit_obj; 2380 | } 2381 | 2382 | /* 2383 | * The dimensions of the result pointers are changed to equal that of the 2384 | * legal dimensions if static memory allocation is specified. 2385 | */ 2386 | #ifdef ULAPACK_USE_STATIC_ALLOC 2387 | U->n_rows = matrix->n_rows; 2388 | U->n_cols = matrix->n_cols; 2389 | 2390 | S->n_rows = matrix->n_cols; 2391 | S->n_cols = 1; 2392 | 2393 | V->n_rows = matrix->n_cols; 2394 | V->n_cols = matrix->n_cols; 2395 | #endif 2396 | 2397 | /* 2398 | * If dynamic memory allocation is specified, the dimensions of result 2399 | * should match that of the dimensions of the pseudo inverse of the matrix. 2400 | */ 2401 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2402 | if (U->n_rows != matrix->n_rows || U->n_cols != matrix->n_cols || 2403 | S->n_rows != matrix->n_cols || S->n_cols != 1 || 2404 | V->n_rows != matrix->n_cols || V->n_cols != matrix->n_cols) { 2405 | return ulapack_invalid_argument; 2406 | } 2407 | #endif 2408 | 2409 | /* 2410 | * Initialize memory for scratch pad vector. 2411 | */ 2412 | #ifdef ULAPACK_USE_STATIC_ALLOC 2413 | if (ulapack_init(temp_vector, 2414 | matrix->n_cols, 1) != ulapack_success) { 2415 | return ulapack_error; 2416 | } 2417 | #endif 2418 | 2419 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2420 | if (ulapack_init(&temp_vector, 2421 | matrix->n_cols, 1) != ulapack_success) { 2422 | return ulapack_error; 2423 | } 2424 | #endif 2425 | 2426 | ulapack_copy(matrix, U); 2427 | 2428 | ulapack_set(S, 0); 2429 | ulapack_set(V, 0); 2430 | 2431 | /* 2432 | * Householder reduction to bidiagonal form. 2433 | */ 2434 | for (i = 0; i < matrix->n_cols; i++) { 2435 | 2436 | /* left-hand reduction */ 2437 | l = i + 1; 2438 | 2439 | temp_vector->entry[i][0] = scale * g; 2440 | 2441 | g = 0; 2442 | s = 0; 2443 | scale = 0; 2444 | 2445 | if (i < matrix->n_rows) { 2446 | 2447 | for (k = i; k < matrix->n_rows; k++) { 2448 | scale += ulapack_abs(U->entry[k][i]); 2449 | } 2450 | 2451 | if (scale) { 2452 | for (k = i; k < matrix->n_rows; k++) 2453 | { 2454 | U->entry[k][i] = U->entry[k][i] / scale; 2455 | s += U->entry[k][i] * U->entry[k][i]; 2456 | } 2457 | 2458 | f = U->entry[i][i]; 2459 | g = -1 * ulapack_sign(sqrt(s), f); 2460 | h = f * g - s; 2461 | U->entry[i][i] = f - g; 2462 | 2463 | if (i != matrix->n_cols - 1) { 2464 | for (j = l; j < matrix->n_cols; j++) { 2465 | 2466 | for (s = 0, k = i; k < matrix->n_rows; k++) { 2467 | s += U->entry[k][i] * U->entry[k][j]; 2468 | } 2469 | 2470 | f = s / h; 2471 | 2472 | for (k = i; k < matrix->n_rows; k++) { 2473 | U->entry[k][j] += f * U->entry[k][i]; 2474 | } 2475 | } 2476 | } 2477 | 2478 | for (k = i; k < matrix->n_rows; k++) { 2479 | U->entry[k][i] = U->entry[k][i] * scale; 2480 | } 2481 | } 2482 | } 2483 | 2484 | S->entry[i][0] = scale * g; 2485 | 2486 | /* 2487 | * Right-hand reduction. 2488 | */ 2489 | g = 0; 2490 | s = 0; 2491 | scale = 0; 2492 | if (i < matrix->n_rows && i != matrix->n_cols - 1) { 2493 | 2494 | for (k = l; k < matrix->n_cols; k++) { 2495 | scale += ulapack_abs(U->entry[i][k]); 2496 | } 2497 | 2498 | if (scale) { 2499 | for (k = l; k < matrix->n_cols; k++) { 2500 | U->entry[i][k] = U->entry[i][k] / scale; 2501 | s += U->entry[i][k] * U->entry[i][k]; 2502 | } 2503 | 2504 | f = U->entry[i][l]; 2505 | g = -1 * ulapack_sign(sqrt(s), f); 2506 | h = f * g - s; 2507 | U->entry[i][l] = f - g; 2508 | 2509 | for (k = l; k < matrix->n_cols; k++) { 2510 | temp_vector->entry[k][0] = U->entry[i][k] / h; 2511 | } 2512 | 2513 | if (i != matrix->n_rows - 1) { 2514 | for (j = l; j < matrix->n_rows; j++) { 2515 | for (s = 0, k = l; k < matrix->n_cols; k++) { 2516 | s += U->entry[j][k] * U->entry[i][k]; 2517 | } 2518 | for (k = l; k < matrix->n_cols; k++) { 2519 | U->entry[j][k] += s * temp_vector->entry[k][0]; 2520 | } 2521 | } 2522 | } 2523 | 2524 | for (k = l; k < matrix->n_cols; k++) { 2525 | U->entry[i][k] = U->entry[i][k] * scale; 2526 | } 2527 | } 2528 | } 2529 | anorm = ulapack_max(anorm, 2530 | (ulapack_abs(S->entry[i][0]) + ulapack_abs(temp_vector->entry[i][0]))); 2531 | } 2532 | 2533 | /* 2534 | * Accumulate the right-hand transformation. 2535 | */ 2536 | for (i = matrix->n_cols - 1; (SIndex_t)i >= 0; i--) { 2537 | if (i < matrix->n_cols - 1) { 2538 | if (g) { 2539 | 2540 | for (j = l; j < matrix->n_cols; j++) { 2541 | V->entry[j][i] = (U->entry[i][j] / U->entry[i][l]) / g; 2542 | } 2543 | 2544 | /* 2545 | * Double division to avoid underflow. 2546 | */ 2547 | for (j = l; j < matrix->n_cols; j++) 2548 | { 2549 | for (s = 0, k = l; k < matrix->n_cols; k++) { 2550 | s += U->entry[i][k] * V->entry[k][j]; 2551 | } 2552 | for (k = l; k < matrix->n_cols; k++) { 2553 | V->entry[k][j] += s * V->entry[k][i]; 2554 | } 2555 | } 2556 | 2557 | } 2558 | 2559 | for (j = l; j < matrix->n_cols; j++) { 2560 | V->entry[i][j] = 0; 2561 | V->entry[j][i] = 0; 2562 | } 2563 | } 2564 | 2565 | V->entry[i][i] = 1; 2566 | g = temp_vector->entry[i][0]; 2567 | l = i; 2568 | } 2569 | 2570 | /* 2571 | * Accumulate the left-hand transformation. 2572 | */ 2573 | for (i = matrix->n_cols - 1; (SIndex_t)i >= 0; i--) { 2574 | l = i + 1; 2575 | g = S->entry[i][0]; 2576 | 2577 | if (i < matrix->n_cols - 1) { 2578 | for (j = l; j < matrix->n_cols; j++) { 2579 | U->entry[i][j] = 0; 2580 | } 2581 | } 2582 | 2583 | if (g) { 2584 | g = 1 / g; 2585 | if (i != matrix->n_cols - 1) { 2586 | for (j = l; j < matrix->n_cols; j++) { 2587 | for (s = 0, k = l; k < matrix->n_rows; k++) { 2588 | s += U->entry[k][i] * U->entry[k][j]; 2589 | } 2590 | 2591 | f = (s / U->entry[i][i]) * g; 2592 | 2593 | for (k = i; k < matrix->n_rows; k++) { 2594 | U->entry[k][j] += f * U->entry[k][i]; 2595 | } 2596 | } 2597 | } 2598 | for (j = i; j < matrix->n_rows; j++) { 2599 | U->entry[j][i] = U->entry[j][i] * g; 2600 | } 2601 | } else { 2602 | for (j = i; j < matrix->n_rows; j++) { 2603 | U->entry[j][i] = 0; 2604 | } 2605 | } 2606 | 2607 | ++(U->entry[i][i]); 2608 | } 2609 | 2610 | /* 2611 | * Diagonalize the bidiagonal form. 2612 | */ 2613 | for (k = matrix->n_cols - 1; (SIndex_t)k >= 0; k--) { 2614 | /* 2615 | * Loop over singular values for MAX_CONV_LOOPS*1000 iterations. 2616 | */ 2617 | for (its = 0; its < MAX_CONV_LOOPS; its++) { 2618 | /* 2619 | * Loop over allowed iterations. 2620 | */ 2621 | flag = 1; 2622 | for (l = k; (SIndex_t)l >= 0; l--) { 2623 | /* 2624 | * Test for splitting. 2625 | */ 2626 | nm = l - 1; 2627 | if (ulapack_abs(temp_vector->entry[l][0]) + anorm == anorm) { 2628 | flag = 0; 2629 | break; 2630 | } 2631 | if (ulapack_abs(S->entry[nm][0]) + anorm == anorm) { 2632 | break; 2633 | } 2634 | } 2635 | 2636 | if (flag) { 2637 | c = 0; 2638 | s = 1; 2639 | for (i = l; i <= k; i++) { 2640 | f = s * temp_vector->entry[i][0]; 2641 | if (ulapack_abs(f) + anorm != anorm) { 2642 | g = S->entry[i][0]; 2643 | h = ulapack_hypot(f, g); 2644 | S->entry[i][0] = h; 2645 | h = 1 / h; 2646 | c = g * h; 2647 | s = -1 * f * h; 2648 | for (j = 0; j < matrix->n_rows; j++) { 2649 | y = U->entry[j][nm]; 2650 | z = U->entry[j][i]; 2651 | U->entry[j][nm] = y * c + z * s; 2652 | U->entry[j][i] = z * c - y * s; 2653 | } 2654 | } 2655 | } 2656 | } 2657 | 2658 | z = S->entry[k][0]; 2659 | 2660 | if (l == k) { 2661 | /* 2662 | * Convergence. 2663 | */ 2664 | if (z < 0) { 2665 | /* 2666 | * Make singular value nonnegative. 2667 | */ 2668 | S->entry[k][0] = -1 * z; 2669 | for (j = 0; j < matrix->n_cols; j++) { 2670 | V->entry[j][k] = -1 * V->entry[j][k]; 2671 | } 2672 | } 2673 | 2674 | break; 2675 | } 2676 | 2677 | /* 2678 | * No convergence after 1000 * MAX_CONV_LOOPS iterations. 2679 | */ 2680 | if (its >= MAX_CONV_LOOPS) { 2681 | 2682 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2683 | ulapack_destroy(temp_vector); 2684 | #endif 2685 | 2686 | return ulapack_error; 2687 | } 2688 | 2689 | /* 2690 | * Shift from bottom 2 x 2 minor. 2691 | */ 2692 | x = S->entry[l][0]; 2693 | nm = k - 1; 2694 | y = S->entry[nm][0]; 2695 | g = temp_vector->entry[nm][0]; 2696 | h = temp_vector->entry[k][0]; 2697 | f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2 * h * y); 2698 | g = ulapack_hypot(f, 1); 2699 | f = ((x - z) * (x + z) + h * 2700 | ((y / (f + ulapack_sign(g, f))) - h)) 2701 | / x; 2702 | 2703 | /* 2704 | * Next QR transformation. 2705 | */ 2706 | c = s = 1; 2707 | for (j = l; j <= nm; j++) { 2708 | i = j + 1; 2709 | g = temp_vector->entry[i][0]; 2710 | y = S->entry[i][0]; 2711 | h = s * g; 2712 | g = c * g; 2713 | z = ulapack_hypot(f, h); 2714 | temp_vector->entry[j][0] = z; 2715 | c = f / z; 2716 | s = h / z; 2717 | f = x * c + g * s; 2718 | g = g * c - x * s; 2719 | h = y * s; 2720 | y = y * c; 2721 | 2722 | for (jj = 0; jj < matrix->n_cols; jj++) { 2723 | x = V->entry[jj][j]; 2724 | z = V->entry[jj][i]; 2725 | V->entry[jj][j] = x * c + z * s; 2726 | V->entry[jj][i] = z * c - x * s; 2727 | } 2728 | 2729 | z = ulapack_hypot(f, h); 2730 | 2731 | S->entry[j][0] = z; 2732 | 2733 | if (z) { 2734 | z = 1 / z; 2735 | c = f * z; 2736 | s = h * z; 2737 | } 2738 | 2739 | f = (c * g) + (s * y); 2740 | x = (c * y) - (s * g); 2741 | 2742 | for (jj = 0; jj < matrix->n_rows; jj++) { 2743 | y = U->entry[jj][j]; 2744 | z = U->entry[jj][i]; 2745 | U->entry[jj][j] = y * c + z * s; 2746 | U->entry[jj][i] = z * c - y * s; 2747 | } 2748 | } 2749 | 2750 | temp_vector->entry[l][0] = 0; 2751 | temp_vector->entry[k][0] = f; 2752 | S->entry[k][0] = x; 2753 | } 2754 | } 2755 | 2756 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2757 | ulapack_destroy(temp_vector); 2758 | #endif 2759 | 2760 | return ulapack_success; 2761 | } 2762 | 2763 | MatrixError_t ulapack_pca(const Matrix_t * const matrix, Matrix_t * const T) { 2764 | MatrixError_t ret; 2765 | 2766 | Matrix_t *matrix_transpose = NULL; 2767 | Matrix_t *mmT = NULL; 2768 | Matrix_t *Sdiag = NULL; 2769 | 2770 | Matrix_t *U = NULL; 2771 | Matrix_t *S = NULL; 2772 | Matrix_t *V = NULL; 2773 | 2774 | #ifdef ULAPACK_USE_STATIC_ALLOC 2775 | Matrix_t mTranmem; 2776 | Matrix_t mmTmem; 2777 | Matrix_t Sdiagmem; 2778 | Matrix_t Umem; 2779 | Matrix_t Smem; 2780 | Matrix_t Vmem; 2781 | 2782 | matrix_transpose = &mTranmem; 2783 | mmT = &mmTmem; 2784 | Sdiag = &Sdiagmem; 2785 | 2786 | U = &Umem; 2787 | S = &Smem; 2788 | V = &Vmem; 2789 | #endif 2790 | 2791 | /* 2792 | * Verify that a valid operand object have been passed in. 2793 | */ 2794 | if (!_ulapack_is_valid_memory(matrix)) { 2795 | return ulapack_uninit_obj; 2796 | } 2797 | 2798 | /* 2799 | * The result pointer should not be NULL because it will be written to. 2800 | */ 2801 | if (!_ulapack_is_valid_memory(T)) { 2802 | return ulapack_uninit_obj; 2803 | } 2804 | 2805 | /* 2806 | * The dimensions of the result pointers are changed to equal that of the 2807 | * legal dimensions if static memory allocation is specified. 2808 | */ 2809 | #ifdef ULAPACK_USE_STATIC_ALLOC 2810 | T->n_rows = matrix->n_rows; 2811 | T->n_cols = matrix->n_rows; 2812 | #endif 2813 | 2814 | /* 2815 | * If dynamic memory allocation is specified, the dimensions of result 2816 | * should match that of the dimensions of the pseudo inverse of the matrix. 2817 | */ 2818 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2819 | if (T->n_rows != matrix->n_rows || T->n_cols != matrix->n_rows) { 2820 | return ulapack_invalid_argument; 2821 | } 2822 | #endif 2823 | 2824 | /* 2825 | * Initialize memory for scratch pad vector. 2826 | */ 2827 | #ifdef ULAPACK_USE_STATIC_ALLOC 2828 | if (ulapack_init(matrix_transpose, matrix->n_cols, matrix->n_rows) 2829 | != ulapack_success) { 2830 | return ulapack_error; 2831 | } 2832 | if (ulapack_init(mmT, matrix->n_rows, matrix->n_rows) 2833 | != ulapack_success) { 2834 | return ulapack_error; 2835 | } 2836 | if (ulapack_init(Sdiag, matrix->n_rows, matrix->n_rows) 2837 | != ulapack_success) { 2838 | return ulapack_error; 2839 | } 2840 | if (ulapack_init(U, matrix->n_rows, matrix->n_rows) 2841 | != ulapack_success) { 2842 | return ulapack_error; 2843 | } 2844 | if (ulapack_init(S, matrix->n_rows, 1) 2845 | != ulapack_success) { 2846 | return ulapack_error; 2847 | } 2848 | if (ulapack_init(V, matrix->n_rows, matrix->n_rows) 2849 | != ulapack_success) { 2850 | return ulapack_error; 2851 | } 2852 | #endif 2853 | 2854 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2855 | ret = ulapack_init(&matrix_transpose, matrix->n_cols, matrix->n_rows); 2856 | if (ret != ulapack_success) { 2857 | return ret; 2858 | } 2859 | 2860 | ret = ulapack_init(&mmT, matrix->n_rows, matrix->n_rows); 2861 | if (ret != ulapack_success) { 2862 | ulapack_destroy(matrix_transpose); 2863 | 2864 | return ret; 2865 | } 2866 | 2867 | ret = ulapack_init(&Sdiag, matrix->n_rows, matrix->n_rows); 2868 | if (ret != ulapack_success) { 2869 | ulapack_destroy(matrix_transpose); 2870 | ulapack_destroy(mmT); 2871 | 2872 | return ret; 2873 | } 2874 | 2875 | ret = ulapack_init(&U, matrix->n_rows, matrix->n_rows); 2876 | if (ret != ulapack_success) { 2877 | ulapack_destroy(matrix_transpose); 2878 | ulapack_destroy(mmT); 2879 | ulapack_destroy(Sdiag); 2880 | 2881 | return ret; 2882 | } 2883 | 2884 | ret = ulapack_init(&S, matrix->n_rows, 1); 2885 | if (ret != ulapack_success) { 2886 | ulapack_destroy(matrix_transpose); 2887 | ulapack_destroy(mmT); 2888 | ulapack_destroy(Sdiag); 2889 | ulapack_destroy(U); 2890 | 2891 | return ret; 2892 | } 2893 | 2894 | ret = ulapack_init(&V, matrix->n_rows, matrix->n_rows); 2895 | if (ret != ulapack_success) { 2896 | ulapack_destroy(matrix_transpose); 2897 | ulapack_destroy(mmT); 2898 | ulapack_destroy(Sdiag); 2899 | ulapack_destroy(U); 2900 | ulapack_destroy(S); 2901 | 2902 | return ret; 2903 | } 2904 | #endif 2905 | 2906 | ret = ulapack_transpose(matrix, matrix_transpose); 2907 | if (ret != ulapack_success) { 2908 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2909 | ulapack_destroy(matrix_transpose); 2910 | ulapack_destroy(mmT); 2911 | ulapack_destroy(Sdiag); 2912 | ulapack_destroy(U); 2913 | ulapack_destroy(S); 2914 | ulapack_destroy(V); 2915 | #endif 2916 | return ret; 2917 | } 2918 | 2919 | ret = ulapack_product(matrix, matrix_transpose, mmT); 2920 | if (ret != ulapack_success) { 2921 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2922 | ulapack_destroy(matrix_transpose); 2923 | ulapack_destroy(mmT); 2924 | ulapack_destroy(Sdiag); 2925 | ulapack_destroy(U); 2926 | ulapack_destroy(S); 2927 | ulapack_destroy(V); 2928 | #endif 2929 | return ret; 2930 | } 2931 | 2932 | ret = ulapack_svd(mmT, U, S, V); 2933 | if (ret != ulapack_success) { 2934 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2935 | ulapack_destroy(matrix_transpose); 2936 | ulapack_destroy(mmT); 2937 | ulapack_destroy(Sdiag); 2938 | ulapack_destroy(U); 2939 | ulapack_destroy(S); 2940 | ulapack_destroy(V); 2941 | #endif 2942 | return ret; 2943 | } 2944 | 2945 | ret = ulapack_diag(S, Sdiag); 2946 | if (ret != ulapack_success) { 2947 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2948 | ulapack_destroy(matrix_transpose); 2949 | ulapack_destroy(mmT); 2950 | ulapack_destroy(Sdiag); 2951 | ulapack_destroy(U); 2952 | ulapack_destroy(S); 2953 | ulapack_destroy(V); 2954 | #endif 2955 | return ret; 2956 | } 2957 | 2958 | ret = ulapack_product(U, Sdiag, T); 2959 | if (ret != ulapack_success) { 2960 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2961 | ulapack_destroy(matrix_transpose); 2962 | ulapack_destroy(mmT); 2963 | ulapack_destroy(Sdiag); 2964 | ulapack_destroy(U); 2965 | ulapack_destroy(S); 2966 | ulapack_destroy(V); 2967 | #endif 2968 | return ret; 2969 | } 2970 | 2971 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 2972 | ulapack_destroy(matrix_transpose); 2973 | ulapack_destroy(mmT); 2974 | ulapack_destroy(Sdiag); 2975 | ulapack_destroy(U); 2976 | ulapack_destroy(S); 2977 | ulapack_destroy(V); 2978 | #endif 2979 | 2980 | return ulapack_success; 2981 | } -------------------------------------------------------------------------------- /src/ulapack.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ulapack.h 3 | * @brief Declarations of uLAPack matrix manipulation functions. 4 | * 5 | * @author Sargis Yonan 6 | * @date July 8, 2017 7 | * 8 | * @version 1.0.1 9 | **/ 10 | 11 | /** 12 | * @todo new line the return codes in dox for readability. 13 | */ 14 | 15 | /* 16 | * File header guards. 17 | */ 18 | #ifndef __ULAPACK_H__ 19 | #define __ULAPACK_H__ 20 | 21 | /* 22 | * Including standard integer header for uintX_t types. 23 | */ 24 | #include 25 | 26 | /* 27 | * uLAPack options header. 28 | */ 29 | #include "ulapack_options.h" 30 | 31 | /* 32 | * uLAPack type definitions. 33 | */ 34 | #include "ulapack_type.h" 35 | 36 | /* 37 | * If the print function is desired, stdio.h must be included for its definition 38 | * of the FILE type and fprintf. 39 | */ 40 | #ifdef ULAPACK_USE_PRINT 41 | #include 42 | #endif 43 | 44 | /** 45 | * @name ulapack_init 46 | * 47 | * Initializes uLAPack matrix object. 48 | * 49 | * @note If static memory allocation is used, then only the n_rows and n_cols 50 | * members of the matrix structure are set. Otherwise, memory is allocated 51 | * using the specified memory allocator call. 52 | * @note If dynamic memory allocation is used, a matrix object can not be 53 | * initialized more than one time. A static allocated object can. 54 | * 55 | * @param[in/out] matrix A pointer to a matrix object type. 56 | * @param n_rows The number of rows the matrix will have. 57 | * @param n_cols The number of columns the matrix will have. 58 | * 59 | * @return ulapack_success is returned upon successful modification and 60 | * initialization of the matrix object. 61 | * 62 | * ulapack_oom is returned if dynamic memory allocation is specified for 63 | * use, and the memory required for the object cannot be allocated. 64 | * 65 | * ulapack_invalid_argument is returned if a NULL pointer was passed in 66 | * while using static memory allocation, or a non-NULL pointer was 67 | * passed in using dynamic memory allocation. 68 | * 69 | * ulapack_invalid_argument is returned when the rows and 70 | * columns passed in exceed the preset maximum values set in the uLAPack 71 | * options. 72 | */ 73 | MatrixError_t ulapack_init( 74 | #ifdef ULAPACK_USE_STATIC_ALLOC 75 | Matrix_t *matrix, 76 | #else 77 | Matrix_t **matrix, 78 | #endif 79 | const Index_t n_rows, const Index_t n_cols); 80 | 81 | /** 82 | * @name ulapack_destroy 83 | * Free a dynamically allocated ULAPack matrix object, and reset its pointers. 84 | * 85 | * @note This function can only be called in dynamic memory allocation is 86 | * specified by explicitly not defining static allocation. 87 | * @note The free function defined by the macro ULAPACK_FREE is used as the 88 | * memory freeing call. 89 | * 90 | * @param[in] obj A pointer to the ULAPack matrix object to destroy. 91 | * 92 | * @return ULAPack success code ulapack_success is returned if the matrix object 93 | * passed in was valid and freed successfully. 94 | * ulapack_uninit_obj is returned if the matrix object is not 95 | * initialized. 96 | */ 97 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 98 | MatrixError_t ulapack_destroy(Matrix_t *obj); 99 | #endif 100 | 101 | /** 102 | * @name ulapack_print 103 | * Print a matrix or vector to a specified output stream. 104 | * 105 | * @note The preprocessor macro "ULAPACK_USE_PRINT" must be defined in order to 106 | * use this function. This is done to avoid extraneously include stdio.h 107 | * since fprintf() is the driving function of this print call. 108 | * @note The assumption that 109 | * 110 | * @param matrix An initialize matrix object. 111 | * @param stream The output file stream to print to. 112 | * 113 | * @return uLAPack Success code. 114 | * ulapack_success is returned if the matrix object passed in was valid. 115 | * ulapack_uninit_obj is returned if the matrix was not initialized. 116 | */ 117 | #ifdef ULAPACK_USE_PRINT 118 | MatrixError_t ulapack_print(const Matrix_t * const matrix, 119 | FILE *stream); 120 | #endif 121 | 122 | /** 123 | * @name ulapack_edit_entry 124 | * Edit a single uLAPack matrix object's element entry. 125 | * 126 | * @param[in/out] matrix The matrix object to edit an entry within. 127 | * @param row The row to edit the matrix within. 128 | * @param col The column to edit the matrix within. 129 | * @param value The value to overwrite the matrix element at the coordinates 130 | * (row, col). 131 | * 132 | * @return ULAPack success code ulapack_success is returned upon a successful 133 | * entry edit. 134 | * ulapack_uninit_obj is returned if the matrix object passed in is not 135 | * initialized. 136 | * ulapack_invalid_argument is returned if the coordinates to edit are 137 | * out of the range of the initialized matrix dimensions. 138 | */ 139 | MatrixError_t ulapack_edit_entry(Matrix_t * const matrix, 140 | const Index_t row, const Index_t col, 141 | const MatrixEntry_t value); 142 | 143 | /** 144 | * @name ulapack_get_entry 145 | * Get a single uLAPack matrix object's element entry. 146 | * 147 | * @param[in] matrix The matrix object to get an entry from. 148 | * @param row The row to edit the matrix within. 149 | * @param col The column to edit the matrix within. 150 | * @param[out] value The variable to copy the matrix value at coordinates 151 | * (row,col) into. ulapack_success is passed back if the matrices 152 | * are equal. ulapack_error is returned if the matrices are not 153 | * equal. 154 | * 155 | * @return ULAPack success code ulapack_success is returned upon a successful 156 | * copy of the matrix element at coordinates (row, col). 157 | * ulapack_uninit_obj is returned if the matrix object is not 158 | * initialized. 159 | * ulapack_invalid_argument is returned if value is NULL. 160 | * ulapack_invalid_argument is returned if the coordinates to get from 161 | * are out of the range of the initialized matrix dimensions. 162 | */ 163 | MatrixError_t ulapack_get_entry(const Matrix_t * const matrix, 164 | const Index_t row, const Index_t col, 165 | MatrixEntry_t * const value); 166 | 167 | /** 168 | * @name ulapack_array_col_copy 169 | * Copy data from an array into a column of a matrix. 170 | * 171 | * @param[in] data The array of data to copy into a matrix. 172 | * @param[out] src The source matrix object to copy into. 173 | * @param col The column of the source matrix object to copy into. 174 | * @param data_points The number of entries to copy. 175 | * 176 | * @return ULAPack status code. 177 | */ 178 | MatrixEntry_t ulapack_array_col_copy(const MatrixEntry_t * const data, 179 | Matrix_t * const dest_obj, 180 | const Index_t col, 181 | const Index_t data_points); 182 | 183 | /** 184 | * @name ulapack_array_row_copy 185 | * Copy data from an array into a row of a matrix. 186 | * 187 | * @param[in] data The array of data to copy into a matrix. 188 | * @param[out] src The source matrix object to copy into. 189 | * @param row The row of the source matrix object to copy into. 190 | * @param data_points The number of entries to copy. 191 | * 192 | * @return ULAPack status code. 193 | */ 194 | MatrixEntry_t ulapack_array_row_copy(const MatrixEntry_t * const data, 195 | Matrix_t * const dest_obj, 196 | const Index_t row, 197 | const Index_t data_points); 198 | 199 | /** 200 | * @name ulapack_size 201 | * Get the dimensions of a matrix object. 202 | * 203 | * @param[in] matrix The matrix object to get dimensions of. 204 | * @param[out] rows The number of rows in the matrix object. 205 | * @param[out] cols The number of columns in the matrix object. 206 | * 207 | * @return ULAPack success code ulapack_success is returned upon a successful 208 | * copy of matrix dimensions into the references rows and cols. 209 | * ulapack_uninit_obj is returned if the matrix object is not 210 | * initialized. 211 | * ulapack_invalid_argument is returned if rows or cols is NULL. 212 | */ 213 | MatrixError_t ulapack_size(const Matrix_t * const matrix, 214 | Index_t * const rows, Index_t * const cols); 215 | 216 | /** 217 | * @name ulapack_set 218 | * Set every entry in a matrix to a specified value. 219 | * 220 | * @param[in/out] matrix An initialized matrix object to modify. 221 | * @param value The value to set the matrix elements to. 222 | * 223 | * @return ULAPack success code ulapack_success is returned upon a successful 224 | * modification of the matrix object. 225 | * ulapack_uninit_obj is returned if a matrix object passed in is not 226 | * initialized. 227 | */ 228 | MatrixError_t ulapack_set(Matrix_t * const matrix, 229 | const MatrixEntry_t value); 230 | 231 | /** 232 | * @name ulapack_is_equal 233 | * Check if the two matrix operands passed in are equal to each other (A == B). 234 | * 235 | * @param[in] A An initialized matrix object operand. 236 | * @param[in] B An initialized matrix object operand. 237 | * @param[out] is_equal The conditional that stores the equality (A == B). 238 | * ulapack_error if not equal, ulapack_success if equal. 239 | * 240 | * @return ULAPack success code ulapack_success is returned upon a successful 241 | * comparison between the two matrices. 242 | * ulapack_uninit_obj is returned if a matrix object passed in is not 243 | * initialized. 244 | * ulapack_invalid_argument is returned if the is_equal pointer is NULL. 245 | */ 246 | MatrixError_t ulapack_is_equal(const Matrix_t * const A, 247 | const Matrix_t * const B, 248 | MatrixError_t * const is_equal); 249 | 250 | /** 251 | * @name ulapack_is_vector 252 | * Check if the input operand is a vector: 1xN or Nx1. 253 | * 254 | * @param[in] vector An initialized vector object operand. 255 | * @param[out] is_vector Pass back if the input is a vector. ulapack_success if 256 | * the input is a vector, and ulapack_error if the input is not a 257 | * vector. 258 | * 259 | * @return ULAPack success code ulapack_success is returned if the input vector 260 | * is a initialized and checked for vector dimensions successful. 261 | * ulapack_uninit_obj is returned if a vector object passed in is not 262 | * initialized. 263 | * ulapack_invalid_argument is returned if the vector pointer is NULL. 264 | */ 265 | MatrixError_t ulapack_is_vector(const Matrix_t * const vector, 266 | MatrixError_t * const is_vector); 267 | 268 | /** 269 | * @name ulapack_eye 270 | * Set a matrix equal to the identity matrix of that dimension. 271 | * 272 | * @note The matrix must be square (n_rows == n_cols). 273 | * 274 | * @param[in/out] matrix An initialized matrix object. 275 | * 276 | * @return ULAPack success code ulapack_success is returned upon a successful 277 | * modification of the matrix object. 278 | * ulapack_uninit_obj is returned if a matrix object passed in is not 279 | * initialized. 280 | * ulapack_invalid_argument is returned if the matrix is not square. 281 | */ 282 | MatrixError_t ulapack_eye(Matrix_t * const matrix); 283 | 284 | /** 285 | * @name ulapack_sum 286 | * Add the elements of a matrix or vector and pass the value back by reference. 287 | * 288 | * @param[in] matrix An initialized matrix object. 289 | * @param[out] result The resultant of the sum of the elements of the matrix 290 | * passed in. 291 | * 292 | * @return ULAPack success code ulapack_success is returned upon a successful 293 | * summation of the matrix object. 294 | * ulapack_uninit_obj is returned if a matrix object passed in is not 295 | * initialized. 296 | * ulapack_invalid_argument is returned if the result pointer is NULL. 297 | */ 298 | MatrixError_t ulapack_sum(const Matrix_t * const matrix, 299 | MatrixEntry_t * const result); 300 | 301 | /** 302 | * @name ulapack_add 303 | * Add two matrices or vectors together. 304 | * 305 | * @note the dimensions of the two matrices must be identical. 306 | * @note if static allocation is specified, the dimensions of the result matrix 307 | * are modified to equal the dimensions of the operand matrices. 308 | * 309 | * @param[in] A An initialized operand matrix. 310 | * @param[in] B An initialized operand matrix. 311 | * @param[out] An initialized matrix object to store the result of A + B. 312 | * 313 | * @return Upon a successful summation ulapack_success is returned. 314 | * ulapack_invalid_argument is returned if size(A) != size(B). 315 | * ulapack_uninit_obj is returned if the operand objects passed in are 316 | * not initialized. 317 | * ulapack_invalid_argument is returned if the result pointer is NULL. 318 | * ulapack_invalid_argument is returned if dynamic allocation is 319 | * specified, and the dimensions of the result matrix do not equal that 320 | * of the operands. 321 | */ 322 | MatrixError_t ulapack_add(const Matrix_t * const A, 323 | const Matrix_t * const B, 324 | Matrix_t * const result); 325 | 326 | /** 327 | * @name ulapack_subtract 328 | * Take the difference between two matrices or vectors. 329 | * 330 | * @note the dimensions of the two matrices must be identical. 331 | * @note if static allocation is specified, the dimensions of the result matrix 332 | * are modified to equal the dimensions of the operand matrices. 333 | * 334 | * @param[in] A An initialized operand matrix. 335 | * @param[in] B An initialized operand matrix. 336 | * @param[out] result An initialized matrix object to store the result of A - B. 337 | * 338 | * @return Upon a successful subtraction ulapack_success is returned. 339 | * ulapack_invalid_argument is returned if size(A) != size(B). 340 | * ulapack_uninit_obj is returned if the operand objects passed in are 341 | * not initialized. 342 | * ulapack_invalid_argument is returned if the result pointer is NULL. 343 | * ulapack_invalid_argument is returned if dynamic allocation is 344 | * specified, and the dimensions of the result matrix do not equal that 345 | * of the operands. 346 | */ 347 | MatrixError_t ulapack_subtract(const Matrix_t * const A, 348 | const Matrix_t * const B, 349 | Matrix_t * const result); 350 | 351 | /** 352 | * @name ulapack_scale 353 | * Multiply a matrix/vector by a scalar. 354 | * 355 | * @param[in] matrix An initialized operand matrix object to scale. 356 | * @param scalar A value to scale a matrix by. 357 | * @param[out] result The matrix to store the result of the scaling into. 358 | * 359 | * @note If static memory is specified, the dimensions of the result matrix are 360 | * set to that of the operand matrix. 361 | * @note If dynamic memory allocation is specified, the initialized dimensions 362 | * of the result matrix must be equal to that of the input matrix. 363 | * 364 | * @return ULAPack success code ulapack_success is returned upon a successful 365 | * operation. 366 | * ulapack_uninit_obj is returned if a matrix object passed in is not 367 | * initialized. 368 | * ulapack_invalid_argument is returned if dynamic allocation is 369 | * specified, and the dimensions of the result matrix do not equal that 370 | * of the operands. 371 | */ 372 | MatrixError_t ulapack_scale(Matrix_t * const matrix, 373 | const MatrixEntry_t scalar, 374 | Matrix_t * const result); 375 | 376 | /** 377 | * @name ulapack_norm 378 | * Take the Frobenius norm of a matrix or norm of a vector. 379 | * 380 | * @param[in] matrix An initialized matrix object to take the norm of. 381 | * @param[out] norm A value to scale a matrix by. 382 | * 383 | * @return ULAPack success code ulapack_success is returned upon a successful 384 | * operation. 385 | * ulapack_uninit_obj is returned if a matrix object passed in is not 386 | * initialized. 387 | * ulapack_invalid_argument is returned if the norm pointer is NULL. 388 | */ 389 | MatrixError_t ulapack_norm(const Matrix_t * const matrix, 390 | MatrixEntry_t * const norm); 391 | 392 | /** 393 | * @name ulapack_trace 394 | * Take the trace of a matrix. 395 | * 396 | * @param[in] matrix An initialized matrix object to take the trace of. 397 | * @param[out] trace The trace output of the matrix. 398 | * 399 | * @return ULAPack success code. ulapack_success is returned upon a successful 400 | * operation. 401 | * ulapack_uninit_obj is returned if a matrix object passed in is not 402 | * initialized. 403 | * ulapack_invalid_argument is returned if the trace pointer is NULL. 404 | */ 405 | MatrixError_t ulapack_trace(const Matrix_t * const matrix, 406 | MatrixEntry_t * const trace); 407 | 408 | /** 409 | * @name ulapack_dot 410 | * Take the dot product of two vectors. 411 | * 412 | * @param[in] vector_a The first vector in the dot product term. 413 | * @param[in] vector_b The second vector in the dot product term. 414 | * @param[out] dot The dot product of the vectors a and b. 415 | * 416 | * @return ULAPack success code. ulapack_success is returned upon a successful 417 | * operation. 418 | * ulapack_uninit_obj is returned if a vector object passed in is not 419 | * initialized. 420 | * ulapack_invalid_argument is returned if the dot pointer is NULL, or 421 | * one or more of the inputs are not vectors, or if the dimensions of 422 | * vectors are not identical. 423 | */ 424 | MatrixError_t ulapack_dot(const Matrix_t * const vector_a, 425 | const Matrix_t * const vector_b, 426 | MatrixEntry_t * const dot); 427 | 428 | /** 429 | * @name ulapack_product 430 | * Multiply two matrices/vectors. 431 | * 432 | * @param[in] A An initialized matrix object operand. 433 | * @param[in] B An initialized matrix object operand. 434 | * @param[out] result An initialized matrix object to store the product of A and 435 | * B. result = AB. 436 | * 437 | * @note For a matrix, A, with dimensions NxM, and a matrix B, with dimensions 438 | * MxK, the product of the two matrices, AB, will have dimensions NxK. 439 | * @note If dynamic memory allocation is specified, the initialized dimensions 440 | * of the result matrix must be NxK. 441 | * @note If static memory allocation is specified, the dimensions of the result 442 | * matrix are set to NxK. 443 | * 444 | * @return ULAPack success code ulapack_success is returned upon a successful 445 | * operation. 446 | * ulapack_uninit_obj is returned if a matrix object passed in is not 447 | * initialized. 448 | * ulapack_invalid_argument is returned if the result pointer is NULL. 449 | * ulapack_invalid_argument is returned if dynamic memory allocation is 450 | * specified and the dimensions of the result matrix do not equal the 451 | * required dimensions of the matrix/vector multiplication. 452 | */ 453 | MatrixError_t ulapack_product(const Matrix_t * const A, 454 | const Matrix_t * const B, 455 | Matrix_t * const result); 456 | 457 | /** 458 | * @name ulapack_transpose 459 | * Take the transpose of a matrix. 460 | * 461 | * @param[in] matrix An initialized matrix object operand. 462 | * @param[out] transpose The transpose of the input matrix. 463 | * 464 | * @note For a matrix, with dimensions NxM, the transpose result matrix will 465 | * have dimensions MxN. 466 | * @note If static memory allocation is specified, the dimensions of the result 467 | * matrix are set to NxK. 468 | * @note If dynamic memory allocation is used, the dimensions of the result 469 | * matrix must be MxN. 470 | * 471 | * @return ULAPack success code ulapack_success is returned upon a successful 472 | * operation. 473 | * ulapack_uninit_obj is returned if a matrix object passed in is not 474 | * initialized. 475 | * ulapack_invalid_argument is returned if the result pointer is NULL. 476 | * ulapack_invalid_argument is returned if dynamic memory allocation is 477 | * specified and the dimensions of the result matrix do not equal the 478 | * required dimensions of the matrix/vector transpose. 479 | */ 480 | MatrixError_t ulapack_transpose(const Matrix_t * const matrix, 481 | Matrix_t * const transpose); 482 | 483 | /** 484 | * @name ulapack_copy 485 | * Copy a matrix. 486 | * 487 | * @param[in] matrix An initialized matrix object operand. 488 | * @param[out] result The copy of the input matrix. 489 | * 490 | * @note For a matrix, with dimensions NxM, the copy result matrix must have 491 | * dimensions JxK, where J >= N and K >= M. 492 | * 493 | * @return ULAPack success code ulapack_success is returned upon a successful 494 | * operation. 495 | * ulapack_uninit_obj is returned if a matrix object passed in is not 496 | * initialized. 497 | * ulapack_uninit_obj is returned if the result pointer is NULL. 498 | * ulapack_invalid_argument is returned if the result matrix is not 499 | * greater than or equal to in size over the input matrix. 500 | */ 501 | MatrixError_t ulapack_copy(const Matrix_t * const matrix, 502 | Matrix_t * const result); 503 | 504 | /** 505 | * @name ulapack_diag 506 | * Put a vector on the diagonal of a matrix. 507 | * 508 | * @note For an Nx1 vector, the matrix must be of size NxN. 509 | * @note For a 1xN vector, the matrix must be of size NxN. 510 | * @note The off diagonal elements of the matrix are set to 0. 511 | * 512 | * @param[in] vector The vector to put on the diagonal of a matrix. 513 | * @param[out] matrix The matrix to put the vector on the diagonal of. 514 | * 515 | * @return ULAPack success code ulapack_success is returned upon a successful 516 | * operation. 517 | * ulapack_uninit_obj is returned if a matrix object passed in is not 518 | * initialized. 519 | * ulapack_uninit_obj is returned if the result pointer is NULL. 520 | * ulapack_invalid_argument is returned if the result matrix does not 521 | * have proper dimensions, or the vector is not a vector. 522 | */ 523 | MatrixError_t ulapack_diag(const Matrix_t * const vector, 524 | Matrix_t * const matrix); 525 | /** 526 | * @name ulapack_det 527 | * Take the determinant of a matrix. 528 | * 529 | * @note The determinant is calculated via an LU decomposition of the matrix. 530 | * For matrices of square dimensions of 4 or more, the matrix is 531 | * decomposed into an upper and lower triangular form first to assist 532 | * in the determinant calculation. 533 | * @note A closed form solution is used for matrices of size 3x3 and smaller. 534 | * 535 | * @param[in] matrix The matrix to take the determinant of. 536 | * @param[out] det The determinant result. 537 | * 538 | * @return ULAPack success code. ulapack_success is returned if the input was 539 | * valid and the matrix determinant was returned successfully. 540 | * ulapack_invalid_argument returned if the input matrix is not square. 541 | * ulapack_invalid_argument if static memory allocation is specified and 542 | * a matrix passed in is larger than the maximum row size limitation set 543 | * by the ULAPACK_MAX_MATRIX_N_ROWS macro. 544 | * ulapack_uninit_obj if the matrix object is not initialized. 545 | */ 546 | MatrixError_t ulapack_det(const Matrix_t * const matrix, 547 | MatrixEntry_t * const det); 548 | 549 | /** 550 | * @ulapack_lu 551 | * LU (lower upper) decompose a matrix. 552 | * 553 | * @note If static memory allocation is specified, the return matrices will have 554 | * equal dimensions to that of the input matrix. 555 | * 556 | * @param[in] matrix The matrix to make upper triangular. 557 | * @param[out] upper_matrix The output upper triangular matrix result. 558 | * @param[out] lower_matrix The output lower triangular matrix result. 559 | * 560 | * @return ULAPack success codes. ulapack_success is returned if both matrices 561 | * passed in are valid to either read from or write to. 562 | * ulapack_uninit_obj is returned if the one or more of the inputs 563 | * are not initialized. 564 | * ulapack_invalid_argument is returned if the input matrix object is 565 | * not square. This is also returned if dynamic memory allocation is 566 | * specified and the upper/lower_matrix argument does not have equal 567 | * dimensions to the input matrix. 568 | */ 569 | MatrixError_t ulapack_lu(const Matrix_t * const matrix, 570 | Matrix_t * const upper_matrix, 571 | Matrix_t * const lower_matrix); 572 | 573 | /** 574 | * @name ulapack_inverse 575 | * Invert a square matrix. 576 | * 577 | * @note If static memory allocation is specified, the dimensions of the result 578 | * matrix are set to that of the input operand matrix. 579 | * @note To compute matrix inversions using LU decomposition, define 580 | * ULAPACK_INVERSE_VIA_LU. Leave undefined for pivot finding/triangulation 581 | * elimination method. 582 | * 583 | * param[in] matrix An initialized matrix to invert. 584 | * param[out] inverse An initialized matrix to store the inverse into. 585 | * 586 | * @return ULAPack success code ulapack_success is returned upon a successful 587 | * operation. 588 | * ulapack_uninit_obj is returned if a matrix object passed in is not 589 | * initialized. 590 | * ulapack_invalid_argument is returned if the result pointer is not 591 | * initialized. 592 | * ulapack_invalid_argument is returned if dynamic memory allocation is 593 | * specified and the dimensions of the result matrix do not equal the 594 | * required dimensions of the matrix inversion. 595 | * ulapack_invalid_argument is returned if the input matrix is not 596 | * square. 597 | */ 598 | MatrixError_t ulapack_inverse(const Matrix_t * const matrix, 599 | Matrix_t * const inverse); 600 | 601 | /** 602 | * @name ulapack_pinverse 603 | * Take the pseudo (Moore-Penrose) inverse, A^*, of a matrix, A. 604 | * 605 | * @note The pseudo inverse operation is computed via A^* = (A^T * A)^-1 * A^T 606 | * As a result, the ulapack_inverse is used and is therefore dependent 607 | * on the method used for matrix inversion operation. 608 | * @note For an input matrix with dimensions NxM, the pseudo inverse will be of 609 | * size MxN. 610 | * 611 | * @todo Make this work with SVD to take the pseudo inverse of poorly 612 | * conditioned matrices. 613 | * 614 | * @param[in] matrix The matrix to take the pseudo inverse of. 615 | * @param[out] pinverse The result pseudo inverse result. 616 | * 617 | * @return ULAPack success code ulapack_success is returned upon a successful 618 | * operation. 619 | * ulapack_uninit_obj is returned if a matrix object passed in is not 620 | * initialized. 621 | * ulapack_invalid_argument is returned if the result pointer is not 622 | * initialized. 623 | * ulapack_invalid_argument is returned if dynamic memory allocation is 624 | * specified and the dimensions of the result matrix do not equal the 625 | * required dimensions of the matrix inversion. 626 | */ 627 | MatrixError_t ulapack_pinverse(const Matrix_t * const matrix, 628 | Matrix_t * const pinverse); 629 | 630 | /** 631 | * @name ulapack_least_squares 632 | * Minimize the sum of the squares of the residuals of a system of equations. 633 | * 634 | * @brief For a system of equations: y = Ax 635 | * Let A be a matrix representing a system of linear equations, or 636 | * mapping from the input to an output of the system. 637 | * Let y be the output vector of that of a system. 638 | * Let x be the independent variable of the system. 639 | * The least squares solutions will yield an approximate value for x 640 | * given a vector of observations, y, and a system, A. 641 | * i.e. x ~= ( (A^T * A)^-1 * A^T ) * y = (A^*)y 642 | * 643 | * @todo Use SVD as the back-end for this function instead of LU decomposition 644 | * within the pinverse function. 645 | * 646 | * @note For a matrix A of dimension NxM, y must have dimensions Nx1, and x must 647 | * have dimensions Mx1. 648 | * 649 | * @param[in] A the matrix representing the system of linear equations. 650 | * @param[in] y The vector of observations or measurements 651 | * @param[out] x The vector to approximate. 652 | * 653 | * @return ULAPack success code ulapack_success is returned upon a successful 654 | * operation. 655 | * ulapack_uninit_obj is returned if a matrix object passed in is not 656 | * initialized. 657 | * ulapack_invalid_argument is returned if the result pointer is not 658 | * initialized. 659 | * ulapack_invalid_argument is returned if dynamic memory allocation is 660 | * specified and the dimensions of the result matrix do not equal the 661 | * required dimensions of the matrix inversion. 662 | * ulapack_invalid_argument is returned if the input matrix and vectors 663 | * do not match their expected dimensions. 664 | */ 665 | MatrixError_t ulapack_least_squares(const Matrix_t * const A, 666 | const Matrix_t * const y, 667 | Matrix_t * const x); 668 | 669 | /** 670 | * @name ulapack_vandermonde 671 | * Create a Vandermonde matrix from a vector. 672 | * 673 | * @brief Let V be the Vandermonde matrix created, V_{i,j} = x_{i}^(j-1). 674 | * 675 | * @note x must be a column vector. 676 | * 677 | * @param[in] x The vector to make the Vandermonde matrix out of. 678 | * @param order The order of the matrix. 679 | * @param[out] V the Vandermonde matrix created. 680 | * 681 | * @return ULAPack success code ulapack_success is returned upon a successful 682 | * operation. 683 | * ulapack_uninit_obj is returned if a matrix object passed in is not 684 | * initialized. 685 | * ulapack_invalid_argument is returned if the result pointer is not 686 | * initialized. 687 | * ulapack_invalid_argument is returned if dynamic memory allocation is 688 | * specified and the dimensions of the result matrix do not equal the 689 | * required dimensions of the matrix operations. 690 | * ulapack_invalid_argument is returned if the input matrix and vectors 691 | * do not match their expected dimensions. 692 | */ 693 | MatrixError_t ulapack_vandermonde(const Matrix_t * const x, 694 | const Index_t order, 695 | Matrix_t * const V); 696 | 697 | /** 698 | * @name ulapack_power 699 | * Take every element in a matrix to the power of a specified value. 700 | * 701 | * @param[in] matrix The matrix to take the power of. 702 | * @param power The power to take the matrix to the power of. 703 | * @param[out] result The result of the operation. 704 | * 705 | * @return ULAPack success code ulapack_success is returned upon a successful 706 | * operation. 707 | * ulapack_uninit_obj is returned if a matrix object passed in is not 708 | * initialized. 709 | * ulapack_invalid_argument is returned if the result pointer is not 710 | * initialized. 711 | */ 712 | MatrixError_t ulapack_power(const Matrix_t * const matrix, 713 | const Index_t power, 714 | Matrix_t * const result); 715 | 716 | /** 717 | * @name ulapack_polyfit 718 | * Fit an nth degree polynomial. 719 | * 720 | * @note x and y must have equal dimensions, Mx1. 721 | * @note x and y must be column vectors. 722 | * @note The result coefficient vector, p, must have dimension n+1 x 1. 723 | * @note n must be greater than or equal to M+1. 724 | * 725 | * @param[in] x The independent variable in a system. 726 | * @param[in] y The dependent variable in a system. 727 | * @param n The number of degrees to run a polynomial regression on. 728 | * @param[out] p The polynomial coefficients of the regression. 729 | * 730 | * @return ULAPack success code ulapack_success is returned upon a successful 731 | * operation. 732 | * ulapack_uninit_obj is returned if a matrix object passed in is not 733 | * initialized. 734 | * ulapack_invalid_argument is returned if the result pointer is not 735 | * initialized. 736 | * ulapack_invalid_argument is returned if dynamic memory allocation is 737 | * specified and the dimensions of the result matrix do not equal the 738 | * required dimensions of the matrix operations. 739 | * ulapack_invalid_argument is returned if the input matrix and vectors 740 | * do not match their expected dimensions. 741 | */ 742 | MatrixError_t ulapack_polyfit(const Matrix_t * const x, 743 | const Matrix_t * const y, 744 | const Index_t n, 745 | Matrix_t * const p); 746 | 747 | /* 748 | * @name ulapack_svd 749 | * Decompose a matrix using Singular Value Decomposition (SVD). 750 | * 751 | * @brief Takes an MxN matrix and decomposes it into UDV, where U and V are left 752 | * and right orthogonal transformation matrices, and D is a diagonal 753 | * matrix of singular values. 754 | * 755 | * This routine is adapted from svdecomp.c in XLISP-STAT 2.1 which is 756 | * code from Numerical Recipes adapted by Luke Tierney and David Betz. 757 | * This code has been modified for use in ULAPack in the following ways: 758 | * - Safer type checking. 759 | * - Use with both statically and dynamically allocated memory. 760 | * - Adapted with non-clib calls (except for sqrt()). 761 | * - Use of ULAPack object types and error codes. 762 | * 763 | * @note The number of rows of the input matrix must be greater than or equal to 764 | * the number of columns in the input matrix, i.e. (M >= N). 765 | * 766 | * @param[in] matrix An MxN tall (m > n) matrix to be decomposed. 767 | * @param[out] U The left orthogonal transformation matrix. U is an MxN matrix. 768 | * @param[out] S The vector of singular values of the matrix. S is an N vector. 769 | * @param[out] V The right orthogonal transformation matrix. V is an NxN matrix. 770 | * 771 | * @return ULAPack error code. 772 | * ulapack_success is returned upon a successful decomposition. 773 | * ulapack_error is returned if the matrix cannot be decomposed due to a 774 | * lack of numerical convergence (1000 * MAX_CONV_LOOPS). 775 | * ulapack_unint_obj is returned if one or more of the passed in matrix 776 | * objects have not been initialized. 777 | * ulapack_invalid_argument is returned if the matrix dimensions are not 778 | * equal to their expected values for the SVD operation if dynamic 779 | * memory allocation is specified. If static allocation is specified 780 | * the dimensions of the passed in parameters are modified. 781 | */ 782 | #define MAX_CONV_LOOPS (30) 783 | MatrixError_t ulapack_svd(const Matrix_t * const matrix, 784 | Matrix_t * const U, Matrix_t * const S, Matrix_t * const V); 785 | 786 | /** 787 | * @name ulapack_pca 788 | * Principle Component Analysis 789 | * 790 | * @brief A statistical procedure that uses an orthogonal transformation to 791 | * convert a set of observations of possibly correlated variables into a 792 | * set of values of linearly uncorrelated variables called principal 793 | * components. 794 | * 795 | * @note ulapack_svd is needed to compute the PCA of a matrix. 796 | * 797 | * @param matrix[in] matrix The matrix to analyze. 798 | * @param matrix[out] T The score matrix. 799 | * 800 | * @return ULAPack error code. 801 | * ulapack_success is returned upon a successful PCA operation. 802 | * ulapack_error is returned if the matrix cannot be decomposed due to a 803 | * lack of numerical convergence (1000 * MAX_CONV_LOOPS) from SVD. 804 | * ulapack_unint_obj is returned if one or more of the passed in matrix 805 | * objects have not been initialized. 806 | * ulapack_invalid_argument is returned if the dimensions of T is not 807 | * equal to its expected value if dynamic memory allocation is 808 | * specified. If static allocation is specified the dimensions of the 809 | * passed in parameters are modified. 810 | */ 811 | MatrixError_t ulapack_pca(const Matrix_t * const matrix, Matrix_t * const T); 812 | 813 | /* 814 | * End header guard definition. 815 | */ 816 | #endif 817 | -------------------------------------------------------------------------------- /src/ulapack_options.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ulapack_options.h 3 | * @brief Settings profile for uLAPack operations and usage. 4 | * 5 | * @author Sargis Yonan 6 | * @date July 8, 2017 7 | * 8 | * @version 1.0.1 9 | **/ 10 | 11 | /* 12 | * File header guards. 13 | */ 14 | #ifndef __ULAPACK_OPTIONS_H__ 15 | #define __ULAPACK_OPTIONS_H__ 16 | 17 | /* 18 | * Define the entry data type. 19 | */ 20 | #ifndef ULAPACK_MATRIX_ENTRY_TYPE 21 | #define ULAPACK_MATRIX_ENTRY_TYPE double 22 | #endif 23 | 24 | /* 25 | * The data type for the row and column elements. 26 | * Unsigned type recommended. 27 | */ 28 | #ifndef ULAPACK_INDEX_TYPE 29 | #include 30 | #define ULAPACK_INDEX_TYPE uint64_t 31 | #endif 32 | 33 | /* 34 | * The signed equivalent data type for ULAPACK_INDEX_TYPE. 35 | */ 36 | #ifndef ULAPACK_SIGNED_INDEX_TYPE 37 | #define ULAPACK_SIGNED_INDEX_TYPE int64_t 38 | #endif 39 | 40 | /* 41 | * Define ULAPACK_USE_PRINT to enable the ulapack_print function. 42 | */ 43 | #define ULAPACK_USE_PRINT 44 | #ifdef ULAPACK_USE_PRINT 45 | /* 46 | * The fprintf delimiter must be defined if ULAPACK_USE_PRINT is defined. 47 | * ULAPACK_MATRIX_ENTRY_TYPE is currently set to double, and so the delimiter is 48 | * set to %lf including the '%' symbol. 49 | */ 50 | #ifndef ULAPACK_PRINT_DELIMITER 51 | #define ULAPACK_PRINT_DELIMITER "%.4lf" 52 | #endif 53 | 54 | #ifndef ULAPACK_PRINT_DELIMITER 55 | #error "ULAPACK_PRINT_DELIMITER must be defined when ULAPACK_USE_PRINT is enabled." 56 | #endif 57 | #endif 58 | 59 | /* 60 | * Small value for floating point tolerance considerations 61 | * for inversions and FP tolerance. 62 | */ 63 | #ifndef MINIMUM_THRESHOLD_TOLERANCE 64 | #define MINIMUM_THRESHOLD_TOLERANCE (.0001) 65 | #endif 66 | 67 | /* 68 | * Defined if memory should be initialized to zeros. 69 | */ 70 | #ifndef ULAPACK_INITIALIZE_MEMORY 71 | #define ULAPACK_INITIALIZE_MEMORY 72 | #endif 73 | 74 | /* 75 | * Option for using LU decomposition for matrix inversions. 76 | */ 77 | #ifndef ULAPACK_INVERSE_VIA_LU 78 | #define ULAPACK_INVERSE_VIA_LU 79 | #endif 80 | 81 | /* 82 | * Keep defined for static memory allocation declarations. 83 | */ 84 | // #define ULAPACK_USE_STATIC_ALLOC 85 | 86 | /* 87 | * Keep defined for dynamic memory allocation. 88 | */ 89 | // #define ULAPACK_USE_DYNAMIC_ALLOC 90 | 91 | #ifndef ULAPACK_USE_STATIC_ALLOC 92 | #ifndef ULAPACK_USE_DYNAMIC_ALLOC 93 | #error "Error: Neither ULAPACK_USE_DYNAMIC_ALLOC or ULAPACK_USE_STATIC_ALLOC are defined." 94 | #endif 95 | #endif 96 | 97 | #ifdef ULAPACK_USE_STATIC_ALLOC 98 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 99 | #error "Error: Both ULAPACK_USE_DYNAMIC_ALLOC and ULAPACK_USE_STATIC_ALLOC can not be simultaneously defined." 100 | #endif 101 | #endif 102 | 103 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 104 | /* 105 | * Specify the memory allocator function to use which takes the form of stdlib's 106 | * malloc function : void *alloc(size_t bytes). 107 | */ 108 | #ifndef ULAPACK_ALLOC 109 | #include 110 | #define ULAPACK_ALLOC malloc 111 | #endif 112 | 113 | /* 114 | * Specify the memory freeing function to use which takes the form of stdlib's 115 | * free function : void free(void* ptr). 116 | */ 117 | #ifndef ULAPACK_FREE 118 | #include 119 | #define ULAPACK_FREE free 120 | #endif 121 | 122 | #endif 123 | 124 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 125 | #ifndef ULAPACK_ALLOC 126 | #error "Error: ULAPACK_ALLOC is not defined even though ULAPACK_USE_DYNAMIC_ALLOC is defined." 127 | #endif 128 | 129 | #ifndef ULAPACK_FREE 130 | #error "Error: ULAPACK_FREE is not defined even though ULAPACK_USE_DYNAMIC_ALLOC is defined." 131 | #endif 132 | #endif 133 | 134 | #ifdef ULAPACK_USE_STATIC_ALLOC 135 | /* 136 | * Set the largest row and column sizes for uLAPack matrix types. 137 | * 138 | * @note only required to be specified when static allocation is used. 139 | */ 140 | #ifndef ULAPACK_MAX_MATRIX_N_ROWS 141 | #define ULAPACK_MAX_MATRIX_N_ROWS (20u) 142 | #endif 143 | #ifndef ULAPACK_MAX_MATRIX_N_COLS 144 | #define ULAPACK_MAX_MATRIX_N_COLS (20u) 145 | #endif 146 | 147 | /* 148 | * The maximum number of rows and columns must be specified 149 | * for static matrix allocation. 150 | */ 151 | #ifndef ULAPACK_MAX_MATRIX_N_ROWS 152 | #error "ULAPACK_MAX_MATRIX_N_ROWS not defined." 153 | #endif 154 | #ifndef ULAPACK_MAX_MATRIX_N_COLS 155 | #error "ULAPACK_MAX_MATRIX_N_COLS not defined." 156 | #endif 157 | #endif 158 | 159 | /* 160 | * End header guard definition. 161 | */ 162 | #endif -------------------------------------------------------------------------------- /src/ulapack_type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ulapack_type.h 3 | * @brief Matrix structure type definitions for the uLAPack library. 4 | * 5 | * This file contains the structure definition for both statically 6 | * and dynamically allocated matrix types. 7 | * 8 | * @author Sargis Yonan 9 | * @date July 8, 2017 10 | * 11 | * @version 1.0.1 12 | **/ 13 | 14 | /* 15 | * File header guards. 16 | */ 17 | #ifndef __ULAPACK_TYPE_H__ 18 | #define __ULAPACK_TYPE_H__ 19 | 20 | /* 21 | * Including standard integer header for uintX_t types. 22 | */ 23 | #include 24 | 25 | /* 26 | * uLAPack options header. 27 | */ 28 | #include "ulapack_options.h" 29 | 30 | /* 31 | * Define the matrix type. 32 | */ 33 | typedef ULAPACK_MATRIX_ENTRY_TYPE MatrixEntry_t; 34 | 35 | /* 36 | * Define the index types. 37 | */ 38 | typedef ULAPACK_INDEX_TYPE Index_t; 39 | typedef ULAPACK_SIGNED_INDEX_TYPE SIndex_t; 40 | 41 | #ifdef ULAPACK_USE_STATIC_ALLOC 42 | /* 43 | * Definition for the static ulapck_matrix object. 44 | */ 45 | typedef struct ulapack_matrix { 46 | /*@{*/ 47 | MatrixEntry_t entry[ULAPACK_MAX_MATRIX_N_ROWS] 48 | [ULAPACK_MAX_MATRIX_N_COLS]; /**< the matrix elements. */ 49 | 50 | uint64_t n_rows; /**< number of rows in the matrix. */ 51 | uint64_t n_cols; /**< number of columns in the matrix. */ 52 | /*@}*/ 53 | } Matrix_t; 54 | #endif 55 | 56 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 57 | /** 58 | * Definition for the dynamic ulapck_matrix object. 59 | */ 60 | typedef struct ulapack_matrix { 61 | /*@{*/ 62 | MatrixEntry_t **entry; /**< pointer to the matrix elements. */ 63 | uint64_t n_rows; /**< number of rows in the matrix. */ 64 | uint64_t n_cols; /**< number of columns in the matrix. */ 65 | /*@}*/ 66 | } Matrix_t; 67 | #endif 68 | 69 | /* 70 | * Definition of error types. 71 | */ 72 | typedef enum { 73 | /*@{*/ 74 | ulapack_error = -1, /**< general error code. */ 75 | ulapack_oom = -2, /**< out of memory error code. */ 76 | ulapack_invalid_argument = -3, /**< bad argument given to function. */ 77 | ulapack_uninit_obj = -4, /**< uninitialized object passed into function. */ 78 | ulapack_success = 1, /**< general success code. */ 79 | /*@}*/ 80 | } MatrixError_t; 81 | 82 | /* 83 | * End header guard definition. 84 | */ 85 | #endif -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | cc=cc 2 | cc_options=-std=c99 -Wall -Wextra -Werror -Os 3 | 4 | static_test_exec=ulapack_test_static 5 | static_test_options=-DULAPACK_USE_STATIC_ALLOC=1 -DULAPACK_MAX_MATRIX_N_COLS=40 -DULAPACK_MAX_MATRIX_N_ROWS=40 6 | 7 | dynamic_test_exec=ulapack_test_dynamic 8 | dynamic_test_options=-DULAPACK_USE_DYNAMIC_ALLOC=1 9 | 10 | src=../src 11 | 12 | all : $(static_test_exec) $(dynamic_test_exec) 13 | 14 | test : $(static_test_exec) $(dynamic_test_exec) 15 | ./$(static_test_exec) 16 | ./$(dynamic_test_exec) 17 | 18 | $(static_test_exec) : static_unit_tests.o ulapack_static.o 19 | $(cc) $(cc_options) unit_tests.o ulapack.o -lm -o $(static_test_exec) 20 | 21 | $(dynamic_test_exec) : dynamic_unit_tests.o ulapack_dynamic.o 22 | $(cc) $(cc_options) unit_tests.o ulapack.o -lm -o $(dynamic_test_exec) 23 | 24 | dynamic_unit_tests.o : unit_tests.c ulapack_dynamic.o 25 | $(cc) $(cc_options) -I$(src) -c unit_tests.c $(dynamic_test_options) 26 | 27 | static_unit_tests.o : unit_tests.c ulapack_static.o 28 | $(cc) $(cc_options) -I$(src) -c unit_tests.c $(static_test_options) 29 | 30 | ulapack_static.o : $(src)/ulapack.c ulapack_headers 31 | $(cc) $(cc_options) -c $(src)/ulapack.c $(static_test_options) 32 | 33 | ulapack_dynamic.o : $(src)/ulapack.c ulapack_headers 34 | $(cc) $(cc_options) -c $(src)/ulapack.c $(dynamic_test_options) 35 | 36 | ulapack_headers : $(src)/ulapack.h $(src)/ulapack_options.h $(src)/ulapack_type.h 37 | 38 | clean : 39 | rm *.o $(dynamic_test_exec) $(static_test_exec) -------------------------------------------------------------------------------- /test/unit_tests.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file main.c 3 | * @brief Main test driving code for uLAPack. 4 | * 5 | * @author Sargis Yonan 6 | * @date July 8, 2017 7 | * 8 | * @version 1.0.1 9 | **/ 10 | 11 | #include "ulapack.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #define UT_PI 3.14159265358979323846264338327 18 | 19 | /* 20 | * Total unit test error counter. 21 | */ 22 | static uint64_t ut_error_counter = 0; 23 | 24 | /** 25 | * @name ut_iserror 26 | * @brief Prints an error message if an error code is given as an argument. 27 | * 28 | * @note The total error counter is incremented upon passed in error. 29 | * 30 | * @param err The error code 31 | * 32 | * @return true if an error code was passed in. False if not an error code. 33 | * 34 | * @todo Print the actual error message describing the actual error if one. 35 | * Haven't had the use for this yet, but the function is piped in for 36 | * that purpose. 37 | */ 38 | static bool ut_iserror(MatrixError_t err) { 39 | if (err != ulapack_success) { 40 | printf("Unit test error: return code %d\n", (int)err); 41 | ut_error_counter++; 42 | return true; 43 | } 44 | 45 | return false; 46 | } 47 | 48 | #define INFO(msg) \ 49 | fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \ 50 | fprintf(stderr, "%s\n", msg) 51 | 52 | #define ut_incr_error_if(condition, msg) do { \ 53 | if (condition) { \ 54 | INFO(msg); \ 55 | ut_error_counter++; \ 56 | break; \ 57 | } else { \ 58 | break; \ 59 | } } while(1) 60 | 61 | #define ut_incr_error_ifnot(condition, msg) do { \ 62 | if (! (condition) ) { \ 63 | INFO(msg); \ 64 | ut_error_counter++; \ 65 | break; \ 66 | } else { \ 67 | break; \ 68 | } } while(1) 69 | 70 | static MatrixError_t test_initialization(void) { 71 | MatrixError_t ret_code; 72 | 73 | #ifdef ULAPACK_USE_STATIC_ALLOC 74 | Matrix_t A; 75 | Matrix_t B; 76 | Matrix_t C; 77 | 78 | /* 79 | * Test under the maximum, and check for success. 80 | */ 81 | ret_code = ulapack_init(&A, 3u, 3u); 82 | if (ut_iserror(ret_code)) { 83 | printf("Cannot initialize matrix A.\n"); 84 | return ret_code; 85 | } 86 | if (A.n_rows != 3 || A.n_cols != 3) { 87 | printf("Matrix A dimensions do not match expected values.\n"); 88 | return ulapack_error; 89 | } 90 | 91 | ret_code = ulapack_init(&B, 92 | ULAPACK_MAX_MATRIX_N_ROWS, 93 | ULAPACK_MAX_MATRIX_N_COLS); 94 | 95 | if (ut_iserror(ret_code)) { 96 | printf("Cannot initialize matrix B.\n"); 97 | return ret_code; 98 | } 99 | if (B.n_rows != ULAPACK_MAX_MATRIX_N_ROWS || 100 | B.n_cols != ULAPACK_MAX_MATRIX_N_COLS) { 101 | printf("Matrix B dimensions do not match expected values.\n"); 102 | return ulapack_error; 103 | } 104 | 105 | /* 106 | * Try to allocate more than allowed and check for failure. 107 | */ 108 | ret_code = ulapack_init(&C, 109 | ULAPACK_MAX_MATRIX_N_ROWS + 1, 110 | ULAPACK_MAX_MATRIX_N_COLS + 1); 111 | 112 | ut_incr_error_ifnot(ret_code == ulapack_invalid_argument, 113 | "Matrix C allocated when exceeding expected limits." 114 | "Error in initialization function."); 115 | 116 | /* 117 | * The rows and cols should be zero. 118 | */ 119 | if (C.n_rows != 0 || C.n_cols != 0) { 120 | printf("Matrix C dimensions does not match expected values of 0.\n"); 121 | return ulapack_error; 122 | } 123 | 124 | /* 125 | * Try to give init NULL. 126 | */ 127 | ret_code = ulapack_init(NULL, 128 | 1, 129 | 1); 130 | 131 | ut_incr_error_ifnot(ret_code == ulapack_invalid_argument, 132 | "NULL allocated. Error in initialization function."); 133 | 134 | #endif 135 | 136 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 137 | Matrix_t *A = NULL; 138 | /* 139 | * Test dynamic initialization. 140 | */ 141 | ret_code = ulapack_init(&A, 3u, 3u); 142 | if (ut_iserror(ret_code)) { 143 | printf("Cannot initialize matrix A.\n"); 144 | return ret_code; 145 | } 146 | if (A->n_rows != 3 || A->n_cols != 3) { 147 | printf("Matrix A dimensions do not match expected values.\n"); 148 | return ulapack_error; 149 | } 150 | 151 | /* 152 | * Try reinitializing the matrix. It should fail. 153 | */ 154 | ret_code = ulapack_init(&A, 3u, 3u); 155 | ut_incr_error_ifnot(ret_code == ulapack_invalid_argument, 156 | "Error: Reinitialize matrix A."); 157 | 158 | if (A->n_rows != 3 || A->n_cols != 3) { 159 | printf("Matrix A reinitialized and lost row and col count.\n"); 160 | return ulapack_error; 161 | } 162 | 163 | ulapack_destroy(A); 164 | 165 | #endif 166 | 167 | return ulapack_success; 168 | } 169 | 170 | static MatrixError_t test_basic_operations(void) { 171 | 172 | Matrix_t *A = NULL; 173 | Matrix_t *B = NULL; 174 | Matrix_t *result = NULL; 175 | Matrix_t *result2 = NULL; 176 | Matrix_t *expected = NULL; 177 | 178 | MatrixEntry_t entry_value = 0; 179 | 180 | MatrixError_t ret_value; 181 | 182 | #ifdef ULAPACK_USE_STATIC_ALLOC 183 | Matrix_t Amem; 184 | Matrix_t Bmem; 185 | Matrix_t Resultmem; 186 | Matrix_t Result2mem; 187 | Matrix_t Expectedmem; 188 | 189 | A = &Amem; 190 | B = &Bmem; 191 | result = &Resultmem; 192 | result2 = &Result2mem; 193 | expected = &Expectedmem; 194 | 195 | ut_iserror(ulapack_init(A, 3u, 3u)); 196 | ut_iserror(ulapack_init(B, 3u, 3u)); 197 | 198 | ut_iserror(ulapack_init(result, 3u, 3u)); 199 | ut_iserror(ulapack_init(result2, 3u, 3u)); 200 | ut_iserror(ulapack_init(expected, 3u, 3u)); 201 | #endif 202 | 203 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 204 | ut_iserror(ulapack_init(&A, 3u, 3u)); 205 | ut_iserror(ulapack_init(&B, 3u, 3u)); 206 | 207 | ut_iserror(ulapack_init(&result, 3u, 3u)); 208 | ut_iserror(ulapack_init(&result2, 3u, 3u)); 209 | ut_iserror(ulapack_init(&expected, 3u, 3u)); 210 | #endif 211 | 212 | /* 213 | * First test the happy path. 214 | */ 215 | 216 | /* 217 | * Set A=3 * I_{3x3}. 218 | */ 219 | ulapack_eye(A); 220 | ulapack_scale(A, 3, A); 221 | 222 | printf("\nMatrix A =\n"); 223 | ulapack_print(A, stdout); 224 | 225 | ulapack_sum(A, &entry_value); 226 | ut_incr_error_ifnot(entry_value == 3+3+3, 227 | "Expected sum result doesn't equal actual."); 228 | printf("sum(A) = " ULAPACK_PRINT_DELIMITER "\n", entry_value); 229 | 230 | 231 | ulapack_norm(A, &entry_value); 232 | ut_incr_error_ifnot(entry_value == sqrt(3*3 + 3*3 + 3*3), 233 | "Expected norm result doesn't equal actual."); 234 | printf("norm(A) = " ULAPACK_PRINT_DELIMITER "\n", entry_value); 235 | 236 | ut_iserror(ulapack_det(A, &entry_value)); 237 | ut_incr_error_ifnot(entry_value == 27, 238 | "Expected determinant result doesn't equal actual."); 239 | printf("det(A) = " ULAPACK_PRINT_DELIMITER "\n", entry_value); 240 | 241 | ut_iserror(ulapack_inverse(A, result)); 242 | printf("\ninv(A) =\n"); 243 | ulapack_print(result, stdout); 244 | 245 | ut_iserror(ulapack_pinverse(A, result2)); 246 | printf("\npinv(A) =\n"); 247 | ulapack_print(result2, stdout); 248 | 249 | /* 250 | * Expected: pinv == inv for this matrix. 251 | */ 252 | ulapack_is_equal(result, result2, &ret_value); 253 | ut_incr_error_ifnot(ret_value == ulapack_success, "Error in pinv or inv."); 254 | 255 | /* 256 | * Set B equal to a matrix of all pi. 257 | */ 258 | ulapack_set(B, UT_PI); 259 | ut_iserror(ulapack_add(A, B, result)); 260 | 261 | ulapack_edit_entry(expected, 0, 0, UT_PI + 3); 262 | ulapack_edit_entry(expected, 0, 1, UT_PI); 263 | ulapack_edit_entry(expected, 0, 2, UT_PI); 264 | ulapack_edit_entry(expected, 1, 0, UT_PI); 265 | ulapack_edit_entry(expected, 1, 1, UT_PI + 3); 266 | ulapack_edit_entry(expected, 1, 2, UT_PI); 267 | ulapack_edit_entry(expected, 2, 0, UT_PI); 268 | ulapack_edit_entry(expected, 2, 1, UT_PI); 269 | ulapack_edit_entry(expected, 2, 2, UT_PI + 3); 270 | 271 | ulapack_is_equal(expected, result, &ret_value); 272 | 273 | ut_incr_error_ifnot(ret_value == ulapack_success, 274 | "Expected addition result doesn't equal actual."); 275 | 276 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 277 | ulapack_destroy(A); 278 | ulapack_destroy(B); 279 | ulapack_destroy(expected); 280 | ulapack_destroy(result); 281 | ulapack_destroy(result2); 282 | #endif 283 | 284 | return ulapack_success; 285 | } 286 | 287 | static MatrixError_t test_polyfit(void) { 288 | Matrix_t *x = NULL; 289 | Matrix_t *y = NULL; 290 | Matrix_t *p = NULL; 291 | 292 | /* 293 | * The number of data points. 294 | */ 295 | const Index_t N = 30; 296 | 297 | /* 298 | * The degree of the polynomial to fit. 299 | */ 300 | const Index_t n = 1; 301 | 302 | MatrixError_t ret_value; 303 | 304 | Index_t element = 0; 305 | 306 | const MatrixEntry_t x_data[] = {1.000000, 307 | 2.000000, 308 | 3.000000, 309 | 4.000000, 310 | 5.000000, 311 | 6.000000, 312 | 7.000000, 313 | 8.000000, 314 | 9.000000, 315 | 10.000000, 316 | 11.000000, 317 | 12.000000, 318 | 13.000000, 319 | 14.000000, 320 | 15.000000, 321 | 16.000000, 322 | 17.000000, 323 | 18.000000, 324 | 19.000000, 325 | 20.000000, 326 | 21.000000, 327 | 22.000000, 328 | 23.000000, 329 | 24.000000, 330 | 25.000000, 331 | 26.000000, 332 | 27.000000, 333 | 28.000000, 334 | 29.000000, 335 | 30.000000}; 336 | 337 | const MatrixEntry_t y_data[] = {0.764894, 338 | 5.877292, 339 | 9.609239, 340 | 15.889726, 341 | 25.277457, 342 | 36.087046, 343 | 49.173593, 344 | 64.453591, 345 | 80.396482, 346 | 99.202976, 347 | 121.360649, 348 | 145.885092, 349 | 168.693178, 350 | 194.987276, 351 | 224.761454, 352 | 255.515985, 353 | 288.672655, 354 | 324.475518, 355 | 360.869995, 356 | 399.405647, 357 | 440.556233, 358 | 482.677470, 359 | 528.140479, 360 | 574.586999, 361 | 624.484014, 362 | 674.914902, 363 | 728.292841, 364 | 783.698345, 365 | 839.799559, 366 | 899.980638}; 367 | 368 | printf("Testing polynomial fitting.\n"); 369 | 370 | #ifdef ULAPACK_USE_STATIC_ALLOC 371 | Matrix_t xmem; 372 | Matrix_t ymem; 373 | Matrix_t pmem; 374 | 375 | x = &xmem; 376 | y = &ymem; 377 | p = &pmem; 378 | 379 | ut_iserror(ulapack_init(x, N, 1)); 380 | ut_iserror(ulapack_init(y, N, 1)); 381 | ut_iserror(ulapack_init(p, n + 1, 1)); 382 | 383 | #endif 384 | 385 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 386 | ut_iserror(ulapack_init(&x, N, 1)); 387 | ut_iserror(ulapack_init(&y, N, 1)); 388 | ut_iserror(ulapack_init(&p, n + 1, 1)); 389 | #endif 390 | 391 | for (element = 0; element < N; element++) { 392 | ulapack_edit_entry(x, element, 0, x_data[element]); 393 | ulapack_edit_entry(y, element, 0, y_data[element]); 394 | } 395 | 396 | ret_value = ulapack_polyfit(x, y, n, p); 397 | if (ut_iserror(ret_value)) { 398 | return ret_value; 399 | } 400 | 401 | printf("\np=\n"); 402 | ulapack_print(p, stdout); 403 | 404 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 405 | ulapack_destroy(x); 406 | ulapack_destroy(y); 407 | ulapack_destroy(p); 408 | #endif 409 | 410 | return ulapack_success; 411 | } 412 | 413 | 414 | static MatrixError_t test_svd(void) { 415 | Matrix_t *A = NULL; 416 | Matrix_t *U = NULL; 417 | Matrix_t *S = NULL; 418 | Matrix_t *V = NULL; 419 | 420 | /* 421 | * The number of rows in A. 422 | */ 423 | const Index_t N = 3; 424 | 425 | MatrixError_t ret_value; 426 | 427 | Index_t row_itor, col_itor = 0; 428 | 429 | const MatrixEntry_t Adata[6][3] = {{0.0120, 2.8743, 6.4553}, 430 | {3.1642, 5.0166, 1.2322}, 431 | {6.9962, 7.6155, 5.0440}, 432 | {6.2526, 7.6241, 3.4726}, 433 | {5.4306, 5.7606, 0.9215}, 434 | {4.3904, 7.4766, 1.4785}}; 435 | 436 | printf("Testing SVD.\n"); 437 | 438 | #ifdef ULAPACK_USE_STATIC_ALLOC 439 | Matrix_t Amem; 440 | Matrix_t Umem; 441 | Matrix_t Smem; 442 | Matrix_t Vmem; 443 | 444 | A = &Amem; 445 | U = &Umem; 446 | S = &Smem; 447 | V = &Vmem; 448 | 449 | ut_iserror(ulapack_init(A, N, N)); 450 | ut_iserror(ulapack_init(U, N, N)); 451 | ut_iserror(ulapack_init(S, N, 1)); 452 | ut_iserror(ulapack_init(V, N, N)); 453 | 454 | #endif 455 | 456 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 457 | ut_iserror(ulapack_init(&A, N, N)); 458 | ut_iserror(ulapack_init(&U, N, N)); 459 | ut_iserror(ulapack_init(&S, N, 1)); 460 | ut_iserror(ulapack_init(&V, N, N)); 461 | #endif 462 | 463 | for (row_itor = 0; row_itor < N; row_itor++) { 464 | for (col_itor = 0; col_itor < N; col_itor++) { 465 | ulapack_edit_entry(A, 466 | row_itor, col_itor, 467 | Adata[row_itor][col_itor]); 468 | } 469 | } 470 | 471 | printf("A = \n"); 472 | ulapack_print(A, stdout); 473 | 474 | ret_value = ulapack_svd(A, U, S, V); 475 | if (ut_iserror(ret_value)) { 476 | return ret_value; 477 | } 478 | 479 | printf("U =\n"); 480 | ulapack_print(U, stdout); 481 | printf("S =\n"); 482 | ulapack_print(S, stdout); 483 | printf("V =\n"); 484 | ulapack_print(V, stdout); 485 | 486 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 487 | ulapack_destroy(A); 488 | 489 | ulapack_destroy(U); 490 | ulapack_destroy(S); 491 | ulapack_destroy(V); 492 | #endif 493 | 494 | return ulapack_success; 495 | } 496 | 497 | static MatrixError_t test_pca(void) { 498 | Matrix_t *A = NULL; 499 | Matrix_t *T = NULL; 500 | 501 | /* 502 | * The number of rows in A. 503 | */ 504 | const Index_t N = 3; 505 | 506 | MatrixError_t ret_value; 507 | 508 | Index_t row_itor, col_itor = 0; 509 | 510 | const MatrixEntry_t Adata[3][3] = {{0.0120, 2.8743, 6.4553}, 511 | {3.1642, 5.0166, 1.2322}, 512 | {6.9962, 7.6155, 5.0440}}; 513 | 514 | printf("Testing PCA.\n"); 515 | 516 | #ifdef ULAPACK_USE_STATIC_ALLOC 517 | Matrix_t Amem; 518 | Matrix_t Tmem; 519 | 520 | A = &Amem; 521 | T = &Tmem; 522 | 523 | ut_iserror(ulapack_init(A, N, N)); 524 | ut_iserror(ulapack_init(T, N, N)); 525 | 526 | #endif 527 | 528 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 529 | ut_iserror(ulapack_init(&A, N, N)); 530 | ut_iserror(ulapack_init(&T, N, N)); 531 | #endif 532 | 533 | for (row_itor = 0; row_itor < N; row_itor++) { 534 | for (col_itor = 0; col_itor < N; col_itor++) { 535 | ulapack_edit_entry(A, 536 | row_itor, col_itor, 537 | Adata[row_itor][col_itor]); 538 | } 539 | } 540 | 541 | printf("A = \n"); 542 | ulapack_print(A, stdout); 543 | 544 | ret_value = ulapack_pca(A, T); 545 | if (ut_iserror(ret_value)) { 546 | return ret_value; 547 | } 548 | 549 | printf("T =\n"); 550 | ulapack_print(T, stdout); 551 | 552 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 553 | ulapack_destroy(A); 554 | ulapack_destroy(T); 555 | #endif 556 | 557 | return ulapack_success; 558 | } 559 | 560 | int main(void) { 561 | 562 | printf("uLAPack Unit Tests.\n"); 563 | #ifdef ULAPACK_USE_STATIC_ALLOC 564 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 565 | #error "Unit Test Error: More than one alloc type defined." 566 | #else 567 | printf("Testing statically allocated structures.\n"); 568 | #endif 569 | #endif 570 | 571 | #ifdef ULAPACK_USE_DYNAMIC_ALLOC 572 | #ifdef ULAPACK_USE_STATIC_ALLOC 573 | #error "Unit Test Error: More than one alloc type defined." 574 | #else 575 | printf("Testing dynamically allocated structures.\n"); 576 | #endif 577 | #endif 578 | 579 | 580 | if (test_initialization() != ulapack_success) { 581 | return ulapack_error; 582 | } 583 | if (test_basic_operations() != ulapack_success) { 584 | return ulapack_error; 585 | } 586 | if (test_polyfit() != ulapack_success) { 587 | return ulapack_error; 588 | } 589 | if (test_svd() != ulapack_success) { 590 | return ulapack_error; 591 | } 592 | if (test_pca() != ulapack_success) { 593 | return ulapack_error; 594 | } 595 | 596 | printf("Total Unit Test errors: %llu\n", 597 | (long long unsigned int)ut_error_counter); 598 | return 0; 599 | } 600 | 601 | 602 | --------------------------------------------------------------------------------