Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
computationgraphbuilder.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2021 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
25
27#include <qle/ad/ssaform.hpp>
28
32
34
35#include <ql/errors.hpp>
36#include <ql/indexes/indexmanager.hpp>
37
38#include <boost/timer/timer.hpp>
39
40#define TRACE(message, n) \
41 { \
42 if (interactive_) { \
43 std::cerr << "\nComputationGraphBuilder: " << message << " at " << to_string((n).locationInfo) << "\n"; \
44 std::cerr << "value (" << std::setw(3) << value.size() << ") = " << value.top() << "\n"; \
45 std::cerr << "value_node (" << std::setw(3) << value_node.size() << ") = " << value_node.top() \
46 << "\n"; \
47 std::cerr << "filter (" << std::setw(3) << filter.size() << ") = " << filter.top() << "\n"; \
48 std::cerr << "filter_node (" << std::setw(3) << filter_node.size() << ") = " << filter_node.top() \
49 << "\n"; \
50 std::cerr << printCodeContext(script_, &n); \
51 std::string c; \
52 do { \
53 std::cerr << "(c)ontext (s)sa-form (q)uit "; \
54 std::getline(std::cin, c); \
55 if (c == "c") \
56 std::cerr << "<<<<\n" << context_ << ">>>>\n"; \
57 else if (c == "s") \
58 std::cerr << "<<<<\n" << ssaForm(g_, opLabels_) << ">>>>\n"; \
59 else if (c == "q") \
60 interactive_ = false; \
61 } while (c == "c" || c == "s"); \
62 } \
63 }
64
65namespace ore {
66namespace data {
67
68namespace {
69class ASTRunner : public AcyclicVisitor,
70 public Visitor<ASTNode>,
71 public Visitor<OperatorPlusNode>,
72 public Visitor<OperatorMinusNode>,
73 public Visitor<OperatorMultiplyNode>,
74 public Visitor<OperatorDivideNode>,
75 public Visitor<NegateNode>,
76 public Visitor<FunctionAbsNode>,
77 public Visitor<FunctionExpNode>,
78 public Visitor<FunctionLogNode>,
79 public Visitor<FunctionSqrtNode>,
80 public Visitor<FunctionNormalCdfNode>,
81 public Visitor<FunctionNormalPdfNode>,
82 public Visitor<FunctionMinNode>,
83 public Visitor<FunctionMaxNode>,
84 public Visitor<FunctionPowNode>,
85 public Visitor<FunctionBlackNode>,
86 public Visitor<FunctionDcfNode>,
87 public Visitor<FunctionDaysNode>,
88 public Visitor<FunctionPayNode>,
89 public Visitor<FunctionLogPayNode>,
90 public Visitor<FunctionNpvNode>,
91 public Visitor<FunctionNpvMemNode>,
92 public Visitor<HistFixingNode>,
93 public Visitor<FunctionDiscountNode>,
94 public Visitor<FunctionFwdCompNode>,
95 public Visitor<FunctionFwdAvgNode>,
96 public Visitor<FunctionAboveProbNode>,
97 public Visitor<FunctionBelowProbNode>,
98 public Visitor<SortNode>,
99 public Visitor<PermuteNode>,
100 public Visitor<ConstantNumberNode>,
101 public Visitor<VariableNode>,
102 public Visitor<SizeOpNode>,
103 public Visitor<FunctionDateIndexNode>,
104 public Visitor<VarEvaluationNode>,
105 public Visitor<AssignmentNode>,
106 public Visitor<RequireNode>,
107 public Visitor<DeclarationNumberNode>,
108 public Visitor<SequenceNode>,
109 public Visitor<ConditionEqNode>,
110 public Visitor<ConditionNeqNode>,
111 public Visitor<ConditionLtNode>,
112 public Visitor<ConditionLeqNode>,
113 public Visitor<ConditionGtNode>,
114 public Visitor<ConditionGeqNode>,
115 public Visitor<ConditionNotNode>,
116 public Visitor<ConditionAndNode>,
117 public Visitor<ConditionOrNode>,
118 public Visitor<IfThenElseNode>,
119 public Visitor<LoopNode> {
120public:
121 ASTRunner(ComputationGraph& g, const std::vector<std::string>& opLabels, const QuantLib::ext::shared_ptr<ModelCG> model,
122 const bool generatePayLog, const bool includePastCashflows, const std::string& script, bool& interactive,
123 Context& context, ASTNode*& lastVisitedNode, std::set<std::size_t>& keepNodes,
124 std::vector<ComputationGraphBuilder::PayLogEntry>& payLogEntries)
125 : g_(g), opLabels_(opLabels), model_(model), size_(model ? model->size() : 1), generatePayLog_(generatePayLog),
126 includePastCashflows_(includePastCashflows), script_(script), interactive_(interactive),
127 keepNodes_(keepNodes), payLogEntries_(payLogEntries), context_(context), lastVisitedNode_(lastVisitedNode) {
128 filter.emplace(size_, true);
129 value.push(RandomVariable());
132 }
133
134 // helper functions to perform operations
135
136 template <typename R>
137 void binaryOp(ASTNode& n, const std::string& name, const std::function<R(ValueType, ValueType)>& op,
138 const std::function<std::size_t(std::size_t, std::size_t)>& op_cg, const bool negateOp = false) {
139 n.args[0]->accept(*this);
140 n.args[1]->accept(*this);
141 checkpoint(n);
142 auto right = value.pop();
143 auto left = value.pop();
144 value.push(op(left, right));
145 auto right_node = value_node.pop();
146 auto left_node = value_node.pop();
147 std::size_t node = ComputationGraph::nan;
148 if (left_node != ComputationGraph::nan && right_node != ComputationGraph::nan) {
149 node = op_cg(left_node, right_node);
150 if (negateOp) {
151 node = cg_subtract(g_, cg_const(g_, 1.0), node);
152 }
153 } else {
154 QL_REQUIRE(left_node == ComputationGraph::nan && right_node == ComputationGraph::nan,
155 "internal error: binaryOp '" << name << "' got one non-number and one number argument.");
156 }
157 value_node.push(node);
158 TRACE(name << "( " << left << " (#" << left_node << "), " << right << " (#" << right_node << "))", n);
159 }
160
161 template <typename R>
162 void unaryOp(ASTNode& n, const std::string& name, const std::function<R(ValueType)>& op,
163 const std::function<std::size_t(std::size_t)>& op_cg, const bool negate = false) {
164 n.args[0]->accept(*this);
165 checkpoint(n);
166 auto arg = value.pop();
167 value.push(op(arg));
168 auto arg_node = value_node.pop();
169 std::size_t tmp = arg_node;
170 if (arg_node != ComputationGraph::nan) {
171 if (op_cg) {
172 tmp = op_cg(arg_node);
173 }
174 if (negate) {
175 tmp = cg_subtract(g_, cg_const(g_, 1.0), tmp);
176 }
177 }
178 value_node.push(tmp);
179 TRACE(name << "( " << arg << " (#" << tmp << "))", n);
180 }
181
182 // get ref to context variable + index (0 for scalars, 0,1,2,... for arrays)
183
184 std::pair<ValueType&, long> getVariableRef(VariableNode& v) {
185 checkpoint(v);
186 if (v.isCached) {
187 if (v.isScalar) {
188 return std::make_pair(QuantLib::ext::ref(*v.cachedScalar), 0);
189 } else {
190 QL_REQUIRE(v.args[0], "array subscript required for variable '" << v.name << "'");
191 v.args[0]->accept(*this);
192 auto arg = value.pop();
193 value_node.pop();
194 QL_REQUIRE(arg.which() == ValueTypeWhich::Number,
195 "array subscript must be of type NUMBER, got " << valueTypeLabels.at(arg.which()));
196 RandomVariable i = QuantLib::ext::get<RandomVariable>(arg);
197 QL_REQUIRE(i.deterministic(), "array subscript must be deterministic");
198 long il = std::lround(i.at(0));
199 QL_REQUIRE(static_cast<long>(v.cachedVector->size()) >= il && il >= 1,
200 "array index " << il << " out of bounds 1..." << v.cachedVector->size());
201 return std::make_pair(QuantLib::ext::ref(v.cachedVector->operator[](il - 1)), il - 1);
202 }
203 } else {
204 auto scalar = context_.scalars.find(v.name);
205 if (scalar != context_.scalars.end()) {
206 QL_REQUIRE(!v.args[0], "no array subscript allowed for variable '" << v.name << "'");
207 v.isCached = true;
208 v.isScalar = true;
209 v.cachedScalar = &scalar->second;
210 return std::make_pair(QuantLib::ext::ref(scalar->second), 0);
211 }
212 auto array = context_.arrays.find(v.name);
213 if (array != context_.arrays.end()) {
214 v.isCached = true;
215 v.isScalar = false;
216 v.cachedVector = &array->second;
217 return getVariableRef(v);
218 }
219 QL_FAIL("variable '" << v.name << "' is not defined.");
220 }
221 }
222
223 // helepr to declare a new context variable
224
225 void declareVariable(const ASTNodePtr arg, const ValueType& val) {
226 checkpoint(*arg);
227 auto v = QuantLib::ext::dynamic_pointer_cast<VariableNode>(arg);
228 QL_REQUIRE(v, "invalid declaration");
229 if (context_.ignoreAssignments.find(v->name) != context_.ignoreAssignments.end()) {
230 TRACE("declare(" << v->name << " ignored, because listed in ignoreAssignment variables set", *arg);
231 return;
232 }
233 auto scalar = context_.scalars.find(v->name);
234 auto array = context_.arrays.find(v->name);
235 QL_REQUIRE(scalar == context_.scalars.end() && array == context_.arrays.end(),
236 "variable '" << v->name << "' already declared.");
237 if (v->args[0]) {
238 v->args[0]->accept(*this);
239 checkpoint(*arg);
240 auto size = value.pop();
241 value_node.pop();
242 QL_REQUIRE(size.which() == ValueTypeWhich::Number, "expected NUMBER for array size definition");
243 RandomVariable arraySize = QuantLib::ext::get<RandomVariable>(size);
244 QL_REQUIRE(arraySize.deterministic(), "array size definition requires deterministic argument");
245 long arraySizeL = std::lround(arraySize.at(0));
246 QL_REQUIRE(arraySizeL >= 0, "expected non-negative array size, got " << arraySizeL);
247 context_.arrays[v->name] = std::vector<ValueType>(arraySizeL, val);
248 std::size_t node_id = 0;
249 for (long i = 0; i < arraySizeL; ++i) {
250 node_id = cg_const(g_, 0.0);
251 g_.setVariable(v->name + "_" + std::to_string(i), node_id);
252 }
253
254 TRACE("declare(" << v->name << "[" << arraySizeL << "], " << val << " (# "
255 << (arraySizeL > 0
256 ? std::to_string(node_id - arraySizeL + 1) + "..." + std::to_string(node_id)
257 : "na")
258 << "))",
259 *arg);
260 } else {
261 context_.scalars[v->name] = val;
262 std::size_t node_id = cg_const(g_, 0.0);
263 g_.setVariable(v->name + "_0", node_id);
264 TRACE("declare(" << v->name << ", " << val << " (#" << node_id << "))", *arg);
265 }
266 }
267
268 // record last visited node for diagnostics
269
270 void checkpoint(ASTNode& n) { lastVisitedNode_ = &n; }
271
272 // if we end up here, a node type is not handled
273
274 void visit(ASTNode& n) override {
275 checkpoint(n);
276 QL_FAIL("unhandled node");
277 }
278
279 // operator / function node types
280
281 void visit(OperatorPlusNode& n) override {
282 binaryOp<ValueType>(n, "plus", operator+,
283 [this](std::size_t a, std::size_t b) { return cg_add(this->g_, a, b); });
284 }
285
286 void visit(OperatorMinusNode& n) override {
287 binaryOp<ValueType>(
288 n, "minus", [](const ValueType& x, const ValueType& y) { return x - y; },
289 [this](std::size_t a, std::size_t b) { return cg_subtract(this->g_, a, b); });
290 }
291
292 void visit(OperatorMultiplyNode& n) override {
293 binaryOp<ValueType>(n, "multiply", operator*,
294 [this](std::size_t a, std::size_t b) { return cg_mult(this->g_, a, b); });
295 }
296
297 void visit(OperatorDivideNode& n) override {
298 binaryOp<ValueType>(n, "divide", operator/,
299 [this](std::size_t a, std::size_t b) { return cg_div(this->g_, a, b); });
300 }
301
302 void visit(NegateNode& n) override {
303 unaryOp<ValueType>(
304 n, "negate", [](const ValueType& x) { return -x; },
305 [this](std::size_t a) { return cg_negative(this->g_, a); });
306 }
307
308 void visit(FunctionAbsNode& n) override {
309 unaryOp<ValueType>(n, "abs", abs, [this](std::size_t a) { return cg_abs(this->g_, a); });
310 }
311
312 void visit(FunctionExpNode& n) override {
313 unaryOp<ValueType>(n, "exp", exp, [this](std::size_t a) { return cg_exp(this->g_, a); });
314 }
315
316 void visit(FunctionLogNode& n) override {
317 unaryOp<ValueType>(n, "log", log, [this](std::size_t a) { return cg_log(this->g_, a); });
318 }
319
320 void visit(FunctionSqrtNode& n) override {
321 unaryOp<ValueType>(n, "sqrt", sqrt, [this](std::size_t a) { return cg_sqrt(this->g_, a); });
322 }
323
324 void visit(FunctionNormalCdfNode& n) override {
325 unaryOp<ValueType>(n, "normalCdf", normalCdf, [this](std::size_t a) { return cg_normalCdf(this->g_, a); });
326 }
327
328 void visit(FunctionNormalPdfNode& n) override {
329 unaryOp<ValueType>(n, "normalPdf", normalPdf, [this](std::size_t a) { return cg_normalPdf(this->g_, a); });
330 }
331
332 void visit(FunctionMinNode& n) override {
333 binaryOp<ValueType>(n, "min", min, [this](std::size_t a, std::size_t b) { return cg_min(this->g_, a, b); });
334 }
335
336 void visit(FunctionMaxNode& n) override {
337 binaryOp<ValueType>(n, "max", max, [this](std::size_t a, std::size_t b) { return cg_max(this->g_, a, b); });
338 }
339
340 void visit(FunctionPowNode& n) override {
341 binaryOp<ValueType>(n, "pow", pow, [this](std::size_t a, std::size_t b) { return cg_pow(this->g_, a, b); });
342 }
343
344 // condition nodes
345
346 void visit(ConditionEqNode& n) override {
347 binaryOp<Filter>(
348 n, "conditionEq", equal, [this](std::size_t a, std::size_t b) { return cg_indicatorEq(this->g_, a, b); },
349 false);
350 }
351
352 void visit(ConditionNeqNode& n) override {
353 binaryOp<Filter>(
354 n, "conditionNeq", notequal,
355 [this](std::size_t a, std::size_t b) { return cg_indicatorEq(this->g_, a, b); }, true);
356 }
357
358 void visit(ConditionLtNode& n) override {
359 binaryOp<Filter>(
360 n, "conditionLt", lt, [this](std::size_t a, std::size_t b) { return cg_indicatorGeq(this->g_, a, b); },
361 true);
362 }
363
364 void visit(ConditionLeqNode& n) override {
365 binaryOp<Filter>(
366 n, "conditionLeq", leq, [this](std::size_t a, std::size_t b) { return cg_indicatorGt(this->g_, a, b); },
367 true);
368 }
369
370 void visit(ConditionGeqNode& n) override {
371 binaryOp<Filter>(
372 n, "conditionGeq", geq, [this](std::size_t a, std::size_t b) { return cg_indicatorGeq(this->g_, a, b); },
373 false);
374 }
375
376 void visit(ConditionGtNode& n) override {
377 binaryOp<Filter>(
378 n, "conditionGt", gt, [this](std::size_t a, std::size_t b) { return cg_indicatorGt(this->g_, a, b); },
379 false);
380 }
381
382 void visit(ConditionNotNode& n) override {
383 unaryOp<Filter>(
384 n, "conditionNot", [](const ValueType& x) { return logicalNot(x); },
385 std::function<std::size_t(std::size_t)>(), true);
386 }
387
388 void visit(ConditionAndNode& n) override {
389 n.args[0]->accept(*this);
390 auto left = value.pop();
391 auto left_node = value_node.pop();
392 checkpoint(n);
393 QL_REQUIRE(left.which() == ValueTypeWhich::Filter, "expected condition");
394 Filter l = QuantLib::ext::get<Filter>(left);
395 if (l.deterministic() && !l[0]) {
396 // short cut if first expression is already false
397 value.push(Filter(l.size(), false));
398 std::size_t node = cg_const(g_, 0.0);
399 value_node.push(node);
400 TRACE("conditionAnd( false, ? ) (#" << node << ")", n);
401 } else {
402 // we have to evaluate right node
403 n.args[1]->accept(*this);
404 auto right = value.pop();
405 auto right_node = value_node.pop();
406 checkpoint(n);
407 std::size_t node;
408 if (l.deterministic() && l[0]) {
409 // if first exptression is always true, the result is the right node
410 value.push(right);
411 node = right_node;
412 } else {
413 // otherwise we evaluate the "and" condition
414 value.push(logicalAnd(left, right));
415 node = cg_mult(g_, left_node, right_node);
416 }
417 value_node.push(node);
418 TRACE("conditionAnd( " << left << " , " << right << " ) (#" << node << ")", n);
419 }
420 }
421
422 void visit(ConditionOrNode& n) override {
423 n.args[0]->accept(*this);
424 auto left = value.pop();
425 auto left_node = value_node.pop();
426 checkpoint(n);
427 QL_REQUIRE(left.which() == ValueTypeWhich::Filter, "expected condition");
428 Filter l = QuantLib::ext::get<Filter>(left);
429 if (l.deterministic() && l[0]) {
430 // short cut if first expression is already true
431 value.push(Filter(l.size(), true));
432 std::size_t node = cg_const(g_, 1.0);
433 value_node.push(node);
434 TRACE("conditionOr( true, ? ) (#" << node << ")", n);
435 } else {
436 // we have to evaluate right node
437 n.args[1]->accept(*this);
438 auto right = value.pop();
439 auto right_node = value_node.pop();
440 checkpoint(n);
441 std::size_t node;
442 if (l.deterministic() && !l[0]) {
443 // if first expression is always false, the result is the right node
444 value.push(right);
445 node = right_node;
446 } else {
447 // otherwise we evaluate the "or" condition
448 value.push(logicalOr(left, right));
449 node = cg_min(g_, cg_const(g_, 1.0), cg_add(g_, left_node, right_node));
450 }
451 value_node.push(node);
452 TRACE("conditionOr( " << left << " , " << right << " ) (#" << node << ")", n);
453 }
454 }
455
456 // constants / variable related nodes
457
458 void visit(ConstantNumberNode& n) override {
459 checkpoint(n);
460 value.push(RandomVariable(size_, n.value));
461 std::size_t node = cg_const(g_, n.value);
462 value_node.push(node);
463 TRACE("constantNumber( " << n.value << " ) (#" << node << ")", n);
464 }
465
466 void visit(VariableNode& n) override {
467 auto const& r = getVariableRef(n);
468 value.push(r.first);
469 std::size_t node = ComputationGraph::nan;
470 if (r.first.which() == ValueTypeWhich::Number) {
471 node = cg_var(g_, n.name + "_" + std::to_string(r.second));
472 }
473 value_node.push(node);
474 checkpoint(n);
475 TRACE("variable( " << n.name << " ) (#" << node << ")", n);
476 }
477
478 void visit(DeclarationNumberNode& n) override {
479 for (auto const& arg : n.args) {
480 declareVariable(arg, RandomVariable(size_, 0.0));
481 checkpoint(n);
482 }
483 }
484
485 void visit(SizeOpNode& n) override {
486 checkpoint(n);
487 auto array = context_.arrays.find(n.name);
488 if (array == context_.arrays.end()) {
489 auto scalar = context_.scalars.find(n.name);
490 if (scalar == context_.scalars.end())
491 QL_FAIL("variable " << n.name << " is not defined");
492 else
493 QL_FAIL("SIZE can only be applied to array, " << n.name << " is a scalar");
494 }
495 auto dbl = static_cast<double>(array->second.size());
496 value.push(RandomVariable(size_, dbl));
497 std::size_t node = cg_const(g_, dbl);
498 value_node.push(node);
499 TRACE("size( " << n.name << " ) (#" << node << ")", n);
500 }
501
502 void visit(FunctionDateIndexNode& n) override {
503 checkpoint(n);
504 auto array = context_.arrays.find(n.name);
505 QL_REQUIRE(array != context_.arrays.end(),
506 "DATEINDEX: second argument event array '" << n.name << "' not found");
507 auto v = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[0]);
508 QL_REQUIRE(v, "DATEINDEX: first argument must be a variable expression");
509 auto ref = getVariableRef(*v);
510 checkpoint(n);
511 QL_REQUIRE(ref.first.which() == ValueTypeWhich::Event, "DATEINDEX: first argument must be of type event");
512 std::size_t node;
513 if (n.op == "EQ") {
514 auto pos = std::find_if(array->second.begin(), array->second.end(),
515 [&ref](const ValueType& v) { return ref.first == v; });
516 if (pos == array->second.end()) {
517 value.push(RandomVariable(size_, 0.0));
518 node = cg_const(g_, 0.0);
519 value_node.push(node);
520 } else {
521 auto dbl = static_cast<double>(std::distance(array->second.begin(), pos) + 1);
522 value.push(RandomVariable(size_, dbl));
523 node = cg_const(g_, dbl);
524 value_node.push(node);
525 }
526 } else if (n.op == "GEQ") {
527 Size pos = std::lower_bound(array->second.begin(), array->second.end(), ref.first,
528 [](const ValueType& l, const ValueType& r) -> bool {
529 return QuantLib::ext::get<EventVec>(l).value < QuantLib::ext::get<EventVec>(r).value;
530 }) -
531 array->second.begin() + 1;
532 auto dbl = static_cast<double>(pos);
533 value.push(RandomVariable(size_, dbl));
534 node = cg_const(g_, dbl);
535 value_node.push(node);
536 } else if (n.op == "GT") {
537 Size pos = std::upper_bound(array->second.begin(), array->second.end(), ref.first,
538 [](const ValueType& l, const ValueType& r) -> bool {
539 return QuantLib::ext::get<EventVec>(l).value < QuantLib::ext::get<EventVec>(r).value;
540 }) -
541 array->second.begin() + 1;
542 auto dbl = static_cast<double>(pos);
543 value.push(RandomVariable(size_, dbl));
544 node = cg_const(g_, dbl);
545 value_node.push(node);
546 } else {
547 QL_FAIL("DATEINDEX: operation '" << n.op << "' not supported, expected EQ, GEQ, GT");
548 }
549 TRACE("dateindex( " << v->name << "[" << (ref.second + 1) << "] , " << n.name << " , " << n.op << " ) (#"
550 << node << ")",
551 n);
552 }
553
554 void visit(AssignmentNode& n) override {
555 n.args[1]->accept(*this);
556 auto right = value.pop();
557 auto right_node = value_node.pop();
558 checkpoint(n);
559 auto v = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[0]);
560 QL_REQUIRE(v, "expected variable identifier on LHS of assignment");
561 if (context_.ignoreAssignments.find(v->name) != context_.ignoreAssignments.end()) {
562 TRACE("assign(" << v->name << ") ignored, because variable is listed in context's ignoreAssignment set",
563 n);
564 return;
565 }
566 QL_REQUIRE(std::find(context_.constants.begin(), context_.constants.end(), v->name) == context_.constants.end(),
567 "can not assign to const variable '" << v->name << "'");
568 auto ref = getVariableRef(*v);
569 checkpoint(n);
570 std::size_t node = 0, ref_node = 0;
571 if (ref.first.which() == ValueTypeWhich::Event || ref.first.which() == ValueTypeWhich::Currency ||
572 ref.first.which() == ValueTypeWhich::Index) {
573 typeSafeAssign(ref.first, right);
574 } else {
575 QL_REQUIRE(ref.first.which() == ValueTypeWhich::Number,
576 "internal error: expected NUMBER, got " << valueTypeLabels.at(ref.first.which()));
577 QL_REQUIRE(right.which() == ValueTypeWhich::Number, "invalid assignment: type "
578 << valueTypeLabels.at(ref.first.which()) << " <- "
579 << valueTypeLabels.at(right.which()));
580 // TODO, better have a RESETTIME() function?
581 QuantLib::ext::get<RandomVariable>(ref.first).setTime(Null<Real>());
582 ref.first = conditionalResult(filter.top(), QuantLib::ext::get<RandomVariable>(right),
583 QuantLib::ext::get<RandomVariable>(ref.first));
584 QuantLib::ext::get<RandomVariable>(ref.first).updateDeterministic();
585 // create result node
586 if (filter.top().deterministic()) {
587 if (filter.top().at(0)) {
588 g_.setVariable(v->name + "_" + std::to_string(ref.second), right_node);
589 node = right_node;
590 } else {
591 // var node stays the same, nothing to do
592 node = cg_var(g_, v->name + "_" + std::to_string(ref.second));
593 }
594 } else {
595 auto ref_node = cg_var(g_, v->name + "_" + std::to_string(ref.second));
596 auto pos_node = cg_mult(g_, filter_node.top(), right_node);
597 auto neg_node = cg_mult(g_, cg_subtract(g_, cg_const(g_, 1.0), filter_node.top()), ref_node);
598 node = cg_add(g_, pos_node, neg_node);
599 g_.setVariable(v->name + "_" + std::to_string(ref.second), node);
600 }
601 }
602 TRACE("assign( " << v->name << "[" << (ref.second + 1) << "] ) (#" << node << ") := " << ref.first << " ("
603 << valueTypeLabels.at(right.which()) << ") (#" << ref_node << ") using filter " << filter.top()
604 << " (#" << filter_node.top() << ")",
605 n);
606 }
607
608 // require node
609
610 void visit(RequireNode& n) override {
611 n.args[0]->accept(*this);
612 auto condition = value.pop();
613 auto condition_node = value_node.pop();
614 checkpoint(n);
615 QL_REQUIRE(condition.which() == ValueTypeWhich::Filter, "expected condition");
616 // check implication filter true => condition true
617 if (filter.top().initialised() && QuantLib::ext::get<Filter>(condition).initialised()) {
618 auto c = !filter.top() || QuantLib::ext::get<Filter>(condition);
619 c.updateDeterministic();
620 QL_REQUIRE(c.deterministic() && c.at(0), "required condition is not (always) fulfilled");
621 TRACE("require( " << condition << " ) (#" << condition_node << ") for filter " << filter.top(), n);
622 } else {
623 TRACE("require(" << condition << ") can not be checked, because filter is model dependent.", n);
624 }
625 }
626
627 // control flow nodes
628
629 void visit(SequenceNode& n) override {
630 TRACE("instruction_sequence()", n);
631 for (auto const& arg : n.args) {
632 arg->accept(*this);
633 checkpoint(n);
634 }
635 }
636
637 void visit(IfThenElseNode& n) override {
638 n.args[0]->accept(*this);
639 auto if_ = value.pop();
640 auto if_node = value_node.pop();
641 checkpoint(n);
642 QL_REQUIRE(if_.which() == ValueTypeWhich::Filter,
643 "IF must be followed by a boolean, got " << valueTypeLabels.at(if_.which()));
644 Filter cond = QuantLib::ext::get<Filter>(if_);
645 TRACE("if( " << cond << " ) (#" << if_node << ")", n);
646 Filter baseFilter = filter.top();
647 Filter currentFilter = baseFilter && cond;
648 currentFilter.updateDeterministic();
649 filter.push(currentFilter);
650 auto baseFilter_node = filter_node.top();
651 std::size_t node = 0;
652 if (currentFilter.deterministic()) {
653 node = currentFilter.at(0) ? cg_const(g_, 1.0) : cg_const(g_, 0.0);
654 } else {
655 auto baseFilter_node = filter_node.top();
656 if (baseFilter.deterministic()) {
657 node = baseFilter.at(0) ? if_node : cg_const(g_, 0.0);
658 } else {
659 node = cg_mult(g_, baseFilter_node, if_node);
660 }
661 }
662 filter_node.push(node);
663 TRACE("then( filter = " << currentFilter << " ) (#" << node << ")", n);
664 if (!currentFilter.deterministic() || currentFilter[0]) {
665 n.args[1]->accept(*this);
666 checkpoint(n);
667 }
668 filter.pop();
669 filter_node.pop();
670 if (n.args[2]) {
671 currentFilter = baseFilter && !cond;
672 currentFilter.updateDeterministic();
673 filter.push(currentFilter);
674 if (currentFilter.deterministic()) {
675 node = currentFilter.at(0) ? cg_const(g_, 1.0) : cg_const(g_, 0.0);
676 } else {
677 if (baseFilter.deterministic()) {
678 node = baseFilter.at(0) ? cg_subtract(g_, cg_const(g_, 1.0), if_node) : g_.constant(0.0);
679 } else {
680 node = cg_mult(g_, baseFilter_node, cg_subtract(g_, cg_const(g_, 1.0), if_node));
681 }
682 }
683 filter_node.push(node);
684 TRACE("else( filter = " << currentFilter << ") (#" << node << ")", n);
685 if (!currentFilter.deterministic() || currentFilter[0]) {
686 n.args[2]->accept(*this);
687 checkpoint(n);
688 }
689 filter.pop();
690 filter_node.pop();
691 }
692 }
693
694 void visit(LoopNode& n) override {
695 checkpoint(n);
696 auto var = context_.scalars.find(n.name);
697 QL_REQUIRE(var != context_.scalars.end(), "loop variable '" << n.name << "' not defined or not scalar");
698 QL_REQUIRE(std::find(context_.constants.begin(), context_.constants.end(), n.name) == context_.constants.end(),
699 "loop variable '" << n.name << "' is constant");
700 n.args[0]->accept(*this);
701 n.args[1]->accept(*this);
702 n.args[2]->accept(*this);
703 auto step = value.pop();
704 auto right = value.pop();
705 auto left = value.pop();
706 value_node.pop(); // dummy
707 value_node.pop(); // dummy
708 value_node.pop(); // dummy
709 checkpoint(n);
710 QL_REQUIRE(left.which() == ValueTypeWhich::Number && right.which() == ValueTypeWhich::Number &&
711 step.which() == ValueTypeWhich::Number,
712 "loop bounds and step must be of type NUMBER, got " << valueTypeLabels.at(left.which()) << ", "
713 << valueTypeLabels.at(right.which()) << ", "
714 << valueTypeLabels.at(step.which()));
715 RandomVariable a = QuantLib::ext::get<RandomVariable>(left);
716 RandomVariable b = QuantLib::ext::get<RandomVariable>(right);
717 RandomVariable s = QuantLib::ext::get<RandomVariable>(step);
718 QL_REQUIRE(a.deterministic(), "first loop bound must be deterministic");
719 QL_REQUIRE(b.deterministic(), "second loop bound must be deterministic");
720 QL_REQUIRE(s.deterministic(), "loop step must be deterministic");
721 long al = std::lround(a.at(0));
722 long bl = std::lround(b.at(0));
723 long sl = std::lround(s.at(0));
724 QL_REQUIRE(sl != 0, "loop step must be non-zero");
725 long cl = al;
726 while ((sl > 0 && cl <= bl) || (sl < 0 && cl >= bl)) {
727 TRACE("for( " << n.name << " : " << cl << " (" << al << "," << bl << "))", n);
728 var->second = RandomVariable(size_, static_cast<double>(cl));
729 n.args[3]->accept(*this);
730 checkpoint(n);
731 QL_REQUIRE(var->second.which() == ValueTypeWhich::Number &&
732 close_enough_all(QuantLib::ext::get<RandomVariable>(var->second),
733 RandomVariable(size_, static_cast<double>(cl))),
734 "loop variable was modified in body from " << cl << " to " << var->second
735 << ", this is illegal.");
736 cl += sl;
737 }
738 }
739
740 // day counter functions
741
742 void dayCounterFunctionHelper(ASTNode& n, DayCounter& daycounter, Date& date1, Date& date2) {
743 n.args[0]->accept(*this);
744 n.args[1]->accept(*this);
745 n.args[2]->accept(*this);
746 checkpoint(n);
747
748 auto d2 = value.pop();
749 auto d1 = value.pop();
750 auto dc = value.pop();
751 value_node.pop(); // dummy
752 value_node.pop(); // dummy
753 value_node.pop(); // dummy
754
755 QL_REQUIRE(dc.which() == ValueTypeWhich::Daycounter, "dc must be DAYCOUNTER");
756 QL_REQUIRE(d1.which() == ValueTypeWhich::Event, "d1 must be EVENT");
757 QL_REQUIRE(d2.which() == ValueTypeWhich::Event, "d2 must be EVENT");
758
759 date1 = QuantLib::ext::get<EventVec>(d1).value;
760 date2 = QuantLib::ext::get<EventVec>(d2).value;
761
762 daycounter = ore::data::parseDayCounter(QuantLib::ext::get<DaycounterVec>(dc).value);
763 }
764
765 void visit(FunctionDcfNode& n) override {
766 Date date1, date2;
767 DayCounter daycounter;
768 dayCounterFunctionHelper(n, daycounter, date1, date2);
769 QL_REQUIRE(model_, "model is null");
770 double dbl = daycounter.yearFraction(date1, date2);
771 value.push(RandomVariable(model_->size(), dbl));
772 value_node.push(cg_const(g_, dbl));
773 TRACE("dcf( " << date1 << " , " << date2 << " )", n);
774 }
775
776 void visit(FunctionDaysNode& n) override {
777 Date date1, date2;
778 DayCounter daycounter;
779 dayCounterFunctionHelper(n, daycounter, date1, date2);
780 QL_REQUIRE(model_, "model is null");
781 double dbl = static_cast<double>(daycounter.dayCount(date1, date2));
782 value.push(RandomVariable(model_->size(), dbl));
783 value_node.push(cg_const(g_, dbl));
784 TRACE("days( " << date1 << " , " << date2 << " )", n);
785 }
786
787 // SORT and PERMUTE instructions
788
789 void visit(SortNode& n) override {
790
791 QL_FAIL("SORT not yet supported by ComputationGraphBuilder.");
792
793 // checkpoint(n);
794
795 // std::vector<RandomVariable*> x, y, p;
796
797 // if (n.args[0]) {
798 // auto xname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[0]);
799 // QL_REQUIRE(xname, "x must be a variable");
800 // QL_REQUIRE(!xname->args[0], "x must not be indexed");
801 // auto xv = context_.arrays.find(xname->name);
802 // QL_REQUIRE(xv != context_.arrays.end(), "did not find array with name '" << xname->name << "'");
803 // for (Size c = 0; c < xv->second.size(); ++c) {
804 // QL_REQUIRE(xv->second[c].which() == ValueTypeWhich::Number, "x must be NUMBER");
805 // x.push_back(&QuantLib::ext::get<RandomVariable>(xv->second[c]));
806 // }
807 // }
808
809 // if (n.args[1]) {
810 // auto yname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[1]);
811 // QL_REQUIRE(yname, "y must be a variable");
812 // QL_REQUIRE(!yname->args[0], "y must not be indexed");
813 // auto yv = context_.arrays.find(yname->name);
814 // QL_REQUIRE(yv != context_.arrays.end(), "did not find array with name '" << yname->name << "'");
815 // for (Size c = 0; c < yv->second.size(); ++c) {
816 // QL_REQUIRE(yv->second[c].which() == ValueTypeWhich::Number, "y must be NUMBER");
817 // y.push_back(&QuantLib::ext::get<RandomVariable>(yv->second[c]));
818 // }
819 // }
820
821 // if (n.args[2]) {
822 // auto pname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[2]);
823 // QL_REQUIRE(pname, "p must be a variable");
824 // QL_REQUIRE(!pname->args[0], "p must not be indexed");
825 // auto pv = context_.arrays.find(pname->name);
826 // QL_REQUIRE(pv != context_.arrays.end(), "did not find array with name '" << pname->name << "'");
827 // for (Size c = 0; c < pv->second.size(); ++c) {
828 // QL_REQUIRE(pv->second[c].which() == ValueTypeWhich::Number, "p must be NUMBER");
829 // p.push_back(&QuantLib::ext::get<RandomVariable>(pv->second[c]));
830 // }
831 // }
832
833 // // set y to target (may be x itself)
834
835 // if (y.empty())
836 // y = x;
837
838 // QL_REQUIRE(x.size() >= 1, "array size must be >= 1");
839 // QL_REQUIRE(y.size() == x.size(),
840 // "y array size (" << y.size() << ") must match x array size (" << x.size() << ")");
841 // QL_REQUIRE(p.empty() || p.size() == x.size(),
842 // "p array size (" << p.size() << ") must match x array size (" << p.size() << ")");
843
844 // for (Size c = 0; c < x.size(); ++c) {
845 // QL_REQUIRE(x[c]->size() == y[c]->size(), "x[" << c << "] size (" << x[c]->size() << ") must match y[" << c
846 // << "] size (" << y[c]->size() << ")");
847 // QL_REQUIRE(p.empty() || x[c]->size() == p[c]->size(), "x[" << c << "] size (" << x[c]->size()
848 // << ") must match i[" << c << "] size ("
849 // << p[c]->size() << ")");
850 // }
851
852 // const Filter& flt = filter.top();
853 // QL_REQUIRE(flt.size() == x[0]->size(),
854 // "filter has size " << flt.size() << ", but x[0] has size " << x[0]->size() << ")");
855 // if (p.empty()) {
856 // std::vector<Real> val(x.size());
857 // for (Size k = 0; k < x[0]->size(); ++k) {
858 // if (!flt[k])
859 // continue;
860 // for (Size c = 0; c < x.size(); ++c) {
861 // val[c] = (*x[c])[k];
862 // }
863 // std::sort(val.begin(), val.end());
864 // for (Size c = 0; c < x.size(); ++c) {
865 // y[c]->set(k, val[c]);
866 // }
867 // }
868 // } else {
869 // std::vector<std::pair<Real, Size>> val(x.size());
870 // for (Size k = 0; k < x[0]->size(); ++k) {
871 // if (!flt[k])
872 // continue;
873 // for (Size c = 0; c < x.size(); ++c) {
874 // val[c].first = (*x[c])[k];
875 // val[c].second = c + 1; // permutation should start at 1
876 // }
877 // std::sort(val.begin(), val.end(), [](const std::pair<Real, Size>& x, const std::pair<Real, Size>& y) {
878 // return x.first < y.first;
879 // });
880 // for (Size c = 0; c < x.size(); ++c) {
881 // y[c]->set(k, val[c].first);
882 // p[c]->set(k, static_cast<Real>(val[c].second));
883 // }
884 // }
885 // }
886
887 // TRACE("sort(...)", n); // TODO what would be a helpful output?
888 }
889
890 void visit(PermuteNode& n) override {
891
892 QL_FAIL("PERMUTE not yet supported by ComputationGraphBuilder.");
893
894 // checkpoint(n);
895
896 // std::vector<RandomVariable*> x, y, p;
897
898 // if (n.args[0]) {
899 // auto xname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[0]);
900 // QL_REQUIRE(xname, "x must be a variable");
901 // QL_REQUIRE(!xname->args[0], "x must not be indexed");
902 // auto xv = context_.arrays.find(xname->name);
903 // QL_REQUIRE(xv != context_.arrays.end(), "did not find array with name '" << xname->name << "'");
904 // for (Size c = 0; c < xv->second.size(); ++c) {
905 // QL_REQUIRE(xv->second[c].which() == ValueTypeWhich::Number, "x must be NUMBER");
906 // x.push_back(&QuantLib::ext::get<RandomVariable>(xv->second[c]));
907 // }
908 // }
909
910 // if (n.args[1]) {
911 // auto yname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[1]);
912 // QL_REQUIRE(yname, "y must be a variable");
913 // QL_REQUIRE(!yname->args[0], "y must not be indexed");
914 // auto yv = context_.arrays.find(yname->name);
915 // QL_REQUIRE(yv != context_.arrays.end(), "did not find array with name '" << yname->name << "'");
916 // for (Size c = 0; c < yv->second.size(); ++c) {
917 // QL_REQUIRE(yv->second[c].which() == ValueTypeWhich::Number, "y must be NUMBER");
918 // y.push_back(&QuantLib::ext::get<RandomVariable>(yv->second[c]));
919 // }
920 // }
921
922 // if (n.args[2]) {
923 // auto pname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[2]);
924 // QL_REQUIRE(pname, "p must be a variable");
925 // QL_REQUIRE(!pname->args[0], "p must not be indexed");
926 // auto pv = context_.arrays.find(pname->name);
927 // QL_REQUIRE(pv != context_.arrays.end(), "did not find array with name '" << pname->name << "'");
928 // for (Size c = 0; c < pv->second.size(); ++c) {
929 // QL_REQUIRE(pv->second[c].which() == ValueTypeWhich::Number, "p must be NUMBER");
930 // p.push_back(&QuantLib::ext::get<RandomVariable>(pv->second[c]));
931 // }
932 // }
933
934 // // x should be source, y target and p permutation
935
936 // if (p.empty()) {
937 // p = y;
938 // y = x;
939 // }
940
941 // Size N = x.size();
942
943 // QL_REQUIRE(y.size() == N, "y array size (" << y.size() << ") must match x array size (" << N << ")");
944 // QL_REQUIRE(p.size() == N, "p array size (" << p.size() << ") must match x array size (" << N << ")");
945 // QL_REQUIRE(N >= 1, "array size must be >= 1");
946
947 // for (Size c = 0; c < N; ++c) {
948 // QL_REQUIRE(x[c]->size() == y[c]->size(), "x[" << c << "] size (" << x[c]->size() << ") must match y[" << c
949 // << "] size (" << y[c]->size() << ")");
950 // QL_REQUIRE(x[c]->size() == p[c]->size(), "x[" << c << "] size (" << x[c]->size() << ") must match p[" << c
951 // << "] size (" << p[c]->size() << ")");
952 // }
953
954 // const Filter& flt = filter.top();
955 // QL_REQUIRE(flt.size() == x[0]->size(),
956 // "filter has size " << flt.size() << ", but x[0] has size " << x[0]->size() << ")");
957
958 // std::vector<Real> val(N);
959 // for (Size k = 0; k < x[0]->size(); ++k) {
960 // if (!flt[k])
961 // continue;
962 // for (Size c = 0; c < N; ++c) {
963 // Size permutedIndex = std::lround((*p[c])[k]);
964 // QL_REQUIRE(permutedIndex >= 1 && permutedIndex <= N, "permuted index p[" << c << "] = " << permutedIndex
965 // << " out of bounds 1..." << N
966 // << " at component " << k);
967 // val[c] = (*x[permutedIndex - 1])[k];
968 // }
969 // for (Size c = 0; c < N; ++c) {
970 // y[c]->set(k, val[c]);
971 // }
972 // }
973
974 // TRACE("permute(...)", n); // TODO what would be a helpful output?
975 }
976
977 // model dependent function nodes
978
979 void visit(FunctionBlackNode& n) override {
980
981 QL_FAIL("BLACK not yet supported by ComputationGraphBuilder.");
982
983 // n.args[0]->accept(*this);
984 // n.args[1]->accept(*this);
985 // n.args[2]->accept(*this);
986 // n.args[3]->accept(*this);
987 // n.args[4]->accept(*this);
988 // n.args[5]->accept(*this);
989 // checkpoint(n);
990
991 // auto impliedvol = value.pop();
992 // auto forward = value.pop();
993 // auto strike = value.pop();
994 // auto expirydate = value.pop();
995 // auto obsdate = value.pop();
996 // auto callput = value.pop();
997
998 // QL_REQUIRE(callput.which() == ValueTypeWhich::Number, "callput must be NUMBER");
999 // QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
1000 // QL_REQUIRE(expirydate.which() == ValueTypeWhich::Event, "expirydate must be EVENT");
1001 // QL_REQUIRE(strike.which() == ValueTypeWhich::Number, "strike must be NUMBER");
1002 // QL_REQUIRE(forward.which() == ValueTypeWhich::Number, "forward must be NUMBER");
1003 // QL_REQUIRE(forward.which() == ValueTypeWhich::Number, "impliedvol must be NUMBER");
1004
1005 // RandomVariable omega = QuantLib::ext::get<RandomVariable>(callput);
1006 // Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
1007 // Date expiry = QuantLib::ext::get<EventVec>(expirydate).value;
1008 // RandomVariable k = QuantLib::ext::get<RandomVariable>(strike);
1009 // RandomVariable f = QuantLib::ext::get<RandomVariable>(forward);
1010 // RandomVariable v = QuantLib::ext::get<RandomVariable>(impliedvol);
1011
1012 // QL_REQUIRE(model_, "model is null");
1013
1014 // QL_REQUIRE(obs <= expiry, "obsdate (" << obs << ") must be <= expirydate (" << expiry << ")");
1015 // RandomVariable t(model_->size(), model_->dt(obs, expiry));
1016
1017 // value.push(black(omega, t, k, f, v));
1018 // TRACE("black( " << callput << " , " << obsdate << " , " << expirydate << " , " << strike << " , " << forward
1019 // << " , " << impliedvol << " ), t=" << t,
1020 // n);
1021 }
1022
1023 void payHelper(ASTNode& n, const bool log) {
1024 n.args[2]->accept(*this);
1025 auto paydate = value.pop();
1026 value_node.pop(); // dummy
1027 checkpoint(n);
1028 QL_REQUIRE(paydate.which() == ValueTypeWhich::Event, "paydate must be EVENT");
1029 QL_REQUIRE(model_, "model is null");
1030 // handle case of past payments: do not evaluate the other parameters, since not needed (e.g. past fixings)
1031 Date pay = boost::get<EventVec>(paydate).value;
1032 if (pay <= model_->referenceDate() && (!log || !includePastCashflows_)) {
1033 value.push(RandomVariable(size_, 0.0));
1034 std::size_t node = cg_const(g_, 0.0);
1035 value_node.push(node);
1036 TRACE("pay() = 0 (#" << node << "), since paydate " << paydate << " <= " << model_->referenceDate(), n);
1037 } else {
1038 n.args[0]->accept(*this);
1039 n.args[1]->accept(*this);
1040 n.args[3]->accept(*this);
1041 auto paycurr = value.pop();
1042 auto obsdate = value.pop();
1043 auto amount = value.pop();
1044 value_node.pop(); // dummy
1045 value_node.pop(); // dummy
1046 auto amount_node = value_node.pop();
1047 checkpoint(n);
1048 QL_REQUIRE(amount.which() == ValueTypeWhich::Number, "amount must be NUMBER");
1049 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
1050 QL_REQUIRE(paycurr.which() == ValueTypeWhich::Currency, "paycurr must be CURRENCY");
1051 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
1052 std::string pccy = QuantLib::ext::get<CurrencyVec>(paycurr).value;
1053 QL_REQUIRE(obs <= pay, "observation date (" << obs << ") <= payment date (" << pay << ") required");
1054 RandomVariable result; // uninitialised, since model dependent
1055 value.push(result);
1056 std::size_t node =
1057 pay <= model_->referenceDate() ? cg_const(g_, 0.0) : model_->pay(amount_node, obs, pay, pccy);
1058 std::size_t cfnode = pay <= model_->referenceDate() ? amount_node : node;
1059 value_node.push(node);
1060 TRACE("pay( " << amount << " , " << obsdate << " , " << paydate << " , " << paycurr << " ) (#" << node
1061 << ")",
1062 n);
1063 if (log && generatePayLog_) {
1064 // cashflow logging
1065 FunctionLogPayNode& pn = dynamic_cast<FunctionLogPayNode&>(n); // throws bad_cast if n has wrong type
1066 long legno = 0, slot = 0;
1067 std::string cftype = "Unspecified";
1068 if (pn.args[4]) {
1069 pn.args[4]->accept(*this);
1070 auto s = value.pop();
1071 value_node.pop(); // dummy;
1072 QL_REQUIRE(s.which() == ValueTypeWhich::Number, "legno must be NUMBER");
1073 RandomVariable sv = QuantLib::ext::get<RandomVariable>(s);
1074 sv.updateDeterministic();
1075 QL_REQUIRE(sv.deterministic(), "legno must be deterministic");
1076 legno = std::lround(sv.at(0));
1077 QL_REQUIRE(slot >= 0, " legNo must be >= 0");
1078 QL_REQUIRE(pn.args[5], "expected cashflow type argument when legno is given");
1079 auto cftname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[5]);
1080 QL_REQUIRE(cftname, "cashflow type must be a variable name");
1081 QL_REQUIRE(!cftname->args[0], "cashflow type must not be indexed");
1082 cftype = cftname->name;
1083 if (pn.args[6]) {
1084 pn.args[6]->accept(*this);
1085 auto s = value.pop();
1086 value_node.pop(); // dummy;
1087 QL_REQUIRE(s.which() == ValueTypeWhich::Number, "slot must be NUMBER");
1088 RandomVariable sv = QuantLib::ext::get<RandomVariable>(s);
1089 sv.updateDeterministic();
1090 QL_REQUIRE(sv.deterministic(), "slot must be deterministic");
1091 slot = std::lround(sv.at(0));
1092 QL_REQUIRE(slot >= 1, " slot must be >= 1");
1093 }
1094 }
1095 // add nodes necessary to write paylog to keepNodes set
1096 std::size_t filterNode = filter_node.top() == ComputationGraph::nan ? cg_const(g_, 1.0) : filter_node.top();
1097 keepNodes_.insert(cfnode);
1098 keepNodes_.insert(filterNode);
1099 // add paylog entry data
1100 payLogEntries_.push_back({cfnode, filterNode, obs, pay, pccy, (Size)legno, cftype, (Size)slot});
1101 }
1102 }
1103 }
1104
1105 void visit(FunctionPayNode& n) override { payHelper(n, false); }
1106
1107 void visit(FunctionLogPayNode& n) override { payHelper(n, true); }
1108
1109 void processNpvNode(ASTNode& n, bool hasMemSlot) {
1110 n.args[0]->accept(*this);
1111 n.args[1]->accept(*this);
1112 ValueType memSlot;
1113 if (hasMemSlot) {
1114 n.args[2]->accept(*this);
1115 memSlot = value.pop();
1116 value_node.pop(); // dummy
1117 }
1118 auto obsdate = value.pop();
1119 auto amount = value.pop();
1120 value_node.pop(); // dummy
1121 auto amount_node = value_node.pop();
1122 checkpoint(n);
1123 Filter regFilter;
1124 std::size_t regFilter_node;
1125 Size opt = hasMemSlot ? 3 : 2;
1126 if (n.args[opt]) {
1127 n.args[opt]->accept(*this);
1128 auto val = value.pop();
1129 checkpoint(n);
1130 QL_REQUIRE(val.which() == ValueTypeWhich::Filter, "filter must be condition");
1131 regFilter = QuantLib::ext::get<Filter>(val);
1132 regFilter_node = value_node.pop();
1133 } else {
1134 regFilter_node = cg_const(g_, 1.0);
1135 }
1136 RandomVariable addRegressor1, addRegressor2;
1137 std::size_t addRegressor1_node, addRegressor2_node;
1138 if(n.args[opt+1]) {
1139 n.args[opt+1]->accept(*this);
1140 auto val = value.pop();
1141 checkpoint(n);
1142 QL_REQUIRE(val.which() == ValueTypeWhich::Number," addRegressor1 must be NUMBER");
1143 addRegressor1 = QuantLib::ext::get<RandomVariable>(val);
1144 addRegressor1_node = value_node.pop();
1145 } else {
1146 addRegressor1_node = ComputationGraph::nan;
1147 }
1148 if(n.args[opt+2]) {
1149 n.args[opt+2]->accept(*this);
1150 auto val = value.pop();
1151 checkpoint(n);
1152 QL_REQUIRE(val.which() == ValueTypeWhich::Number," addRegressor2 must be NUMBER");
1153 addRegressor2 = QuantLib::ext::get<RandomVariable>(val);
1154 addRegressor2_node = value_node.pop();
1155 } else {
1156 addRegressor2_node = ComputationGraph::nan;
1157 }
1158 QL_REQUIRE(amount.which() == ValueTypeWhich::Number, "amount must be NUMBER");
1159 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
1160 if (hasMemSlot) {
1161 QL_REQUIRE(memSlot.which() == ValueTypeWhich::Number, "memorySlot must be NUMBER");
1162 }
1163 QL_REQUIRE(model_, "model is null");
1164 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
1165 // roll back to past dates is treated as roll back to TODAY for convenience
1166 obs = std::max(obs, model_->referenceDate());
1167 boost::optional<long> mem(boost::none);
1168 if (hasMemSlot) {
1169 RandomVariable v = QuantLib::ext::get<RandomVariable>(memSlot);
1170 QL_REQUIRE(v.deterministic(), "memory slot must be deterministic");
1171 mem = static_cast<long>(v.at(0));
1172 }
1173 value.push(RandomVariable()); // uninitialized, since model dependent
1174 std::size_t node = model_->npv(amount_node, obs, regFilter_node, mem, addRegressor1_node, addRegressor2_node);
1175 value_node.push(node);
1176 if (hasMemSlot) {
1177 TRACE("npvmem( " << amount << " , " << obsdate << " , " << memSlot << " , " << regFilter << " , "
1178 << addRegressor1 << " , " << addRegressor2 << " ) (#" << node << ")",
1179 n);
1180 } else {
1181 TRACE("npv( " << amount << " , " << obsdate << " , " << regFilter << " , " << addRegressor1 << " , "
1182 << addRegressor2 << " ) (#" << node << ")",
1183 n);
1184 }
1185 }
1186
1187 void visit(FunctionNpvNode& n) override { processNpvNode(n, false); }
1188
1189 void visit(FunctionNpvMemNode& n) override { processNpvNode(n, true); }
1190
1191 void visit(HistFixingNode& n) override {
1192 checkpoint(n);
1193 QL_REQUIRE(model_, "model is null");
1194 n.args[0]->accept(*this);
1195 n.args[1]->accept(*this);
1196 auto obsdate = value.pop();
1197 auto underlying = value.pop();
1198 value_node.pop(); // dummy
1199 value_node.pop(); // dummy
1200 checkpoint(n);
1201 QL_REQUIRE(underlying.which() == ValueTypeWhich::Index, "underlying must be INDEX");
1202 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
1203 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
1204 std::string und = QuantLib::ext::get<IndexVec>(underlying).value;
1205 // if observation date is in the future, the answer is always zero
1206 std::size_t node;
1207 if (obs > model_->referenceDate()) {
1208 value.push(RandomVariable(model_->size(), 0.0));
1209 node = cg_const(g_, 0.0);
1210 } else {
1211 // otherwise check whether a fixing is present in the historical time series
1212 TimeSeries<Real> series = IndexManager::instance().getHistory(IndexInfo(und).index()->name());
1213 if (series[obs] == Null<Real>()) {
1214 value.push(RandomVariable(model_->size(), 0.0));
1215 node = cg_const(g_, 0.0);
1216 } else {
1217 value.push(RandomVariable(model_->size(), 1.0));
1218 node = cg_const(g_, 0.0);
1219 }
1220 }
1221 value_node.push(node);
1222 TRACE("histfixing( " << underlying << " , " << obsdate << " ) (#" << node << ")", n);
1223 }
1224
1225 void visit(FunctionDiscountNode& n) override {
1226 QL_FAIL("Discount not supported by ComputationGraphBuilder");
1227
1228 // checkpoint(n);
1229 // QL_REQUIRE(model_, "model is null");
1230 // n.args[0]->accept(*this);
1231 // n.args[1]->accept(*this);
1232 // n.args[2]->accept(*this);
1233 // auto paycurr = value.pop();
1234 // auto paydate = value.pop();
1235 // auto obsdate = value.pop();
1236 // checkpoint(n);
1237 // QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
1238 // QL_REQUIRE(paydate.which() == ValueTypeWhich::Event, "paydate must be EVENT");
1239 // QL_REQUIRE(paycurr.which() == ValueTypeWhich::Currency, "paycurr must be CURRENCY");
1240 // Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
1241 // Date pay = QuantLib::ext::get<EventVec>(paydate).value;
1242 // QL_REQUIRE(obs >= model_->referenceDate(),
1243 // "observation date (" << obs << ") >= reference date (" << model_->referenceDate() << ")
1244 // required");
1245 // QL_REQUIRE(obs <= pay, "observation date (" << obs << ") <= payment date (" << pay << ") required");
1246 // value.push(model_->discount(obs, pay, QuantLib::ext::get<CurrencyVec>(paycurr).value));
1247 // TRACE("discount( " << obsdate << " , " << paydate << " , " << paycurr << " )", n);
1248 }
1249
1250 void processFwdCompAvgNode(ASTNode& n, const bool isAvg) {
1251 checkpoint(n);
1252 QL_REQUIRE(model_, "model is null");
1253 n.args[0]->accept(*this);
1254 n.args[1]->accept(*this);
1255 n.args[2]->accept(*this);
1256 n.args[3]->accept(*this);
1257 auto enddate = value.pop();
1258 auto startdate = value.pop();
1259 auto obsdate = value.pop();
1260 auto underlying = value.pop();
1261 value_node.pop(); // dummy
1262 value_node.pop(); // dummy
1263 value_node.pop(); // dummy
1264 value_node.pop(); // dummy
1265 QL_REQUIRE(underlying.which() == ValueTypeWhich::Index, "underlying must be INDEX");
1266 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
1267 QL_REQUIRE(startdate.which() == ValueTypeWhich::Event, "start must be EVENT");
1268 QL_REQUIRE(enddate.which() == ValueTypeWhich::Event, "end must be EVENT");
1269 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
1270 Date start = QuantLib::ext::get<EventVec>(startdate).value;
1271 Date end = QuantLib::ext::get<EventVec>(enddate).value;
1272 QL_REQUIRE(obs <= start, "observation date (" << obs << ") must be <= start date (" << start << ")");
1273 QL_REQUIRE(start < end, "start date (" << start << ") must be < end date (" << end << ")");
1274 RandomVariable spreadValue(model_->size(), 0.0);
1275 RandomVariable gearingValue(model_->size(), 1.0);
1276 RandomVariable lookbackValue(model_->size(), 0.0);
1277 RandomVariable rateCutoffValue(model_->size(), 0.0);
1278 RandomVariable fixingDaysValue(model_->size(), 0.0);
1279 RandomVariable includeSpreadValue(model_->size(), -1.0);
1280 RandomVariable capValue(model_->size(), 999999.0);
1281 RandomVariable floorValue(model_->size(), -999999.0);
1282 RandomVariable nakedOptionValue(model_->size(), -1.0);
1283 RandomVariable localCapFloorValue(model_->size(), 1.0);
1284 if (n.args[4]) {
1285 QL_REQUIRE(n.args[5], "internal error: Fwd[Comp|Avg]: if spread is given, gearing must be given too");
1286 n.args[4]->accept(*this);
1287 auto spread = value.pop();
1288 QL_REQUIRE(spread.which() == ValueTypeWhich::Number, "spread must be NUMBER");
1289 spreadValue = QuantLib::ext::get<RandomVariable>(spread);
1290 QL_REQUIRE(spreadValue.deterministic(), "spread must be deterministic");
1291 value_node.pop(); // dummy
1292 n.args[5]->accept(*this);
1293 auto gearing = value.pop();
1294 QL_REQUIRE(gearing.which() == ValueTypeWhich::Number, "gearing must be NUMBER");
1295 gearingValue = QuantLib::ext::get<RandomVariable>(gearing);
1296 QL_REQUIRE(gearingValue.deterministic(), "gearing must be deterministic");
1297 value_node.pop(); // dummy
1298 }
1299 if (n.args[6]) {
1300 QL_REQUIRE(n.args[7] && n.args[8] && n.args[9],
1301 "internal error: Fwd[Comp|Avg]: if lookback is given, rateCutoff, fixingDays and includeSpread "
1302 "must be given too");
1303 n.args[6]->accept(*this);
1304 auto lookback = value.pop();
1305 QL_REQUIRE(lookback.which() == ValueTypeWhich::Number, "lookback must be NUMBER");
1306 lookbackValue = QuantLib::ext::get<RandomVariable>(lookback);
1307 QL_REQUIRE(lookbackValue.deterministic(), "lookback must be deterministic");
1308 value_node.pop(); // dummy
1309 n.args[7]->accept(*this);
1310 auto rateCutoff = value.pop();
1311 QL_REQUIRE(rateCutoff.which() == ValueTypeWhich::Number, "rateCutoff must be NUMBER");
1312 rateCutoffValue = QuantLib::ext::get<RandomVariable>(rateCutoff);
1313 QL_REQUIRE(rateCutoffValue.deterministic(), "rateCutoff must be deterministic");
1314 value_node.pop(); // dummy
1315 n.args[8]->accept(*this);
1316 auto fixingDays = value.pop();
1317 QL_REQUIRE(fixingDays.which() == ValueTypeWhich::Number, "fixingDays must be NUMBER");
1318 fixingDaysValue = QuantLib::ext::get<RandomVariable>(fixingDays);
1319 QL_REQUIRE(fixingDaysValue.deterministic(), "fixingDays must be deterministic");
1320 value_node.pop(); // dummy
1321 n.args[9]->accept(*this);
1322 auto includeSpread = value.pop();
1323 QL_REQUIRE(includeSpread.which() == ValueTypeWhich::Number, "lookback must be NUMBER");
1324 includeSpreadValue = QuantLib::ext::get<RandomVariable>(includeSpread);
1325 QL_REQUIRE(includeSpreadValue.deterministic() && (QuantLib::close_enough(includeSpreadValue.at(0), 1.0) ||
1326 QuantLib::close_enough(includeSpreadValue.at(0), -1.0)),
1327 "includeSpread must be deterministic and +1 or -1");
1328 value_node.pop(); // dummy
1329 }
1330 if (n.args[10]) {
1331 QL_REQUIRE(
1332 n.args[11] && n.args[12] && n.args[13],
1333 "internal error: Fwd[Comp|Avg]: if cap is given, floor, nakedOption, localCapFloor must be given too");
1334 n.args[10]->accept(*this);
1335 auto cap = value.pop();
1336 QL_REQUIRE(cap.which() == ValueTypeWhich::Number, "cap must be NUMBER");
1337 capValue = QuantLib::ext::get<RandomVariable>(cap);
1338 QL_REQUIRE(capValue.deterministic(), "cap must be deterministic");
1339 value_node.pop(); // dummy
1340 n.args[11]->accept(*this);
1341 auto floor = value.pop();
1342 QL_REQUIRE(floor.which() == ValueTypeWhich::Number, "floor must be NUMBER");
1343 floorValue = QuantLib::ext::get<RandomVariable>(floor);
1344 QL_REQUIRE(floorValue.deterministic(), "floor must be deterministic");
1345 value_node.pop(); // dummy
1346 n.args[12]->accept(*this);
1347 auto nakedOption = value.pop();
1348 QL_REQUIRE(nakedOption.which() == ValueTypeWhich::Number, "nakedOption must be NUMBER");
1349 nakedOptionValue = QuantLib::ext::get<RandomVariable>(nakedOption);
1350 QL_REQUIRE(nakedOptionValue.deterministic() && (QuantLib::close_enough(nakedOptionValue.at(0), 1.0) ||
1351 QuantLib::close_enough(nakedOptionValue.at(0), -1.0)),
1352 "nakedOption must be deterministic and +1 or -1");
1353 value_node.pop(); // dummy
1354 n.args[13]->accept(*this);
1355 auto localCapFloor = value.pop();
1356 QL_REQUIRE(localCapFloor.which() == ValueTypeWhich::Number, "localCapFloor must be NUMBER");
1357 localCapFloorValue = QuantLib::ext::get<RandomVariable>(localCapFloor);
1358 QL_REQUIRE(localCapFloorValue.deterministic() && (QuantLib::close_enough(localCapFloorValue.at(0), 1.0) ||
1359 QuantLib::close_enough(localCapFloorValue.at(0), -1.0)),
1360 "localCapFloor must be deterministic and +1 or -1");
1361 value_node.pop(); // dummy
1362 }
1363
1364 bool includeSpreadBool = QuantLib::close_enough(includeSpreadValue.at(0), 1.0);
1365 bool nakedOptionBool = QuantLib::close_enough(nakedOptionValue.at(0), 1.0);
1366 bool localCapFloorBool = QuantLib::close_enough(localCapFloorValue.at(0), 1.0);
1367
1368 value.push(RandomVariable()); // uninitialized, since model dependent
1369 value_node.push(model_->fwdCompAvg(
1370 isAvg, QuantLib::ext::get<IndexVec>(underlying).value, obs, start, end, spreadValue.at(0), gearingValue.at(0),
1371 static_cast<Integer>(lookbackValue.at(0)), static_cast<Natural>(rateCutoffValue.at(0)),
1372 static_cast<Natural>(fixingDaysValue.at(0)), includeSpreadBool, capValue.at(0), floorValue.at(0),
1373 nakedOptionBool, localCapFloorBool));
1374
1375 TRACE("fwdCompAvg(" << isAvg << " , " << underlying << " , " << obsdate << " , " << startdate << " , "
1376 << enddate << " , " << spreadValue.at(0) << " , " << gearingValue.at(0) << " , "
1377 << lookbackValue.at(0) << " , " << rateCutoffValue.at(0) << " , " << fixingDaysValue.at(0)
1378 << " , " << includeSpreadBool << " , " << capValue.at(0) << " , " << floorValue << " , "
1379 << nakedOptionBool << " , " << localCapFloorBool << ")",
1380 n);
1381 }
1382
1383 void visit(FunctionFwdCompNode& n) override { processFwdCompAvgNode(n, false); }
1384
1385 void visit(FunctionFwdAvgNode& n) override { processFwdCompAvgNode(n, true); }
1386
1387 void processProbNode(ASTNode& n, const bool above) {
1388 checkpoint(n);
1389 QL_REQUIRE(model_, "model is null");
1390 n.args[0]->accept(*this);
1391 n.args[1]->accept(*this);
1392 n.args[2]->accept(*this);
1393 n.args[3]->accept(*this);
1394 auto barrier = value.pop();
1395 auto obsdate2 = value.pop();
1396 auto obsdate1 = value.pop();
1397 auto underlying = value.pop();
1398 auto barrierNode = value_node.pop();
1399 value_node.pop();
1400 value_node.pop();
1401 value_node.pop();
1402 QL_REQUIRE(underlying.which() == ValueTypeWhich::Index, "underlying must be INDEX");
1403 QL_REQUIRE(obsdate1.which() == ValueTypeWhich::Event, "obsdate1 must be EVENT");
1404 QL_REQUIRE(obsdate2.which() == ValueTypeWhich::Event, "obsdate2 must be EVENT");
1405 QL_REQUIRE(barrier.which() == ValueTypeWhich::Number, "barrier must be NUMBER");
1406 std::string und = QuantLib::ext::get<IndexVec>(underlying).value;
1407 Date obs1 = QuantLib::ext::get<EventVec>(obsdate1).value;
1408 Date obs2 = QuantLib::ext::get<EventVec>(obsdate2).value;
1409 if (obs1 > obs2) {
1410 value.push(RandomVariable(model_->size(), 0.0));
1411 value_node.push(cg_const(g_,0.0));
1412 } else {
1413 value.push(RandomVariable());
1414 value_node.push(model_->barrierProbability(und, obs1, obs2, barrierNode, above));
1415 }
1416 TRACE((above ? "above" : "below") << "prob(" << underlying << " , " << obsdate1 << " , " << obsdate2 << " , "
1417 << barrier << " (#" << barrierNode << "))",
1418 n);
1419 }
1420
1421 void visit(FunctionAboveProbNode& n) override { processProbNode(n, true); }
1422
1423 void visit(FunctionBelowProbNode& n) override { processProbNode(n, false); }
1424
1425 void visit(VarEvaluationNode& n) override {
1426 n.args[0]->accept(*this);
1427 checkpoint(n);
1428 n.args[1]->accept(*this);
1429 auto right = value.pop();
1430 auto left = value.pop();
1431 value_node.pop(); // dummy
1432 value_node.pop(); // dummy
1433 QL_REQUIRE(left.which() == ValueTypeWhich::Index,
1434 "evaluation operator () can only be applied to an INDEX, got " << valueTypeLabels.at(left.which()));
1435 QL_REQUIRE(right.which() == ValueTypeWhich::Event,
1436 "evaluation operator () argument obsDate must be EVENT, got " << valueTypeLabels.at(right.which()));
1437 checkpoint(n);
1438 Date obs = QuantLib::ext::get<EventVec>(right).value, fwd = Null<Date>();
1439 QL_REQUIRE(model_, "model is null");
1440 if (n.args[2]) {
1441 n.args[2]->accept(*this);
1442 auto fwdDate = value.pop();
1443 value_node.pop(); // dummy
1444 checkpoint(n);
1445 QL_REQUIRE(fwdDate.which() == ValueTypeWhich::Event,
1446 "evaluation operator () argument fwdDate must be EVENT, got "
1447 << valueTypeLabels.at(fwdDate.which()));
1448 fwd = QuantLib::ext::get<EventVec>(fwdDate).value;
1449 if (fwd == obs)
1450 fwd = Null<Date>();
1451 else {
1452 QL_REQUIRE(obs < fwd,
1453 "evaluation operator() requires obsDate (" << obs << ") < fwdDate (" << fwd << ")");
1454 }
1455 }
1456 value.push(RandomVariable());
1457 std::size_t node = model_->eval(QuantLib::ext::get<IndexVec>(left).value, obs, fwd);
1458 value_node.push(node);
1459 TRACE("indexEval( " << left << " , " << right << " , " << fwd << " ) (#" << node << ")", n);
1460 }
1461
1462 // inputs
1463 ComputationGraph& g_;
1464 const std::vector<std::string> opLabels_;
1465 const QuantLib::ext::shared_ptr<ModelCG> model_;
1466 const Size size_;
1469 const std::string script_;
1471 std::set<std::size_t>& keepNodes_;
1472 std::vector<ComputationGraphBuilder::PayLogEntry>& payLogEntries_;
1473 // working variables
1474 Context& context_;
1476 // state of the runner
1477 SafeStack<Filter> filter;
1478 SafeStack<ValueType> value;
1479 SafeStack<std::size_t> filter_node;
1480 SafeStack<std::size_t> value_node;
1481};
1482
1483} // namespace
1484
1485void ComputationGraphBuilder::run(const bool generatePayLog, const bool includePastCashflows, const std::string& script,
1486 bool interactive) {
1487
1488 keepNodes_.clear();
1489 payLogEntries_.clear();
1490
1491 ASTNode* loc;
1492 ASTRunner runner(g_, opLabels_, model_, generatePayLog, generatePayLog && includePastCashflows, script, interactive,
1494
1496 if (model_ == nullptr || model_->type() == ModelCG::Type::MC) {
1497 pattern = randomvariable_output_pattern(randomvariable_output_pattern::pattern::expectation);
1498 } else if (model_->type() == ModelCG::Type::FD) {
1499 pattern = randomvariable_output_pattern(randomvariable_output_pattern::pattern::left_middle_right);
1500 } else {
1501 QL_FAIL("model type not handled when setting output pattern for random variables");
1502 }
1503
1504 DLOG("run computation graph builder, context is:");
1505 DLOGGERSTREAM(pattern << *context_);
1506
1507 if (interactive) {
1508 std::cerr << pattern << "\nInitial Context: \n" << (*context_) << std::endl;
1509 }
1510
1511 boost::timer::cpu_timer timer;
1512 try {
1513 reset(root_);
1514 root_->accept(runner);
1515 timer.stop();
1516 QL_REQUIRE(runner.value.size() == 1, "ComputationGraphBuilder::run(): value stack has wrong size ("
1517 << runner.value.size() << "), should be 1");
1518 QL_REQUIRE(runner.filter.size() == 1, "ComputationGraphBuilder::run(): filter stack has wrong size ("
1519 << runner.filter.size() << "), should be 1");
1520 QL_REQUIRE(runner.value_node.size() == 1, "ComputationGraphBuilder::run(): value_node stack has wrong size ("
1521 << runner.value_node.size() << "), should be 1");
1522 QL_REQUIRE(runner.filter_node.size() == 1, "ComputationGraphBuilder::run(): filter_node stack has wrong size ("
1523 << runner.filter_node.size() << "), should be 1");
1524 DLOG("computation graph builder successfully finished, context after run is:");
1525
1526 if (interactive) {
1527 std::cerr << "\ncomputation graph builder finished without errors. Context after run:" << std::endl;
1528 }
1529
1530 } catch (const std::exception& e) {
1531 std::ostringstream errorMessage;
1532 errorMessage << "Error during computation graph building: " << e.what() << " at "
1533 << (loc ? to_string(loc->locationInfo) : "(last visited ast node not known)") << ": "
1534 << printCodeContext(script, loc, true);
1535
1536 std::ostringstream strippedErrorMsg;
1537 strippedErrorMsg << "Error during computation graph building: " << e.what() << " at "
1538 << (loc ? to_string(loc->locationInfo) : "(last visited ast node not known)");
1539
1540 DLOGGERSTREAM(strippedErrorMsg.str());
1542 DLOGGERSTREAM("Context when hitting the error:\n");
1543 DLOGGERSTREAM(pattern << *context_);
1544 DLOGGERSTREAM("SSA-Form when hitting the error:\n");
1545 DLOGGERSTREAM(pattern << ssaForm(g_, opLabels_));
1546
1547 if (interactive) {
1548 std::cerr << strippedErrorMsg.str() << "\n";
1549 std::cerr << printCodeContext(script, loc);
1550 std::cerr << "Context when hitting the error:" << std::endl;
1551 std::cerr << (*context_) << std::endl;
1552 std::cerr << "SSA-Form when hitting the error:";
1553 std::cerr << pattern << ssaForm(g_, opLabels_);
1554 std::cin.get();
1555 }
1556
1557 QL_FAIL(errorMessage.str());
1558 }
1559
1560 DLOGGERSTREAM(pattern << *context_);
1561 DLOG("computation graph builder running time: " << timer.elapsed().wall / 1E3 << " mus");
1562
1563 if (interactive) {
1564 std::cerr << pattern << "<<<<\n" << *context_ << ">>>>\n" << std::endl;
1565 std::cerr << pattern << "<<<<\n" << ssaForm(g_, opLabels_) << ">>>>\n" << std::endl;
1566 std::cin.get();
1567 }
1568}
1569
1570} // namespace data
1571} // namespace ore
resets cached values in ast
std::string script
static std::size_t nan
const QuantLib::ext::shared_ptr< Context > context_
const std::vector< std::string > opLabels_
void run(const bool generatePayLog, const bool includePastCashflows=false, const std::string &script="", bool interactive=false)
const QuantLib::ext::shared_ptr< ModelCG > model_
SafeStack< std::size_t > value_node
std::set< std::size_t > & keepNodes_
#define TRACE(message, n)
ASTNode *& lastVisitedNode_
bool & interactive_
const std::string script_
SafeStack< ValueType > value
const Size size_
SafeStack< std::size_t > filter_node
std::vector< ComputationGraphBuilder::PayLogEntry > & payLogEntries_
const bool generatePayLog_
const std::vector< std::string > opLabels_
SafeStack< Filter > filter
ComputationGraph & g_
Context & context_
const QuantLib::ext::shared_ptr< ModelCG > model_
const bool includePastCashflows_
computation graph builder
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define DLOGGERSTREAM(text)
Definition: log.hpp:632
Date referenceDate
Definition: utilities.cpp:442
RandomVariable max(RandomVariable x, const RandomVariable &y)
std::size_t cg_min(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_const(ComputationGraph &g, const double value)
RandomVariable exp(RandomVariable x)
RandomVariable sqrt(RandomVariable x)
std::size_t cg_indicatorGeq(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_subtract(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
RandomVariable log(RandomVariable x)
std::size_t cg_max(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
RandomVariable pow(RandomVariable x, const RandomVariable &y)
std::size_t cg_pow(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
bool close_enough_all(const RandomVariable &x, const RandomVariable &y)
std::string ssaForm(const ComputationGraph &g, const std::vector< std::string > &opCodeLabels, const std::vector< T > &values)
RandomVariable conditionalResult(const Filter &f, RandomVariable x, const RandomVariable &y)
std::size_t cg_exp(ComputationGraph &g, const std::size_t a, const std::string &label)
std::size_t cg_negative(ComputationGraph &g, const std::size_t a, const std::string &label)
std::size_t cg_mult(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
RandomVariable normalCdf(RandomVariable x)
RandomVariable abs(RandomVariable x)
std::size_t cg_add(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_var(ComputationGraph &g, const std::string &name, const bool createIfNotExists)
std::size_t cg_normalCdf(ComputationGraph &g, const std::size_t a, const std::string &label)
std::size_t cg_log(ComputationGraph &g, const std::size_t a, const std::string &label)
std::size_t cg_abs(ComputationGraph &g, const std::size_t a, const std::string &label)
RandomVariable normalPdf(RandomVariable x)
Filter equal(Filter x, const Filter &y)
std::size_t cg_sqrt(ComputationGraph &g, const std::size_t a, const std::string &label)
std::size_t cg_div(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_normalPdf(ComputationGraph &g, const std::size_t a, const std::string &label)
std::size_t cg_indicatorEq(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_indicatorGt(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
RandomVariable min(RandomVariable x, const RandomVariable &y)
Filter lt(const ValueType &x, const ValueType &y)
Definition: value.cpp:263
Filter notequal(const ValueType &x, const ValueType &y)
Definition: value.cpp:239
Filter gt(const ValueType &x, const ValueType &y)
Definition: value.cpp:282
ValueType typeSafeAssign(ValueType &x, const ValueType &y)
Definition: value.cpp:213
Filter logicalAnd(const ValueType &x, const ValueType &y)
Definition: value.cpp:352
std::string printCodeContext(std::string script, const ASTNode *loc, bool compact)
Size size(const ValueType &v)
Definition: value.cpp:145
void reset(const ASTNodePtr root)
Definition: astresetter.cpp:44
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Filter logicalOr(const ValueType &x, const ValueType &y)
Definition: value.cpp:368
Filter leq(const ValueType &x, const ValueType &y)
Definition: value.cpp:301
Filter geq(const ValueType &x, const ValueType &y)
Definition: value.cpp:321
boost::variant< RandomVariable, EventVec, CurrencyVec, IndexVec, DaycounterVec, Filter > ValueType
Definition: value.hpp:60
Filter logicalNot(const ValueType &x)
Definition: value.cpp:341
QuantLib::ext::shared_ptr< ASTNode > ASTNodePtr
Definition: ast.hpp:46
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
stack with safety checks and pop() that returns rvalue reference of top element
some utility functions
script parser
LocationInfo locationInfo
Definition: ast.hpp:63
string conversion utilities
string name