SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
sddsmatrixmult.c
Go to the documentation of this file.
1/**
2 * @file sddsmatrixmult.c
3 * @brief Multiplies matrices from SDDS files and outputs the result.
4 *
5 * @details
6 * This program reads two SDDS files containing matrix data, performs matrix multiplication,
7 * and writes the resulting product matrix to an output SDDS file. It supports options for
8 * data reuse, matrix commutation, output format selection, and verbosity for debugging and
9 * diagnostics.
10 *
11 * @section Usage
12 * ```
13 * sddsmatrixmult [<file1>] <file2> [<output>]
14 * [-pipe=[input][,output]]
15 * [-majorOrder=row|column]
16 * [-commute]
17 * [-reuse]
18 * [-verbose]
19 * [-ascii]
20 * [-threads=<number>]
21 * ```
22 *
23 * @section Options
24 * | Option | Description |
25 * |------------------------|-----------------------------------------------------------------|
26 * | `-pipe` | Use input and/or output pipes. |
27 * | `-majorOrder` | Specify output in row-major or column-major order. |
28 * | `-commute` | Swap file1 and file2 to reverse the order of multiplication. |
29 * | `-reuse` | Reuse the last data page if a file runs out of pages. |
30 * | `-ascii` | Output file in ASCII mode. |
31 * | `-verbose` | Enable detailed output for diagnostics. |
32 * | `-threads` | Number of threads for matrix multiplication. |
33 *
34 * @copyright
35 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
36 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
37 *
38 * @license
39 * This file is distributed under the terms of the Software License Agreement
40 * found in the file LICENSE included with this distribution.
41 *
42 * @author
43 * M. Borland, C. Saunders, L. Emery, R. Soliday, H. Shang
44 */
45
46#include "mdb.h"
47#include "scan.h"
48#include "match_string.h"
49#include "matlib.h"
50#include "SDDS.h"
51
52/* Enumeration for option types */
53enum option_type {
54 CLO_PIPE,
55 CLO_VERBOSE,
56 CLO_ASCII,
57 CLO_REUSE,
58 CLO_COMMUTE,
59 CLO_MAJOR_ORDER,
60 CLO_THREADS,
61 N_OPTIONS
62};
63
64char *commandline_option[N_OPTIONS] = {
65 "pipe",
66 "verbose",
67 "ascii",
68 "reuse",
69 "commute",
70 "majorOrder",
71 "threads",
72};
73
74static char *USAGE =
75 "sddsmatrixmult [OPTIONS] [<file1>] <file2>\n"
76 " [-pipe=[input][,output]]\n"
77 " [-majorOrder=row|column]\n"
78 " [-commute]\n"
79 " [-reuse]\n"
80 " [-verbose]\n"
81 " [-ascii]\n"
82 " [-threads=<number>]\n"
83 "Options:\n"
84 " -pipe=[input][,output] Read input from and/or write output to a pipe.\n"
85 " -majorOrder=row|column Specify output in row or column major order.\n"
86 " -commute Use file1 as the right-hand matrix and file2 as the left-hand matrix.\n"
87 " -reuse Reuse the last data page if a file runs out of data pages.\n"
88 " -verbose Write diagnostic messages to stderr.\n"
89 " -ascii Output the file in ASCII mode.\n\n"
90 " -threads=<number> Number of threads for matrix multiplication.\n\n"
91 "Description:\n"
92 " Multiplies matrices from SDDS files file1 and file2.\n"
93 " - file1: SDDS file for the left-hand matrix of the product.\n"
94 " - file2: SDDS file for the right-hand matrix of the product.\n"
95 " - output: SDDS file for the resulting product matrix.\n\n"
96 "Author:\n"
97 " L. Emery ANL (" __DATE__ " " __TIME__ ", SVN revision: " SVN_VERSION ")\n";
98
99static int threaded_matrix_mult(MATRIX *C, MATRIX *A, MATRIX *B, int threads);
100
101int main(int argc, char **argv) {
102 SCANNED_ARG *s_arg;
103 SDDS_TABLE input1Page, input2Page, outputPage;
104
105 char *inputfile1, *inputfile2, *outputfile;
106 char **Input1Column, **Input1DoubleColumn;
107 char **Input2Column, **Input2DoubleColumn;
108 char **OutputDoubleColumn;
109 long Input1Rows, Input1DoubleColumns;
110 long Input2Rows, Input2DoubleColumns;
111 long OutputRows;
112 int32_t Input1Columns, Input2Columns, OutputDoubleColumns;
113
114 long i, i_arg, col, commute;
115#define BUFFER_SIZE_INCREMENT 20
116 MATRIX *R1, *R1Trans, *R2, *R2Trans, *R3, *R3Trans;
117 long verbose;
118 long ipage, ipage1, ipage2, lastPage1, lastPage2, ascii;
119 unsigned long pipeFlags, majorOrderFlag;
120 long tmpfile_used, noWarnings;
121 long reuse;
122 int threads;
123 short columnMajorOrder = -1;
124
126 argc = scanargs(&s_arg, argc, argv);
127 if (argc == 1)
128 bomb(NULL, USAGE);
129
130 inputfile1 = inputfile2 = outputfile = NULL;
131 Input1DoubleColumn = Input2DoubleColumn = OutputDoubleColumn = NULL;
132 Input1Rows = Input1DoubleColumns = Input2Rows = Input2DoubleColumns = OutputRows = lastPage1 = lastPage2 = 0;
133 tmpfile_used = 0;
134 verbose = 0;
135 pipeFlags = 0;
136 noWarnings = 0;
137 reuse = commute = ascii = 0;
138 threads = 1;
139
140 for (i_arg = 1; i_arg < argc; i_arg++) {
141 if (s_arg[i_arg].arg_type == OPTION) {
142 switch (match_string(s_arg[i_arg].list[0], commandline_option, N_OPTIONS, UNIQUE_MATCH)) {
143 case CLO_MAJOR_ORDER:
144 majorOrderFlag = 0;
145 s_arg[i_arg].n_items--;
146 if (s_arg[i_arg].n_items > 0 &&
147 (!scanItemList(&majorOrderFlag, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
148 "row", -1, NULL, 0, SDDS_ROW_MAJOR_ORDER,
149 "column", -1, NULL, 0, SDDS_COLUMN_MAJOR_ORDER, NULL))) {
150 SDDS_Bomb("Invalid -majorOrder syntax/values");
151 }
152 if (majorOrderFlag & SDDS_COLUMN_MAJOR_ORDER)
153 columnMajorOrder = 1;
154 else if (majorOrderFlag & SDDS_ROW_MAJOR_ORDER)
155 columnMajorOrder = 0;
156 break;
157 case CLO_PIPE:
158 if (!processPipeOption(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, &pipeFlags))
159 SDDS_Bomb("Invalid -pipe syntax");
160 break;
161 case CLO_VERBOSE:
162 verbose = 1;
163 break;
164 case CLO_ASCII:
165 ascii = 1;
166 break;
167 case CLO_REUSE:
168 reuse = 1;
169 break;
170 case CLO_COMMUTE:
171 commute = 1;
172 break;
173 case CLO_THREADS:
174 if (s_arg[i_arg].n_items != 2 ||
175 sscanf(s_arg[i_arg].list[1], "%d", &threads) != 1 || threads < 1)
176 SDDS_Bomb("Invalid -threads syntax");
177 break;
178 default:
179 bomb("Unrecognized option given", USAGE);
180 }
181 } else {
182 if (!inputfile1)
183 inputfile1 = s_arg[i_arg].list[0];
184 else if (!inputfile2)
185 inputfile2 = s_arg[i_arg].list[0];
186 else if (!outputfile)
187 outputfile = s_arg[i_arg].list[0];
188 else
189 bomb("Too many filenames given", USAGE);
190 }
191 }
192
193 if (pipeFlags & USE_STDIN && inputfile1) {
194 if (outputfile)
195 SDDS_Bomb("Too many filenames (sddsxref)");
196 outputfile = inputfile2;
197 inputfile2 = inputfile1;
198 inputfile1 = NULL;
199 }
200
201 processFilenames("sddsmatrixmult", &inputfile1, &outputfile, pipeFlags, noWarnings, &tmpfile_used);
202 if (!inputfile2)
203 SDDS_Bomb("Second input file not specified");
204
205 if (commute) {
206 char *ptr = inputfile1;
207 inputfile1 = inputfile2;
208 inputfile2 = ptr;
209 }
210
211 /* Initialize input files */
212 if (!SDDS_InitializeInput(&input1Page, inputfile1))
213 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
214
215 Input1Column = (char **)SDDS_GetColumnNames(&input1Page, &Input1Columns);
216
217 if (!SDDS_InitializeInput(&input2Page, inputfile2))
218 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
219
220 Input2Column = (char **)SDDS_GetColumnNames(&input2Page, &Input2Columns);
221
222 if (!SDDS_InitializeOutput(&outputPage, ascii ? SDDS_ASCII : SDDS_BINARY, 1, "Matrix product", "Matrix product", outputfile))
223 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
224
225 if (columnMajorOrder != -1)
226 outputPage.layout.data_mode.column_major = columnMajorOrder;
227 else
228 outputPage.layout.data_mode.column_major = input1Page.layout.data_mode.column_major;
229
230 /* Process data pages */
231 while ((ipage1 = SDDS_ReadTable(&input1Page)) && (ipage2 = SDDS_ReadTable(&input2Page))) {
232 if ((reuse && (ipage1 < 0 && ipage2 < 0)) ||
233 (!reuse && (ipage1 < 0 || ipage2 < 0)))
234 break;
235
236 ipage = MAX(ipage1, ipage2);
237
238 /* Process first input file */
239 if (ipage1 == 1) {
240 Input1DoubleColumns = 0;
241 Input1DoubleColumn = (char **)malloc(Input1Columns * sizeof(char *));
242 /* Count the numerical columns in input1 */
243 for (i = 0; i < Input1Columns; i++) {
244 if (SDDS_NUMERIC_TYPE(SDDS_GetColumnType(&input1Page, i))) {
245 Input1DoubleColumn[Input1DoubleColumns] = Input1Column[i];
246 Input1DoubleColumns++;
247 }
248 }
249 if (!Input1DoubleColumns) {
250 if (verbose) {
251 fprintf(stderr, "No numerical columns in page %ld of file %s.\n", ipage, inputfile1 ? inputfile1 : "stdin");
252 }
253 }
254 Input1Rows = SDDS_CountRowsOfInterest(&input1Page);
255 if (Input1DoubleColumns && Input1Rows)
256 m_alloc(&R1, Input1DoubleColumns, Input1Rows);
257 else if (!Input1Rows) {
258 if (verbose)
259 fprintf(stderr, "No rows in page %ld of file %s.\n", ipage, inputfile1 ? inputfile1 : "stdin");
260 }
261 }
262
263 if (ipage1 > 0) {
264 if (Input1Rows != SDDS_CountRowsOfInterest(&input1Page))
265 fprintf(stderr, "Number of rows in page %ld of file %s changed.\n", ipage, inputfile1 ? inputfile1 : "stdin");
266 for (col = 0; col < Input1DoubleColumns; col++) {
267 if (!(R1->a[col] = (double *)SDDS_GetColumnInDoubles(&input1Page, Input1DoubleColumn[col])))
268 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
269 }
270 lastPage1 = ipage1;
271 if (verbose)
272 fprintf(stderr, "Using page %ld of file %s.\n", lastPage1, inputfile1 ? inputfile1 : "stdin");
273 } else if (ipage1 < 0) {
274 if (verbose)
275 fprintf(stderr, "Reusing page %ld of file %s.\n", lastPage1, inputfile1 ? inputfile1 : "stdin");
276 }
277
278 if (verbose && Input1DoubleColumns && Input1Rows) {
279 m_alloc(&R1Trans, Input1Rows, Input1DoubleColumns);
280 m_trans(R1Trans, R1);
281 m_show(R1Trans, "%9.6le ", "Input matrix 1:\n", stderr);
282 m_free(&R1Trans);
283 }
284
285 /* Process second input file */
286 if (ipage2 == 1) {
287 Input2DoubleColumns = 0;
288 Input2DoubleColumn = (char **)malloc(Input2Columns * sizeof(char *));
289 /* Count the numerical columns in input2 */
290 for (i = 0; i < Input2Columns; i++) {
291 if (SDDS_NUMERIC_TYPE(SDDS_GetColumnType(&input2Page, i))) {
292 Input2DoubleColumn[Input2DoubleColumns] = Input2Column[i];
293 Input2DoubleColumns++;
294 }
295 }
296 if (!Input2DoubleColumns) {
297 if (verbose) {
298 fprintf(stderr, "No numerical columns in page %ld of file %s.\n", ipage, inputfile2);
299 }
300 }
301 Input2Rows = SDDS_CountRowsOfInterest(&input2Page);
302 if (Input2DoubleColumns && Input2Rows)
303 m_alloc(&R2, Input2DoubleColumns, Input2Rows);
304 else if (!Input2Rows) {
305 if (verbose)
306 fprintf(stderr, "No rows in page %ld of file %s.\n", ipage, inputfile2);
307 }
308 }
309
310 if (ipage2 > 0) {
311 if (Input2Rows != SDDS_CountRowsOfInterest(&input2Page))
312 fprintf(stderr, "Number of rows in page %ld of file %s changed.\n", ipage, inputfile2);
313 for (col = 0; col < Input2DoubleColumns; col++) {
314 if (!(R2->a[col] = (double *)SDDS_GetColumnInDoubles(&input2Page, Input2DoubleColumn[col])))
315 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
316 }
317 lastPage2 = ipage2;
318 if (verbose)
319 fprintf(stderr, "Using page %ld of file %s.\n", lastPage2, inputfile2 ? inputfile2 : "stdin");
320 } else if (ipage2 < 0) {
321 if (verbose)
322 fprintf(stderr, "Reusing page %ld of file %s.\n", lastPage2, inputfile2 ? inputfile2 : "stdin");
323 }
324
325 if (verbose && Input2DoubleColumns && Input2Rows) {
326 m_alloc(&R2Trans, Input2Rows, Input2DoubleColumns);
327 m_trans(R2Trans, R2);
328 m_show(R2Trans, "%9.6le ", "Input matrix 2:\n", stderr);
329 m_free(&R2Trans);
330 }
331
332 /* Define output table */
333 if (ipage == 1) {
334 OutputRows = Input1Rows;
335 OutputDoubleColumns = Input2DoubleColumns;
336 if (Input1DoubleColumns != Input2Rows) {
337 fprintf(stderr, "Error: Dimension mismatch in files.\n");
338 fprintf(stderr, "Right-hand matrix (%s) is %ldx%ld.\n",
339 inputfile2 ? inputfile2 : "stdin", Input2Rows, Input2DoubleColumns);
340 fprintf(stderr, "Left-hand matrix (%s) is %ldx%ld.\n",
341 inputfile1 ? inputfile1 : "stdin", Input1Rows, Input1DoubleColumns);
342 exit(EXIT_FAILURE);
343 }
344 }
345
346 /* Perform matrix multiplication */
347 if (OutputRows && OutputDoubleColumns) {
348 if (ipage == 1)
349 m_alloc(&R3, OutputDoubleColumns, OutputRows);
350 if (verbose)
351 fprintf(stderr, "Multiplying %d x %d matrix by %d x %d matrix\n", R2->m, R2->n, R1->m, R1->n);
352 if (!threaded_matrix_mult(R3, R2, R1, threads))
353 SDDS_Bomb("Matrix multiplication failed");
354 if (verbose) {
355 m_alloc(&R3Trans, OutputRows, OutputDoubleColumns);
356 m_trans(R3Trans, R3);
357 m_show(R3Trans, "%9.6le ", "Output matrix:\n", stderr);
358 m_free(&R3Trans);
359 }
360 } else {
361 if (verbose)
362 fprintf(stderr, "Output file will either have no columns or no rows in page %ld.\n", ipage);
363 }
364
365 if (ipage == 1) {
366 for (i = 0; i < Input2DoubleColumns; i++) {
367 if (SDDS_TransferColumnDefinition(&outputPage, &input2Page, Input2DoubleColumn[i], NULL) < 0)
368 SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors | SDDS_VERBOSE_PrintErrors);
369 }
370 OutputDoubleColumn = (char **)SDDS_GetColumnNames(&outputPage, &OutputDoubleColumns);
371 if (!SDDS_WriteLayout(&outputPage))
372 SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors | SDDS_VERBOSE_PrintErrors);
373 }
374
375 if (!SDDS_StartTable(&outputPage, OutputRows))
376 SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors | SDDS_VERBOSE_PrintErrors);
377
378 /* Assign values to output table */
379 if (OutputRows && OutputDoubleColumns) {
380 for (i = 0; i < OutputDoubleColumns; i++) { /* i is the column index */
381 if (!SDDS_SetColumnFromDoubles(&outputPage, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE,
382 R3->a[i], OutputRows, OutputDoubleColumn[i]))
383 SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors | SDDS_VERBOSE_PrintErrors);
384 }
385 }
386
387 if (!SDDS_WriteTable(&outputPage))
388 SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors | SDDS_VERBOSE_PrintErrors);
389 }
390
391 /* Terminate SDDS tables */
392 if (!SDDS_Terminate(&input1Page) || !SDDS_Terminate(&input2Page) || !SDDS_Terminate(&outputPage))
393 SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors | SDDS_VERBOSE_PrintErrors);
394
395 if (tmpfile_used && !replaceFileAndBackUp(inputfile1, outputfile))
396 return EXIT_FAILURE;
397
398 return EXIT_SUCCESS;
399}
400
401static int threaded_matrix_mult(MATRIX *C, MATRIX *A, MATRIX *B, int threads) {
402 long i;
403 long n, m, p;
404
405 if ((m = A->m) != B->n || (n = A->n) != C->n || (p = B->m) != C->m)
406 return 0;
407 if (threads <= 1)
408 return m_mult(C, A, B);
409
410#pragma omp parallel for if (threads > 1) num_threads(threads)
411 for (i = 0; i < n; i++) {
412 long j, k;
413 double *a_i = A->a[i];
414 double *c_i = C->a[i];
415 for (j = 0; j < p; j++) {
416 double sum = 0;
417 for (k = 0; k < m; k++)
418 sum += a_i[k] * B->a[k][j];
419 c_i[j] = sum;
420 }
421 }
422 return 1;
423}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_SetColumnFromDoubles(SDDS_DATASET *SDDS_dataset, int32_t mode, double *data, int64_t rows,...)
Sets the values for a single data column using double-precision floating-point numbers.
int64_t SDDS_CountRowsOfInterest(SDDS_DATASET *SDDS_dataset)
Counts the number of rows marked as "of interest" in the current data table.
double * SDDS_GetColumnInDoubles(SDDS_DATASET *SDDS_dataset, char *column_name)
Retrieves the data of a specified numerical column as an array of doubles, considering only rows mark...
int32_t SDDS_InitializeInput(SDDS_DATASET *SDDS_dataset, char *filename)
Definition SDDS_input.c:50
int32_t SDDS_Terminate(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
int32_t SDDS_TransferColumnDefinition(SDDS_DATASET *target, SDDS_DATASET *source, char *name, char *newName)
Transfers a column definition from a source dataset to a target dataset.
char ** SDDS_GetColumnNames(SDDS_DATASET *SDDS_dataset, int32_t *number)
Retrieves the names of all columns in the SDDS dataset.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
Definition SDDS_utils.c:474
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
Definition SDDS_utils.c:318
int32_t SDDS_GetColumnType(SDDS_DATASET *SDDS_dataset, int32_t index)
Retrieves the data type of a column in the SDDS dataset by its index.
void SDDS_Bomb(char *message)
Terminates the program after printing an error message and recorded errors.
Definition SDDS_utils.c:380
#define SDDS_NUMERIC_TYPE(type)
Checks if the given type identifier corresponds to any numeric type.
Definition SDDStypes.h:138
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
Definition bomb.c:26
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
long replaceFileAndBackUp(char *file, char *replacement)
Replaces a file with a replacement file and creates a backup of the original.
Definition replacefile.c:78
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
Definition scanargs.c:36
long processPipeOption(char **item, long items, unsigned long *flags)
Definition scanargs.c:357
void processFilenames(char *programName, char **input, char **output, unsigned long pipeFlags, long noWarnings, long *tmpOutputUsed)
Definition scanargs.c:391
long scanItemList(unsigned long *flags, char **item, long *items, unsigned long mode,...)
Scans a list of items and assigns values based on provided keywords and types.