34#include <boost/accumulators/accumulators.hpp>
35#include <boost/accumulators/statistics/mean.hpp>
36#include <boost/accumulators/statistics/stats.hpp>
37#include <boost/accumulators/statistics/variance.hpp>
46struct anyGetterCg :
public boost::static_visitor<boost::any> {
47 boost::any operator()(
const RandomVariable& x)
const { QL_FAIL(
"unexpected call to anyGetter (RandomVariable)"); }
48 boost::any operator()(
const EventVec& x)
const {
return x.value; }
49 boost::any operator()(
const IndexVec& x)
const {
return x.value; }
50 boost::any operator()(
const CurrencyVec& x)
const {
return x.value; }
51 boost::any operator()(
const DaycounterVec& x)
const {
return x.value; }
52 boost::any operator()(
const Filter& x)
const { QL_FAIL(
"unexpected call to anyGetter (Filter)"); }
55boost::any valueToAnyCg(
const ValueType& v) {
return boost::apply_visitor(anyGetterCg(), v); }
57double externalAverage(
const std::vector<double>& v) {
58 boost::accumulators::accumulator_set<double, boost::accumulators::stats<boost::accumulators::tag::mean>> acc;
59 for (
auto const& x : v) {
62 return boost::accumulators::mean(acc);
73 const std::string& npv,
const std::vector<std::pair<std::string, std::string>>& additionalResults,
74 const QuantLib::ext::shared_ptr<ModelCG>& model,
const ASTNodePtr ast,
75 const QuantLib::ext::shared_ptr<Context>& context,
const Model::McParams& mcParams,
const std::string&
script,
76 const bool interactive,
const bool generateAdditionalResults,
const bool includePastCashflows,
77 const bool useCachedSensis,
const bool useExternalComputeFramework,
78 const bool useDoublePrecisionForExternalCalculation)
79 : npv_(npv), additionalResults_(additionalResults),
model_(model), ast_(ast),
context_(context),
82 useCachedSensis_(useCachedSensis), useExternalComputeFramework_(useExternalComputeFramework),
83 useDoublePrecisionForExternalCalculation_(useDoublePrecisionForExternalCalculation) {
107 auto g =
model_->computationGraph();
128 auto r = QuantLib::ext::get<RandomVariable>(v.second);
129 QL_REQUIRE(r.deterministic(),
"ScriptedInstrumentPricingEngineCG::calculate(): expected variable '"
130 << v.first <<
"' from initial context to be deterministic, got "
132 g->setVariable(v.first +
"_0",
cg_const(*g, r.at(0)));
137 for (Size i = 0; i < a.second.size(); ++i) {
139 auto r = QuantLib::ext::get<RandomVariable>(a.second[i]);
140 QL_REQUIRE(r.deterministic(),
"ScriptedInstrumentPricingEngineCG::calculate(): expected variable '"
141 << a.first <<
"[" << i
142 <<
"]' from initial context to be deterministic, got " << r);
143 g->setVariable(a.first +
"_" + std::to_string(i),
cg_const(*g, r.at(0)));
153 DLOG(
"Built computation graph version " <<
cgVersion_ <<
" size is " << g->size());
168 "ScriptedInstrumentPricingEngineCG: when using external compute framework, generation of additional "
169 "results is not supported yet.");
171 "ScriptedInstrumentPricingEngineCG: when using external compute framework, usage of cached sensis is "
172 "not supported yet");
173 QL_REQUIRE(
model_->trainingSamples() == Null<Size>(),
"ScriptedInstrumentPricingEngineCG: separate training phase "
174 "not supported, trainingSamples can not be specified.");
184 auto g =
model_->computationGraph();
186 bool newExternalCalc =
false;
188 QL_REQUIRE(ComputeEnvironment::instance().hasContext(),
189 "ScriptedInstrumentPricingEngineCG::calculate(): no compute enviroment context selected.");
190 ComputeContext::Settings settings;
191 settings.debug =
false;
206 std::vector<ExternalRandomVariable> valuesExternal;
208 valuesExternal.resize(g->size());
212 for (
auto const& c : g->constants()) {
224 TLOG(
"setting model parameter at node " << p.first <<
" to value " << std::setprecision(16) << p.second);
235 auto const& rv =
model_->randomVariates();
238 if (newExternalCalc) {
240 ComputeEnvironment::instance().context().createInputVariates(rv.size(), rv.front().size());
241 for (Size k = 0; k < rv.size(); ++k) {
242 for (Size j = 0; j < rv.front().
size(); ++j)
251 auto rng = std::make_unique<MersenneTwisterUniformRng>(
mcParams_.
seed);
252 QuantLib::InverseCumulativeNormal icn;
253 for (Size j = 0; j < rv.front().
size(); ++j) {
254 for (Size i = 0; i < rv.size(); ++i) {
255 for (Size path = 0; path <
model_->size(); ++path) {
256 values[rv[i][j]].set(path, icn(rng->nextReal()));
265 for (Size path = 0; path <
model_->size(); ++path) {
266 auto p = gen->next();
267 for (Size j = 0; j < rv.front().
size(); ++j) {
268 for (Size k = 0; k < rv.size(); ++k) {
269 values[rv[k][j]].set(path, p.value[j][k]);
275 DLOG(
"generated random variates for dim = " << rv.size() <<
", steps = " << rv.front().size());
280 std::vector<bool> keepNodes(g->size(),
false);
285 keepNodes[p.first] =
true;
291 keepNodes[
cg_var(*g, r.second +
"_0")] =
true;
295 for (Size i = 0; i < v->second.size(); ++i) {
296 keepNodes[
cg_var(*g, r.second +
"_" + std::to_string(i))] =
true;
306 std::size_t baseNpvNode =
cg_var(*g,
npv_ +
"_0");
309 if (newExternalCalc) {
312 valuesExternal[baseNpvNode].declareAsOutput();
315 DLOG(
"ran forward evaluation");
320 DLOG(
"ran forward evaluation");
345 bool resultSet =
false;
349 model_->extractT0Result(values[
cg_var(*g, r.second +
"_0")]);
351 boost::any t = valueToAnyCg(s->second);
354 DLOG(
"got additional result '" << r.first <<
"' referencing script variable '" << r.second <<
"'");
359 QL_REQUIRE(!resultSet,
"result variable '"
360 << r.first <<
"' referencing script variable '" << r.second
361 <<
"' appears both as a scalar and an array, this is unexpected");
362 QL_REQUIRE(!v->second.empty(),
"result variable '" << v->first <<
"' is an empty array.");
363 std::vector<double> tmpdouble;
364 std::vector<std::string> tmpstring;
365 std::vector<QuantLib::Date> tmpdate;
366 for (Size i = 0; i < v->second.size(); ++i) {
369 model_->extractT0Result(values[
cg_var(*g, r.second +
"_" + std::to_string(i))]));
371 boost::any t = valueToAnyCg(v->second[i]);
372 if (t.type() ==
typeid(std::string))
373 tmpstring.push_back(boost::any_cast<std::string>(t));
374 else if (t.type() ==
typeid(QuantLib::Date))
375 tmpdate.push_back(boost::any_cast<QuantLib::Date>(t));
377 QL_FAIL(
"unexpected result type '" << t.type().name() <<
"' for result variable '"
378 << r.first <<
"' referencing script variable '"
383 QL_REQUIRE((
int)!tmpdouble.empty() + (
int)!tmpstring.empty() + (
int)!tmpdate.empty() == 1,
384 "expected exactly one result type in result array '" << v->first <<
"'");
385 DLOG(
"got additional result '" << r.first <<
"' referencing script variable '" << r.second
386 <<
"' vector of size "
387 << tmpdouble.size() + tmpstring.size() + tmpdate.size());
388 if (!tmpdouble.empty())
390 else if (!tmpstring.empty())
392 else if (!tmpdate.empty())
395 QL_FAIL(
"got empty result vector for result variable '"
396 << r.first <<
"' referencing script variable '" << r.second <<
"', this is unexpected");
400 QL_REQUIRE(resultSet,
"could not set additional result '"
401 << r.first <<
"' referencing script variable '" << r.second <<
"'");
406 auto paylog = QuantLib::ext::make_shared<PayLog>();
408 paylog->write(values[p.value],
410 p.pay, p.ccy, p.legNo, p.cashflowType, p.slot);
413 paylog->consolidateAndSort();
414 std::vector<CashFlowResults> cashFlowResults(paylog->size());
415 for (Size i = 0; i < paylog->size(); ++i) {
420 if (paylog->dates().at(i) >
model_->referenceDate()) {
421 fx =
model_->getDirectFxSpotT0(paylog->currencies().at(i),
model_->baseCcy());
422 discount =
model_->getDirectDiscountT0(paylog->dates().at(i), paylog->currencies().at(i));
424 cashFlowResults[i].amount =
model_->extractT0Result(paylog->amounts().at(i)) / fx / discount;
425 cashFlowResults[i].payDate = paylog->dates().at(i);
426 cashFlowResults[i].currency = paylog->currencies().at(i);
427 cashFlowResults[i].legNumber = paylog->legNos().at(i);
428 cashFlowResults[i].type = paylog->cashflowTypes().at(i);
429 DLOG(
"got cashflow " << QuantLib::io::iso_date(cashFlowResults[i].payDate) <<
" "
430 << cashFlowResults[i].currency << cashFlowResults[i].amount <<
" "
431 << cashFlowResults[i].currency <<
"-" <<
model_->baseCcy() <<
" " << fx
432 <<
"discount(" << cashFlowResults[i].currency <<
") " << discount);
454 DLOG(
"got backward sensitivities");
465 auto modelParams =
model_->modelParameters();
468 DLOG(
"computing npv using baseNpv " <<
baseNpv_ <<
" and sensis.");
471 QL_REQUIRE(modelParams[i].first ==
baseModelParams_[i].first,
"internal error: modelParams["
472 << i <<
"] node " << modelParams[i].first
473 <<
" does not match baseModelParams node "
477 DLOG(
"node " << modelParams[i].first <<
": [" << modelParams[i].second <<
" (current) - "
const Instrument::results * results_
static std::function< void(ExternalRandomVariable &)> deleter
const std::set< std::size_t > & keepNodes() const
const std::vector< PayLogEntry > & payLogEntries() const
void run(const bool generatePayLog, const bool includePastCashflows=false, const std::string &script="", bool interactive=false)
std::vector< double * > externalOutputPtr_
const bool useDoublePrecisionForExternalCalculation_
std::vector< ExternalRandomVariableGrad > gradsExternal_
void buildComputationGraph() const
std::vector< ComputationGraphBuilder::PayLogEntry > payLogEntries_
std::vector< double > sensis_
const QuantLib::ext::shared_ptr< Context > context_
const std::string script_
void calculate() const override
std::vector< std::pair< std::size_t, double > > baseModelParams_
const bool useExternalComputeFramework_
std::vector< RandomVariableOpNodeRequirements > opNodeRequirements_
std::vector< std::vector< double > > externalOutput_
std::vector< RandomVariableOp > ops_
QuantLib::ext::shared_ptr< Context > workingContext_
const std::vector< std::pair< std::string, std::string > > additionalResults_
std::map< std::string, boost::any > instrumentAdditionalResults_
const bool generateAdditionalResults_
const bool useCachedSensis_
std::vector< ExternalRandomVariableOp > opsExternal_
std::set< std::size_t > keepNodes_
~ScriptedInstrumentPricingEngineCG()
std::vector< RandomVariableGrad > grads_
const Model::McParams mcParams_
bool lastCalculationWasValid_
ScriptedInstrumentPricingEngineCG(const std::string &npv, const std::vector< std::pair< std::string, std::string > > &additionalResults, const QuantLib::ext::shared_ptr< ModelCG > &model, const ASTNodePtr ast, const QuantLib::ext::shared_ptr< Context > &context, const Model::McParams &mcParams, const std::string &script="", const bool interactive=false, const bool generateAdditionalResults=false, const bool includePastCashflows=false, const bool useCachedSensis=false, const bool useExternalComputeFramework=false, const bool useDoublePrecisionForExternalCalculation=false)
const QuantLib::ext::shared_ptr< ModelCG > model_
const bool includePastCashflows_
std::size_t externalCalculationId_
const std::string script_
const QuantLib::ext::shared_ptr< ModelCG > model_
const bool includePastCashflows_
Classes and functions for log message handling.
#define DLOG(text)
Logging Macro (Level = Debug)
#define TLOGGERSTREAM(text)
#define TLOG(text)
Logging Macro (Level = Data)
std::size_t cg_const(ComputationGraph &g, const double value)
void forwardEvaluation(const ComputationGraph &g, std::vector< T > &values, const std::vector< std::function< T(const std::vector< const T * > &)> > &ops, std::function< void(T &)> deleter={}, bool keepValuesForDerivatives=true, const std::vector< std::function< std::pair< std::vector< bool >, bool >(const std::size_t)> > &opRequiresNodesForDerivatives={}, const std::vector< bool > &keepNodes={})
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
std::vector< ExternalRandomVariableOp > getExternalRandomVariableOps()
std::string ssaForm(const ComputationGraph &g, const std::vector< std::string > &opCodeLabels, const std::vector< T > &values)
std::vector< RandomVariableOpNodeRequirements > getRandomVariableOpNodeRequirements()
boost::shared_ptr< MultiPathVariateGeneratorBase > makeMultiPathVariateGenerator(const SequenceType s, const Size dimension, const Size timeSteps, const BigNatural seed, const SobolBrownianGenerator::Ordering ordering, const SobolRsg::DirectionIntegers directionIntegers)
std::vector< ExternalRandomVariableGrad > getExternalRandomVariableGradients()
std::size_t cg_var(ComputationGraph &g, const std::string &name, const bool createIfNotExists)
std::vector< std::string > getRandomVariableOpLabels()
std::vector< RandomVariableOp > getRandomVariableOps(const Size size, const std::map< Size, std::vector< std::function< RandomVariable(const std::vector< const RandomVariable * > &)> > > &basisFn)
std::vector< RandomVariableGrad > getRandomVariableGradients(const Size size, const double eps, const std::vector< std::function< RandomVariable(const std::vector< const RandomVariable * > &)> > &basisFn)
void backwardDerivatives(const ComputationGraph &g, const std::vector< T > &values, std::vector< T > &derivatives, const std::vector< std::function< std::vector< T >(const std::vector< const T * > &, const T *)> > &grad, std::function< void(T &)> deleter={}, const std::vector< bool > &keepNodes={})
Size size(const ValueType &v)
void checkDuplicateName(const QuantLib::ext::shared_ptr< Context > context, const std::string &name)
boost::variant< RandomVariable, EventVec, CurrencyVec, IndexVec, DaycounterVec, Filter > ValueType
QuantLib::ext::shared_ptr< ASTNode > ASTNodePtr
Serializable Credit Default Swap.
amc calculator for scripted trades
scripted instrument pricing engine using a cg model
static std::function< void(RandomVariable &)> deleter
bool externalDeviceCompatibilityMode
QuantExt::SequenceType sequenceType
QuantLib::SobolBrownianGenerator::Ordering sobolOrdering
QuantLib::SobolRsg::DirectionIntegers sobolDirectionIntegers
QuantLib::Real regressionVarianceCutoff
QuantLib::LsmBasisSystem::PolynomialType polynomType