// 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 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 tder; /** Vector of multiindices corresponding to the vector of * derivatives. */ vector 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 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& 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 formulas; /** The vector to derivatives, each vector corresponds to a * formula in the vector formulas. */ vector 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& 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& 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& 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& 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& 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 terms; public: /** Construct from FormulaParser and given list of terms. */ FormulaCustomEvaluator(const FormulaParser& fp, const vector& ts) : etree(fp.otree), terms(ts) {} /** Construct from OperationTree and given list of terms. */ FormulaCustomEvaluator(const OperationTree& ot, const vector& 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& 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 ders; /** A copy of tree indices corresponding to atoms to with * respect the derivatives were taken. */ vector 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& mp, const AtomValues& av, FormulaDerEvalLoader& loader, int order); }; }; #endif // Local Variables: // mode:C++ // End: