ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmtxop.c
Revision: 2.14
Committed: Mon Aug 12 02:26:46 2019 UTC (4 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.13: +21 -9 lines
Log Message:
Added support for trailing unary operations on final matrix result

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.14 static const char RCSid[] = "$Id: rmtxop.c,v 2.13 2019/08/12 01:20:26 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * General component matrix operations.
6     */
7    
8     #include <stdio.h>
9     #include <stdlib.h>
10 greg 2.11 #include <errno.h>
11 greg 2.1 #include "rtio.h"
12     #include "resolu.h"
13     #include "rmatrix.h"
14 greg 2.10 #include "platform.h"
15 greg 2.1
16 greg 2.13 #define MAXCOMP 16 /* #components we support */
17 greg 2.1
18 greg 2.13 static const char stdin_name[] = "<stdin>";
19    
20     /* unary matrix operation(s) */
21 greg 2.1 typedef struct {
22     double sca[MAXCOMP]; /* scalar coefficients */
23     double cmat[MAXCOMP*MAXCOMP]; /* component transformation */
24 greg 2.13 short nsf; /* number of scalars */
25     short clen; /* number of coefficients */
26     short transpose; /* do transpose? */
27     } RUNARYOP;
28    
29     /* matrix input source and requested operation(s) */
30     typedef struct {
31     const char *inspec; /* input specification */
32     RUNARYOP preop; /* unary operation(s) */
33     RMATRIX *mtx; /* original matrix if loaded */
34     int binop; /* binary op with next (or 0) */
35     } ROPMAT;
36 greg 2.1
37     int verbose = 0; /* verbose reporting? */
38    
39 greg 2.13 /* Load matrix */
40     static int
41     loadmatrix(ROPMAT *rop)
42 greg 2.1 {
43 greg 2.13 if (rop->mtx != NULL)
44     return(0);
45    
46     rop->mtx = rmx_load(rop->inspec == stdin_name ?
47     (const char *)NULL : rop->inspec);
48     if (rop->mtx == NULL) {
49     fputs(rop->inspec, stderr);
50     fputs(": cannot load matrix\n", stderr);
51     return(-1);
52     }
53     return(1);
54 greg 2.1 }
55    
56 greg 2.13 /* Get matrix and perform unary operations */
57 greg 2.1 static RMATRIX *
58 greg 2.13 loadop(ROPMAT *rop)
59 greg 2.1 {
60 greg 2.13 RMATRIX *mres;
61 greg 2.1 int i;
62    
63 greg 2.13 if (loadmatrix(rop) < 0) /* make sure we're loaded */
64 greg 2.1 return(NULL);
65 greg 2.13
66     if (rop->preop.nsf > 0) { /* apply scalar(s) */
67     if (rop->preop.clen > 0) {
68 greg 2.1 fputs("Options -s and -c are exclusive\n", stderr);
69 greg 2.13 goto failure;
70 greg 2.1 }
71 greg 2.13 if (rop->preop.nsf == 1) {
72     for (i = rop->mtx->ncomp; --i; )
73     rop->preop.sca[i] = rop->preop.sca[0];
74     } else if (rop->preop.nsf != rop->mtx->ncomp) {
75 greg 2.1 fprintf(stderr, "%s: -s must have one or %d factors\n",
76 greg 2.13 rop->inspec, rop->mtx->ncomp);
77     goto failure;
78 greg 2.1 }
79 greg 2.13 if (!rmx_scale(rop->mtx, rop->preop.sca)) {
80     fputs(rop->inspec, stderr);
81 greg 2.1 fputs(": scalar operation failed\n", stderr);
82 greg 2.13 goto failure;
83 greg 2.1 }
84     if (verbose) {
85 greg 2.13 fputs(rop->inspec, stderr);
86 greg 2.1 fputs(": applied scalar (", stderr);
87 greg 2.13 for (i = 0; i < rop->preop.nsf; i++)
88     fprintf(stderr, " %f", rop->preop.sca[i]);
89 greg 2.1 fputs(" )\n", stderr);
90     }
91     }
92 greg 2.13 if (rop->preop.clen > 0) { /* apply transform */
93     if (rop->preop.clen % rop->mtx->ncomp) {
94 greg 2.1 fprintf(stderr, "%s: -c must have N x %d coefficients\n",
95 greg 2.13 rop->inspec, rop->mtx->ncomp);
96     goto failure;
97 greg 2.1 }
98 greg 2.13 mres = rmx_transform(rop->mtx, rop->preop.clen/rop->mtx->ncomp,
99     rop->preop.cmat);
100     if (mres == NULL) {
101     fprintf(stderr, "%s: matrix transform failed\n",
102     rop->inspec);
103     goto failure;
104 greg 2.1 }
105     if (verbose)
106     fprintf(stderr, "%s: applied %d x %d transform\n",
107 greg 2.13 rop->inspec, mres->ncomp,
108     rop->mtx->ncomp);
109     rmx_free(rop->mtx);
110     rop->mtx = mres;
111 greg 2.1 }
112 greg 2.13 if (rop->preop.transpose) { /* transpose matrix? */
113     mres = rmx_transpose(rop->mtx);
114     if (mres == NULL) {
115     fputs(rop->inspec, stderr);
116 greg 2.12 fputs(": transpose failed\n", stderr);
117 greg 2.13 goto failure;
118 greg 2.12 }
119     if (verbose) {
120 greg 2.13 fputs(rop->inspec, stderr);
121 greg 2.12 fputs(": transposed rows and columns\n", stderr);
122     }
123 greg 2.13 rmx_free(rop->mtx);
124     rop->mtx = mres;
125     }
126     mres = rop->mtx;
127     rop->mtx = NULL;
128     return(mres);
129     failure:
130     rmx_free(rop->mtx);
131     return(rop->mtx = NULL);
132     }
133    
134     /* Execute binary operation, free matrix arguments and return new result */
135     static RMATRIX *
136     binaryop(const char *inspec, RMATRIX *mleft, int op, RMATRIX *mright)
137     {
138     RMATRIX *mres = NULL;
139     int i;
140    
141     if ((mleft == NULL) | (mright == NULL))
142     return(NULL);
143    
144     switch (op) {
145     case '.': /* concatenate */
146     mres = rmx_multiply(mleft, mright);
147     rmx_free(mleft);
148 greg 2.12 rmx_free(mright);
149 greg 2.1 if (mres == NULL) {
150 greg 2.13 fputs(inspec, stderr);
151 greg 2.1 if (mleft->ncols != mright->nrows)
152     fputs(": mismatched dimensions for multiply\n",
153     stderr);
154     else
155     fputs(": concatenation failed\n", stderr);
156     return(NULL);
157     }
158     if (verbose) {
159 greg 2.13 fputs(inspec, stderr);
160 greg 2.1 fputs(": concatenated matrix\n", stderr);
161     }
162 greg 2.13 break;
163     case '+':
164     if (!rmx_sum(mleft, mright, NULL)) {
165     fputs(inspec, stderr);
166 greg 2.1 fputs(": matrix sum failed\n", stderr);
167 greg 2.13 rmx_free(mleft);
168 greg 2.1 rmx_free(mright);
169     return(NULL);
170     }
171     if (verbose) {
172 greg 2.13 fputs(inspec, stderr);
173 greg 2.1 fputs(": added in matrix\n", stderr);
174     }
175     rmx_free(mright);
176 greg 2.13 mres = mleft;
177     break;
178     case '*':
179     case '/': {
180     const char * tnam = (op == '/') ?
181 greg 2.11 "division" : "multiplication";
182     errno = 0;
183 greg 2.13 if (!rmx_elemult(mleft, mright, (op == '/'))) {
184 greg 2.11 fprintf(stderr, "%s: element-wise %s failed\n",
185 greg 2.13 inspec, tnam);
186     rmx_free(mleft);
187 greg 2.11 rmx_free(mright);
188     return(NULL);
189     }
190     if (errno)
191     fprintf(stderr,
192     "%s: warning - error during element-wise %s\n",
193 greg 2.13 inspec, tnam);
194 greg 2.11 else if (verbose)
195 greg 2.13 fprintf(stderr, "%s: element-wise %s\n", inspec, tnam);
196 greg 2.11 rmx_free(mright);
197 greg 2.13 mres = mleft;
198     } break;
199     default:
200     fprintf(stderr, "%s: unknown operation '%c'\n", inspec, op);
201     rmx_free(mleft);
202 greg 2.1 rmx_free(mright);
203     return(NULL);
204     }
205 greg 2.13 return(mres);
206     }
207    
208     /* Perform matrix operations from left to right */
209     static RMATRIX *
210     op_left2right(ROPMAT *mop)
211     {
212     RMATRIX *mleft = loadop(mop);
213    
214     while (mop->binop) {
215     if (mleft == NULL)
216     break;
217     mleft = binaryop(mop[1].inspec,
218     mleft, mop->binop, loadop(mop+1));
219     mop++;
220     }
221 greg 2.1 return(mleft);
222     }
223    
224 greg 2.13 /* Perform matrix operations from right to left */
225     static RMATRIX *
226     op_right2left(ROPMAT *mop)
227     {
228     RMATRIX *mright;
229     int rpos = 0;
230     /* find end of list */
231     while (mop[rpos].binop)
232 greg 2.14 if (mop[rpos++].binop != '.') {
233     fputs(
234     "Right-to-left evaluation only for matrix multiplication!\n",
235     stderr);
236     return(NULL);
237     }
238 greg 2.13 mright = loadop(mop+rpos);
239     while (rpos-- > 0) {
240     if (mright == NULL)
241     break;
242     mright = binaryop(mop[rpos].inspec,
243     loadop(mop+rpos), mop[rpos].binop, mright);
244     }
245     return(mright);
246     }
247    
248     #define t_nrows(mop) ((mop)->preop.transpose ? (mop)->mtx->ncols \
249     : (mop)->mtx->nrows)
250     #define t_ncols(mop) ((mop)->preop.transpose ? (mop)->mtx->nrows \
251     : (mop)->mtx->ncols)
252    
253     /* Should we prefer concatenating from rightmost matrix towards left? */
254     static int
255     prefer_right2left(ROPMAT *mop)
256     {
257     int mri = 0;
258    
259     while (mop[mri].binop) /* find rightmost matrix */
260     if (mop[mri++].binop != '.')
261     return(0); /* pre-empt reversal for other ops */
262    
263     if (mri <= 1)
264     return(0); /* won't matter */
265    
266     if (loadmatrix(mop+mri) < 0) /* load rightmost cat */
267     return(1); /* fail will bail in a moment */
268    
269     if (t_ncols(mop+mri) == 1)
270     return(1); /* definitely better R->L */
271    
272     if (t_ncols(mop+mri) >= t_nrows(mop+mri))
273     return(0); /* ...probably worse */
274    
275     if (loadmatrix(mop) < 0) /* load leftmost */
276     return(0); /* fail will bail in a moment */
277    
278     return(t_ncols(mop+mri) < t_nrows(mop));
279     }
280    
281 greg 2.1 static int
282     get_factors(double da[], int n, char *av[])
283     {
284     int ac;
285    
286     for (ac = 0; ac < n && isflt(av[ac]); ac++)
287     da[ac] = atof(av[ac]);
288     return(ac);
289     }
290    
291 greg 2.13 static ROPMAT *
292     grow_moparray(ROPMAT *mop, int n2alloc)
293     {
294     int nmats = 0;
295    
296     while (mop[nmats++].binop)
297     ;
298     mop = (ROPMAT *)realloc(mop, n2alloc*sizeof(ROPMAT));
299     if (mop == NULL) {
300     fputs("Out of memory in grow_moparray()\n", stderr);
301     exit(1);
302     }
303     if (n2alloc > nmats)
304     memset(mop+nmats, 0, (n2alloc-nmats)*sizeof(ROPMAT));
305     return(mop);
306     }
307    
308 greg 2.1 /* Load one or more matrices and operate on them, sending results to stdout */
309     int
310     main(int argc, char *argv[])
311     {
312 greg 2.7 int outfmt = DTfromHeader;
313 greg 2.13 int nall = 2;
314     ROPMAT *mop = (ROPMAT *)calloc(nall, sizeof(ROPMAT));
315     int nmats = 0;
316 greg 2.1 RMATRIX *mres = NULL;
317 greg 2.13 int stdin_used = 0;
318 greg 2.1 int i;
319     /* get options and arguments */
320 greg 2.13 for (i = 1; i < argc; i++) {
321 greg 2.11 if (argv[i][0] && !argv[i][1] &&
322 greg 2.13 strchr(".+*/", argv[i][0]) != NULL) {
323     if (mop[nmats].inspec == NULL || mop[nmats].binop) {
324 greg 2.14 fprintf(stderr,
325     "%s: missing matrix argument for '%c' operation\n",
326     argv[0], argv[i][0]);
327 greg 2.13 return(1);
328     }
329     mop[nmats++].binop = argv[i][0];
330 greg 2.1 } else if (argv[i][0] != '-' || !argv[i][1]) {
331 greg 2.13 if (argv[i][0] == '-') {
332     if (stdin_used++) {
333     fprintf(stderr,
334     "%s: standard input used for more than one matrix\n",
335     argv[0]);
336     return(1);
337     }
338     mop[nmats].inspec = stdin_name;
339     } else
340     mop[nmats].inspec = argv[i];
341     if (nmats > 0 && !mop[nmats-1].binop)
342     mop[nmats-1].binop = '.';
343     nmats++;
344 greg 2.1 } else {
345     int n = argc-1 - i;
346     switch (argv[i][1]) { /* get option */
347     case 'v':
348 greg 2.14 verbose++;
349 greg 2.1 break;
350     case 'f':
351     switch (argv[i][2]) {
352     case 'd':
353     outfmt = DTdouble;
354     break;
355     case 'f':
356     outfmt = DTfloat;
357     break;
358     case 'a':
359     outfmt = DTascii;
360     break;
361     case 'c':
362     outfmt = DTrgbe;
363     break;
364     default:
365     goto userr;
366     }
367     break;
368     case 't':
369 greg 2.13 mop[nmats].preop.transpose = 1;
370 greg 2.1 break;
371     case 's':
372     if (n > MAXCOMP) n = MAXCOMP;
373 greg 2.13 i += mop[nmats].preop.nsf =
374     get_factors(mop[nmats].preop.sca,
375     n, argv+i+1);
376 greg 2.1 break;
377     case 'c':
378     if (n > MAXCOMP*MAXCOMP) n = MAXCOMP*MAXCOMP;
379 greg 2.13 i += mop[nmats].preop.clen =
380     get_factors(mop[nmats].preop.cmat,
381     n, argv+i+1);
382 greg 2.1 break;
383     default:
384     fprintf(stderr, "%s: unknown operation '%s'\n",
385     argv[0], argv[i]);
386     goto userr;
387     }
388     }
389 greg 2.14 if (nmats >= nall)
390     mop = grow_moparray(mop, nall += 2);
391 greg 2.13 }
392     if (mop[0].inspec == NULL) /* nothing to do? */
393 greg 2.1 goto userr;
394 greg 2.13 /* favor quicker concatenation */
395 greg 2.14 mop[nmats].mtx = prefer_right2left(mop) ? op_right2left(mop)
396     : op_left2right(mop);
397     if (mop[nmats].mtx == NULL)
398     return(1);
399     /* apply trailing unary operations */
400     mop[nmats].inspec = "trailing_ops";
401     mres = loadop(mop+nmats);
402     if (mres == NULL)
403 greg 2.13 return(1);
404 greg 2.1 /* write result to stdout */
405 greg 2.6 if (outfmt == DTfromHeader)
406     outfmt = mres->dtype;
407 greg 2.4 if (outfmt != DTascii)
408 greg 2.10 SET_FILE_BINARY(stdout);
409 greg 2.1 newheader("RADIANCE", stdout);
410     printargs(argc, argv, stdout);
411 greg 2.5 if (!rmx_write(mres, outfmt, stdout)) {
412 greg 2.1 fprintf(stderr, "%s: error writing result matrix\n", argv[0]);
413     return(1);
414     }
415 greg 2.13 /* rmx_free(mres); free(mop); */
416 greg 2.1 return(0);
417     userr:
418     fprintf(stderr,
419 greg 2.13 "Usage: %s [-v][-f[adfc][-t][-s sf .. | -c ce ..] m1 [.+*/] .. > mres\n",
420 greg 2.1 argv[0]);
421     return(1);
422     }