35#include <ql/errors.hpp>
36#include <ql/indexes/indexmanager.hpp>
38#include <boost/timer/timer.hpp>
40#define TRACE(message, n) \
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() \
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() \
50 std::cerr << printCodeContext(script_, &n); \
53 std::cerr << "(c)ontext (s)sa-form (q)uit "; \
54 std::getline(std::cin, c); \
56 std::cerr << "<<<<\n" << context_ << ">>>>\n"; \
58 std::cerr << "<<<<\n" << ssaForm(g_, opLabels_) << ">>>>\n"; \
60 interactive_ = false; \
61 } while (c == "c" || c == "s"); \
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> {
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)
129 value.push(RandomVariable());
136 template <
typename R>
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);
142 auto right =
value.pop();
143 auto left =
value.pop();
144 value.push(op(left, right));
149 node = op_cg(left_node, right_node);
155 "internal error: binaryOp '" <<
name <<
"' got one non-number and one number argument.");
158 TRACE(
name <<
"( " << left <<
" (#" << left_node <<
"), " << right <<
" (#" << right_node <<
"))", n);
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);
166 auto arg =
value.pop();
169 std::size_t tmp = arg_node;
172 tmp = op_cg(arg_node);
179 TRACE(
name <<
"( " << arg <<
" (#" << tmp <<
"))", n);
184 std::pair<ValueType&, long> getVariableRef(VariableNode& v) {
188 return std::make_pair(QuantLib::ext::ref(*v.cachedScalar), 0);
190 QL_REQUIRE(v.args[0],
"array subscript required for variable '" << v.name <<
"'");
191 v.args[0]->accept(*
this);
192 auto arg =
value.pop();
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);
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 <<
"'");
209 v.cachedScalar = &scalar->second;
210 return std::make_pair(QuantLib::ext::ref(scalar->second), 0);
212 auto array =
context_.arrays.find(v.name);
213 if (array !=
context_.arrays.end()) {
216 v.cachedVector = &array->second;
217 return getVariableRef(v);
219 QL_FAIL(
"variable '" << v.name <<
"' is not defined.");
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);
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.");
238 v->args[0]->accept(*
this);
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) {
251 g_.setVariable(v->name +
"_" + std::to_string(i), node_id);
254 TRACE(
"declare(" << v->name <<
"[" << arraySizeL <<
"], " << val <<
" (# "
256 ? std::to_string(node_id - arraySizeL + 1) +
"..." + std::to_string(node_id)
263 g_.setVariable(v->name +
"_0", node_id);
264 TRACE(
"declare(" << v->name <<
", " << val <<
" (#" << node_id <<
"))", *arg);
274 void visit(ASTNode& n)
override {
276 QL_FAIL(
"unhandled node");
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); });
286 void visit(OperatorMinusNode& n)
override {
289 [
this](std::size_t a, std::size_t b) {
return cg_subtract(this->
g_, a, b); });
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); });
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); });
302 void visit(NegateNode& n)
override {
304 n,
"negate", [](
const ValueType& x) {
return -x; },
308 void visit(FunctionAbsNode& n)
override {
309 unaryOp<ValueType>(n,
"abs",
abs, [
this](std::size_t a) {
return cg_abs(this->
g_, a); });
312 void visit(FunctionExpNode& n)
override {
313 unaryOp<ValueType>(n,
"exp",
exp, [
this](std::size_t a) {
return cg_exp(this->
g_, a); });
316 void visit(FunctionLogNode& n)
override {
317 unaryOp<ValueType>(n,
"log",
log, [
this](std::size_t a) {
return cg_log(this->
g_, a); });
320 void visit(FunctionSqrtNode& n)
override {
321 unaryOp<ValueType>(n,
"sqrt",
sqrt, [
this](std::size_t a) {
return cg_sqrt(this->
g_, a); });
324 void visit(FunctionNormalCdfNode& n)
override {
328 void visit(FunctionNormalPdfNode& n)
override {
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); });
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); });
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); });
346 void visit(ConditionEqNode& n)
override {
348 n,
"conditionEq",
equal, [
this](std::size_t a, std::size_t b) {
return cg_indicatorEq(this->
g_, a, b); },
352 void visit(ConditionNeqNode& n)
override {
355 [
this](std::size_t a, std::size_t b) {
return cg_indicatorEq(this->
g_, a, b); },
true);
358 void visit(ConditionLtNode& n)
override {
360 n,
"conditionLt",
lt, [
this](std::size_t a, std::size_t b) {
return cg_indicatorGeq(this->
g_, a, b); },
364 void visit(ConditionLeqNode& n)
override {
366 n,
"conditionLeq",
leq, [
this](std::size_t a, std::size_t b) {
return cg_indicatorGt(this->
g_, a, b); },
370 void visit(ConditionGeqNode& n)
override {
372 n,
"conditionGeq",
geq, [
this](std::size_t a, std::size_t b) {
return cg_indicatorGeq(this->
g_, a, b); },
376 void visit(ConditionGtNode& n)
override {
378 n,
"conditionGt",
gt, [
this](std::size_t a, std::size_t b) {
return cg_indicatorGt(this->
g_, a, b); },
382 void visit(ConditionNotNode& n)
override {
385 std::function<std::size_t(std::size_t)>(),
true);
388 void visit(ConditionAndNode& n)
override {
389 n.args[0]->accept(*
this);
390 auto left =
value.pop();
394 Filter l = QuantLib::ext::get<Filter>(left);
395 if (l.deterministic() && !l[0]) {
397 value.push(Filter(l.size(),
false));
400 TRACE(
"conditionAnd( false, ? ) (#" << node <<
")", n);
403 n.args[1]->accept(*
this);
404 auto right =
value.pop();
408 if (l.deterministic() && l[0]) {
415 node =
cg_mult(
g_, left_node, right_node);
418 TRACE(
"conditionAnd( " << left <<
" , " << right <<
" ) (#" << node <<
")", n);
422 void visit(ConditionOrNode& n)
override {
423 n.args[0]->accept(*
this);
424 auto left =
value.pop();
428 Filter l = QuantLib::ext::get<Filter>(left);
429 if (l.deterministic() && l[0]) {
431 value.push(Filter(l.size(),
true));
434 TRACE(
"conditionOr( true, ? ) (#" << node <<
")", n);
437 n.args[1]->accept(*
this);
438 auto right =
value.pop();
442 if (l.deterministic() && !l[0]) {
452 TRACE(
"conditionOr( " << left <<
" , " << right <<
" ) (#" << node <<
")", n);
458 void visit(ConstantNumberNode& n)
override {
463 TRACE(
"constantNumber( " << n.value <<
" ) (#" << node <<
")", n);
466 void visit(VariableNode& n)
override {
467 auto const& r = getVariableRef(n);
471 node =
cg_var(
g_, n.name +
"_" + std::to_string(r.second));
475 TRACE(
"variable( " << n.name <<
" ) (#" << node <<
")", n);
478 void visit(DeclarationNumberNode& n)
override {
479 for (
auto const& arg : n.args) {
480 declareVariable(arg, RandomVariable(
size_, 0.0));
485 void visit(SizeOpNode& n)
override {
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");
493 QL_FAIL(
"SIZE can only be applied to array, " << n.name <<
" is a scalar");
495 auto dbl =
static_cast<double>(array->second.size());
499 TRACE(
"size( " << n.name <<
" ) (#" << node <<
")", n);
502 void visit(FunctionDateIndexNode& n)
override {
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);
511 QL_REQUIRE(ref.first.which() ==
ValueTypeWhich::Event,
"DATEINDEX: first argument must be of type event");
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()) {
521 auto dbl =
static_cast<double>(std::distance(array->second.begin(), pos) + 1);
526 }
else if (n.op ==
"GEQ") {
527 Size pos = std::lower_bound(array->second.begin(), array->second.end(), ref.first,
529 return QuantLib::ext::get<EventVec>(l).value < QuantLib::ext::get<EventVec>(r).value;
531 array->second.begin() + 1;
532 auto dbl =
static_cast<double>(pos);
536 }
else if (n.op ==
"GT") {
537 Size pos = std::upper_bound(array->second.begin(), array->second.end(), ref.first,
539 return QuantLib::ext::get<EventVec>(l).value < QuantLib::ext::get<EventVec>(r).value;
541 array->second.begin() + 1;
542 auto dbl =
static_cast<double>(pos);
547 QL_FAIL(
"DATEINDEX: operation '" << n.op <<
"' not supported, expected EQ, GEQ, GT");
549 TRACE(
"dateindex( " << v->name <<
"[" << (ref.second + 1) <<
"] , " << n.name <<
" , " << n.op <<
" ) (#"
554 void visit(AssignmentNode& n)
override {
555 n.args[1]->accept(*
this);
556 auto right =
value.pop();
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",
567 "can not assign to const variable '" << v->name <<
"'");
568 auto ref = getVariableRef(*v);
570 std::size_t node = 0, ref_node = 0;
576 "internal error: expected NUMBER, got " << valueTypeLabels.at(ref.first.which()));
578 << valueTypeLabels.at(ref.first.which()) <<
" <- "
579 << valueTypeLabels.at(right.which()));
581 QuantLib::ext::get<RandomVariable>(ref.first).setTime(Null<Real>());
583 QuantLib::ext::get<RandomVariable>(ref.first));
584 QuantLib::ext::get<RandomVariable>(ref.first).updateDeterministic();
586 if (
filter.top().deterministic()) {
588 g_.setVariable(v->name +
"_" + std::to_string(ref.second), right_node);
592 node =
cg_var(
g_, v->name +
"_" + std::to_string(ref.second));
595 auto ref_node =
cg_var(
g_, v->name +
"_" + std::to_string(ref.second));
598 node =
cg_add(
g_, pos_node, neg_node);
599 g_.setVariable(v->name +
"_" + std::to_string(ref.second), node);
602 TRACE(
"assign( " << v->name <<
"[" << (ref.second + 1) <<
"] ) (#" << node <<
") := " << ref.first <<
" ("
603 << valueTypeLabels.at(right.which()) <<
") (#" << ref_node <<
") using filter " <<
filter.top()
610 void visit(RequireNode& n)
override {
611 n.args[0]->accept(*
this);
612 auto condition =
value.pop();
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);
623 TRACE(
"require(" << condition <<
") can not be checked, because filter is model dependent.", n);
629 void visit(SequenceNode& n)
override {
630 TRACE(
"instruction_sequence()", n);
631 for (
auto const& arg : n.args) {
637 void visit(IfThenElseNode& n)
override {
638 n.args[0]->accept(*
this);
639 auto if_ =
value.pop();
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);
651 std::size_t node = 0;
652 if (currentFilter.deterministic()) {
656 if (baseFilter.deterministic()) {
657 node = baseFilter.at(0) ? if_node :
cg_const(
g_, 0.0);
659 node =
cg_mult(
g_, baseFilter_node, if_node);
663 TRACE(
"then( filter = " << currentFilter <<
" ) (#" << node <<
")", n);
664 if (!currentFilter.deterministic() || currentFilter[0]) {
665 n.args[1]->accept(*
this);
671 currentFilter = baseFilter && !cond;
672 currentFilter.updateDeterministic();
673 filter.push(currentFilter);
674 if (currentFilter.deterministic()) {
677 if (baseFilter.deterministic()) {
684 TRACE(
"else( filter = " << currentFilter <<
") (#" << node <<
")", n);
685 if (!currentFilter.deterministic() || currentFilter[0]) {
686 n.args[2]->accept(*
this);
694 void visit(LoopNode& n)
override {
696 auto var =
context_.scalars.find(n.name);
697 QL_REQUIRE(var !=
context_.scalars.end(),
"loop variable '" << n.name <<
"' not defined or not scalar");
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();
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");
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);
733 RandomVariable(
size_,
static_cast<double>(cl))),
734 "loop variable was modified in body from " << cl <<
" to " << var->second
735 <<
", this is illegal.");
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);
748 auto d2 =
value.pop();
749 auto d1 =
value.pop();
750 auto dc =
value.pop();
759 date1 = QuantLib::ext::get<EventVec>(d1).value;
760 date2 = QuantLib::ext::get<EventVec>(d2).value;
765 void visit(FunctionDcfNode& n)
override {
767 DayCounter daycounter;
768 dayCounterFunctionHelper(n, daycounter, date1, date2);
769 QL_REQUIRE(
model_,
"model is null");
770 double dbl = daycounter.yearFraction(date1, date2);
773 TRACE(
"dcf( " << date1 <<
" , " << date2 <<
" )", n);
776 void visit(FunctionDaysNode& n)
override {
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));
784 TRACE(
"days( " << date1 <<
" , " << date2 <<
" )", n);
789 void visit(SortNode& n)
override {
791 QL_FAIL(
"SORT not yet supported by ComputationGraphBuilder.");
890 void visit(PermuteNode& n)
override {
892 QL_FAIL(
"PERMUTE not yet supported by ComputationGraphBuilder.");
979 void visit(FunctionBlackNode& n)
override {
981 QL_FAIL(
"BLACK not yet supported by ComputationGraphBuilder.");
1023 void payHelper(ASTNode& n,
const bool log) {
1024 n.args[2]->accept(*
this);
1025 auto paydate =
value.pop();
1029 QL_REQUIRE(
model_,
"model is null");
1031 Date pay = boost::get<EventVec>(paydate).value;
1036 TRACE(
"pay() = 0 (#" << node <<
"), since paydate " << paydate <<
" <= " <<
model_->referenceDate(), n);
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();
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;
1058 std::size_t cfnode = pay <=
model_->referenceDate() ? amount_node : node;
1060 TRACE(
"pay( " << amount <<
" , " << obsdate <<
" , " << paydate <<
" , " << paycurr <<
" ) (#" << node
1065 FunctionLogPayNode& pn =
dynamic_cast<FunctionLogPayNode&
>(n);
1066 long legno = 0, slot = 0;
1067 std::string cftype =
"Unspecified";
1069 pn.args[4]->accept(*
this);
1070 auto s =
value.pop();
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;
1084 pn.args[6]->accept(*
this);
1085 auto s =
value.pop();
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");
1100 payLogEntries_.push_back({cfnode, filterNode, obs, pay, pccy, (Size)legno, cftype, (Size)slot});
1105 void visit(FunctionPayNode& n)
override { payHelper(n,
false); }
1107 void visit(FunctionLogPayNode& n)
override { payHelper(n,
true); }
1109 void processNpvNode(ASTNode& n,
bool hasMemSlot) {
1110 n.args[0]->accept(*
this);
1111 n.args[1]->accept(*
this);
1114 n.args[2]->accept(*
this);
1115 memSlot =
value.pop();
1118 auto obsdate =
value.pop();
1119 auto amount =
value.pop();
1124 std::size_t regFilter_node;
1125 Size opt = hasMemSlot ? 3 : 2;
1127 n.args[opt]->accept(*
this);
1128 auto val =
value.pop();
1131 regFilter = QuantLib::ext::get<Filter>(val);
1136 RandomVariable addRegressor1, addRegressor2;
1137 std::size_t addRegressor1_node, addRegressor2_node;
1139 n.args[opt+1]->accept(*
this);
1140 auto val =
value.pop();
1143 addRegressor1 = QuantLib::ext::get<RandomVariable>(val);
1149 n.args[opt+2]->accept(*
this);
1150 auto val =
value.pop();
1153 addRegressor2 = QuantLib::ext::get<RandomVariable>(val);
1163 QL_REQUIRE(
model_,
"model is null");
1164 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
1166 obs = std::max(obs,
model_->referenceDate());
1167 boost::optional<long> mem(boost::none);
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));
1173 value.push(RandomVariable());
1174 std::size_t node =
model_->npv(amount_node, obs, regFilter_node, mem, addRegressor1_node, addRegressor2_node);
1177 TRACE(
"npvmem( " << amount <<
" , " << obsdate <<
" , " << memSlot <<
" , " << regFilter <<
" , "
1178 << addRegressor1 <<
" , " << addRegressor2 <<
" ) (#" << node <<
")",
1181 TRACE(
"npv( " << amount <<
" , " << obsdate <<
" , " << regFilter <<
" , " << addRegressor1 <<
" , "
1182 << addRegressor2 <<
" ) (#" << node <<
")",
1187 void visit(FunctionNpvNode& n)
override { processNpvNode(n,
false); }
1189 void visit(FunctionNpvMemNode& n)
override { processNpvNode(n,
true); }
1191 void visit(HistFixingNode& n)
override {
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();
1203 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
1204 std::string und = QuantLib::ext::get<IndexVec>(underlying).value;
1207 if (obs >
model_->referenceDate()) {
1212 TimeSeries<Real> series = IndexManager::instance().getHistory(IndexInfo(und).index()->
name());
1213 if (series[obs] == Null<Real>()) {
1222 TRACE(
"histfixing( " << underlying <<
" , " << obsdate <<
" ) (#" << node <<
")", n);
1225 void visit(FunctionDiscountNode& n)
override {
1226 QL_FAIL(
"Discount not supported by ComputationGraphBuilder");
1250 void processFwdCompAvgNode(ASTNode& n,
const bool isAvg) {
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();
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);
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();
1289 spreadValue = QuantLib::ext::get<RandomVariable>(spread);
1290 QL_REQUIRE(spreadValue.deterministic(),
"spread must be deterministic");
1292 n.args[5]->accept(*
this);
1293 auto gearing =
value.pop();
1295 gearingValue = QuantLib::ext::get<RandomVariable>(gearing);
1296 QL_REQUIRE(gearingValue.deterministic(),
"gearing must be deterministic");
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();
1306 lookbackValue = QuantLib::ext::get<RandomVariable>(lookback);
1307 QL_REQUIRE(lookbackValue.deterministic(),
"lookback must be deterministic");
1309 n.args[7]->accept(*
this);
1310 auto rateCutoff =
value.pop();
1312 rateCutoffValue = QuantLib::ext::get<RandomVariable>(rateCutoff);
1313 QL_REQUIRE(rateCutoffValue.deterministic(),
"rateCutoff must be deterministic");
1315 n.args[8]->accept(*
this);
1316 auto fixingDays =
value.pop();
1318 fixingDaysValue = QuantLib::ext::get<RandomVariable>(fixingDays);
1319 QL_REQUIRE(fixingDaysValue.deterministic(),
"fixingDays must be deterministic");
1321 n.args[9]->accept(*
this);
1322 auto includeSpread =
value.pop();
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");
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();
1337 capValue = QuantLib::ext::get<RandomVariable>(cap);
1338 QL_REQUIRE(capValue.deterministic(),
"cap must be deterministic");
1340 n.args[11]->accept(*
this);
1341 auto floor =
value.pop();
1343 floorValue = QuantLib::ext::get<RandomVariable>(floor);
1344 QL_REQUIRE(floorValue.deterministic(),
"floor must be deterministic");
1346 n.args[12]->accept(*
this);
1347 auto nakedOption =
value.pop();
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");
1354 n.args[13]->accept(*
this);
1355 auto localCapFloor =
value.pop();
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");
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);
1368 value.push(RandomVariable());
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));
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 <<
")",
1383 void visit(FunctionFwdCompNode& n)
override { processFwdCompAvgNode(n,
false); }
1385 void visit(FunctionFwdAvgNode& n)
override { processFwdCompAvgNode(n,
true); }
1387 void processProbNode(ASTNode& n,
const bool above) {
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();
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;
1413 value.push(RandomVariable());
1414 value_node.push(
model_->barrierProbability(und, obs1, obs2, barrierNode, above));
1416 TRACE((above ?
"above" :
"below") <<
"prob(" << underlying <<
" , " << obsdate1 <<
" , " << obsdate2 <<
" , "
1417 << barrier <<
" (#" << barrierNode <<
"))",
1421 void visit(FunctionAboveProbNode& n)
override { processProbNode(n,
true); }
1423 void visit(FunctionBelowProbNode& n)
override { processProbNode(n,
false); }
1425 void visit(VarEvaluationNode& n)
override {
1426 n.args[0]->accept(*
this);
1428 n.args[1]->accept(*
this);
1429 auto right =
value.pop();
1430 auto left =
value.pop();
1434 "evaluation operator () can only be applied to an INDEX, got " << valueTypeLabels.at(left.which()));
1436 "evaluation operator () argument obsDate must be EVENT, got " << valueTypeLabels.at(right.which()));
1438 Date obs = QuantLib::ext::get<EventVec>(right).value, fwd = Null<Date>();
1439 QL_REQUIRE(
model_,
"model is null");
1441 n.args[2]->accept(*
this);
1442 auto fwdDate =
value.pop();
1446 "evaluation operator () argument fwdDate must be EVENT, got "
1447 << valueTypeLabels.at(fwdDate.which()));
1448 fwd = QuantLib::ext::get<EventVec>(fwdDate).value;
1452 QL_REQUIRE(obs < fwd,
1453 "evaluation operator() requires obsDate (" << obs <<
") < fwdDate (" << fwd <<
")");
1456 value.push(RandomVariable());
1457 std::size_t node =
model_->eval(QuantLib::ext::get<IndexVec>(left).
value, obs, fwd);
1459 TRACE(
"indexEval( " << left <<
" , " << right <<
" , " << fwd <<
" ) (#" << node <<
")", n);
1465 const QuantLib::ext::shared_ptr<ModelCG>
model_;
1492 ASTRunner runner(
g_,
opLabels_,
model_, generatePayLog, generatePayLog && includePastCashflows,
script, interactive,
1501 QL_FAIL(
"model type not handled when setting output pattern for random variables");
1504 DLOG(
"run computation graph builder, context is:");
1508 std::cerr << pattern <<
"\nInitial Context: \n" << (*context_) << std::endl;
1511 boost::timer::cpu_timer timer;
1514 root_->accept(runner);
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:");
1527 std::cerr <<
"\ncomputation graph builder finished without errors. Context after run:" << std::endl;
1530 }
catch (
const std::exception& e) {
1531 std::ostringstream errorMessage;
1532 errorMessage <<
"Error during computation graph building: " << e.what() <<
" at "
1536 std::ostringstream strippedErrorMsg;
1537 strippedErrorMsg <<
"Error during computation graph building: " << e.what() <<
" at "
1548 std::cerr << strippedErrorMsg.str() <<
"\n";
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:";
1557 QL_FAIL(errorMessage.str());
1561 DLOG(
"computation graph builder running time: " << timer.elapsed().wall / 1E3 <<
" mus");
1564 std::cerr << pattern <<
"<<<<\n" << *
context_ <<
">>>>\n" << std::endl;
resets cached values in ast
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)
std::set< std::size_t > keepNodes_
const QuantLib::ext::shared_ptr< ModelCG > model_
std::vector< PayLogEntry > payLogEntries_
SafeStack< std::size_t > value_node
std::set< std::size_t > & keepNodes_
#define TRACE(message, n)
ASTNode *& lastVisitedNode_
const std::string script_
SafeStack< ValueType > value
SafeStack< std::size_t > filter_node
std::vector< ComputationGraphBuilder::PayLogEntry > & payLogEntries_
const bool generatePayLog_
const std::vector< std::string > opLabels_
SafeStack< Filter > filter
const QuantLib::ext::shared_ptr< ModelCG > model_
const bool includePastCashflows_
computation graph builder
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Classes and functions for log message handling.
#define DLOG(text)
Logging Macro (Level = Debug)
#define DLOGGERSTREAM(text)
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)
Filter notequal(const ValueType &x, const ValueType &y)
Filter gt(const ValueType &x, const ValueType &y)
ValueType typeSafeAssign(ValueType &x, const ValueType &y)
Filter logicalAnd(const ValueType &x, const ValueType &y)
std::string printCodeContext(std::string script, const ASTNode *loc, bool compact)
Size size(const ValueType &v)
void reset(const ASTNodePtr root)
std::string to_string(const LocationInfo &l)
Filter logicalOr(const ValueType &x, const ValueType &y)
Filter leq(const ValueType &x, const ValueType &y)
Filter geq(const ValueType &x, const ValueType &y)
boost::variant< RandomVariable, EventVec, CurrencyVec, IndexVec, DaycounterVec, Filter > ValueType
Filter logicalNot(const ValueType &x)
QuantLib::ext::shared_ptr< ASTNode > ASTNodePtr
Serializable Credit Default Swap.
Map text representations to QuantLib/QuantExt types.
stack with safety checks and pop() that returns rvalue reference of top element
LocationInfo locationInfo
string conversion utilities