1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
|
// Copyright (C) 2005-2011, Ondra Kamenik
#ifndef OGP_FORMULA_PARSER_H
#define OGP_FORMULA_PARSER_H
#include "tree.h"
namespace ogp {
using std::vector;
/** Pure virtual class defining a minimal interface for
* representation of nulary terms within FormulaParser. */
class Atoms {
public:
Atoms() {}
virtual ~Atoms() {}
/** This returns previously assigned internal index to the
* given atom, or returns -1 if the atom has not been assigned
* yet. The method can raise an exception, if the Atoms
* implementation is strict and the name is not among
* prescribed possible values. */
virtual int check(const char* name) const = 0;
/** This method assigns an internal index to the nulary term
* described by the name. The internal index is allocated by
* OperationTree class. */
virtual void assign(const char* name, int t) = 0;
/** Returns a number of variables which will be used for
* differentiations. */
virtual int nvar() const = 0;
/** Returns a vector of variable's internal indices which will
* be used for differentiations. */
virtual vector<int> variables() const = 0;
/** Debug print. */
virtual void print() const = 0;
};
/** Pure virtual class defining interface for all classes able to
* set nulary terms to evaluation tree EvalTree. The
* implementations of this class will have to be connected with
* Atoms to have knowledge about the atoms and their indices in
* the tree, and will call EvalTree::set_nulary. */
class AtomValues {
public:
virtual ~AtomValues() {}
virtual void setValues(EvalTree& et) const = 0;
};
class FormulaDerEvaluator;
class FoldMultiIndex;
/** For ordering FoldMultiIndex in the std::map. */
struct ltfmi {
bool operator()(const FoldMultiIndex& i1, const FoldMultiIndex& i2) const;
};
/** This class stores derivatives (tree indices) of one formula
* for all orders upto a given one. It stores the derivatives as a
* sequence (vector) of these tree indices and sequence of the
* multidimensional indices of variables wrt which the derivatives
* were taken. In order to speed up querying for a derivative
* given the variables, we have a map mapping the multidimensional
* index to the order of the derivative in the sequence.
*
* The only reason we do not have only this map is that the
* iterators of the map do not survive the insertions to the map,
* and implementation of the constructor has to be very difficult.
*/
class FormulaDerivatives {
friend class FormulaDerEvaluator;
protected:
/** Vector of derivatives. This is a list of derivatives (tree
* indices), the ordering is given by the algorithm used to
* create it. Currently, it starts with zero-th derivative,
* the formula itself and carries with first order, second,
* etc. */
vector<int> tder;
/** Vector of multiindices corresponding to the vector of
* derivatives. */
vector<FoldMultiIndex> indices;
/** For retrieving derivatives via a multiindex, we have a map
* mapping a multiindex to a derivative in the tder
* ordering. This means that indices[ind2der[index]] == index. */
typedef map<FoldMultiIndex, int, ltfmi> Tfmiintmap;
Tfmiintmap ind2der;
/** The number of variables. */
int nvar;
/** The maximum order of derivatives. */
int order;
public:
/** The constructor allocates and fills the sequence of the
* indices of derivatives for a formula.
* @param otree the OperationTree for which all work is done
* and to which the derivatives are added.
* @param vars the vector of nulary terms in the tree; the
* derivatives are taken with respect to these variables in
* the ordering given by the vector.
* @param f the index of the formula being differentiated. The
* zero derivative is set to f.
* @param max_order the maximum order of differentiation.
*/
FormulaDerivatives(OperationTree& otree, const vector<int>& vars, int f, int max_order);
/** Copy constructor. */
FormulaDerivatives(const FormulaDerivatives& fd);
virtual ~FormulaDerivatives(){}
/** Random access to the derivatives via multiindex. */
int derivative(const FoldMultiIndex& mi) const;
/** Return the order. */
int get_order() const
{return order;}
/** Debug print. */
void print(const OperationTree& otree) const;
};
class FormulaEvaluator;
/** This class is able to parse a number of formulas and
* differentiate them. The life cycle of the object is as follows:
* After it is created, a few calls to parse will add formulas
* (zero derivatives) to the object. Then a method differentiate()
* can be called and a vector of pointers to derivatives for each
* formula is created. After this, no one should call other
* parse() or differentiate(). A const reference of the object can
* be used in constructors of FormulaEvaluator and
* FormulaDerEvaluator in order to evaluate formulas (zero
* derivatives) and higher derivatives resp. */
class FormulaParser {
friend class FormulaCustomEvaluator;
friend class FormulaDerEvaluator;
protected:
/** The OperationTree of all formulas, including derivatives. */
OperationTree otree;
/** Reference to Atoms. The Atoms are filled with nulary terms
* during execution of parse(). */
Atoms& atoms;
/** Vector of formulas (zero derivatives) in the order as they
* have been parsed. */
vector<int> formulas;
/** The vector to derivatives, each vector corresponds to a
* formula in the vector formulas. */
vector<FormulaDerivatives*> ders;
public:
/** Construct an empty formula parser. */
FormulaParser(Atoms& a)
: atoms(a) {}
/** Copy constructor using a different instance of Atoms. */
FormulaParser(const FormulaParser& fp, Atoms& a);
virtual ~FormulaParser();
/** Requires an addition of the formula; called from the
* parser. */
void add_formula(int t);
/** Requires an addition of the binary operation; called from
* the parser. */
int add_binary(code_t code, int t1, int t2);
/** Requires an addition of the unary operation; called from
* the parser. */
int add_unary(code_t code, int t);
/** Requires an addition of the nulary operation given by the
* string. The Atoms are consulted for uniquness and are given
* an internal index generated by the OperationTree. This is
* the channel through which the Atoms are filled. */
int add_nulary(const char* str);
/** Adds a derivative to the tree. This just calls
* OperationTree::add_derivative. */
int add_derivative(int t, int v)
{return otree.add_derivative(t, v);}
/** Adds a substitution. This just calls
* OperationTree::add_substitution. */
int add_substitution(int t, const map<int,int>& subst)
{return otree.add_substitution(t, subst);}
/** Add the substitution given by the map where left sides of
* substitutions come from another parser. The right sides are
* from this object. The given t is from the given parser fp. */
int add_substitution(int t, const map<int,int>& subst,
const FormulaParser& fp)
{return otree.add_substitution(t, subst, fp.otree);}
/** This adds formulas from the given parser with (possibly)
* different atoms applying substitutions from the given map
* mapping atoms from fp to atoms of the object. */
void add_subst_formulas(const map<int,int>& subst, const FormulaParser& fp);
/** Substitute formulas. For each i from 1 through all
* formulas, it adds a substitution of the i-th formula and
* make it to be i-th formula.*/
void substitute_formulas(const std::map<int,int>& subst);
/** This method turns the given term to nulary operation. It
* should be used with caution, since this method does not
* anything do with atoms, but usually some action is also
* needed (at leat to assign the tree index t to some
* atom). */
void nularify(int t)
{otree.nularify(t);}
/** Returns a set of nulary terms of the given term. Just
* calls OperationTree::nulary_of_term. */
const unordered_set<int>& nulary_of_term(int t) const
{return otree.nulary_of_term(t);}
/** Parse a given string containing one or more formulas. The
* formulas are parsed and added to the OperationTree and to
* the formulas vector. */
void parse(int length, const char* stream);
/** Processes a syntax error from bison. */
void error(const char* mes) const;
/** Differentiate all the formulas up to the given order. The
* variables with respect to which the derivatives are taken
* are obtained by Atoms::variables(). If the derivates exist,
* they are destroyed and created again (with possibly
* different order). */
void differentiate(int max_order);
/** Return i-th formula derivatives. */
const FormulaDerivatives& derivatives(int i) const;
/** This returns a maximum index of zero derivative formulas
* including all nulary terms. This is a mimumum length of the
* tree for which it is safe to evaluate zero derivatives of
* the formulas. */
int last_formula() const;
/** This returns a tree index of the i-th formula in the
* vector. */
int formula(int i) const
{return formulas[i];}
/** This returns a tree index of the last formula and pops its
* item from the formulas vector. The number of formulas is
* then less by one. Returns -1 if there is no formula. If
* there are derivatives of the last formula, they are
* destroyed and the vector ders is popped from the back. */
int pop_last_formula();
/** This returns a number of formulas. */
int nformulas() const
{return (int)(formulas.size());}
/** This returns a reference to atoms. */
const Atoms& getAtoms() const
{return atoms;}
Atoms& getAtoms()
{return atoms;}
/** This returns the tree. */
const OperationTree& getTree() const
{return otree;}
OperationTree& getTree()
{return otree;}
/** Debug print. */
void print() const;
private:
/** Hide this copy constructor declaration by declaring it as
* private. */
FormulaParser(const FormulaParser& fp);
/** Destroy all derivatives. */
void destroy_derivatives();
};
/** This is a pure virtual class defining an interface for all
* classes which will load the results of formula (zero
* derivative) evaluations. A primitive implementation of this
* class can be a vector of doubles. */
class FormulaEvalLoader {
public:
virtual ~FormulaEvalLoader() {}
/** Set the value res for the given formula. The formula is
* identified by an index corresponding to the ordering in
* which the formulas have been parsed (starting from
* zero). */
virtual void load(int i, double res) = 0;
};
/** This class evaluates a selected subset of terms of the
* tree. In the protected constructor, one can constraint the
* initialization of the evaluation tree to a given number of
* terms in the beginning. Using this constructor, one has to make
* sure, that the terms in the beginning do not refer to terms
* behind the initial part. */
class FormulaCustomEvaluator {
protected:
/** The evaluation tree. */
EvalTree etree;
/** The custom tree indices to be evaluated. */
vector<int> terms;
public:
/** Construct from FormulaParser and given list of terms. */
FormulaCustomEvaluator(const FormulaParser& fp, const vector<int>& ts)
: etree(fp.otree), terms(ts)
{}
/** Construct from OperationTree and given list of terms. */
FormulaCustomEvaluator(const OperationTree& ot, const vector<int>& ts)
: etree(ot), terms(ts)
{}
/** Evaluate the terms using the given AtomValues and load the
* results using the given loader. The loader is called for
* each term in the order of the terms. */
void eval(const AtomValues& av, FormulaEvalLoader& loader);
protected:
FormulaCustomEvaluator(const FormulaParser& fp)
: etree(fp.otree, fp.last_formula()), terms(fp.formulas)
{}
};
/** This class evaluates zero derivatives of the FormulaParser. */
class FormulaEvaluator : public FormulaCustomEvaluator {
public:
/** Construct from FormulaParser. */
FormulaEvaluator(const FormulaParser& fp)
: FormulaCustomEvaluator(fp) {}
};
/** This is a pure virtual class defining an interface for all
* classes which will load the results of formula derivative
* evaluations. */
class FormulaDerEvalLoader {
public:
virtual ~FormulaDerEvalLoader() {}
/** This loads the result of the derivative of the given
* order. The semantics of i is the same as in
* FormulaEvalLoader::load. The indices of variables with
* respect to which the derivative was taken are stored in
* memory pointed by vars. These are the tree indices of the
* variables. */
virtual void load(int i, int order, const int* vars, double res) = 0;
};
/** This class is a utility class representing the tensor
* multindex. It can basically increment itself, and calculate
* its offset in the folded tensor. */
class FoldMultiIndex {
/** Number of variables. */
int nvar;
/** Dimension. */
int ord;
/** The multiindex. */
int* data;
public:
/** Initializes to the zero derivative. Order is 0, data is
* empty. */
FoldMultiIndex(int nv);
/** Initializes the multiindex to zeros or given i. */
FoldMultiIndex(int nv, int order, int i = 0);
/** Makes a new multiindex of the same order applying a given
* mapping to the indices. The mapping is supposed to be monotone. */
FoldMultiIndex(int nv, const FoldMultiIndex& mi, const vector<int>& mp);
/** Shifting constructor. This adds a given number of orders
* to the end, copying the last item to the newly added items,
* keeping the index ordered. If the index was empty (zero-th
* dimension), then zeros are added. */
FoldMultiIndex(const FoldMultiIndex& fmi, int new_orders);
/** Copy constructor. */
FoldMultiIndex(const FoldMultiIndex& fmi);
/** Desctructor. */
virtual ~FoldMultiIndex()
{delete [] data;}
/** Assignment operator. */
const FoldMultiIndex& operator=(const FoldMultiIndex& fmi);
/** Operator < implementing lexicographic ordering within one
* order, increasing order across orders. */
bool operator<(const FoldMultiIndex& fmi) const;
bool operator==(const FoldMultiIndex& fmi) const;
/** Increment the multiindex. */
void increment();
/** Return offset of the multiindex in the folded tensor. */
int offset() const;
const int& operator[](int i) const
{return data[i];}
/** Return order of the multiindex, i.e. dimension of the
* tensor. */
int order() const
{return ord;}
/** Return the number of variables. */
int nv() const
{return nvar;}
/** Return the data. */
const int* ind() const
{return data;}
/** Return true if the end of the tensor is reached. The
* result of a subsequent increment should be considered
* unpredictable. */
bool past_the_end() const
{return (ord == 0) || (data[0] == nvar);}
/** Prints the multiindex in the brackets. */
void print() const;
private:
static int offset_recurse(int* data, int len, int nv);
};
/** This class evaluates derivatives of the FormulaParser. */
class FormulaDerEvaluator {
/** Its own instance of EvalTree. */
EvalTree etree;
/** The indices of derivatives for each formula. This is a
* const copy FormulaParser::ders. We do not allocate nor
* deallocate anything here. */
vector<const FormulaDerivatives*> ders;
/** A copy of tree indices corresponding to atoms to with
* respect the derivatives were taken. */
vector<int> der_atoms;
public:
/** Construct the object from FormulaParser. */
FormulaDerEvaluator(const FormulaParser& fp);
/** Evaluate the derivatives from the FormulaParser wrt to all
* atoms in variables vector at the given AtomValues. The
* given loader is used for output. */
void eval(const AtomValues& av, FormulaDerEvalLoader& loader, int order);
/** Evaluate the derivatives from the FormulaParser wrt to a
* selection of atoms of the atoms in der_atoms vector at the
* given AtomValues. The selection is given by a monotone
* mapping to the indices (not values) of the der_atoms. */
void eval(const vector<int>& mp, const AtomValues& av, FormulaDerEvalLoader& loader,
int order);
};
};
#endif
// Local Variables:
// mode:C++
// End:
|