Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
formulaparser.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 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
19/*! \file ored/utilities/formulaparser.hpp
20 \brief generic formula parser
21 \ingroup portfolio
22*/
23
24#pragma once
25
26#include <qle/math/compiledformula.hpp>
27
28#include <ql/errors.hpp>
29#include <ql/math/comparison.hpp>
30
31#include <boost/phoenix/core.hpp>
32#include <boost/phoenix/operator.hpp>
33#include <boost/spirit/include/qi.hpp>
34
35#include <stack>
36#include <string>
37
38namespace ore {
39namespace data {
40
41/*! evaluate arithmetic expression, T must provide operators
42 T+T, T-T, -T, T*T, T/T,
43 abs(T), exp(T), gtZero(T), geqZero(T), log(T), max(T,T), min(T,T), pow(T,T)
44 variables are written as "{variable}"
45*/
46template <class T> T parseFormula(const std::string& text, const std::function<T(std::string)>& variableMapping = {});
47
48/*! parse formula and store it as a CompiledFormula instance, the variables vector contains the label
49 of the variables for each index */
50QuantExt::CompiledFormula parseFormula(const std::string& text, std::vector<std::string>& variables);
51
52/* the formula parser provides a few non-standard operators which are not defined for T = double, for convenience we
53 provide them here; */
54inline double gtZero(const double x) { return x > 0.0 && !QuantLib::close_enough(x, 0.0) ? 1.0 : 0.0; }
55inline double geqZero(const double x) { return x > 0.0 || QuantLib::close_enough(x, 0.0) ? 1.0 : 0.0; }
56inline double max(const double x, const double y) { return std::max(x, y); }
57inline double min(const double x, const double y) { return std::min(x, y); }
58
59using std::abs;
60
61// implementation
62template <class T> T parseFormula(const std::string& text, const std::function<T(std::string)>& variableMapping) {
63
64 namespace fusion = boost::fusion;
65 namespace phoenix = boost::phoenix;
66 namespace qi = boost::spirit::qi;
67 namespace ascii = boost::spirit::ascii;
68
69 typedef std::string::const_iterator Iterator;
70
71 using ascii::alnum;
72 using ascii::char_;
73 using ascii::space;
74 using qi::double_;
75 using qi::lexeme;
76 using qi::lit;
77 using namespace qi::labels;
78
79 qi::rule<Iterator, std::string(), ascii::space_type> variable;
80 qi::rule<Iterator, ascii::space_type> factor;
81 qi::rule<Iterator, ascii::space_type> term;
82 qi::rule<Iterator, ascii::space_type> expression;
83
84 std::stack<T> evalStack;
85
86 auto pushConstant = [&evalStack](const double x) { evalStack.push(T(x)); };
87 auto pushVariable = [&variableMapping, &evalStack, &text](const std::string& s) {
88 QL_REQUIRE(variableMapping, "parseFormula(" << text << "): could not resolve variable \"" << s
89 << "\", because no variable mapping is given");
90 evalStack.push(variableMapping(s));
91 };
92
93 struct doUnaryOp {
94 doUnaryOp(std::stack<T>& evalStack, const std::function<T(T)>& op) : evalStack_(evalStack), op_(op) {}
95 void operator()() const {
96 QL_REQUIRE(evalStack_.size() > 0, "parseFormula(): internal error, empty stack for unary operation");
97 T x = evalStack_.top();
98 evalStack_.pop();
99 T y = op_(x);
100 evalStack_.push(y);
101 }
102 std::stack<T>& evalStack_;
103 const std::function<T(T)> op_;
104 };
105
106 struct doBinaryOp {
107 doBinaryOp(std::stack<T>& evalStack, const std::function<T(T, T)>& op) : evalStack_(evalStack), op_(op) {}
108 void operator()() const {
109 QL_REQUIRE(evalStack_.size() > 1,
110 "parseFormula(): internal error, stack size too small for binary operation");
111 T rhs = evalStack_.top();
112 evalStack_.pop();
113 T lhs = evalStack_.top();
114 evalStack_.pop();
115 evalStack_.push(op_(lhs, rhs));
116 }
117 std::stack<T>& evalStack_;
118 const std::function<T(T, T)> op_;
119 };
120
121 auto doNegate = doUnaryOp(evalStack, [](const T& x) { return -x; });
122 auto doAbs = doUnaryOp(evalStack, [](const T& x) { return abs(x); });
123 auto doGtZero = doUnaryOp(evalStack, [](const T& x) { return gtZero(x); });
124 auto doGeqZero = doUnaryOp(evalStack, [](const T& x) { return geqZero(x); });
125 auto doExp = doUnaryOp(evalStack, [](const T& x) { return exp(x); });
126 auto doLog = doUnaryOp(evalStack, [](const T& x) { return log(x); });
127 //
128 auto doMultiply = doBinaryOp(evalStack, [](const T& x, const T& y) { return x * y; });
129 auto doDivide = doBinaryOp(evalStack, [](const T& x, const T& y) { return x / y; });
130 auto doPlus = doBinaryOp(evalStack, [](const T& x, const T& y) { return x + y; });
131 auto doMinus = doBinaryOp(evalStack, [](const T& x, const T& y) { return x - y; });
132 auto doMax = doBinaryOp(evalStack, [](const T& x, const T& y) { return max(x, y); });
133 auto doMin = doBinaryOp(evalStack, [](const T& x, const T& y) { return min(x, y); });
134 auto doPow = doBinaryOp(evalStack, [](const T& x, const T& y) { return pow(x, y); });
135
136 // clang-format off
137
138 variable = '{' >> lexeme[+(char_ - '}') [_val += qi::labels::_1] ] >> '}';
139 factor = double_ [ pushConstant ]
140 | variable [ pushVariable ]
141 | '(' >> expression >> ')'
142 | '-' >> factor [ doNegate ]
143 | lit("abs(") >> expression [ doAbs ] >> ')'
144 | lit("exp(") >> expression [ doExp ] >> ')'
145 | lit("gtZero(") >> expression [ doGtZero ] >> ')'
146 | lit("geqZero(") >> expression [ doGeqZero ] >> ')'
147 | lit("log(") >> expression [ doLog ] >> ')'
148 | lit("max(") >> expression >> ',' >> expression [ doMax ] >> ')'
149 | lit("min(") >> expression >> ',' >> expression [ doMin ] >> ')'
150 | lit("pow(") >> expression >> ',' >> expression [ doPow ] >> ')';
151 term = factor >> *( ('*' >> factor) [ doMultiply ]
152 | ('/' >> factor) [ doDivide ] );
153 expression = term >> *( ('+' >> term) [ doPlus ]
154 | ('-' >> term) [ doMinus ]);
155
156 // clang-format on
157
158 std::string::const_iterator iter = text.begin();
159 std::string::const_iterator end = text.end();
160 bool r = phrase_parse(iter, end, expression, space);
161
162 if (r && iter == end) {
163 return evalStack.top();
164 } else {
165 std::string::const_iterator some = iter + 30;
166 std::string context(iter, (some > end) ? end : some);
167 QL_FAIL("parseFormula(" << text << "): parsing failed, stopped at \"" + context + "...\"");
168 }
169 return evalStack.top();
170}
171
172} // namespace data
173} // namespace ore
@ data
Definition: log.hpp:77
RandomVariable max(RandomVariable x, const RandomVariable &y)
RandomVariable exp(RandomVariable x)
RandomVariable log(RandomVariable x)
RandomVariable pow(RandomVariable x, const RandomVariable &y)
RandomVariable abs(RandomVariable x)
RandomVariable min(RandomVariable x, const RandomVariable &y)
QuantExt::CompiledFormula parseFormula(const std::string &text, std::vector< std::string > &variables)
double geqZero(const double x)
double gtZero(const double x)
Serializable Credit Default Swap.
Definition: namespaces.docs:23
const std::function< RandomVariable(const RandomVariable &, const RandomVariable &)> op_
Definition: value.cpp:66