00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042 #include "SundanceDiffOpEvaluator.hpp"
00043 #include "SundanceEvalManager.hpp"
00044 #include "SundanceDiffOp.hpp"
00045
00046
00047 #include "PlayaTabs.hpp"
00048 #include "SundanceOut.hpp"
00049 #include "SundanceUnknownFuncElement.hpp"
00050 #include "SundanceTestFuncElement.hpp"
00051 #include "SundanceDiscreteFuncElement.hpp"
00052 #include "SundanceDiscreteFuncEvaluator.hpp"
00053 #include "SundanceEvaluatableExpr.hpp"
00054 #include "SundanceZeroExpr.hpp"
00055
00056 using namespace Sundance;
00057 using namespace Sundance;
00058 using namespace Sundance;
00059 using namespace Teuchos;
00060
00061
00062
00063
00064
00065 DiffOpEvaluator
00066 ::DiffOpEvaluator(const DiffOp* expr,
00067 const EvalContext& context)
00068 : UnaryEvaluator<DiffOp>(expr, context),
00069 isConstant_(this->sparsity()->numDerivs()),
00070 resultIndices_(this->sparsity()->numDerivs()),
00071 constantMonomials_(this->sparsity()->numDerivs()),
00072 vectorMonomials_(this->sparsity()->numDerivs()),
00073 constantFuncCoeffs_(this->sparsity()->numDerivs()),
00074 vectorFuncCoeffs_(this->sparsity()->numDerivs()),
00075 funcEvaluators_(),
00076 constantCoeffFuncIndices_(this->sparsity()->numDerivs()),
00077 constantCoeffFuncMi_(this->sparsity()->numDerivs()),
00078 vectorCoeffFuncIndices_(this->sparsity()->numDerivs()),
00079 vectorCoeffFuncMi_(this->sparsity()->numDerivs())
00080 {
00081 int verb = context.setupVerbosity();
00082 Tabs tabs;
00083 SUNDANCE_MSG1(verb, tabs << "initializing diff op evaluator for "
00084 << expr->toString());
00085
00086 {
00087 Tabs tab0;
00088
00089 SUNDANCE_MSG2(verb, tab0 << "return sparsity " << std::endl << *(this->sparsity)());
00090
00091 SUNDANCE_MSG2(verb, tab0 << "argument sparsity subset " << std::endl
00092 << *(argSparsitySuperset()));
00093
00094 Map<const DiscreteFuncElementEvaluator*, int> funcToIndexMap;
00095
00096 int vecResultIndex = 0;
00097 int constResultIndex = 0;
00098
00099 for (int i=0; i<this->sparsity()->numDerivs(); i++)
00100 {
00101 Tabs tab1;
00102 const MultipleDeriv& resultDeriv = this->sparsity()->deriv(i);
00103 SUNDANCE_MSG3(verb, tab0 << "working out procedure for computing "
00104 << resultDeriv);
00105
00106 if (this->sparsity()->state(i)==ConstantDeriv)
00107 {
00108 Tabs tab2;
00109 addConstantIndex(i, constResultIndex);
00110 resultIndices_[i] = constResultIndex++;
00111 isConstant_[i] = true;
00112 SUNDANCE_MSG3(verb, tab2 << "deriv is constant, will be stored at index "
00113 << resultIndices_[i] << " in the const result array");
00114 }
00115 else
00116 {
00117 Tabs tab2;
00118 addVectorIndex(i, vecResultIndex);
00119 resultIndices_[i] = vecResultIndex++;
00120 isConstant_[i] = false;
00121 SUNDANCE_MSG3(verb, tab2 << "deriv is variable, will be stored at index "
00122 << resultIndices_[i] << " in the var result array");
00123 }
00124
00125 int order = resultDeriv.order();
00126 const Set<MultipleDeriv>& RArg
00127 = argExpr()->findR(order, context);
00128 const Set<MultipleDeriv>& RArgPlus
00129 = argExpr()->findR(order+1, context);
00130 const Set<MultipleDeriv>& W1Arg
00131 = argExpr()->findW(1, context);
00132
00133
00134 SUNDANCE_MSG3(verb, tab1 << "RArg = " << RArg);
00135 SUNDANCE_MSG3(verb, tab1 << "RArgPlus = " << RArgPlus);
00136 SUNDANCE_MSG3(verb, tab1 << "W1Arg = " << W1Arg);
00137
00138 Set<MultipleDeriv> funcTermCoeffs
00139 = RArgPlus.intersection(increasedDerivs(resultDeriv, W1Arg, verb));
00140 SUNDANCE_MSG3(verb, tab1 << "function term coeffs = " << funcTermCoeffs);
00141
00142
00143 if (funcTermCoeffs.size()==0)
00144 {
00145 SUNDANCE_MSG3(verb, tab1 << "no direct chain rule terms");
00146 }
00147 else
00148 {
00149 SUNDANCE_MSG3(verb, tab1 << "getting direct chain rule terms");
00150 }
00151
00152
00153 for (Set<MultipleDeriv>::const_iterator
00154 j=funcTermCoeffs.begin(); j != funcTermCoeffs.end(); j++)
00155 {
00156 Tabs tab2;
00157 SUNDANCE_MSG3(verb, tab2 << "getting coefficient of " << *j);
00158
00159 int argIndex = argSparsitySuperset()->getIndex(*j);
00160 TEUCHOS_TEST_FOR_EXCEPTION(argIndex==-1, std::runtime_error,
00161 "Derivative " << *j << " expected in argument "
00162 "but not found");
00163
00164 Deriv lambda = remainder(*j, resultDeriv, verb);
00165
00166 if (lambda.isCoordDeriv())
00167 {
00168 Tabs tab3;
00169 SUNDANCE_MSG3(verb, tab2 << "detected coordinate deriv");
00170 if (lambda.coordDerivDir()!=expr->mi().firstOrderDirection())
00171 {
00172 SUNDANCE_MSG3(verb, tab2 << "direction mismatch, skipping");
00173 continue;
00174 }
00175 const DerivState& argState = argSparsitySuperset()->state(argIndex);
00176 if (argState==ConstantDeriv)
00177 {
00178 int constArgIndex = argEval()->constantIndexMap().get(argIndex);
00179 constantMonomials_[i].append(constArgIndex);
00180 }
00181 else
00182 {
00183 int vectorArgIndex = argEval()->vectorIndexMap().get(argIndex);
00184 vectorMonomials_[i].append(vectorArgIndex);
00185 }
00186 }
00187 else if (lambda.opOnFunc().isPartial() || lambda.opOnFunc().isIdentity())
00188 {
00189 Tabs tab3;
00190 SUNDANCE_MSG3(verb, tab3 << "detected functional deriv " << lambda);
00191 const SymbolicFuncElement* f = lambda.symbFuncElem();
00192 const MultiIndex& mi = expr->mi() + lambda.opOnFunc().mi();
00193 SUNDANCE_MSG3(verb, tab3 << "modified multiIndex is " << mi);
00194
00195 const TestFuncElement* t
00196 = dynamic_cast<const TestFuncElement*>(f);
00197 if (t != 0) continue;
00198
00199 const UnknownFuncElement* u
00200 = dynamic_cast<const UnknownFuncElement*>(f);
00201 TEUCHOS_TEST_FOR_EXCEPTION(u==0, std::logic_error,
00202 "Non-unknown function detected where an unknown "
00203 "function was expected in "
00204 "DiffOpEvaluator ctor");
00205
00206
00207 const EvaluatableExpr* evalPt = u->evalPt();
00208 const ZeroExpr* z = dynamic_cast<const ZeroExpr*>(evalPt);
00209 if (z != 0) continue;
00210 TEUCHOS_TEST_FOR_EXCEPTION(z != 0, std::logic_error,
00211 "DiffOpEvaluator detected identically zero "
00212 "function");
00213
00214 const DiscreteFuncElement* df
00215 = dynamic_cast<const DiscreteFuncElement*>(evalPt);
00216
00217 TEUCHOS_TEST_FOR_EXCEPTION(df==0, std::logic_error,
00218 "DiffOpEvaluator ctor: evaluation point of "
00219 "unknown function " << u->toString()
00220 << " is not a discrete function");
00221
00222 const SymbolicFuncElementEvaluator* uEval
00223 = dynamic_cast<const SymbolicFuncElementEvaluator*>(u->evaluator(context).get());
00224
00225 const DiscreteFuncElementEvaluator* dfEval = uEval->dfEval();
00226
00227
00228 TEUCHOS_TEST_FOR_EXCEPTION(dfEval==0, std::logic_error,
00229 "DiffOpEvaluator ctor: evaluator for "
00230 "evaluation point is not a "
00231 "DiscreteFuncElementEvaluator");
00232
00233 TEUCHOS_TEST_FOR_EXCEPTION(!dfEval->hasMultiIndex(mi), std::logic_error,
00234 "DiffOpEvaluator ctor: evaluator for "
00235 "discrete function " << df->toString()
00236 << " does not know about multiindex "
00237 << mi.toString());
00238
00239 int fIndex;
00240 int miIndex = dfEval->miIndex(mi);
00241
00242 if (funcToIndexMap.containsKey(dfEval))
00243 {
00244 fIndex = funcToIndexMap.get(dfEval);
00245 }
00246 else
00247 {
00248 fIndex = funcEvaluators_.size();
00249 funcEvaluators_.append(dfEval);
00250 funcToIndexMap.put(dfEval, fIndex);
00251 }
00252
00253
00254 const DerivState& argState = argSparsitySuperset()->state(argIndex);
00255 if (argState==ConstantDeriv)
00256 {
00257 int constArgIndex = argEval()->constantIndexMap().get(argIndex);
00258 constantCoeffFuncIndices_[i].append(fIndex);
00259 constantCoeffFuncMi_[i].append(miIndex);
00260 constantFuncCoeffs_[i].append(constArgIndex);
00261 }
00262 else
00263 {
00264 int vectorArgIndex = argEval()->vectorIndexMap().get(argIndex);
00265 vectorCoeffFuncIndices_[i].append(fIndex);
00266 vectorCoeffFuncMi_[i].append(miIndex);
00267 vectorFuncCoeffs_[i].append(vectorArgIndex);
00268 }
00269 }
00270 else
00271 {
00272 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
00273 "DiffOpEvaluator has been asked to preprocess a Deriv that "
00274 "is not a simple partial derivative. The problem child is: "
00275 << lambda);
00276 }
00277 }
00278
00279
00280 Set<MultipleDeriv> isolatedTerms
00281 = RArg.intersection(backedDerivs(resultDeriv, W1Arg, verb));
00282
00283 if (isolatedTerms.size()==0)
00284 {
00285 SUNDANCE_MSG3(verb, tab1 << "no indirect chain rule terms");
00286 }
00287 else
00288 {
00289 SUNDANCE_MSG3(verb, tab1 << "getting indirect chain rule terms");
00290 SUNDANCE_MSG3(verb, tab1 << "isolated terms = " << isolatedTerms);
00291 }
00292
00293 for (Set<MultipleDeriv>::const_iterator
00294 j=isolatedTerms.begin(); j != isolatedTerms.end(); j++)
00295 {
00296 int argIndex = argSparsitySuperset()->getIndex(*j);
00297 TEUCHOS_TEST_FOR_EXCEPTION(argIndex==-1, std::runtime_error,
00298 "Derivative " << *j << " expected in argument "
00299 "but not found");
00300 const DerivState& argState = argSparsitySuperset()->state(argIndex);
00301 if (argState==ConstantDeriv)
00302 {
00303 int constArgIndex = argEval()->constantIndexMap().get(argIndex);
00304 constantMonomials_[i].append(constArgIndex);
00305 }
00306 else
00307 {
00308 int vectorArgIndex = argEval()->vectorIndexMap().get(argIndex);
00309 vectorMonomials_[i].append(vectorArgIndex);
00310 }
00311 }
00312 }
00313 }
00314
00315 if (verb > 2)
00316 {
00317 Out::os() << tabs << "instruction tables for summing spatial/functional chain rule" << std::endl;
00318 for (int i=0; i<this->sparsity()->numDerivs(); i++)
00319 {
00320 Tabs tab1;
00321 Out::os() << tab1 << "deriv " << sparsity()->deriv(i) << std::endl;
00322 {
00323 Tabs tab2;
00324 Out::os() << tab2 << "constant monomials: " << constantMonomials_[i]
00325 << std::endl;
00326 Out::os() << tab2 << "vector monomials: " << vectorMonomials_[i]
00327 << std::endl;
00328
00329 Out::os() << tab2 << "constant coeff functions: " << std::endl;
00330 for (int j=0; j<constantFuncCoeffs_[i].size(); j++)
00331 {
00332 Tabs tab3;
00333 Out::os() << tab3 << "func=" << constantCoeffFuncIndices_[i][j]
00334 << " mi=" << constantCoeffFuncMi_[i][j] << std::endl;
00335 }
00336 Out::os() << tab2 << "vector coeff functions: " << std::endl;
00337 for (int j=0; j<vectorFuncCoeffs_[i].size(); j++)
00338 {
00339 Tabs tab3;
00340 Out::os() << tab3 << "func=" << vectorCoeffFuncIndices_[i][j]
00341 << " mi=" << vectorCoeffFuncMi_[i][j] << std::endl;
00342 }
00343
00344 }
00345 }
00346 }
00347 }
00348
00349
00350 Deriv DiffOpEvaluator::remainder(const MultipleDeriv& big,
00351 const MultipleDeriv& little, int verb) const
00352 {
00353 Tabs tab;
00354 SUNDANCE_MSG5(verb, tab << "computing remainder: big=" << big << ", little="
00355 << little);
00356 TEUCHOS_TEST_FOR_EXCEPT(big.order()-little.order() != 1);
00357
00358 MultipleDeriv r;
00359 if (little.order()==0) r = big;
00360 else r = big.factorOutDeriv(little);
00361
00362 SUNDANCE_MSG5(verb, tab << "remainder = " << r);
00363
00364 TEUCHOS_TEST_FOR_EXCEPT(r.order() != 1);
00365
00366 return *(r.begin());
00367 }
00368
00369 Set<MultipleDeriv> DiffOpEvaluator
00370 ::increasedDerivs(const MultipleDeriv& mu,
00371 const Set<MultipleDeriv>& W1, int verb) const
00372 {
00373 Tabs tabs;
00374 SUNDANCE_MSG3(verb, tabs << "computing increased derivs");
00375 Set<MultipleDeriv> rtn;
00376 for (Set<MultipleDeriv>::const_iterator i=W1.begin(); i!=W1.end(); i++)
00377 {
00378 MultipleDeriv md = *i;
00379 TEUCHOS_TEST_FOR_EXCEPT(md.order() != 1);
00380 Deriv lambda = *(md.begin());
00381 MultipleDeriv lambdaMu = mu;
00382 lambdaMu.put(lambda);
00383 rtn.put(lambdaMu);
00384 }
00385 SUNDANCE_MSG3(verb, tabs << "increased derivs = " << rtn);
00386 return rtn;
00387 }
00388
00389 Set<MultipleDeriv> DiffOpEvaluator
00390 ::backedDerivs(const MultipleDeriv& mu,
00391 const Set<MultipleDeriv>& W1, int verb) const
00392 {
00393 Tabs tabs;
00394 SUNDANCE_MSG3(verb, tabs << "computing backed-out derivs for mu= " << mu
00395 << ", W1=" << W1);
00396 Set<MultipleDeriv> rtn;
00397 if (mu.order() != 0)
00398 {
00399 const MultiIndex& alpha = expr()->mi();
00400
00401 for (Set<MultipleDeriv>::const_iterator i=W1.begin(); i!=W1.end(); i++)
00402 {
00403 const MultipleDeriv& md = *i;
00404 TEUCHOS_TEST_FOR_EXCEPT(md.order() != 1);
00405 Deriv lambda = *(md.begin());
00406 if (lambda.isCoordDeriv()) continue;
00407 TEUCHOS_TEST_FOR_EXCEPT(!lambda.isFunctionalDeriv());
00408 FunctionIdentifier lambda_fid = lambda.fid();
00409 const MultiIndex& lambda_mi = lambda.opOnFunc().mi();
00410 for (MultipleDeriv::const_iterator j=mu.begin(); j!=mu.end(); j++)
00411 {
00412 const Deriv& d = *j;
00413 if (d.isCoordDeriv()) continue;
00414 FunctionIdentifier d_fid = d.fid();
00415 const MultiIndex& d_mi = d.opOnFunc().mi();
00416 if (d_fid != lambda_fid) continue;
00417 if (!(alpha + lambda_mi == d_mi)) continue;
00418 MultipleDeriv z = mu.factorOutDeriv(d);
00419 z.put(lambda);
00420 rtn.put(z);
00421 }
00422 }
00423 }
00424 SUNDANCE_MSG3(verb, tabs << "backed-out derivs = " << rtn);
00425 return rtn;
00426 }
00427
00428
00429
00430 void DiffOpEvaluator::internalEval(const EvalManager& mgr,
00431 Array<double>& constantResults,
00432 Array<RCP<EvalVector> >& vectorResults) const
00433 {
00434 Tabs tabs;
00435 SUNDANCE_MSG1(mgr.verb(), tabs << "DiffOpEvaluator::eval() expr="
00436 << expr()->toString());
00437
00438
00439 Array<RCP<EvalVector> > argVectorResults;
00440 Array<double> argConstantResults;
00441
00442 SUNDANCE_MSG2(mgr.verb(), tabs << "evaluating operand");
00443 evalOperand(mgr, argConstantResults, argVectorResults);
00444
00445
00446 if (mgr.verb() > 2)
00447 {
00448 Tabs tab1;
00449 Out::os() << tabs << "DiffOp operand results" << std::endl;
00450 mgr.showResults(Out::os(), argSparsitySuperset(), argVectorResults,
00451 argConstantResults);
00452 }
00453
00454
00455
00456
00457 SUNDANCE_MSG2(mgr.verb(), tabs << "evaluating discrete functions, num funcs= " << funcEvaluators_.size());
00458
00459 Array<Array<RCP<EvalVector> > > funcVectorResults(funcEvaluators_.size());
00460 Array<double> funcConstantResults;
00461 for (int i=0; i<funcEvaluators_.size(); i++)
00462 {
00463 funcEvaluators_[i]->eval(mgr, funcConstantResults, funcVectorResults[i]);
00464 }
00465
00466 constantResults.resize(this->sparsity()->numConstantDerivs());
00467 vectorResults.resize(this->sparsity()->numVectorDerivs());
00468
00469 SUNDANCE_MSG3(mgr.verb(), tabs << "summing spatial/functional chain rule");
00470
00471 for (int i=0; i<this->sparsity()->numDerivs(); i++)
00472 {
00473 Tabs tab1;
00474 SUNDANCE_MSG4(mgr.verb(), tab1 << "working on deriv "
00475 << this->sparsity()->deriv(i));
00476
00477
00478 SUNDANCE_MSG4(mgr.verb(), tab1 << "have " << constantMonomials_[i].size()
00479 << " constant monomials");
00480 double constantVal = 0.0;
00481 for (int j=0; j<constantMonomials_[i].size(); j++)
00482 {
00483 SUNDANCE_MSG4(mgr.verb(), tab1 << "adding in constant monomial (index "
00484 << constantMonomials_[i][j]
00485 << " in arg results)");
00486 constantVal += argConstantResults[constantMonomials_[i][j]];
00487 }
00488 if (isConstant_[i])
00489 {
00490 constantResults[resultIndices_[i]] = constantVal;
00491 SUNDANCE_MSG4(mgr.verb(), tab1 << "result is constant: value="
00492 << constantVal);
00493 continue;
00494 }
00495
00496 RCP<EvalVector> result;
00497 bool vecHasBeenAllocated = false;
00498
00499
00500 const Array<int>& vm = vectorMonomials_[i];
00501 SUNDANCE_MSG4(mgr.verb(), tab1 << "have " << vm.size()
00502 << " vector monomials");
00503 for (int j=0; j<vm.size(); j++)
00504 {
00505 Tabs tab2;
00506
00507 const RCP<EvalVector>& v = argVectorResults[vm[j]];
00508
00509 SUNDANCE_MSG4(mgr.verb(), tab2 << "found vec monomial term " << v->str());
00510
00511
00512
00513 if (!vecHasBeenAllocated)
00514 {
00515 SUNDANCE_MSG4(mgr.verb(), tab2 << "allocated result vector");
00516 result = mgr.popVector();
00517 vecHasBeenAllocated = true;
00518 if (isZero(constantVal))
00519 {
00520 result->setTo_V(v.get());
00521 }
00522 else
00523 {
00524 result->setTo_S_add_V(constantVal, v.get());
00525 }
00526 }
00527 else
00528 {
00529 result->add_V(v.get());
00530 }
00531 SUNDANCE_MSG4(mgr.verb(), tab2 << "result is " << result->str());
00532 }
00533
00534
00535 const Array<int>& cf = constantFuncCoeffs_[i];
00536 SUNDANCE_MSG4(mgr.verb(), tab1 << "adding " << cf.size()
00537 << " func terms with constant coeffs");
00538 for (int j=0; j<cf.size(); j++)
00539 {
00540 Tabs tab2;
00541 const double& coeff = argConstantResults[cf[j]];
00542 int fIndex = constantCoeffFuncIndices_[i][j];
00543 int miIndex = constantCoeffFuncMi_[i][j];
00544 const RCP<EvalVector>& fValue
00545 = funcVectorResults[fIndex][miIndex];
00546
00547 SUNDANCE_MSG4(mgr.verb(), tab2 << "found term: coeff= "
00548 << coeff << ", func value=" << fValue->str());
00549
00550
00551
00552 if (!vecHasBeenAllocated)
00553 {
00554 SUNDANCE_MSG4(mgr.verb(), tab2 << "allocated result vector");
00555 result = mgr.popVector();
00556 vecHasBeenAllocated = true;
00557 if (isOne(coeff))
00558 {
00559 if (isZero(constantVal))
00560 {
00561 result->setTo_V(fValue.get());
00562 }
00563 else
00564 {
00565 result->setTo_S_add_V(constantVal, fValue.get());
00566 }
00567 }
00568 else
00569 {
00570 if (isZero(constantVal))
00571 {
00572 result->setTo_SV(coeff, fValue.get());
00573 }
00574 else
00575 {
00576 result->setTo_S_add_SV(constantVal, coeff, fValue.get());
00577 }
00578 }
00579 }
00580 else
00581 {
00582 if (isOne(coeff))
00583 {
00584 result->add_V(fValue.get());
00585 }
00586 else
00587 {
00588 result->add_SV(coeff, fValue.get());
00589 }
00590 }
00591 SUNDANCE_MSG4(mgr.verb(), tab2 << "result is " << result->str());
00592 }
00593
00594
00595
00596 const Array<int>& vf = vectorFuncCoeffs_[i];
00597 SUNDANCE_MSG4(mgr.verb(), tab1 << "adding " << vf.size()
00598 << " func terms with vector coeffs");
00599 for (int j=0; j<vf.size(); j++)
00600 {
00601 Tabs tab2;
00602
00603 const RCP<EvalVector>& coeff = argVectorResults[vf[j]];
00604 int fIndex = vectorCoeffFuncIndices_[i][j];
00605 int miIndex = vectorCoeffFuncMi_[i][j];
00606 const RCP<EvalVector>& fValue
00607 = funcVectorResults[fIndex][miIndex];
00608
00609 SUNDANCE_MSG4(mgr.verb(), tab2 << "found term: coeff= "
00610 << coeff->str() << ", func value="
00611 << fValue->str());
00612
00613
00614
00615 if (!vecHasBeenAllocated)
00616 {
00617 SUNDANCE_MSG4(mgr.verb(), tab2 << "allocated result vector");
00618 result = mgr.popVector();
00619 vecHasBeenAllocated = true;
00620 result->setTo_VV(coeff.get(), fValue.get());
00621 }
00622 else
00623 {
00624 result->add_VV(coeff.get(), fValue.get());
00625 }
00626 SUNDANCE_MSG4(mgr.verb(), tab2 << "result is " << result->str());
00627 }
00628
00629 TEUCHOS_TEST_FOR_EXCEPTION(!vecHasBeenAllocated, std::logic_error,
00630 "created empty vector in DiffOpEvaluator::internalEval");
00631 vectorResults[resultIndices_[i]] = result;
00632 }
00633
00634 if (mgr.verb() > 1)
00635 {
00636 Out::os() << tabs << "diff op results" << std::endl;
00637 mgr.showResults(Out::os(), sparsity(), vectorResults,
00638 constantResults);
00639 }
00640 SUNDANCE_MSG1(mgr.verb(), tabs << "done spatial/functional chain rule");
00641 }
00642
00643
00644
00645 void DiffOpEvaluator::resetNumCalls() const
00646 {
00647 argEval()->resetNumCalls();
00648 for (int i=0; i<funcEvaluators_.size(); i++)
00649 {
00650 funcEvaluators_[i]->resetNumCalls();
00651 }
00652 Evaluator::resetNumCalls();
00653 }