2019-06-19 14:34:30 +02:00
|
|
|
/*
|
|
|
|
* Copyright © 2005-2011 Ondra Kamenik
|
|
|
|
* Copyright © 2019 Dynare Team
|
|
|
|
*
|
|
|
|
* This file is part of Dynare.
|
|
|
|
*
|
|
|
|
* Dynare is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Dynare is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Dynare. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
|
|
#include "utils/cc/exception.hh"
|
|
|
|
|
|
|
|
#include "tree.hh"
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
#include <limits>
|
2019-04-24 14:52:30 +02:00
|
|
|
#include <sstream>
|
|
|
|
#include <iomanip>
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
|
|
using namespace ogp;
|
|
|
|
|
|
|
|
/** Here we initialize OperationTree to contain only zero, one, nan
|
|
|
|
* and two_over_pi terms. */
|
|
|
|
OperationTree::OperationTree()
|
|
|
|
{
|
|
|
|
last_nulary = -1;
|
|
|
|
// allocate space for the constants
|
|
|
|
for (int i = 0; i < num_constants; i++)
|
|
|
|
add_nulary();
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
OperationTree::add_nulary()
|
|
|
|
{
|
|
|
|
int op = terms.size();
|
2019-04-19 17:09:04 +02:00
|
|
|
terms.push_back({});
|
2019-01-08 17:12:05 +01:00
|
|
|
_Tintset s;
|
|
|
|
s.insert(op);
|
|
|
|
nul_incidence.push_back(s);
|
2019-04-19 17:09:04 +02:00
|
|
|
derivatives.push_back({});
|
2019-01-08 17:12:05 +01:00
|
|
|
last_nulary = op;
|
|
|
|
return op;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
OperationTree::add_unary(code_t code, int op)
|
|
|
|
{
|
|
|
|
if (op == zero
|
2019-04-19 17:09:04 +02:00
|
|
|
&& (code == code_t::UMINUS
|
|
|
|
|| code == code_t::SIN
|
|
|
|
|| code == code_t::TAN
|
|
|
|
|| code == code_t::SQRT
|
|
|
|
|| code == code_t::ERF))
|
2019-01-08 17:12:05 +01:00
|
|
|
return zero;
|
2019-04-19 17:09:04 +02:00
|
|
|
if ((op == zero && code == code_t::LOG) || op == nan)
|
2019-01-08 17:12:05 +01:00
|
|
|
return nan;
|
2019-04-19 17:09:04 +02:00
|
|
|
if (op == zero && (code == code_t::EXP
|
|
|
|
|| code == code_t::COS
|
|
|
|
|| code == code_t::ERFC))
|
2019-01-08 17:12:05 +01:00
|
|
|
return one;
|
|
|
|
|
|
|
|
Operation unary(code, op);
|
2019-04-19 17:09:04 +02:00
|
|
|
auto i = opmap.find(unary);
|
2019-01-08 17:12:05 +01:00
|
|
|
if (i == opmap.end())
|
|
|
|
{
|
|
|
|
int newop = terms.size();
|
|
|
|
// add to the terms
|
|
|
|
terms.push_back(unary);
|
|
|
|
// copy incidence of the operand
|
|
|
|
nul_incidence.push_back(nul_incidence[op]);
|
|
|
|
// insert it to opmap
|
2019-04-19 17:09:04 +02:00
|
|
|
opmap.emplace(unary, newop);
|
2019-01-08 17:12:05 +01:00
|
|
|
// add empty map of derivatives
|
|
|
|
_Tderivmap empty;
|
|
|
|
derivatives.push_back(empty);
|
|
|
|
return newop;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
return i->second;
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
OperationTree::add_binary(code_t code, int op1, int op2)
|
|
|
|
{
|
|
|
|
// quick exits for special values
|
|
|
|
if (op1 == nan || op2 == nan)
|
|
|
|
return nan;
|
|
|
|
// for plus
|
2019-04-19 17:09:04 +02:00
|
|
|
if (code == code_t::PLUS)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
if (op1 == zero && op2 == zero)
|
|
|
|
return zero;
|
|
|
|
else if (op1 == zero)
|
|
|
|
return op2;
|
|
|
|
else if (op2 == zero)
|
|
|
|
return op1;
|
|
|
|
}
|
|
|
|
// for minus
|
2019-04-19 17:09:04 +02:00
|
|
|
if (code == code_t::MINUS)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
if (op1 == zero && op2 == zero)
|
|
|
|
return zero;
|
|
|
|
else if (op1 == zero)
|
2019-04-19 17:09:04 +02:00
|
|
|
return add_unary(code_t::UMINUS, op2);
|
2019-01-08 17:12:05 +01:00
|
|
|
else if (op2 == zero)
|
|
|
|
return op1;
|
|
|
|
}
|
|
|
|
// for times
|
2019-04-19 17:09:04 +02:00
|
|
|
if (code == code_t::TIMES)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
if (op1 == zero || op2 == zero)
|
|
|
|
return zero;
|
|
|
|
else if (op1 == one)
|
|
|
|
return op2;
|
|
|
|
else if (op2 == one)
|
|
|
|
return op1;
|
|
|
|
}
|
|
|
|
// for divide
|
2019-04-19 17:09:04 +02:00
|
|
|
if (code == code_t::DIVIDE)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
if (op1 == op2)
|
|
|
|
return one;
|
|
|
|
else if (op1 == zero)
|
|
|
|
return zero;
|
|
|
|
else if (op2 == zero)
|
|
|
|
return nan;
|
|
|
|
}
|
|
|
|
// for power
|
2019-04-19 17:09:04 +02:00
|
|
|
if (code == code_t::POWER)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
if (op1 == zero && op2 == zero)
|
|
|
|
return nan;
|
|
|
|
else if (op1 == zero)
|
|
|
|
return zero;
|
|
|
|
else if (op2 == zero)
|
|
|
|
return one;
|
|
|
|
else if (op1 == one)
|
|
|
|
return one;
|
|
|
|
else if (op2 == one)
|
|
|
|
return op1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// order operands of commutative operations
|
2019-04-19 17:09:04 +02:00
|
|
|
if (code == code_t::TIMES || code == code_t::PLUS)
|
2019-01-08 17:12:05 +01:00
|
|
|
if (op1 > op2)
|
|
|
|
{
|
|
|
|
int tmp = op1;
|
|
|
|
op1 = op2;
|
|
|
|
op2 = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// construct operation and check/add it
|
|
|
|
Operation binary(code, op1, op2);
|
2019-04-19 17:09:04 +02:00
|
|
|
auto i = opmap.find(binary);
|
2019-01-08 17:12:05 +01:00
|
|
|
if (i == opmap.end())
|
|
|
|
{
|
|
|
|
int newop = terms.size();
|
|
|
|
terms.push_back(binary);
|
|
|
|
// sum both sets of incidenting nulary operations
|
|
|
|
nul_incidence.push_back(nul_incidence[op1]);
|
|
|
|
nul_incidence.back().insert(nul_incidence[op2].begin(), nul_incidence[op2].end());
|
|
|
|
// add to opmap
|
2019-04-19 17:09:04 +02:00
|
|
|
opmap.emplace(binary, newop);
|
2019-01-08 17:12:05 +01:00
|
|
|
// add empty map of derivatives
|
|
|
|
_Tderivmap empty;
|
|
|
|
derivatives.push_back(empty);
|
|
|
|
return newop;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
return i->second;
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
OperationTree::add_derivative(int t, int v)
|
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
if (t < 0 || t >= static_cast<int>(terms.size()))
|
2019-01-08 17:12:05 +01:00
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"Wrong value for tree index in OperationTree::add_derivative");
|
|
|
|
|
|
|
|
// quick returns for nulary terms or empty incidence
|
|
|
|
if (terms[t].nary() == 0 && t != v)
|
2019-04-19 17:09:04 +02:00
|
|
|
return zero;
|
|
|
|
|
2019-01-08 17:12:05 +01:00
|
|
|
if (terms[t].nary() == 0 && t == v)
|
2019-04-19 17:09:04 +02:00
|
|
|
return one;
|
|
|
|
|
2019-01-08 17:12:05 +01:00
|
|
|
if (nul_incidence[t].end() == nul_incidence[t].find(v))
|
2019-04-19 17:09:04 +02:00
|
|
|
return zero;
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
|
|
// quick return if the derivative has been registered
|
2019-04-19 17:09:04 +02:00
|
|
|
auto i = derivatives[t].find(v);
|
2019-01-08 17:12:05 +01:00
|
|
|
if (i != derivatives[t].end())
|
2019-04-19 17:09:04 +02:00
|
|
|
return i->second;
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
|
|
int res = -1;
|
|
|
|
switch (terms[t].getCode())
|
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::UMINUS:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp = add_derivative(terms[t].getOp1(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_unary(code_t::UMINUS, tmp);
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::LOG:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp = add_derivative(terms[t].getOp1(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_binary(code_t::DIVIDE, tmp, terms[t].getOp1());
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::EXP:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp = add_derivative(terms[t].getOp1(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_binary(code_t::TIMES, t, tmp);
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::SIN:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp = add_derivative(terms[t].getOp1(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_binary(code_t::TIMES, add_unary(code_t::COS, terms[t].getOp1()), tmp);
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::COS:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp = add_derivative(terms[t].getOp1(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_unary(code_t::UMINUS, add_binary(code_t::TIMES, add_unary(code_t::SIN, terms[t].getOp1()), tmp));
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::TAN:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp = add_derivative(terms[t].getOp1(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
int tmp2 = add_unary(code_t::COS, terms[t].getOp1());
|
|
|
|
res = add_binary(code_t::DIVIDE, tmp, add_binary(code_t::TIMES, tmp2, tmp2));
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::SQRT:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp = add_derivative(terms[t].getOp1(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_binary(code_t::DIVIDE, tmp,
|
|
|
|
add_binary(code_t::PLUS, t, t));
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::ERF:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
int tmp = add_binary(code_t::TIMES, terms[t].getOp1(), terms[t].getOp1());
|
|
|
|
tmp = add_unary(code_t::UMINUS, tmp);
|
|
|
|
tmp = add_unary(code_t::EXP, tmp);
|
2019-01-08 17:12:05 +01:00
|
|
|
int der = add_derivative(terms[t].getOp1(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
tmp = add_binary(code_t::TIMES, tmp, der);
|
|
|
|
res = add_binary(code_t::TIMES, two_over_pi, tmp);
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::ERFC:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
int tmp = add_binary(code_t::TIMES, terms[t].getOp1(), terms[t].getOp1());
|
|
|
|
tmp = add_unary(code_t::UMINUS, tmp);
|
|
|
|
tmp = add_unary(code_t::EXP, tmp);
|
2019-01-08 17:12:05 +01:00
|
|
|
int der = add_derivative(terms[t].getOp1(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
tmp = add_binary(code_t::TIMES, tmp, der);
|
|
|
|
tmp = add_binary(code_t::TIMES, two_over_pi, tmp);
|
|
|
|
res = add_unary(code_t::UMINUS, tmp);
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::PLUS:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp1 = add_derivative(terms[t].getOp1(), v);
|
|
|
|
int tmp2 = add_derivative(terms[t].getOp2(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_binary(code_t::PLUS, tmp1, tmp2);
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::MINUS:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp1 = add_derivative(terms[t].getOp1(), v);
|
|
|
|
int tmp2 = add_derivative(terms[t].getOp2(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_binary(code_t::MINUS, tmp1, tmp2);
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::TIMES:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp1 = add_derivative(terms[t].getOp1(), v);
|
|
|
|
int tmp2 = add_derivative(terms[t].getOp2(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
int res1 = add_binary(code_t::TIMES, terms[t].getOp1(), tmp2);
|
2019-12-20 14:36:20 +01:00
|
|
|
int res2 = add_binary(code_t::TIMES, tmp1, terms[t].getOp2());
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_binary(code_t::PLUS, res1, res2);
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::DIVIDE:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp1 = add_derivative(terms[t].getOp1(), v);
|
|
|
|
int tmp2 = add_derivative(terms[t].getOp2(), v);
|
|
|
|
if (tmp2 == zero)
|
2019-04-19 17:09:04 +02:00
|
|
|
res = add_binary(code_t::DIVIDE, tmp1, terms[t].getOp2());
|
2019-01-08 17:12:05 +01:00
|
|
|
else
|
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
int nom = add_binary(code_t::MINUS,
|
|
|
|
add_binary(code_t::TIMES, tmp1, terms[t].getOp2()),
|
|
|
|
add_binary(code_t::TIMES, tmp2, terms[t].getOp1()));
|
|
|
|
int den = add_binary(code_t::TIMES, terms[t].getOp2(), terms[t].getOp2());
|
|
|
|
res = add_binary(code_t::DIVIDE, nom, den);
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::POWER:
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
int tmp1 = add_derivative(terms[t].getOp1(), v);
|
|
|
|
int tmp2 = add_derivative(terms[t].getOp2(), v);
|
2019-04-19 17:09:04 +02:00
|
|
|
int s1 = add_binary(code_t::TIMES, tmp2,
|
|
|
|
add_binary(code_t::TIMES, t,
|
|
|
|
add_unary(code_t::LOG, terms[t].getOp1())));
|
|
|
|
int s2 = add_binary(code_t::TIMES, tmp1,
|
|
|
|
add_binary(code_t::TIMES, terms[t].getOp2(),
|
|
|
|
add_binary(code_t::POWER, terms[t].getOp1(),
|
|
|
|
add_binary(code_t::MINUS, terms[t].getOp2(), one))));
|
|
|
|
res = add_binary(code_t::PLUS, s1, s2);
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::NONE:
|
2019-01-08 17:12:05 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res == -1)
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"Unknown operation code.");
|
|
|
|
|
|
|
|
register_derivative(t, v, res);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
OperationTree::add_substitution(int t, const map<int, int> &subst)
|
|
|
|
{
|
|
|
|
return add_substitution(t, subst, *this);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
OperationTree::add_substitution(int t, const map<int, int> &subst,
|
|
|
|
const OperationTree &otree)
|
|
|
|
{
|
|
|
|
// return substitution of t if it is in the map
|
2019-01-09 15:51:19 +01:00
|
|
|
auto it = subst.find(t);
|
2019-01-08 17:12:05 +01:00
|
|
|
if (subst.end() != it)
|
2019-04-19 17:09:04 +02:00
|
|
|
return it->second;
|
2019-01-08 17:12:05 +01:00
|
|
|
|
|
|
|
int nary = otree.terms[t].nary();
|
|
|
|
if (nary == 2)
|
|
|
|
{
|
|
|
|
// return the binary operation of the substituted terms
|
|
|
|
int t1 = add_substitution(otree.terms[t].getOp1(), subst, otree);
|
|
|
|
int t2 = add_substitution(otree.terms[t].getOp2(), subst, otree);
|
|
|
|
return add_binary(otree.terms[t].getCode(), t1, t2);
|
|
|
|
}
|
|
|
|
else if (nary == 1)
|
|
|
|
{
|
|
|
|
// return the unary operation of the substituted term
|
|
|
|
int t1 = add_substitution(otree.terms[t].getOp1(), subst, otree);
|
|
|
|
return add_unary(otree.terms[t].getCode(), t1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if t is not the first num_constants, and otree is not this
|
|
|
|
// tree, then raise and exception. Otherwise return t, since
|
|
|
|
// it is either a special term (having the same semantics in
|
|
|
|
// both trees), or the trees are the same, hence t has the
|
|
|
|
// same semantics
|
|
|
|
if (t < num_constants || this == &otree)
|
|
|
|
return t;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"Incomplete substitution map in OperationTree::add_substitution");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OperationTree::nularify(int t)
|
|
|
|
{
|
|
|
|
// remove the original operation from opmap
|
2019-01-09 15:51:19 +01:00
|
|
|
auto it = opmap.find(terms[t]);
|
2019-01-08 17:12:05 +01:00
|
|
|
if (it != opmap.end())
|
|
|
|
opmap.erase(it);
|
|
|
|
// turn the operation to nulary
|
|
|
|
Operation nulary_op;
|
|
|
|
terms[t] = nulary_op;
|
|
|
|
// update last nulary
|
|
|
|
if (last_nulary < t)
|
|
|
|
last_nulary = t;
|
|
|
|
// update nul_incidence information for all terms including t
|
|
|
|
update_nul_incidence_after_nularify(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OperationTree::register_derivative(int t, int v, int tder)
|
|
|
|
{
|
|
|
|
// todo: might check that the insert inserts a new pair
|
2019-04-19 17:09:04 +02:00
|
|
|
derivatives[t].emplace(v, tder);
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
unordered_set<int>
|
|
|
|
OperationTree::select_terms(int t, const opselector &sel) const
|
|
|
|
{
|
|
|
|
unordered_set<int> subterms;
|
|
|
|
select_terms(t, sel, subterms);
|
|
|
|
return subterms;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OperationTree::select_terms(int t, const opselector &sel, unordered_set<int> &subterms) const
|
|
|
|
{
|
|
|
|
const Operation &op = terms[t];
|
|
|
|
|
|
|
|
if (sel(t))
|
|
|
|
subterms.insert(t);
|
|
|
|
else
|
|
|
|
if (op.nary() == 2)
|
|
|
|
{
|
|
|
|
select_terms(op.getOp1(), sel, subterms);
|
|
|
|
select_terms(op.getOp2(), sel, subterms);
|
|
|
|
}
|
|
|
|
else if (op.nary() == 1)
|
2019-04-19 17:09:04 +02:00
|
|
|
select_terms(op.getOp1(), sel, subterms);
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
unordered_set<int>
|
|
|
|
OperationTree::select_terms_inv(int t, const opselector &sel) const
|
|
|
|
{
|
|
|
|
unordered_set<int> subterms;
|
|
|
|
select_terms_inv(t, sel, subterms);
|
|
|
|
return subterms;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OperationTree::select_terms_inv(int t, const opselector &sel, unordered_set<int> &subterms) const
|
|
|
|
{
|
|
|
|
const Operation &op = terms[t];
|
|
|
|
|
|
|
|
if (op.nary() == 2)
|
|
|
|
{
|
|
|
|
bool a1 = select_terms_inv(op.getOp1(), sel, subterms);
|
|
|
|
bool a2 = select_terms_inv(op.getOp2(), sel, subterms);
|
|
|
|
if (a1 && a2 && sel(t))
|
|
|
|
{
|
|
|
|
subterms.insert(t);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (op.nary() == 1)
|
|
|
|
{
|
|
|
|
bool a1 = select_terms_inv(op.getOp1(), sel, subterms);
|
|
|
|
if (a1 && sel(t))
|
|
|
|
{
|
|
|
|
subterms.insert(t);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (sel(t))
|
|
|
|
{
|
|
|
|
subterms.insert(t);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OperationTree::forget_derivative_maps()
|
|
|
|
{
|
2019-12-20 14:36:20 +01:00
|
|
|
for (auto &derivative : derivatives)
|
2019-01-09 15:44:26 +01:00
|
|
|
derivative.clear();
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-04-19 17:09:04 +02:00
|
|
|
OperationTree::print_operation_tree(int t, std::ostream &os, OperationFormatter &f) const
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
f.format(terms[t], t, os);
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OperationTree::print_operation(int t) const
|
|
|
|
{
|
|
|
|
DefaultOperationFormatter dof(*this);
|
2019-04-19 17:09:04 +02:00
|
|
|
print_operation_tree(t, std::cout, dof);
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OperationTree::update_nul_incidence_after_nularify(int t)
|
|
|
|
{
|
|
|
|
unordered_set<int> updated;
|
2019-04-19 17:09:04 +02:00
|
|
|
for (int tnode = num_constants; tnode < static_cast<int>(terms.size()); tnode++)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
const Operation &op = terms[tnode];
|
|
|
|
if (op.nary() == 2)
|
|
|
|
{
|
|
|
|
int op1 = op.getOp1();
|
|
|
|
int op2 = op.getOp2();
|
|
|
|
if (op1 >= tnode || op2 >= tnode)
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"Tree disorder asserted");
|
|
|
|
bool updated1 = (updated.end() != updated.find(op1));
|
|
|
|
bool updated2 = (updated.end() != updated.find(op2));
|
|
|
|
if (updated1 || updated2)
|
|
|
|
{
|
|
|
|
nul_incidence[tnode] = nul_incidence[op1];
|
|
|
|
nul_incidence[tnode].insert(nul_incidence[op2].begin(), nul_incidence[op2].end());
|
|
|
|
updated.insert(tnode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (op.nary() == 1)
|
|
|
|
{
|
|
|
|
int op1 = op.getOp1();
|
|
|
|
if (op1 >= tnode)
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"Tree disorder asserted");
|
|
|
|
bool updated1 = (updated.end() != updated.find(op1));
|
|
|
|
if (updated1)
|
|
|
|
{
|
|
|
|
nul_incidence[tnode] = nul_incidence[op1];
|
|
|
|
updated.insert(tnode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (op.nary() == 0)
|
|
|
|
{
|
|
|
|
if (tnode == t)
|
|
|
|
{
|
|
|
|
nul_incidence[tnode].clear();
|
|
|
|
nul_incidence[tnode].insert(tnode);
|
|
|
|
updated.insert(tnode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EvalTree::EvalTree(const OperationTree &ot, int last)
|
|
|
|
: otree(ot),
|
2019-04-19 17:09:04 +02:00
|
|
|
values(std::make_unique<double[]>((last == -1) ? ot.terms.size() : last+1)),
|
|
|
|
flags(std::make_unique<bool[]>((last == -1) ? ot.terms.size() : last+1)),
|
2019-01-08 17:12:05 +01:00
|
|
|
last_operation((last == -1) ? ot.terms.size()-1 : last)
|
|
|
|
{
|
|
|
|
if (last_operation < OperationTree::num_constants-1
|
2019-04-19 17:09:04 +02:00
|
|
|
|| last_operation > static_cast<int>(ot.terms.size())-1)
|
2019-01-08 17:12:05 +01:00
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"Wrong last in EvalTree constructor.");
|
|
|
|
|
|
|
|
values[0] = 0.0;
|
|
|
|
flags[0] = true;
|
|
|
|
values[1] = 1.0;
|
|
|
|
flags[1] = true;
|
|
|
|
values[2] = std::numeric_limits<double>::quiet_NaN();
|
|
|
|
flags[2] = true;
|
2019-04-24 14:52:30 +02:00
|
|
|
values[3] = 2.0/std::sqrt(M_PI);
|
2019-01-08 17:12:05 +01:00
|
|
|
flags[3] = true;
|
|
|
|
// this sets from num_constants on
|
|
|
|
reset_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
EvalTree::reset_all()
|
|
|
|
{
|
|
|
|
for (int i = OperationTree::num_constants; i <= last_operation; i++)
|
|
|
|
flags[i] = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
EvalTree::set_nulary(int t, double val)
|
|
|
|
{
|
|
|
|
if (t < 0 || t > last_operation)
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"The tree index out of bounds in EvalTree::set_nulary");
|
|
|
|
if (t < OperationTree::num_constants || otree.terms[t].nary() != 0)
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"The term is not nulary assignable in EvalTree::set_nulary");
|
|
|
|
|
|
|
|
values[t] = val;
|
|
|
|
flags[t] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
double
|
|
|
|
EvalTree::eval(int t)
|
|
|
|
{
|
|
|
|
if (t < 0 || t > last_operation)
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"The tree index out of bounds in EvalTree::eval");
|
|
|
|
if (otree.terms[t].nary() == 0 && flags[t] == false)
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"Nulary term has not been assigned a value in EvalTree::eval");
|
|
|
|
|
|
|
|
if (!flags[t])
|
|
|
|
{
|
|
|
|
const Operation &op = otree.terms[t];
|
|
|
|
if (op.nary() == 1)
|
|
|
|
{
|
|
|
|
double r1 = eval(op.getOp1());
|
|
|
|
double res;
|
2019-04-19 17:09:04 +02:00
|
|
|
if (op.getCode() == code_t::UMINUS)
|
2019-01-08 17:12:05 +01:00
|
|
|
res = -r1;
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::LOG)
|
2019-01-08 17:12:05 +01:00
|
|
|
res = log(r1);
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::EXP)
|
2019-01-08 17:12:05 +01:00
|
|
|
res = exp(r1);
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::SIN)
|
2019-01-08 17:12:05 +01:00
|
|
|
res = sin(r1);
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::COS)
|
2019-01-08 17:12:05 +01:00
|
|
|
res = cos(r1);
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::TAN)
|
2019-01-08 17:12:05 +01:00
|
|
|
res = tan(r1);
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::SQRT)
|
2019-01-08 17:12:05 +01:00
|
|
|
res = sqrt(r1);
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::ERF)
|
2019-01-08 17:49:15 +01:00
|
|
|
res = erf(r1);
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::ERFC)
|
2019-01-08 17:49:15 +01:00
|
|
|
res = erfc(r1);
|
2019-01-08 17:12:05 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"Unknown unary operation code in EvalTree::eval");
|
|
|
|
res = 0.0;
|
|
|
|
}
|
|
|
|
values[t] = res;
|
|
|
|
flags[t] = true;
|
|
|
|
}
|
|
|
|
else if (op.nary() == 2)
|
|
|
|
{
|
|
|
|
double res;
|
2019-04-19 17:09:04 +02:00
|
|
|
if (op.getCode() == code_t::PLUS)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
double r1 = eval(op.getOp1());
|
|
|
|
double r2 = eval(op.getOp2());
|
|
|
|
res = r1 + r2;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::MINUS)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
double r1 = eval(op.getOp1());
|
|
|
|
double r2 = eval(op.getOp2());
|
|
|
|
res = r1 - r2;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::TIMES)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
// pickup less complex formula first
|
|
|
|
unsigned int nul1 = otree.nulary_of_term(op.getOp1()).size();
|
|
|
|
unsigned int nul2 = otree.nulary_of_term(op.getOp2()).size();
|
|
|
|
if (nul1 < nul2)
|
|
|
|
{
|
|
|
|
double r1 = eval(op.getOp1());
|
|
|
|
if (r1 == 0.0)
|
|
|
|
res = 0.0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
double r2 = eval(op.getOp2());
|
|
|
|
res = r1 * r2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
double r2 = eval(op.getOp2());
|
|
|
|
if (r2 == 0)
|
|
|
|
res = 0.0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
double r1 = eval(op.getOp1());
|
|
|
|
res = r1*r2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::DIVIDE)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
double r1 = eval(op.getOp1());
|
|
|
|
if (r1 == 0)
|
|
|
|
res = 0.0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
double r2 = eval(op.getOp2());
|
|
|
|
res = r1 / r2;
|
|
|
|
}
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::POWER)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
// suppose that more complex is the first op in average
|
|
|
|
double r2 = eval(op.getOp2());
|
|
|
|
if (r2 == 0.0)
|
|
|
|
res = 1.0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
double r1 = eval(op.getOp1());
|
|
|
|
res = pow(r1, r2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw ogu::Exception(__FILE__, __LINE__,
|
|
|
|
"Unknown binary operation code in EvalTree::eval");
|
|
|
|
res = 0.0;
|
|
|
|
}
|
|
|
|
values[t] = res;
|
|
|
|
flags[t] = true;
|
|
|
|
}
|
|
|
|
return values[t];
|
|
|
|
}
|
|
|
|
|
|
|
|
return values[t];
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
EvalTree::print() const
|
|
|
|
{
|
2019-04-24 14:52:30 +02:00
|
|
|
std::cout << "last_op=" << last_operation << '\n'
|
|
|
|
<< " 0 1 2 3 4 5 6 7 8 9\n"
|
|
|
|
<< u8"────────────────────────────────────────────────────────────────\n";
|
2019-01-08 17:12:05 +01:00
|
|
|
for (int i = 0; i <= (last_operation+1)/10; i++)
|
|
|
|
{
|
2019-04-24 14:52:30 +02:00
|
|
|
std::cout << std::setw(3) << i << u8"│";
|
2019-01-08 17:12:05 +01:00
|
|
|
int j = 0;
|
|
|
|
while (j < 10 && 10*i+j < last_operation+1)
|
|
|
|
{
|
|
|
|
int k = 10*i+j;
|
|
|
|
if (flags[k])
|
2019-04-24 14:52:30 +02:00
|
|
|
std::cout << " " << std::setw(5) << std::setprecision(1) << values[k];
|
2019-01-08 17:12:05 +01:00
|
|
|
else
|
2019-04-24 14:52:30 +02:00
|
|
|
std::cout << u8" ─────";
|
2019-01-08 17:12:05 +01:00
|
|
|
j++;
|
|
|
|
}
|
2019-04-24 14:52:30 +02:00
|
|
|
std::cout << "\n";
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-04-19 17:09:04 +02:00
|
|
|
DefaultOperationFormatter::format(const Operation &op, int t, std::ostream &os)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
// add to the stop_set
|
|
|
|
if (stop_set.end() == stop_set.find(t))
|
|
|
|
stop_set.insert(t);
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
// call recursively non-nulary terms of the operation
|
|
|
|
if (op.nary() == 2)
|
|
|
|
{
|
|
|
|
int t1 = op.getOp1();
|
|
|
|
const Operation &op1 = otree.terms[t1];
|
|
|
|
int t2 = op.getOp2();
|
|
|
|
const Operation &op2 = otree.terms[t2];
|
|
|
|
if (op1.nary() > 0)
|
2019-04-19 17:09:04 +02:00
|
|
|
format(op1, t1, os);
|
2019-01-08 17:12:05 +01:00
|
|
|
if (op2.nary() > 0)
|
2019-04-19 17:09:04 +02:00
|
|
|
format(op2, t2, os);
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
if (op.nary() == 1)
|
|
|
|
{
|
|
|
|
int t1 = op.getOp1();
|
|
|
|
const Operation &op1 = otree.terms[t1];
|
|
|
|
if (op1.nary() > 0)
|
2019-04-19 17:09:04 +02:00
|
|
|
format(op1, t1, os);
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// print 'term ='
|
2019-04-19 17:09:04 +02:00
|
|
|
format_term(t, os);
|
|
|
|
os << " = ";
|
2019-01-08 17:12:05 +01:00
|
|
|
if (op.nary() == 0)
|
2019-04-19 17:09:04 +02:00
|
|
|
format_nulary(t, os);
|
2019-01-08 17:12:05 +01:00
|
|
|
else if (op.nary() == 1)
|
|
|
|
{
|
|
|
|
int t1 = op.getOp1();
|
|
|
|
const Operation &op1 = otree.terms[t1];
|
2019-04-19 17:09:04 +02:00
|
|
|
std::string opname = "unknown";
|
2019-01-08 17:12:05 +01:00
|
|
|
switch (op.getCode())
|
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::UMINUS:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "-";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::LOG:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "log";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::EXP:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "exp";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::SIN:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "sin";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::COS:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "cos";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::TAN:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "tan";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::SQRT:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "sqrt";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::ERF:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "erf";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::ERFC:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "erfc";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
os << opname << '(';
|
2019-01-08 17:12:05 +01:00
|
|
|
if (op1.nary() == 0)
|
2019-04-19 17:09:04 +02:00
|
|
|
format_nulary(t1, os);
|
2019-01-08 17:12:05 +01:00
|
|
|
else
|
2019-04-19 17:09:04 +02:00
|
|
|
format_term(t1, os);
|
|
|
|
os << ")";
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int t1 = op.getOp1();
|
|
|
|
const Operation &op1 = otree.terms[t1];
|
|
|
|
int t2 = op.getOp2();
|
|
|
|
const Operation &op2 = otree.terms[t2];
|
2019-04-19 17:09:04 +02:00
|
|
|
std::string opname = "unknown";
|
2019-01-08 17:12:05 +01:00
|
|
|
switch (op.getCode())
|
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::PLUS:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "+";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::MINUS:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "-";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::TIMES:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "*";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::DIVIDE:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "/";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::POWER:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "^";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (op1.nary() == 0)
|
2019-04-19 17:09:04 +02:00
|
|
|
format_nulary(t1, os);
|
2019-01-08 17:12:05 +01:00
|
|
|
else
|
2019-04-19 17:09:04 +02:00
|
|
|
format_term(t1, os);
|
|
|
|
os << ' ' << opname << ' ';
|
2019-01-08 17:12:05 +01:00
|
|
|
if (op2.nary() == 0)
|
2019-04-19 17:09:04 +02:00
|
|
|
format_nulary(t2, os);
|
2019-01-08 17:12:05 +01:00
|
|
|
else
|
2019-04-19 17:09:04 +02:00
|
|
|
format_term(t2, os);
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
2019-04-19 17:09:04 +02:00
|
|
|
print_delim(os);
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-04-19 17:09:04 +02:00
|
|
|
DefaultOperationFormatter::format_term(int t, std::ostream &os) const
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
os << '$' << t;
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-04-19 17:09:04 +02:00
|
|
|
DefaultOperationFormatter::format_nulary(int t, std::ostream &os) const
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
if (t == OperationTree::zero)
|
2019-04-19 17:09:04 +02:00
|
|
|
os << '0';
|
2019-01-08 17:12:05 +01:00
|
|
|
else if (t == OperationTree::one)
|
2019-04-19 17:09:04 +02:00
|
|
|
os << '1';
|
2019-01-08 17:12:05 +01:00
|
|
|
else if (t == OperationTree::nan)
|
2019-04-19 17:09:04 +02:00
|
|
|
os << "NaN";
|
2019-01-08 17:12:05 +01:00
|
|
|
else
|
2019-04-19 17:09:04 +02:00
|
|
|
os << '$' << t;
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-04-19 17:09:04 +02:00
|
|
|
DefaultOperationFormatter::print_delim(std::ostream &os) const
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
os << ";\n";
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
OperationStringConvertor::convert(const Operation &op, int t) const
|
|
|
|
{
|
|
|
|
if (op.nary() == 0)
|
|
|
|
{
|
|
|
|
if (t < OperationTree::num_constants)
|
|
|
|
if (t == OperationTree::zero)
|
2019-04-24 14:52:30 +02:00
|
|
|
return "0";
|
2019-01-08 17:12:05 +01:00
|
|
|
else if (t == OperationTree::one)
|
2019-04-24 14:52:30 +02:00
|
|
|
return "1";
|
2019-01-08 17:12:05 +01:00
|
|
|
else if (t == OperationTree::nan)
|
2019-04-24 14:52:30 +02:00
|
|
|
return "NaN";
|
2019-01-08 17:12:05 +01:00
|
|
|
else if (t == OperationTree::two_over_pi)
|
|
|
|
{
|
2019-04-24 14:52:30 +02:00
|
|
|
std::ostringstream buf;
|
|
|
|
buf << std::setprecision(std::numeric_limits<double>::max_digits10)
|
|
|
|
<< 2.0/std::sqrt(M_PI);
|
|
|
|
return buf.str();
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
else
|
2019-04-24 14:52:30 +02:00
|
|
|
return "error!error";
|
2019-01-08 17:12:05 +01:00
|
|
|
else
|
|
|
|
return nulsc.convert(t);
|
|
|
|
}
|
|
|
|
else if (op.nary() == 1)
|
|
|
|
{
|
|
|
|
int t1 = op.getOp1();
|
|
|
|
const Operation &op1 = otree.operation(t1);
|
2019-04-24 14:52:30 +02:00
|
|
|
std::string opname = "unknown";
|
2019-01-08 17:12:05 +01:00
|
|
|
switch (op.getCode())
|
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::UMINUS:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "-";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::LOG:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "log";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::EXP:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "exp";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::SIN:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "sin";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::COS:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "cos";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::TAN:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "tan";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::SQRT:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "sqrt";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::ERF:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "erf";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::ERFC:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "erfc";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
std::string s1 = convert(op1, t1);
|
2019-04-24 14:52:30 +02:00
|
|
|
return opname + "(" + s1 + ")";
|
2019-01-08 17:12:05 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int t1 = op.getOp1();
|
|
|
|
const Operation &op1 = otree.operation(t1);
|
|
|
|
int t2 = op.getOp2();
|
|
|
|
const Operation &op2 = otree.operation(t2);
|
2019-04-24 14:52:30 +02:00
|
|
|
std::string opname = "unknown";
|
2019-01-08 17:12:05 +01:00
|
|
|
switch (op.getCode())
|
|
|
|
{
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::PLUS:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "+";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::MINUS:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "-";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::TIMES:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "*";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::DIVIDE:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "/";
|
|
|
|
break;
|
2019-04-19 17:09:04 +02:00
|
|
|
case code_t::POWER:
|
2019-01-08 17:12:05 +01:00
|
|
|
opname = "^";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// decide about parenthesis
|
|
|
|
bool op1_par = true;
|
|
|
|
bool op2_par = true;
|
2019-04-19 17:09:04 +02:00
|
|
|
if (op.getCode() == code_t::PLUS)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
op1_par = false;
|
|
|
|
op2_par = false;
|
|
|
|
}
|
2019-04-19 17:09:04 +02:00
|
|
|
else if (op.getCode() == code_t::MINUS)
|
2019-01-08 17:12:05 +01:00
|
|
|
{
|
|
|
|
op1_par = false;
|
2019-04-19 17:09:04 +02:00
|
|
|
if (op2.getCode() != code_t::MINUS && op2.getCode() != code_t::PLUS)
|
2019-01-08 17:12:05 +01:00
|
|
|
op2_par = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (op1.nary() < 2)
|
|
|
|
op1_par = false;
|
|
|
|
if (op2.nary() < 2)
|
|
|
|
op2_par = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string res;
|
|
|
|
if (op1_par)
|
|
|
|
res += "(";
|
|
|
|
res += convert(op1, t1);
|
|
|
|
if (op1_par)
|
|
|
|
res += ")";
|
|
|
|
res += " ";
|
|
|
|
res += opname;
|
|
|
|
res += " ";
|
|
|
|
if (op2_par)
|
|
|
|
res += "(";
|
|
|
|
res += convert(op2, t2);
|
|
|
|
if (op2_par)
|
|
|
|
res += ")";
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|