Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
computationgraph.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
22
23#include <ql/errors.hpp>
24#include <ql/math/comparison.hpp>
25
26#include <boost/math/distributions/normal.hpp>
27
28namespace QuantExt {
29
30std::size_t ComputationGraph::nan = std::numeric_limits<std::size_t>::max();
31
33 predecessors_.clear();
34 opId_.clear();
36 redBlockId_.clear();
37 isConstant_.clear();
38 constantValue_.clear();
39 constants_.clear();
40 variables_.clear();
41 variableVersion_.clear();
42 labels_.clear();
43}
44
45std::size_t ComputationGraph::size() const { return predecessors_.size(); }
46
47std::size_t ComputationGraph::insert(const std::string& label) {
48 std::size_t node = predecessors_.size();
49 predecessors_.push_back(std::vector<std::size_t>());
50 opId_.push_back(0);
51 maxNodeRequiringArg_.push_back(0);
53 isConstant_.push_back(false);
54 constantValue_.push_back(0.0);
55 if (enableLabels_ && !label.empty())
56 labels_[node].insert(label);
57 return node;
58}
59
60std::size_t ComputationGraph::insert(const std::vector<std::size_t>& predecessors, const std::size_t opId,
61 const std::string& label) {
62 std::size_t node = predecessors_.size();
63 predecessors_.push_back(predecessors);
64 opId_.push_back(opId);
65 for (auto const& p : predecessors) {
66 maxNodeRequiringArg_[p] = node;
67 }
68 maxNodeRequiringArg_.push_back(0);
70 if (currentRedBlockId_ != 0) {
71 for (auto const& p : predecessors) {
73 redBlockDependencies_.insert(p);
74 }
75 }
76 }
77 isConstant_.push_back(false);
78 constantValue_.push_back(0.0);
79 if (enableLabels_ && !label.empty())
80 labels_[node].insert(label);
81 return node;
82}
83
84const std::vector<std::size_t>& ComputationGraph::predecessors(const std::size_t node) const {
85 return predecessors_[node];
86}
87
88std::size_t ComputationGraph::opId(const std::size_t node) const { return opId_[node]; }
89
90std::size_t ComputationGraph::maxNodeRequiringArg(const std::size_t node) const { return maxNodeRequiringArg_[node]; }
91
92std::size_t ComputationGraph::constant(const double x) {
93 auto c = constants_.find(x);
94 if (c != constants_.end())
95 return c->second;
96 else {
97 std::size_t node = predecessors_.size();
98 constants_.insert(std::make_pair(x, node));
99 predecessors_.push_back(std::vector<std::size_t>());
100 opId_.push_back(0);
101 maxNodeRequiringArg_.push_back(0);
103 isConstant_.push_back(true);
104 constantValue_.push_back(x);
105 if (enableLabels_)
106 labels_[node].insert(std::to_string(x));
107 return node;
108 }
109}
110
111const std::map<double, std::size_t>& ComputationGraph::constants() const { return constants_; }
112
113std::size_t ComputationGraph::variable(const std::string& name, const VarDoesntExist v) {
114 auto c = variables_.find(name);
115 if (c != variables_.end())
116 return c->second;
117 else if (v == VarDoesntExist::Create) {
118 std::size_t node = predecessors_.size();
119 variables_.insert(std::make_pair(name, node));
120 variableVersion_[name] = 0;
121 if (enableLabels_)
122 labels_[node].insert(name + "(v" + std::to_string(++variableVersion_[name]) + ")");
123 predecessors_.push_back(std::vector<std::size_t>());
124 opId_.push_back(0);
125 maxNodeRequiringArg_.push_back(0);
127 isConstant_.push_back(false);
128 constantValue_.push_back(0.0);
129 return node;
130 } else if (v == VarDoesntExist::Nan) {
131 return nan;
132 } else if (v == VarDoesntExist::Throw) {
133 QL_FAIL("ComputationGraph::variable(" << name << ") not found.");
134 } else {
135 QL_FAIL("ComputationGraph::variable(): internal error, VarDoesntExist enum '" << static_cast<int>(v)
136 << "' not covered.");
137 }
138}
139
140const std::map<std::string, std::size_t>& ComputationGraph::variables() const { return variables_; }
141
142void ComputationGraph::setVariable(const std::string& name, const std::size_t node) {
143 auto v = variables_.find(name);
144 if (v != variables_.end()) {
145 if (v->second != node) {
146 if (enableLabels_)
147 labels_[node].insert(name + "(v" + std::to_string(++variableVersion_[name]) + ")");
148 v->second = node;
149 }
150 } else {
151 variableVersion_[name] = 0;
152 if (enableLabels_)
153 labels_[node].insert(name + "(v" + std::to_string(++variableVersion_[name]) + ")");
154 variables_[name] = node;
155 }
156}
157
159
160const std::map<std::size_t, std::set<std::string>>& ComputationGraph::labels() const { return labels_; }
161
164 if (!redBlockRange_.empty())
165 redBlockRange_.back().second = size();
166 redBlockRange_.push_back(std::make_pair(size(), nan));
167}
168
170 QL_REQUIRE(currentRedBlockId_ > 0, "ComputationGraph::endRedBlock(): not in an active red block.");
172 redBlockRange_.back().second = size();
173}
174
175const std::vector<std::pair<std::size_t, std::size_t>>& ComputationGraph::redBlockRanges() const {
176 return redBlockRange_;
177}
178
179const std::set<std::size_t>& ComputationGraph::redBlockDependencies() const { return redBlockDependencies_; }
180
181std::size_t ComputationGraph::redBlockId(const std::size_t node) const { return redBlockId_[node]; }
182
183bool ComputationGraph::isConstant(const std::size_t node) const { return isConstant_[node]; }
184
185double ComputationGraph::constantValue(const std::size_t node) const { return constantValue_[node]; }
186
187std::size_t cg_const(ComputationGraph& g, const double value) { return g.constant(value); }
188
189std::size_t cg_insert(ComputationGraph& g, const std::string& label) { return g.insert(label); }
190
191std::size_t cg_var(ComputationGraph& g, const std::string& name, const ComputationGraph::VarDoesntExist v) {
192 return g.variable(name, v);
193}
194
195std::size_t cg_add(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
196 if (g.isConstant(a) && g.isConstant(b))
197 return cg_const(g, g.constantValue(a) + g.constantValue(b));
198 if (g.isConstant(a) && QuantLib::close_enough(g.constantValue(a), 0.0))
199 return b;
200 if (g.isConstant(b) && QuantLib::close_enough(g.constantValue(b), 0.0))
201 return a;
202 return g.insert({a, b}, RandomVariableOpCode::Add, label);
203}
204
205std::size_t cg_subtract(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
206 if (a == b)
207 return cg_const(g, 0.0);
208 if (g.isConstant(a) && g.isConstant(b))
209 return cg_const(g, g.constantValue(a) - g.constantValue(b));
210 if (g.isConstant(a) && QuantLib::close_enough(g.constantValue(a), 0.0))
211 return cg_negative(g, b);
212 if (g.isConstant(b) && QuantLib::close_enough(g.constantValue(b), 0.0))
213 return a;
214 return g.insert({a, b}, RandomVariableOpCode::Subtract, label);
215}
216
217std::size_t cg_negative(ComputationGraph& g, const std::size_t a, const std::string& label) {
218 if (g.isConstant(a))
219 return cg_const(g, -g.constantValue(a));
220 return g.insert({a}, RandomVariableOpCode::Negative, label);
221}
222
223std::size_t cg_mult(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
224 if (g.isConstant(a) && g.isConstant(b))
225 return cg_const(g, g.constantValue(a) * g.constantValue(b));
226 if (g.isConstant(a) && QuantLib::close_enough(g.constantValue(a), 1.0))
227 return b;
228 if (g.isConstant(b) && QuantLib::close_enough(g.constantValue(b), 1.0))
229 return a;
230 if ((g.isConstant(a) && QuantLib::close_enough(g.constantValue(a), 0.0)) ||
231 (g.isConstant(b) && QuantLib::close_enough(g.constantValue(b), 0.0)))
232 return cg_const(g, 0.0);
233 return g.insert({a, b}, RandomVariableOpCode::Mult, label);
234}
235
236std::size_t cg_div(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
237 if (a == b)
238 return cg_const(g, 1.0);
239 if (g.isConstant(a) && g.isConstant(b))
240 return cg_const(g, g.constantValue(a) / g.constantValue(b));
241 if (g.isConstant(b) && QuantLib::close_enough(g.constantValue(b), 1.0))
242 return a;
243 if (g.isConstant(a) && QuantLib::close_enough(g.constantValue(a), 0.0))
244 return cg_const(g, 0.0);
245 return g.insert({a, b}, RandomVariableOpCode::Div, label);
246}
247
248std::size_t cg_conditionalExpectation(ComputationGraph& g, const std::size_t regressand,
249 const std::vector<std::size_t>& regressor, const std::size_t filter,
250 const std::string& label) {
251 if (g.isConstant(regressand))
252 return regressand;
253 std::vector<size_t> args;
254 args.push_back(regressand);
255 args.push_back(filter);
256 args.insert(args.end(), regressor.begin(), regressor.end());
258}
259
260std::size_t cg_indicatorEq(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
261 if (g.isConstant(a) && g.isConstant(b))
262 return cg_const(g, QuantLib::close_enough(g.constantValue(a), g.constantValue(b)) ? 1.0 : 0.0);
263 return g.insert({a, b}, RandomVariableOpCode::IndicatorEq, label);
264}
265
266std::size_t cg_indicatorGt(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
267 if (g.isConstant(a) && g.isConstant(b))
268 return cg_const(g, g.constantValue(a) > g.constantValue(b) &&
269 !QuantLib::close_enough(g.constantValue(a), g.constantValue(b))
270 ? 1.0
271 : 0.0);
272 return g.insert({a, b}, RandomVariableOpCode::IndicatorGt, label);
273}
274
275std::size_t cg_indicatorGeq(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
276 if (g.isConstant(a) && g.isConstant(b))
277 return cg_const(g, g.constantValue(a) > g.constantValue(b) ||
278 QuantLib::close_enough(g.constantValue(a), g.constantValue(b))
279 ? 1.0
280 : 0.0);
281 return g.insert({a, b}, RandomVariableOpCode::IndicatorGeq, label);
282}
283
284std::size_t cg_min(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
285 if (g.isConstant(a) && g.isConstant(b))
286 return cg_const(g, std::min(g.constantValue(a), g.constantValue(b)));
287 return g.insert({a, b}, RandomVariableOpCode::Min, label);
288}
289
290std::size_t cg_max(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
291 if (g.isConstant(a) && g.isConstant(b))
292 return cg_const(g, std::max(g.constantValue(a), g.constantValue(b)));
293 return g.insert({a, b}, RandomVariableOpCode::Max, label);
294}
295
296std::size_t cg_abs(ComputationGraph& g, const std::size_t a, const std::string& label) {
297 if (g.isConstant(a))
298 return cg_const(g, std::abs(g.constantValue(a)));
299 return g.insert({a}, RandomVariableOpCode::Abs, label);
300}
301
302std::size_t cg_exp(ComputationGraph& g, const std::size_t a, const std::string& label) {
303 if (g.isConstant(a))
304 return cg_const(g, std::exp(g.constantValue(a)));
305 return g.insert({a}, RandomVariableOpCode::Exp, label);
306}
307
308std::size_t cg_sqrt(ComputationGraph& g, const std::size_t a, const std::string& label) {
309 if (g.isConstant(a))
310 return cg_const(g, std::sqrt(g.constantValue(a)));
311 return g.insert({a}, RandomVariableOpCode::Sqrt, label);
312}
313
314std::size_t cg_log(ComputationGraph& g, const std::size_t a, const std::string& label) {
315 if (g.isConstant(a))
316 return cg_const(g, std::log(g.constantValue(a)));
317 return g.insert({a}, RandomVariableOpCode::Log, label);
318}
319
320std::size_t cg_pow(ComputationGraph& g, const std::size_t a, const std::size_t b, const std::string& label) {
321 if (g.isConstant(a) && g.isConstant(b))
322 return cg_const(g, std::pow(g.constantValue(a), g.constantValue(b)));
323 return g.insert({a, b}, RandomVariableOpCode::Pow, label);
324}
325
326std::size_t cg_normalCdf(ComputationGraph& g, const std::size_t a, const std::string& label) {
327 static const boost::math::normal_distribution<double> n;
328 if (g.isConstant(a))
329 return cg_const(g, boost::math::cdf(n, g.constantValue(a)));
330 return g.insert({a}, RandomVariableOpCode::NormalCdf, label);
331}
332
333std::size_t cg_normalPdf(ComputationGraph& g, const std::size_t a, const std::string& label) {
334 static const boost::math::normal_distribution<double> n;
335 if (g.isConstant(a))
336 return cg_const(g, boost::math::pdf(n, g.constantValue(a)));
337 return g.insert({a}, RandomVariableOpCode::NormalPdf, label);
338}
339
340} // namespace QuantExt
std::set< std::size_t > redBlockDependencies_
const std::set< std::size_t > & redBlockDependencies() const
const std::map< std::size_t, std::set< std::string > > & labels() const
const std::map< double, std::size_t > & constants() const
std::vector< bool > isConstant_
std::size_t constant(const double c)
std::map< std::size_t, std::set< std::string > > labels_
std::size_t variable(const std::string &name, const VarDoesntExist v=VarDoesntExist::Throw)
std::map< std::string, std::size_t > variableVersion_
std::map< double, std::size_t > constants_
std::vector< std::pair< std::size_t, std::size_t > > redBlockRange_
void setVariable(const std::string &name, const std::size_t node)
std::vector< double > constantValue_
const std::vector< std::size_t > & predecessors(const std::size_t node) const
std::vector< std::size_t > redBlockId_
double constantValue(const std::size_t node) const
std::size_t maxNodeRequiringArg(const std::size_t node) const
std::map< std::string, std::size_t > variables_
std::size_t redBlockId(const std::size_t node) const
std::size_t insert(const std::string &label=std::string())
const std::map< std::string, std::size_t > & variables() const
std::vector< std::vector< std::size_t > > predecessors_
bool isConstant(const std::size_t node) const
std::vector< std::size_t > opId_
const std::vector< std::pair< std::size_t, std::size_t > > & redBlockRanges() const
std::vector< std::size_t > maxNodeRequiringArg_
void enableLabels(const bool b=true)
std::size_t opId(const std::size_t node) const
computation graph
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)
std::size_t cg_insert(ComputationGraph &g, const std::string &label)
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)
std::size_t cg_max(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_pow(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
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)
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_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)
std::size_t cg_conditionalExpectation(ComputationGraph &g, const std::size_t regressand, const std::vector< std::size_t > &regressor, const std::size_t filter, const std::string &label)
std::size_t cg_var(ComputationGraph &g, const std::string &name, const ComputationGraph::VarDoesntExist v)
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)
static constexpr std::size_t Sqrt
static constexpr std::size_t IndicatorEq
static constexpr std::size_t Max
static constexpr std::size_t Add
static constexpr std::size_t Mult
static constexpr std::size_t IndicatorGeq
static constexpr std::size_t Log
static constexpr std::size_t Pow
static constexpr std::size_t Min
static constexpr std::size_t Negative
static constexpr std::size_t Subtract
static constexpr std::size_t NormalCdf
static constexpr std::size_t NormalPdf
static constexpr std::size_t Abs
static constexpr std::size_t ConditionalExpectation
static constexpr std::size_t IndicatorGt
static constexpr std::size_t Div
static constexpr std::size_t Exp