1 |
|
2 |
/******************************************************* |
3 |
* |
4 |
* Copyright (c) 2003-2008 by University of Queensland |
5 |
* Earth Systems Science Computational Center (ESSCC) |
6 |
* http://www.uq.edu.au/esscc |
7 |
* |
8 |
* Primary Business: Queensland, Australia |
9 |
* Licensed under the Open Software License version 3.0 |
10 |
* http://www.opensource.org/licenses/osl-3.0.php |
11 |
* |
12 |
*******************************************************/ |
13 |
|
14 |
|
15 |
#include "DataLazy.h" |
16 |
#ifdef USE_NETCDF |
17 |
#include <netcdfcpp.h> |
18 |
#endif |
19 |
#ifdef PASO_MPI |
20 |
#include <mpi.h> |
21 |
#endif |
22 |
#ifdef _OPENMP |
23 |
#include <omp.h> |
24 |
#endif |
25 |
#include "FunctionSpace.h" |
26 |
#include "DataTypes.h" |
27 |
#include "Data.h" |
28 |
#include "UnaryFuncs.h" // for escript::fsign |
29 |
#include "Utils.h" |
30 |
|
31 |
// #define LAZYDEBUG(X) X; |
32 |
#define LAZYDEBUG(X) |
33 |
|
34 |
/* |
35 |
How does DataLazy work? |
36 |
~~~~~~~~~~~~~~~~~~~~~~~ |
37 |
|
38 |
Each instance represents a single operation on one or two other DataLazy instances. These arguments are normally |
39 |
denoted left and right. |
40 |
|
41 |
A special operation, IDENTITY, stores an instance of DataReady in the m_id member. |
42 |
This means that all "internal" nodes in the structure are instances of DataLazy. |
43 |
|
44 |
Each operation has a string representation as well as an opgroup - eg G_IDENTITY, G_BINARY, ... |
45 |
Note that IDENITY is not considered a unary operation. |
46 |
|
47 |
I am avoiding calling the structure formed a tree because it is not guaranteed to be one (eg c=a+a). |
48 |
It must however form a DAG (directed acyclic graph). |
49 |
I will refer to individual DataLazy objects with the structure as nodes. |
50 |
|
51 |
Each node also stores: |
52 |
- m_readytype \in {'E','T','C','?'} ~ indicates what sort of DataReady would be produced if the expression was |
53 |
evaluated. |
54 |
- m_buffsrequired ~ the larged number of samples which would need to be kept simultaneously in order to |
55 |
evaluate the expression. |
56 |
- m_samplesize ~ the number of doubles stored in a sample. |
57 |
|
58 |
When a new node is created, the above values are computed based on the values in the child nodes. |
59 |
Eg: if left requires 4 samples and right requires 6 then left+right requires 7 samples. |
60 |
|
61 |
The resolve method, which produces a DataReady from a DataLazy, does the following: |
62 |
1) Create a DataReady to hold the new result. |
63 |
2) Allocate a vector (v) big enough to hold m_buffsrequired samples. |
64 |
3) For each sample, call resolveSample with v, to get its values and copy them into the result object. |
65 |
|
66 |
(In the case of OMP, multiple samples are resolved in parallel so the vector needs to be larger.) |
67 |
|
68 |
resolveSample returns a Vector* and an offset within that vector where the result is stored. |
69 |
Normally, this would be v, but for identity nodes their internal vector is returned instead. |
70 |
|
71 |
The convention that I use, is that the resolve methods should store their results starting at the offset they are passed. |
72 |
|
73 |
For expressions which evaluate to Constant or Tagged, there is a different evaluation method. |
74 |
The collapse method invokes the (non-lazy) operations on the Data class to evaluate the expression. |
75 |
|
76 |
To add a new operator you need to do the following (plus anything I might have forgotten): |
77 |
1) Add to the ES_optype. |
78 |
2) determine what opgroup your operation belongs to (X) |
79 |
3) add a string for the op to the end of ES_opstrings |
80 |
4) increase ES_opcount |
81 |
5) add an entry (X) to opgroups |
82 |
6) add an entry to the switch in collapseToReady |
83 |
7) add an entry to resolveX |
84 |
*/ |
85 |
|
86 |
|
87 |
using namespace std; |
88 |
using namespace boost; |
89 |
|
90 |
namespace escript |
91 |
{ |
92 |
|
93 |
namespace |
94 |
{ |
95 |
|
96 |
enum ES_opgroup |
97 |
{ |
98 |
G_UNKNOWN, |
99 |
G_IDENTITY, |
100 |
G_BINARY, // pointwise operations with two arguments |
101 |
G_UNARY, // pointwise operations with one argument |
102 |
G_NP1OUT, // non-pointwise op with one output |
103 |
G_NP1OUT_P, // non-pointwise op with one output requiring a parameter |
104 |
G_TENSORPROD // general tensor product |
105 |
}; |
106 |
|
107 |
|
108 |
|
109 |
|
110 |
string ES_opstrings[]={"UNKNOWN","IDENTITY","+","-","*","/","^", |
111 |
"sin","cos","tan", |
112 |
"asin","acos","atan","sinh","cosh","tanh","erf", |
113 |
"asinh","acosh","atanh", |
114 |
"log10","log","sign","abs","neg","pos","exp","sqrt", |
115 |
"1/","where>0","where<0","where>=0","where<=0", |
116 |
"symmetric","nonsymmetric", |
117 |
"prod", |
118 |
"transpose", |
119 |
"trace"}; |
120 |
int ES_opcount=38; |
121 |
ES_opgroup opgroups[]={G_UNKNOWN,G_IDENTITY,G_BINARY,G_BINARY,G_BINARY,G_BINARY, G_BINARY, |
122 |
G_UNARY,G_UNARY,G_UNARY, //10 |
123 |
G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY, // 17 |
124 |
G_UNARY,G_UNARY,G_UNARY, // 20 |
125 |
G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY, // 28 |
126 |
G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY, // 33 |
127 |
G_NP1OUT,G_NP1OUT, |
128 |
G_TENSORPROD, |
129 |
G_NP1OUT_P, G_NP1OUT_P}; |
130 |
inline |
131 |
ES_opgroup |
132 |
getOpgroup(ES_optype op) |
133 |
{ |
134 |
return opgroups[op]; |
135 |
} |
136 |
|
137 |
// return the FunctionSpace of the result of "left op right" |
138 |
FunctionSpace |
139 |
resultFS(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op) |
140 |
{ |
141 |
// perhaps this should call interpolate and throw or something? |
142 |
// maybe we need an interpolate node - |
143 |
// that way, if interpolate is required in any other op we can just throw a |
144 |
// programming error exception. |
145 |
|
146 |
FunctionSpace l=left->getFunctionSpace(); |
147 |
FunctionSpace r=right->getFunctionSpace(); |
148 |
if (l!=r) |
149 |
{ |
150 |
if (r.probeInterpolation(l)) |
151 |
{ |
152 |
return l; |
153 |
} |
154 |
if (l.probeInterpolation(r)) |
155 |
{ |
156 |
return r; |
157 |
} |
158 |
throw DataException("Cannot interpolate between the FunctionSpaces given for operation "+opToString(op)+"."); |
159 |
} |
160 |
return l; |
161 |
} |
162 |
|
163 |
// return the shape of the result of "left op right" |
164 |
// the shapes resulting from tensor product are more complex to compute so are worked out elsewhere |
165 |
DataTypes::ShapeType |
166 |
resultShape(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op) |
167 |
{ |
168 |
if (left->getShape()!=right->getShape()) |
169 |
{ |
170 |
if ((getOpgroup(op)!=G_BINARY) && (getOpgroup(op)!=G_NP1OUT)) |
171 |
{ |
172 |
throw DataException("Shapes not the name - shapes must match for (point)binary operations."); |
173 |
} |
174 |
if (left->getRank()==0) // we need to allow scalar * anything |
175 |
{ |
176 |
return right->getShape(); |
177 |
} |
178 |
if (right->getRank()==0) |
179 |
{ |
180 |
return left->getShape(); |
181 |
} |
182 |
throw DataException("Shapes not the same - arguments must have matching shapes (or be scalars) for (point)binary operations on lazy data."); |
183 |
} |
184 |
return left->getShape(); |
185 |
} |
186 |
|
187 |
// return the shape for "op left" |
188 |
|
189 |
DataTypes::ShapeType |
190 |
resultShape(DataAbstract_ptr left, ES_optype op) |
191 |
{ |
192 |
switch(op) |
193 |
{ |
194 |
case TRANS: |
195 |
return left->getShape(); |
196 |
break; |
197 |
case TRACE: |
198 |
return DataTypes::scalarShape; |
199 |
break; |
200 |
default: |
201 |
throw DataException("Programmer error - resultShape(left,op) can't compute shapes for operator "+opToString(op)+"."); |
202 |
} |
203 |
} |
204 |
|
205 |
// determine the output shape for the general tensor product operation |
206 |
// the additional parameters return information required later for the product |
207 |
// the majority of this code is copy pasted from C_General_Tensor_Product |
208 |
DataTypes::ShapeType |
209 |
GTPShape(DataAbstract_ptr left, DataAbstract_ptr right, int axis_offset, int transpose, int& SL, int& SM, int& SR) |
210 |
{ |
211 |
|
212 |
// Get rank and shape of inputs |
213 |
int rank0 = left->getRank(); |
214 |
int rank1 = right->getRank(); |
215 |
const DataTypes::ShapeType& shape0 = left->getShape(); |
216 |
const DataTypes::ShapeType& shape1 = right->getShape(); |
217 |
|
218 |
// Prepare for the loops of the product and verify compatibility of shapes |
219 |
int start0=0, start1=0; |
220 |
if (transpose == 0) {} |
221 |
else if (transpose == 1) { start0 = axis_offset; } |
222 |
else if (transpose == 2) { start1 = rank1-axis_offset; } |
223 |
else { throw DataException("DataLazy GeneralTensorProduct Constructor: Error - transpose should be 0, 1 or 2"); } |
224 |
|
225 |
if (rank0<axis_offset) |
226 |
{ |
227 |
throw DataException("DataLazy GeneralTensorProduct Constructor: Error - rank of left < axisoffset"); |
228 |
} |
229 |
|
230 |
// Adjust the shapes for transpose |
231 |
DataTypes::ShapeType tmpShape0(rank0); // pre-sizing the vectors rather |
232 |
DataTypes::ShapeType tmpShape1(rank1); // than using push_back |
233 |
for (int i=0; i<rank0; i++) { tmpShape0[i]=shape0[(i+start0)%rank0]; } |
234 |
for (int i=0; i<rank1; i++) { tmpShape1[i]=shape1[(i+start1)%rank1]; } |
235 |
|
236 |
// Prepare for the loops of the product |
237 |
SL=1, SM=1, SR=1; |
238 |
for (int i=0; i<rank0-axis_offset; i++) { |
239 |
SL *= tmpShape0[i]; |
240 |
} |
241 |
for (int i=rank0-axis_offset; i<rank0; i++) { |
242 |
if (tmpShape0[i] != tmpShape1[i-(rank0-axis_offset)]) { |
243 |
throw DataException("C_GeneralTensorProduct: Error - incompatible shapes"); |
244 |
} |
245 |
SM *= tmpShape0[i]; |
246 |
} |
247 |
for (int i=axis_offset; i<rank1; i++) { |
248 |
SR *= tmpShape1[i]; |
249 |
} |
250 |
|
251 |
// Define the shape of the output (rank of shape is the sum of the loop ranges below) |
252 |
DataTypes::ShapeType shape2(rank0+rank1-2*axis_offset); |
253 |
{ // block to limit the scope of out_index |
254 |
int out_index=0; |
255 |
for (int i=0; i<rank0-axis_offset; i++, ++out_index) { shape2[out_index]=tmpShape0[i]; } // First part of arg_0_Z |
256 |
for (int i=axis_offset; i<rank1; i++, ++out_index) { shape2[out_index]=tmpShape1[i]; } // Last part of arg_1_Z |
257 |
} |
258 |
|
259 |
if (shape2.size()>ESCRIPT_MAX_DATA_RANK) |
260 |
{ |
261 |
ostringstream os; |
262 |
os << "C_GeneralTensorProduct: Error - Attempt to create a rank " << shape2.size() << " object. The maximum rank is " << ESCRIPT_MAX_DATA_RANK << "."; |
263 |
throw DataException(os.str()); |
264 |
} |
265 |
|
266 |
return shape2; |
267 |
} |
268 |
|
269 |
|
270 |
// determine the number of points in the result of "left op right" |
271 |
// note that determining the resultLength for G_TENSORPROD is more complex and will not be processed here |
272 |
// size_t |
273 |
// resultLength(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op) |
274 |
// { |
275 |
// switch (getOpgroup(op)) |
276 |
// { |
277 |
// case G_BINARY: return left->getLength(); |
278 |
// case G_UNARY: return left->getLength(); |
279 |
// case G_NP1OUT: return left->getLength(); |
280 |
// default: |
281 |
// throw DataException("Programmer Error - attempt to getLength() for operator "+opToString(op)+"."); |
282 |
// } |
283 |
// } |
284 |
|
285 |
// determine the number of samples requires to evaluate an expression combining left and right |
286 |
// NP1OUT needs an extra buffer because we can't write the answers over the top of the input. |
287 |
// The same goes for G_TENSORPROD |
288 |
int |
289 |
calcBuffs(const DataLazy_ptr& left, const DataLazy_ptr& right, ES_optype op) |
290 |
{ |
291 |
switch(getOpgroup(op)) |
292 |
{ |
293 |
case G_IDENTITY: return 1; |
294 |
case G_BINARY: return max(left->getBuffsRequired(),right->getBuffsRequired()+1); |
295 |
case G_UNARY: return max(left->getBuffsRequired(),1); |
296 |
case G_NP1OUT: return 1+max(left->getBuffsRequired(),1); |
297 |
case G_NP1OUT_P: return 1+max(left->getBuffsRequired(),1); |
298 |
case G_TENSORPROD: return 1+max(left->getBuffsRequired(),right->getBuffsRequired()+1); |
299 |
default: |
300 |
throw DataException("Programmer Error - attempt to calcBuffs() for operator "+opToString(op)+"."); |
301 |
} |
302 |
} |
303 |
|
304 |
|
305 |
} // end anonymous namespace |
306 |
|
307 |
|
308 |
|
309 |
// Return a string representing the operation |
310 |
const std::string& |
311 |
opToString(ES_optype op) |
312 |
{ |
313 |
if (op<0 || op>=ES_opcount) |
314 |
{ |
315 |
op=UNKNOWNOP; |
316 |
} |
317 |
return ES_opstrings[op]; |
318 |
} |
319 |
|
320 |
|
321 |
DataLazy::DataLazy(DataAbstract_ptr p) |
322 |
: parent(p->getFunctionSpace(),p->getShape()), |
323 |
m_op(IDENTITY), |
324 |
m_axis_offset(0), |
325 |
m_transpose(0), |
326 |
m_SL(0), m_SM(0), m_SR(0) |
327 |
{ |
328 |
if (p->isLazy()) |
329 |
{ |
330 |
// I don't want identity of Lazy. |
331 |
// Question: Why would that be so bad? |
332 |
// Answer: We assume that the child of ID is something we can call getVector on |
333 |
throw DataException("Programmer error - attempt to create identity from a DataLazy."); |
334 |
} |
335 |
else |
336 |
{ |
337 |
m_id=dynamic_pointer_cast<DataReady>(p); |
338 |
if(p->isConstant()) {m_readytype='C';} |
339 |
else if(p->isExpanded()) {m_readytype='E';} |
340 |
else if (p->isTagged()) {m_readytype='T';} |
341 |
else {throw DataException("Unknown DataReady instance in DataLazy constructor.");} |
342 |
} |
343 |
m_buffsRequired=1; |
344 |
m_samplesize=getNumDPPSample()*getNoValues(); |
345 |
m_maxsamplesize=m_samplesize; |
346 |
LAZYDEBUG(cout << "(1)Lazy created with " << m_samplesize << endl;) |
347 |
} |
348 |
|
349 |
|
350 |
|
351 |
|
352 |
DataLazy::DataLazy(DataAbstract_ptr left, ES_optype op) |
353 |
: parent(left->getFunctionSpace(),left->getShape()), |
354 |
m_op(op), |
355 |
m_axis_offset(0), |
356 |
m_transpose(0), |
357 |
m_SL(0), m_SM(0), m_SR(0) |
358 |
{ |
359 |
if ((getOpgroup(op)!=G_UNARY) && (getOpgroup(op)!=G_NP1OUT)) |
360 |
{ |
361 |
throw DataException("Programmer error - constructor DataLazy(left, op) will only process UNARY operations."); |
362 |
} |
363 |
|
364 |
DataLazy_ptr lleft; |
365 |
if (!left->isLazy()) |
366 |
{ |
367 |
lleft=DataLazy_ptr(new DataLazy(left)); |
368 |
} |
369 |
else |
370 |
{ |
371 |
lleft=dynamic_pointer_cast<DataLazy>(left); |
372 |
} |
373 |
m_readytype=lleft->m_readytype; |
374 |
m_left=lleft; |
375 |
m_buffsRequired=calcBuffs(m_left, m_right,m_op); // yeah m_right will be null at this point |
376 |
m_samplesize=getNumDPPSample()*getNoValues(); |
377 |
m_maxsamplesize=max(m_samplesize,m_left->getMaxSampleSize()); |
378 |
} |
379 |
|
380 |
|
381 |
// In this constructor we need to consider interpolation |
382 |
DataLazy::DataLazy(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op) |
383 |
: parent(resultFS(left,right,op), resultShape(left,right,op)), |
384 |
m_op(op), |
385 |
m_SL(0), m_SM(0), m_SR(0) |
386 |
{ |
387 |
if ((getOpgroup(op)!=G_BINARY)) |
388 |
{ |
389 |
throw DataException("Programmer error - constructor DataLazy(left, right, op) will only process BINARY operations."); |
390 |
} |
391 |
|
392 |
if (getFunctionSpace()!=left->getFunctionSpace()) // left needs to be interpolated |
393 |
{ |
394 |
FunctionSpace fs=getFunctionSpace(); |
395 |
Data ltemp(left); |
396 |
Data tmp(ltemp,fs); |
397 |
left=tmp.borrowDataPtr(); |
398 |
} |
399 |
if (getFunctionSpace()!=right->getFunctionSpace()) // right needs to be interpolated |
400 |
{ |
401 |
Data tmp(Data(right),getFunctionSpace()); |
402 |
right=tmp.borrowDataPtr(); |
403 |
} |
404 |
left->operandCheck(*right); |
405 |
|
406 |
if (left->isLazy()) // the children need to be DataLazy. Wrap them in IDENTITY if required |
407 |
{ |
408 |
m_left=dynamic_pointer_cast<DataLazy>(left); |
409 |
} |
410 |
else |
411 |
{ |
412 |
m_left=DataLazy_ptr(new DataLazy(left)); |
413 |
} |
414 |
if (right->isLazy()) |
415 |
{ |
416 |
m_right=dynamic_pointer_cast<DataLazy>(right); |
417 |
} |
418 |
else |
419 |
{ |
420 |
m_right=DataLazy_ptr(new DataLazy(right)); |
421 |
} |
422 |
char lt=m_left->m_readytype; |
423 |
char rt=m_right->m_readytype; |
424 |
if (lt=='E' || rt=='E') |
425 |
{ |
426 |
m_readytype='E'; |
427 |
} |
428 |
else if (lt=='T' || rt=='T') |
429 |
{ |
430 |
m_readytype='T'; |
431 |
} |
432 |
else |
433 |
{ |
434 |
m_readytype='C'; |
435 |
} |
436 |
m_samplesize=getNumDPPSample()*getNoValues(); |
437 |
m_maxsamplesize=max(max(m_samplesize,m_right->getMaxSampleSize()),m_left->getMaxSampleSize()); |
438 |
m_buffsRequired=calcBuffs(m_left, m_right,m_op); |
439 |
LAZYDEBUG(cout << "(3)Lazy created with " << m_samplesize << endl;) |
440 |
} |
441 |
|
442 |
DataLazy::DataLazy(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op, int axis_offset, int transpose) |
443 |
: parent(resultFS(left,right,op), GTPShape(left,right, axis_offset, transpose, m_SL,m_SM, m_SR)), |
444 |
m_op(op), |
445 |
m_axis_offset(axis_offset), |
446 |
m_transpose(transpose) |
447 |
{ |
448 |
if ((getOpgroup(op)!=G_TENSORPROD)) |
449 |
{ |
450 |
throw DataException("Programmer error - constructor DataLazy(left, right, op, ax, tr) will only process BINARY operations which require parameters."); |
451 |
} |
452 |
if ((transpose>2) || (transpose<0)) |
453 |
{ |
454 |
throw DataException("DataLazy GeneralTensorProduct constructor: Error - transpose should be 0, 1 or 2"); |
455 |
} |
456 |
if (getFunctionSpace()!=left->getFunctionSpace()) // left needs to be interpolated |
457 |
{ |
458 |
FunctionSpace fs=getFunctionSpace(); |
459 |
Data ltemp(left); |
460 |
Data tmp(ltemp,fs); |
461 |
left=tmp.borrowDataPtr(); |
462 |
} |
463 |
if (getFunctionSpace()!=right->getFunctionSpace()) // right needs to be interpolated |
464 |
{ |
465 |
Data tmp(Data(right),getFunctionSpace()); |
466 |
right=tmp.borrowDataPtr(); |
467 |
} |
468 |
left->operandCheck(*right); |
469 |
|
470 |
if (left->isLazy()) // the children need to be DataLazy. Wrap them in IDENTITY if required |
471 |
{ |
472 |
m_left=dynamic_pointer_cast<DataLazy>(left); |
473 |
} |
474 |
else |
475 |
{ |
476 |
m_left=DataLazy_ptr(new DataLazy(left)); |
477 |
} |
478 |
if (right->isLazy()) |
479 |
{ |
480 |
m_right=dynamic_pointer_cast<DataLazy>(right); |
481 |
} |
482 |
else |
483 |
{ |
484 |
m_right=DataLazy_ptr(new DataLazy(right)); |
485 |
} |
486 |
char lt=m_left->m_readytype; |
487 |
char rt=m_right->m_readytype; |
488 |
if (lt=='E' || rt=='E') |
489 |
{ |
490 |
m_readytype='E'; |
491 |
} |
492 |
else if (lt=='T' || rt=='T') |
493 |
{ |
494 |
m_readytype='T'; |
495 |
} |
496 |
else |
497 |
{ |
498 |
m_readytype='C'; |
499 |
} |
500 |
m_samplesize=getNumDPPSample()*getNoValues(); |
501 |
m_maxsamplesize=max(max(m_samplesize,m_right->getMaxSampleSize()),m_left->getMaxSampleSize()); |
502 |
m_buffsRequired=calcBuffs(m_left, m_right,m_op); |
503 |
LAZYDEBUG(cout << "(4)Lazy created with " << m_samplesize << endl;) |
504 |
} |
505 |
|
506 |
|
507 |
DataLazy::DataLazy(DataAbstract_ptr left, ES_optype op, int axis_offset) |
508 |
: parent(left->getFunctionSpace(), resultShape(left,op)), |
509 |
m_op(op), |
510 |
m_axis_offset(axis_offset), |
511 |
m_transpose(0) |
512 |
{ |
513 |
if ((getOpgroup(op)!=G_NP1OUT_P)) |
514 |
{ |
515 |
throw DataException("Programmer error - constructor DataLazy(left, op, ax) will only process UNARY operations which require parameters."); |
516 |
} |
517 |
DataLazy_ptr lleft; |
518 |
if (!left->isLazy()) |
519 |
{ |
520 |
lleft=DataLazy_ptr(new DataLazy(left)); |
521 |
} |
522 |
else |
523 |
{ |
524 |
lleft=dynamic_pointer_cast<DataLazy>(left); |
525 |
} |
526 |
m_readytype=lleft->m_readytype; |
527 |
m_left=lleft; |
528 |
m_buffsRequired=calcBuffs(m_left, m_right,m_op); // yeah m_right will be null at this point |
529 |
m_samplesize=getNumDPPSample()*getNoValues(); |
530 |
m_maxsamplesize=max(m_samplesize,m_left->getMaxSampleSize()); |
531 |
LAZYDEBUG(cout << "(5)Lazy created with " << m_samplesize << endl;) |
532 |
} |
533 |
|
534 |
|
535 |
DataLazy::~DataLazy() |
536 |
{ |
537 |
} |
538 |
|
539 |
|
540 |
int |
541 |
DataLazy::getBuffsRequired() const |
542 |
{ |
543 |
return m_buffsRequired; |
544 |
} |
545 |
|
546 |
|
547 |
size_t |
548 |
DataLazy::getMaxSampleSize() const |
549 |
{ |
550 |
return m_maxsamplesize; |
551 |
} |
552 |
|
553 |
|
554 |
|
555 |
size_t |
556 |
DataLazy::getSampleBufferSize() const |
557 |
{ |
558 |
return m_maxsamplesize*(max(1,m_buffsRequired)); |
559 |
} |
560 |
|
561 |
/* |
562 |
\brief Evaluates the expression using methods on Data. |
563 |
This does the work for the collapse method. |
564 |
For reasons of efficiency do not call this method on DataExpanded nodes. |
565 |
*/ |
566 |
DataReady_ptr |
567 |
DataLazy::collapseToReady() |
568 |
{ |
569 |
if (m_readytype=='E') |
570 |
{ // this is more an efficiency concern than anything else |
571 |
throw DataException("Programmer Error - do not use collapse on Expanded data."); |
572 |
} |
573 |
if (m_op==IDENTITY) |
574 |
{ |
575 |
return m_id; |
576 |
} |
577 |
DataReady_ptr pleft=m_left->collapseToReady(); |
578 |
Data left(pleft); |
579 |
Data right; |
580 |
if ((getOpgroup(m_op)==G_BINARY) || (getOpgroup(m_op)==G_TENSORPROD)) |
581 |
{ |
582 |
right=Data(m_right->collapseToReady()); |
583 |
} |
584 |
Data result; |
585 |
switch(m_op) |
586 |
{ |
587 |
case ADD: |
588 |
result=left+right; |
589 |
break; |
590 |
case SUB: |
591 |
result=left-right; |
592 |
break; |
593 |
case MUL: |
594 |
result=left*right; |
595 |
break; |
596 |
case DIV: |
597 |
result=left/right; |
598 |
break; |
599 |
case SIN: |
600 |
result=left.sin(); |
601 |
break; |
602 |
case COS: |
603 |
result=left.cos(); |
604 |
break; |
605 |
case TAN: |
606 |
result=left.tan(); |
607 |
break; |
608 |
case ASIN: |
609 |
result=left.asin(); |
610 |
break; |
611 |
case ACOS: |
612 |
result=left.acos(); |
613 |
break; |
614 |
case ATAN: |
615 |
result=left.atan(); |
616 |
break; |
617 |
case SINH: |
618 |
result=left.sinh(); |
619 |
break; |
620 |
case COSH: |
621 |
result=left.cosh(); |
622 |
break; |
623 |
case TANH: |
624 |
result=left.tanh(); |
625 |
break; |
626 |
case ERF: |
627 |
result=left.erf(); |
628 |
break; |
629 |
case ASINH: |
630 |
result=left.asinh(); |
631 |
break; |
632 |
case ACOSH: |
633 |
result=left.acosh(); |
634 |
break; |
635 |
case ATANH: |
636 |
result=left.atanh(); |
637 |
break; |
638 |
case LOG10: |
639 |
result=left.log10(); |
640 |
break; |
641 |
case LOG: |
642 |
result=left.log(); |
643 |
break; |
644 |
case SIGN: |
645 |
result=left.sign(); |
646 |
break; |
647 |
case ABS: |
648 |
result=left.abs(); |
649 |
break; |
650 |
case NEG: |
651 |
result=left.neg(); |
652 |
break; |
653 |
case POS: |
654 |
// it doesn't mean anything for delayed. |
655 |
// it will just trigger a deep copy of the lazy object |
656 |
throw DataException("Programmer error - POS not supported for lazy data."); |
657 |
break; |
658 |
case EXP: |
659 |
result=left.exp(); |
660 |
break; |
661 |
case SQRT: |
662 |
result=left.sqrt(); |
663 |
break; |
664 |
case RECIP: |
665 |
result=left.oneOver(); |
666 |
break; |
667 |
case GZ: |
668 |
result=left.wherePositive(); |
669 |
break; |
670 |
case LZ: |
671 |
result=left.whereNegative(); |
672 |
break; |
673 |
case GEZ: |
674 |
result=left.whereNonNegative(); |
675 |
break; |
676 |
case LEZ: |
677 |
result=left.whereNonPositive(); |
678 |
break; |
679 |
case SYM: |
680 |
result=left.symmetric(); |
681 |
break; |
682 |
case NSYM: |
683 |
result=left.nonsymmetric(); |
684 |
break; |
685 |
case PROD: |
686 |
result=C_GeneralTensorProduct(left,right,m_axis_offset, m_transpose); |
687 |
break; |
688 |
case TRANS: |
689 |
result=left.transpose(m_axis_offset); |
690 |
break; |
691 |
case TRACE: |
692 |
result=left.trace(m_axis_offset); |
693 |
break; |
694 |
default: |
695 |
throw DataException("Programmer error - collapseToReady does not know how to resolve operator "+opToString(m_op)+"."); |
696 |
} |
697 |
return result.borrowReadyPtr(); |
698 |
} |
699 |
|
700 |
/* |
701 |
\brief Converts the DataLazy into an IDENTITY storing the value of the expression. |
702 |
This method uses the original methods on the Data class to evaluate the expressions. |
703 |
For this reason, it should not be used on DataExpanded instances. (To do so would defeat |
704 |
the purpose of using DataLazy in the first place). |
705 |
*/ |
706 |
void |
707 |
DataLazy::collapse() |
708 |
{ |
709 |
if (m_op==IDENTITY) |
710 |
{ |
711 |
return; |
712 |
} |
713 |
if (m_readytype=='E') |
714 |
{ // this is more an efficiency concern than anything else |
715 |
throw DataException("Programmer Error - do not use collapse on Expanded data."); |
716 |
} |
717 |
m_id=collapseToReady(); |
718 |
m_op=IDENTITY; |
719 |
} |
720 |
|
721 |
/* |
722 |
\brief Compute the value of the expression (unary operation) for the given sample. |
723 |
\return Vector which stores the value of the subexpression for the given sample. |
724 |
\param v A vector to store intermediate results. |
725 |
\param offset Index in v to begin storing results. |
726 |
\param sampleNo Sample number to evaluate. |
727 |
\param roffset (output parameter) the offset in the return vector where the result begins. |
728 |
|
729 |
The return value will be an existing vector so do not deallocate it. |
730 |
If the result is stored in v it should be stored at the offset given. |
731 |
Everything from offset to the end of v should be considered available for this method to use. |
732 |
*/ |
733 |
DataTypes::ValueType* |
734 |
DataLazy::resolveUnary(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const |
735 |
{ |
736 |
// we assume that any collapsing has been done before we get here |
737 |
// since we only have one argument we don't need to think about only |
738 |
// processing single points. |
739 |
if (m_readytype!='E') |
740 |
{ |
741 |
throw DataException("Programmer error - resolveUnary should only be called on expanded Data."); |
742 |
} |
743 |
const ValueType* vleft=m_left->resolveSample(v,offset,sampleNo,roffset); |
744 |
const double* left=&((*vleft)[roffset]); |
745 |
double* result=&(v[offset]); |
746 |
roffset=offset; |
747 |
switch (m_op) |
748 |
{ |
749 |
case SIN: |
750 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::sin); |
751 |
break; |
752 |
case COS: |
753 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::cos); |
754 |
break; |
755 |
case TAN: |
756 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::tan); |
757 |
break; |
758 |
case ASIN: |
759 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::asin); |
760 |
break; |
761 |
case ACOS: |
762 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::acos); |
763 |
break; |
764 |
case ATAN: |
765 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::atan); |
766 |
break; |
767 |
case SINH: |
768 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::sinh); |
769 |
break; |
770 |
case COSH: |
771 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::cosh); |
772 |
break; |
773 |
case TANH: |
774 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::tanh); |
775 |
break; |
776 |
case ERF: |
777 |
#if defined (_WIN32) && !defined(__INTEL_COMPILER) |
778 |
throw DataException("Error - Data:: erf function is not supported on _WIN32 platforms."); |
779 |
#else |
780 |
tensor_unary_operation(m_samplesize, left, result, ::erf); |
781 |
break; |
782 |
#endif |
783 |
case ASINH: |
784 |
#if defined (_WIN32) && !defined(__INTEL_COMPILER) |
785 |
tensor_unary_operation(m_samplesize, left, result, escript::asinh_substitute); |
786 |
#else |
787 |
tensor_unary_operation(m_samplesize, left, result, ::asinh); |
788 |
#endif |
789 |
break; |
790 |
case ACOSH: |
791 |
#if defined (_WIN32) && !defined(__INTEL_COMPILER) |
792 |
tensor_unary_operation(m_samplesize, left, result, escript::acosh_substitute); |
793 |
#else |
794 |
tensor_unary_operation(m_samplesize, left, result, ::acosh); |
795 |
#endif |
796 |
break; |
797 |
case ATANH: |
798 |
#if defined (_WIN32) && !defined(__INTEL_COMPILER) |
799 |
tensor_unary_operation(m_samplesize, left, result, escript::atanh_substitute); |
800 |
#else |
801 |
tensor_unary_operation(m_samplesize, left, result, ::atanh); |
802 |
#endif |
803 |
break; |
804 |
case LOG10: |
805 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::log10); |
806 |
break; |
807 |
case LOG: |
808 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::log); |
809 |
break; |
810 |
case SIGN: |
811 |
tensor_unary_operation(m_samplesize, left, result, escript::fsign); |
812 |
break; |
813 |
case ABS: |
814 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::fabs); |
815 |
break; |
816 |
case NEG: |
817 |
tensor_unary_operation(m_samplesize, left, result, negate<double>()); |
818 |
break; |
819 |
case POS: |
820 |
// it doesn't mean anything for delayed. |
821 |
// it will just trigger a deep copy of the lazy object |
822 |
throw DataException("Programmer error - POS not supported for lazy data."); |
823 |
break; |
824 |
case EXP: |
825 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::exp); |
826 |
break; |
827 |
case SQRT: |
828 |
tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::sqrt); |
829 |
break; |
830 |
case RECIP: |
831 |
tensor_unary_operation(m_samplesize, left, result, bind1st(divides<double>(),1.)); |
832 |
break; |
833 |
case GZ: |
834 |
tensor_unary_operation(m_samplesize, left, result, bind2nd(greater<double>(),0.0)); |
835 |
break; |
836 |
case LZ: |
837 |
tensor_unary_operation(m_samplesize, left, result, bind2nd(less<double>(),0.0)); |
838 |
break; |
839 |
case GEZ: |
840 |
tensor_unary_operation(m_samplesize, left, result, bind2nd(greater_equal<double>(),0.0)); |
841 |
break; |
842 |
case LEZ: |
843 |
tensor_unary_operation(m_samplesize, left, result, bind2nd(less_equal<double>(),0.0)); |
844 |
break; |
845 |
|
846 |
default: |
847 |
throw DataException("Programmer error - resolveUnary can not resolve operator "+opToString(m_op)+"."); |
848 |
} |
849 |
return &v; |
850 |
} |
851 |
|
852 |
|
853 |
/* |
854 |
\brief Compute the value of the expression (unary operation) for the given sample. |
855 |
\return Vector which stores the value of the subexpression for the given sample. |
856 |
\param v A vector to store intermediate results. |
857 |
\param offset Index in v to begin storing results. |
858 |
\param sampleNo Sample number to evaluate. |
859 |
\param roffset (output parameter) the offset in the return vector where the result begins. |
860 |
|
861 |
The return value will be an existing vector so do not deallocate it. |
862 |
If the result is stored in v it should be stored at the offset given. |
863 |
Everything from offset to the end of v should be considered available for this method to use. |
864 |
*/ |
865 |
DataTypes::ValueType* |
866 |
DataLazy::resolveNP1OUT(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const |
867 |
{ |
868 |
// we assume that any collapsing has been done before we get here |
869 |
// since we only have one argument we don't need to think about only |
870 |
// processing single points. |
871 |
if (m_readytype!='E') |
872 |
{ |
873 |
throw DataException("Programmer error - resolveNP1OUT should only be called on expanded Data."); |
874 |
} |
875 |
// since we can't write the result over the input, we need a result offset further along |
876 |
size_t subroffset=roffset+m_samplesize; |
877 |
const ValueType* vleft=m_left->resolveSample(v,offset,sampleNo,subroffset); |
878 |
roffset=offset; |
879 |
switch (m_op) |
880 |
{ |
881 |
case SYM: |
882 |
DataMaths::symmetric(*vleft,m_left->getShape(),subroffset, v, getShape(), offset); |
883 |
break; |
884 |
case NSYM: |
885 |
DataMaths::nonsymmetric(*vleft,m_left->getShape(),subroffset, v, getShape(), offset); |
886 |
break; |
887 |
default: |
888 |
throw DataException("Programmer error - resolveNP1OUT can not resolve operator "+opToString(m_op)+"."); |
889 |
} |
890 |
return &v; |
891 |
} |
892 |
|
893 |
/* |
894 |
\brief Compute the value of the expression (unary operation) for the given sample. |
895 |
\return Vector which stores the value of the subexpression for the given sample. |
896 |
\param v A vector to store intermediate results. |
897 |
\param offset Index in v to begin storing results. |
898 |
\param sampleNo Sample number to evaluate. |
899 |
\param roffset (output parameter) the offset in the return vector where the result begins. |
900 |
|
901 |
The return value will be an existing vector so do not deallocate it. |
902 |
If the result is stored in v it should be stored at the offset given. |
903 |
Everything from offset to the end of v should be considered available for this method to use. |
904 |
*/ |
905 |
DataTypes::ValueType* |
906 |
DataLazy::resolveNP1OUT_P(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const |
907 |
{ |
908 |
// we assume that any collapsing has been done before we get here |
909 |
// since we only have one argument we don't need to think about only |
910 |
// processing single points. |
911 |
if (m_readytype!='E') |
912 |
{ |
913 |
throw DataException("Programmer error - resolveNP1OUT_P should only be called on expanded Data."); |
914 |
} |
915 |
// since we can't write the result over the input, we need a result offset further along |
916 |
size_t subroffset=roffset+m_samplesize; |
917 |
const ValueType* vleft=m_left->resolveSample(v,offset,sampleNo,subroffset); |
918 |
roffset=offset; |
919 |
switch (m_op) |
920 |
{ |
921 |
case TRACE: |
922 |
DataMaths::trace(*vleft,m_left->getShape(),subroffset, v,getShape(),offset,m_axis_offset); |
923 |
break; |
924 |
case TRANS: |
925 |
DataMaths::transpose(*vleft,m_left->getShape(),subroffset, v,getShape(),offset,m_axis_offset); |
926 |
break; |
927 |
default: |
928 |
throw DataException("Programmer error - resolveNP1OUTP can not resolve operator "+opToString(m_op)+"."); |
929 |
} |
930 |
return &v; |
931 |
} |
932 |
|
933 |
|
934 |
#define PROC_OP(TYPE,X) \ |
935 |
for (int i=0;i<steps;++i,resultp+=resultStep) \ |
936 |
{ \ |
937 |
tensor_binary_operation< TYPE >(chunksize, &((*left)[lroffset]), &((*right)[rroffset]), resultp, X); \ |
938 |
lroffset+=leftStep; \ |
939 |
rroffset+=rightStep; \ |
940 |
} |
941 |
|
942 |
/* |
943 |
\brief Compute the value of the expression (binary operation) for the given sample. |
944 |
\return Vector which stores the value of the subexpression for the given sample. |
945 |
\param v A vector to store intermediate results. |
946 |
\param offset Index in v to begin storing results. |
947 |
\param sampleNo Sample number to evaluate. |
948 |
\param roffset (output parameter) the offset in the return vector where the result begins. |
949 |
|
950 |
The return value will be an existing vector so do not deallocate it. |
951 |
If the result is stored in v it should be stored at the offset given. |
952 |
Everything from offset to the end of v should be considered available for this method to use. |
953 |
*/ |
954 |
// This method assumes that any subexpressions which evaluate to Constant or Tagged Data |
955 |
// have already been collapsed to IDENTITY. So we must have at least one expanded child. |
956 |
// If both children are expanded, then we can process them in a single operation (we treat |
957 |
// the whole sample as one big datapoint. |
958 |
// If one of the children is not expanded, then we need to treat each point in the sample |
959 |
// individually. |
960 |
// There is an additional complication when scalar operations are considered. |
961 |
// For example, 2+Vector. |
962 |
// In this case each double within the point is treated individually |
963 |
DataTypes::ValueType* |
964 |
DataLazy::resolveBinary(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const |
965 |
{ |
966 |
LAZYDEBUG(cout << "Resolve binary: " << toString() << endl;) |
967 |
|
968 |
size_t lroffset=0, rroffset=0; // offsets in the left and right result vectors |
969 |
// first work out which of the children are expanded |
970 |
bool leftExp=(m_left->m_readytype=='E'); |
971 |
bool rightExp=(m_right->m_readytype=='E'); |
972 |
if (!leftExp && !rightExp) |
973 |
{ |
974 |
throw DataException("Programmer Error - please use collapse if neither argument has type 'E'."); |
975 |
} |
976 |
bool leftScalar=(m_left->getRank()==0); |
977 |
bool rightScalar=(m_right->getRank()==0); |
978 |
bool bigloops=((leftExp && rightExp) || (!leftExp && !rightExp)); // is processing in single step? |
979 |
int steps=(bigloops?1:getNumDPPSample()); |
980 |
size_t chunksize=(bigloops? m_samplesize : getNoValues()); // if bigloops, pretend the whole sample is a datapoint |
981 |
if (m_left->getRank()!=m_right->getRank()) // need to deal with scalar * ? ops |
982 |
{ |
983 |
if (!leftScalar && !rightScalar) |
984 |
{ |
985 |
throw DataException("resolveBinary - ranks of arguments must match unless one of them is scalar."); |
986 |
} |
987 |
steps=getNumDPPSample()*max(m_left->getNoValues(),m_right->getNoValues()); |
988 |
chunksize=1; // for scalar |
989 |
} |
990 |
int leftStep=((leftExp && (!rightExp || rightScalar))? m_right->getNoValues() : 0); |
991 |
int rightStep=((rightExp && (!leftExp || leftScalar))? m_left->getNoValues() : 0); |
992 |
int resultStep=max(leftStep,rightStep); // only one (at most) should be !=0 |
993 |
// Get the values of sub-expressions |
994 |
const ValueType* left=m_left->resolveSample(v,offset,sampleNo,lroffset); |
995 |
const ValueType* right=m_right->resolveSample(v,offset+m_samplesize,sampleNo,rroffset); // Note |
996 |
// the right child starts further along. |
997 |
double* resultp=&(v[offset]); // results are stored at the vector offset we recieved |
998 |
switch(m_op) |
999 |
{ |
1000 |
case ADD: |
1001 |
PROC_OP(NO_ARG,plus<double>()); |
1002 |
break; |
1003 |
case SUB: |
1004 |
PROC_OP(NO_ARG,minus<double>()); |
1005 |
break; |
1006 |
case MUL: |
1007 |
PROC_OP(NO_ARG,multiplies<double>()); |
1008 |
break; |
1009 |
case DIV: |
1010 |
PROC_OP(NO_ARG,divides<double>()); |
1011 |
break; |
1012 |
case POW: |
1013 |
PROC_OP(double (double,double),::pow); |
1014 |
break; |
1015 |
default: |
1016 |
throw DataException("Programmer error - resolveBinary can not resolve operator "+opToString(m_op)+"."); |
1017 |
} |
1018 |
roffset=offset; |
1019 |
return &v; |
1020 |
} |
1021 |
|
1022 |
|
1023 |
/* |
1024 |
\brief Compute the value of the expression (tensor product) for the given sample. |
1025 |
\return Vector which stores the value of the subexpression for the given sample. |
1026 |
\param v A vector to store intermediate results. |
1027 |
\param offset Index in v to begin storing results. |
1028 |
\param sampleNo Sample number to evaluate. |
1029 |
\param roffset (output parameter) the offset in the return vector where the result begins. |
1030 |
|
1031 |
The return value will be an existing vector so do not deallocate it. |
1032 |
If the result is stored in v it should be stored at the offset given. |
1033 |
Everything from offset to the end of v should be considered available for this method to use. |
1034 |
*/ |
1035 |
// This method assumes that any subexpressions which evaluate to Constant or Tagged Data |
1036 |
// have already been collapsed to IDENTITY. So we must have at least one expanded child. |
1037 |
// unlike the other resolve helpers, we must treat these datapoints separately. |
1038 |
DataTypes::ValueType* |
1039 |
DataLazy::resolveTProd(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const |
1040 |
{ |
1041 |
LAZYDEBUG(cout << "Resolve TensorProduct: " << toString() << endl;) |
1042 |
|
1043 |
size_t lroffset=0, rroffset=0; // offsets in the left and right result vectors |
1044 |
// first work out which of the children are expanded |
1045 |
bool leftExp=(m_left->m_readytype=='E'); |
1046 |
bool rightExp=(m_right->m_readytype=='E'); |
1047 |
int steps=getNumDPPSample(); |
1048 |
int leftStep=((leftExp && !rightExp)? m_right->getNoValues() : 0); |
1049 |
int rightStep=((rightExp && !leftExp)? m_left->getNoValues() : 0); |
1050 |
int resultStep=max(leftStep,rightStep); // only one (at most) should be !=0 |
1051 |
// Get the values of sub-expressions (leave a gap of one sample for the result). |
1052 |
const ValueType* left=m_left->resolveSample(v,offset+m_samplesize,sampleNo,lroffset); |
1053 |
const ValueType* right=m_right->resolveSample(v,offset+2*m_samplesize,sampleNo,rroffset); |
1054 |
double* resultp=&(v[offset]); // results are stored at the vector offset we recieved |
1055 |
switch(m_op) |
1056 |
{ |
1057 |
case PROD: |
1058 |
for (int i=0;i<steps;++i,resultp+=resultStep) |
1059 |
{ |
1060 |
const double *ptr_0 = &((*left)[lroffset]); |
1061 |
const double *ptr_1 = &((*right)[rroffset]); |
1062 |
matrix_matrix_product(m_SL, m_SM, m_SR, ptr_0, ptr_1, resultp, m_transpose); |
1063 |
lroffset+=leftStep; |
1064 |
rroffset+=rightStep; |
1065 |
} |
1066 |
break; |
1067 |
default: |
1068 |
throw DataException("Programmer error - resolveTProduct can not resolve operator "+opToString(m_op)+"."); |
1069 |
} |
1070 |
roffset=offset; |
1071 |
return &v; |
1072 |
} |
1073 |
|
1074 |
|
1075 |
|
1076 |
/* |
1077 |
\brief Compute the value of the expression for the given sample. |
1078 |
\return Vector which stores the value of the subexpression for the given sample. |
1079 |
\param v A vector to store intermediate results. |
1080 |
\param offset Index in v to begin storing results. |
1081 |
\param sampleNo Sample number to evaluate. |
1082 |
\param roffset (output parameter) the offset in the return vector where the result begins. |
1083 |
|
1084 |
The return value will be an existing vector so do not deallocate it. |
1085 |
*/ |
1086 |
// the vector and the offset are a place where the method could write its data if it wishes |
1087 |
// it is not obligated to do so. For example, if it has its own storage already, it can use that. |
1088 |
// Hence the return value to indicate where the data is actually stored. |
1089 |
// Regardless, the storage should be assumed to be used, even if it isn't. |
1090 |
|
1091 |
// the roffset is the offset within the returned vector where the data begins |
1092 |
const DataTypes::ValueType* |
1093 |
DataLazy::resolveSample(ValueType& v, size_t offset, int sampleNo, size_t& roffset) |
1094 |
{ |
1095 |
LAZYDEBUG(cout << "Resolve sample " << toString() << endl;) |
1096 |
// collapse so we have a 'E' node or an IDENTITY for some other type |
1097 |
if (m_readytype!='E' && m_op!=IDENTITY) |
1098 |
{ |
1099 |
collapse(); |
1100 |
} |
1101 |
if (m_op==IDENTITY) |
1102 |
{ |
1103 |
const ValueType& vec=m_id->getVector(); |
1104 |
if (m_readytype=='C') |
1105 |
{ |
1106 |
roffset=0; |
1107 |
return &(vec); |
1108 |
} |
1109 |
roffset=m_id->getPointOffset(sampleNo, 0); |
1110 |
return &(vec); |
1111 |
} |
1112 |
if (m_readytype!='E') |
1113 |
{ |
1114 |
throw DataException("Programmer Error - Collapse did not produce an expanded node."); |
1115 |
} |
1116 |
switch (getOpgroup(m_op)) |
1117 |
{ |
1118 |
case G_UNARY: return resolveUnary(v, offset,sampleNo,roffset); |
1119 |
case G_BINARY: return resolveBinary(v, offset,sampleNo,roffset); |
1120 |
case G_NP1OUT: return resolveNP1OUT(v, offset, sampleNo,roffset); |
1121 |
case G_NP1OUT_P: return resolveNP1OUT_P(v, offset, sampleNo,roffset); |
1122 |
case G_TENSORPROD: return resolveTProd(v,offset, sampleNo,roffset); |
1123 |
default: |
1124 |
throw DataException("Programmer Error - resolveSample does not know how to process "+opToString(m_op)+"."); |
1125 |
} |
1126 |
} |
1127 |
|
1128 |
|
1129 |
// To simplify the memory management, all threads operate on one large vector, rather than one each. |
1130 |
// Each sample is evaluated independently and copied into the result DataExpanded. |
1131 |
DataReady_ptr |
1132 |
DataLazy::resolve() |
1133 |
{ |
1134 |
|
1135 |
LAZYDEBUG(cout << "Sample size=" << m_samplesize << endl;) |
1136 |
LAZYDEBUG(cout << "Buffers=" << m_buffsRequired << endl;) |
1137 |
if (m_readytype!='E') // if the whole sub-expression is Constant or Tagged, then evaluate it normally |
1138 |
{ |
1139 |
collapse(); |
1140 |
} |
1141 |
if (m_op==IDENTITY) // So a lazy expression of Constant or Tagged data will be returned here. |
1142 |
{ |
1143 |
return m_id; |
1144 |
} |
1145 |
// from this point on we must have m_op!=IDENTITY and m_readytype=='E' |
1146 |
size_t threadbuffersize=m_maxsamplesize*(max(1,m_buffsRequired)); // Each thread needs to have enough |
1147 |
// storage to evaluate its expression |
1148 |
int numthreads=1; |
1149 |
#ifdef _OPENMP |
1150 |
numthreads=getNumberOfThreads(); |
1151 |
#endif |
1152 |
ValueType v(numthreads*threadbuffersize); |
1153 |
LAZYDEBUG(cout << "Buffer created with size=" << v.size() << endl;) |
1154 |
DataExpanded* result=new DataExpanded(getFunctionSpace(),getShape(), ValueType(getNoValues())); |
1155 |
ValueType& resvec=result->getVector(); |
1156 |
DataReady_ptr resptr=DataReady_ptr(result); |
1157 |
int sample; |
1158 |
size_t outoffset; // offset in the output data |
1159 |
int totalsamples=getNumSamples(); |
1160 |
const ValueType* res=0; // Vector storing the answer |
1161 |
size_t resoffset=0; // where in the vector to find the answer |
1162 |
#pragma omp parallel for private(sample,resoffset,outoffset,res) schedule(static) |
1163 |
for (sample=0;sample<totalsamples;++sample) |
1164 |
{ |
1165 |
LAZYDEBUG(cout << "################################# " << sample << endl;) |
1166 |
#ifdef _OPENMP |
1167 |
res=resolveSample(v,threadbuffersize*omp_get_thread_num(),sample,resoffset); |
1168 |
#else |
1169 |
res=resolveSample(v,0,sample,resoffset); // res would normally be v, but not if its a single IDENTITY op. |
1170 |
#endif |
1171 |
LAZYDEBUG(cerr << "-------------------------------- " << endl;) |
1172 |
outoffset=result->getPointOffset(sample,0); |
1173 |
LAZYDEBUG(cerr << "offset=" << outoffset << endl;) |
1174 |
for (unsigned int i=0;i<m_samplesize;++i,++outoffset,++resoffset) // copy values into the output vector |
1175 |
{ |
1176 |
resvec[outoffset]=(*res)[resoffset]; |
1177 |
} |
1178 |
LAZYDEBUG(cerr << "*********************************" << endl;) |
1179 |
} |
1180 |
return resptr; |
1181 |
} |
1182 |
|
1183 |
std::string |
1184 |
DataLazy::toString() const |
1185 |
{ |
1186 |
ostringstream oss; |
1187 |
oss << "Lazy Data:"; |
1188 |
intoString(oss); |
1189 |
return oss.str(); |
1190 |
} |
1191 |
|
1192 |
|
1193 |
void |
1194 |
DataLazy::intoString(ostringstream& oss) const |
1195 |
{ |
1196 |
switch (getOpgroup(m_op)) |
1197 |
{ |
1198 |
case G_IDENTITY: |
1199 |
if (m_id->isExpanded()) |
1200 |
{ |
1201 |
oss << "E"; |
1202 |
} |
1203 |
else if (m_id->isTagged()) |
1204 |
{ |
1205 |
oss << "T"; |
1206 |
} |
1207 |
else if (m_id->isConstant()) |
1208 |
{ |
1209 |
oss << "C"; |
1210 |
} |
1211 |
else |
1212 |
{ |
1213 |
oss << "?"; |
1214 |
} |
1215 |
oss << '@' << m_id.get(); |
1216 |
break; |
1217 |
case G_BINARY: |
1218 |
oss << '('; |
1219 |
m_left->intoString(oss); |
1220 |
oss << ' ' << opToString(m_op) << ' '; |
1221 |
m_right->intoString(oss); |
1222 |
oss << ')'; |
1223 |
break; |
1224 |
case G_UNARY: |
1225 |
case G_NP1OUT: |
1226 |
case G_NP1OUT_P: |
1227 |
oss << opToString(m_op) << '('; |
1228 |
m_left->intoString(oss); |
1229 |
oss << ')'; |
1230 |
break; |
1231 |
case G_TENSORPROD: |
1232 |
oss << opToString(m_op) << '('; |
1233 |
m_left->intoString(oss); |
1234 |
oss << ", "; |
1235 |
m_right->intoString(oss); |
1236 |
oss << ')'; |
1237 |
break; |
1238 |
default: |
1239 |
oss << "UNKNOWN"; |
1240 |
} |
1241 |
} |
1242 |
|
1243 |
DataAbstract* |
1244 |
DataLazy::deepCopy() |
1245 |
{ |
1246 |
switch (getOpgroup(m_op)) |
1247 |
{ |
1248 |
case G_IDENTITY: return new DataLazy(m_id->deepCopy()->getPtr()); |
1249 |
case G_UNARY: return new DataLazy(m_left->deepCopy()->getPtr(),m_op); |
1250 |
case G_BINARY: return new DataLazy(m_left->deepCopy()->getPtr(),m_right->deepCopy()->getPtr(),m_op); |
1251 |
case G_NP1OUT: return new DataLazy(m_left->deepCopy()->getPtr(), m_right->deepCopy()->getPtr(),m_op); |
1252 |
case G_TENSORPROD: return new DataLazy(m_left->deepCopy()->getPtr(), m_right->deepCopy()->getPtr(), m_op, m_axis_offset, m_transpose); |
1253 |
default: |
1254 |
throw DataException("Programmer error - do not know how to deepcopy operator "+opToString(m_op)+"."); |
1255 |
} |
1256 |
} |
1257 |
|
1258 |
|
1259 |
// There is no single, natural interpretation of getLength on DataLazy. |
1260 |
// Instances of DataReady can look at the size of their vectors. |
1261 |
// For lazy though, it could be the size the data would be if it were resolved; |
1262 |
// or it could be some function of the lengths of the DataReady instances which |
1263 |
// form part of the expression. |
1264 |
// Rather than have people making assumptions, I have disabled the method. |
1265 |
DataTypes::ValueType::size_type |
1266 |
DataLazy::getLength() const |
1267 |
{ |
1268 |
throw DataException("getLength() does not make sense for lazy data."); |
1269 |
} |
1270 |
|
1271 |
|
1272 |
DataAbstract* |
1273 |
DataLazy::getSlice(const DataTypes::RegionType& region) const |
1274 |
{ |
1275 |
throw DataException("getSlice - not implemented for Lazy objects."); |
1276 |
} |
1277 |
|
1278 |
|
1279 |
// To do this we need to rely on our child nodes |
1280 |
DataTypes::ValueType::size_type |
1281 |
DataLazy::getPointOffset(int sampleNo, |
1282 |
int dataPointNo) |
1283 |
{ |
1284 |
if (m_op==IDENTITY) |
1285 |
{ |
1286 |
return m_id->getPointOffset(sampleNo,dataPointNo); |
1287 |
} |
1288 |
if (m_readytype!='E') |
1289 |
{ |
1290 |
collapse(); |
1291 |
return m_id->getPointOffset(sampleNo,dataPointNo); |
1292 |
} |
1293 |
// at this point we do not have an identity node and the expression will be Expanded |
1294 |
// so we only need to know which child to ask |
1295 |
if (m_left->m_readytype=='E') |
1296 |
{ |
1297 |
return m_left->getPointOffset(sampleNo,dataPointNo); |
1298 |
} |
1299 |
else |
1300 |
{ |
1301 |
return m_right->getPointOffset(sampleNo,dataPointNo); |
1302 |
} |
1303 |
} |
1304 |
|
1305 |
// To do this we need to rely on our child nodes |
1306 |
DataTypes::ValueType::size_type |
1307 |
DataLazy::getPointOffset(int sampleNo, |
1308 |
int dataPointNo) const |
1309 |
{ |
1310 |
if (m_op==IDENTITY) |
1311 |
{ |
1312 |
return m_id->getPointOffset(sampleNo,dataPointNo); |
1313 |
} |
1314 |
if (m_readytype=='E') |
1315 |
{ |
1316 |
// at this point we do not have an identity node and the expression will be Expanded |
1317 |
// so we only need to know which child to ask |
1318 |
if (m_left->m_readytype=='E') |
1319 |
{ |
1320 |
return m_left->getPointOffset(sampleNo,dataPointNo); |
1321 |
} |
1322 |
else |
1323 |
{ |
1324 |
return m_right->getPointOffset(sampleNo,dataPointNo); |
1325 |
} |
1326 |
} |
1327 |
if (m_readytype=='C') |
1328 |
{ |
1329 |
return m_left->getPointOffset(sampleNo,dataPointNo); // which child doesn't matter |
1330 |
} |
1331 |
throw DataException("Programmer error - getPointOffset on lazy data may require collapsing (but this object is marked const)."); |
1332 |
} |
1333 |
|
1334 |
// It would seem that DataTagged will need to be treated differently since even after setting all tags |
1335 |
// to zero, all the tags from all the DataTags would be in the result. |
1336 |
// However since they all have the same value (0) whether they are there or not should not matter. |
1337 |
// So I have decided that for all types this method will create a constant 0. |
1338 |
// It can be promoted up as required. |
1339 |
// A possible efficiency concern might be expanded->constant->expanded which has an extra memory management |
1340 |
// but we can deal with that if it arrises. |
1341 |
void |
1342 |
DataLazy::setToZero() |
1343 |
{ |
1344 |
DataTypes::ValueType v(getNoValues(),0); |
1345 |
m_id=DataReady_ptr(new DataConstant(getFunctionSpace(),getShape(),v)); |
1346 |
m_op=IDENTITY; |
1347 |
m_right.reset(); |
1348 |
m_left.reset(); |
1349 |
m_readytype='C'; |
1350 |
m_buffsRequired=1; |
1351 |
} |
1352 |
|
1353 |
bool |
1354 |
DataLazy::actsExpanded() const |
1355 |
{ |
1356 |
return (m_readytype=='E'); |
1357 |
} |
1358 |
|
1359 |
} // end namespace |