dynare/dynare++/parser/cc/dynamic_atoms.hh

471 lines
18 KiB
C++

/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
#ifndef OGP_DYNAMIC_ATOMS_H
#define OGP_DYNAMIC_ATOMS_H
#include "formula_parser.hh"
#include <vector>
#include <map>
#include <set>
#include <string>
#include <limits>
#include <memory>
namespace ogp
{
using std::vector;
using std::map;
using std::set;
using std::string;
/** Class storing names. We will keep names of variables in
* various places, and all these pointers will point to one
* storage, which will be responsible for allocation and
* deallocation. The main function of the class is to allocate
* space for names, and return a pointer of the stored name if
* required. */
class NameStorage
{
protected:
/** Vector of names allocated, this is the storage. */
vector<string> name_store;
/** Map useful to quickly decide if the name is already
* allocated or not. */
set<string> name_set;
public:
/** Query for the name. If the name has been stored, it
* true, otherwise false. */
bool
query(const string &name) const
{
return name_set.find(name) != name_set.end();
}
/** Insert the name if it has not been inserted yet. */
void insert(string name);
int
num() const
{
return static_cast<int>(name_store.size());
}
const string &
get_name(int i) const
{
return name_store[i];
}
/** Debug print. */
void print() const;
};
class Constants : public AtomValues
{
public:
/** Type for a map mapping tree indices to double values. */
using Tconstantmap = map<int, double>;
using Tintintmap = map<int, int>;
protected:
/** Map mapping a tree index of a constant to its double value. */
Tconstantmap cmap;
public:
Constants() = default;
/** Copy constructor. */
Constants(const Constants &c)
: cmap(c.cmap), cinvmap(c.cinvmap)
{
}
/** Copy constructor registering the constants in the given
* tree. The mapping from old tree indices to new ones is
* traced in tmap. */
Constants(const Constants &c, OperationTree &otree, Tintintmap &tmap)
{
import_constants(c, otree, tmap);
}
/** Import constants registering their tree indices in the
* given tree. The mapping form old tree indices to new ones
* is traced in tmap. */
void import_constants(const Constants &c, OperationTree &otree, Tintintmap &tmap);
/** Implements AtomValues interface. This sets the values to
* the evaluation tree EvalTree. */
void setValues(EvalTree &et) const override;
/** This adds a constant with the given tree index. The
* constant must be checked previously and asserted that it
* does not exist. */
void add_constant(int t, double val);
/** Returns true if the tree index is either an hardwired
* constant (initial number OperationTree:num_constants in
* OperationTree) or the tree index is a registered constant
* by add_constant method. */
bool is_constant(int t) const;
double get_constant_value(int t) const;
/** Return -1 if the given string representation of a constant
* is not among the constants (double represenations). If it
* is, its tree index is returned. */
int check(const string &str) const;
/** Debug print. */
void print() const;
const Tconstantmap &
get_constantmap() const
{
return cmap;
}
private:
/** Inverse map to Tconstantmap. */
using Tconstantinvmap = map<double, int>;
/** This is an inverse map to cmap. This is only used for fast
* queries for the existing double constants in check
* method and add_constant. */
Tconstantinvmap cinvmap;
};
/** This class is a parent to Atoms classes which distinguish between
* constants (numerical literals), and variables with lags and
* leads. This abstraction does not distinguish between a parameter
* and a variable without lag or lead. In this sense, everything is a
* variable.*/
class DynamicAtoms : public Atoms, public Constants
{
public:
/** Definition of a type mapping lags to the indices of the variables. */
using Tlagmap = map<int, int>;
protected:
/** Definition of a type mapping names of the atoms to Tlagmap. */
using Tvarmap = map<string, Tlagmap>;
/** Definition of a type mapping indices of variables to the variable names. */
using Tindexmap = map<int, string>;
/** This is just a storage for variable names, since all other
* instances of a variable name just point to the memory
* allocated by this object. */
NameStorage varnames;
/** This is the map for variables. Each variable name is
* mapped to the Tlagmap, which maps lags/leads to the nulary
* term indices in the tree. */
Tvarmap vars;
/** This is almost inverse map to the vars. It maps variable
* indices to the names. A returned name can be in turn used
* as a key in vars. */
Tindexmap indices;
/** Number of variables. */
int nv{0};
/** Minimum lag, if there is at least one lag, than this is a negative number. */
int minlag{std::numeric_limits<int>::max()};
/** Maximum lead, if there is at least one lead, than this is a positive number. */
int maxlead{std::numeric_limits<int>::min()};
public:
/** Construct empty DynamicAtoms. */
DynamicAtoms() = default;
/** Check the nulary term identified by its string
* representation. The nulary term can be either a constant or
* a variable. If constant, -1 is returned so that it could be
* assigned regardless if the same constant has already
* appeared or not. If variable, then -1 is returned only if
* the variable has not been assigned an index, otherwise the
* assigned index is returned. */
int check(const string &name) const override;
/** Assign the nulary term identified by its string
* representation. This method should be called when check()
* returns -1. */
void assign(const string &name, int t) override;
/** Return a number of all variables. */
int
nvar() const override
{
return nv;
}
/** Return the vector of variable indices. */
vector<int> variables() const override;
/** Return max lead and min lag for a variable given by the
* index. If a variable cannot be found, the method retursn
* the smallest integer as maxlead and the largest integer as
* minlag. */
void varspan(int t, int &mlead, int &mlag) const;
/** Return max lead and min lag for a variable given by the
* name (without lead, lag). The same is valid if the variable
* name cannot be found. */
void varspan(const string &name, int &mlead, int &mlag) const;
/** Return max lead and min lag for a vector of variables given by the names. */
void varspan(const vector<string> &names, int &mlead, int &mlag) const;
/** Return true for all tree indices corresponding to a
* variable in the sense of this class. (This is parameters,
* exo and endo). Since the semantics of 'variable' will be
* changed in subclasses, we use name 'named atom'. These are
* all atoms but constants. */
bool is_named_atom(int t) const;
/** Return index of the variable described by the variable
* name and lag/lead. If it doesn't exist, return -1. */
int index(const string &name, int ll) const;
/** Return true if a variable is referenced, i.e. it has lag
* map. */
bool is_referenced(const string &name) const;
/** Return the lag map for the variable name. */
const Tlagmap &lagmap(const string &name) const;
/** Return the variable name for the tree index. It throws an
* exception if the tree index t is not a named atom. */
const string &name(int t) const;
/** Return the lead/lag for the tree index. It throws an
* exception if the tree index t is not a named atom. */
int lead(int t) const;
/** Return maximum lead. */
int
get_maxlead() const
{
return maxlead;
}
/** Return minimum lag. */
int
get_minlag() const
{
return minlag;
}
/** Return the name storage to allow querying to other
* classes. */
const NameStorage &
get_name_storage() const
{
return varnames;
}
/** Assign the variable with a given lead. The varname must be
* from the varnames storage. The method checks if the
* variable iwht the given lead/lag is not assigned. If so, an
* exception is thrown. */
void assign_variable(const string &varname, int ll, int t);
/** Unassign the variable with a given lead and given tree
* index. The tree index is only provided as a check. An
* exception is thrown if the name, ll, and the tree index t
* are not consistent. The method also updates nv, indices,
* maxlead and minlag. The varname must be from the varnames
* storage. */
void unassign_variable(const string &varname, int ll, int t);
/** Debug print. */
void print() const override;
protected:
/** Do the check for the variable. A subclass may need to
* reimplement this so that it could raise an error if the
* variable is not among a given list. */
virtual int check_variable(const string &name) const;
/** Assign the constant. */
void assign_constant(const string &name, int t);
/** Assign the variable. */
void assign_variable(const string &name, int t);
/** The method just updates minlag or/and maxlead. Note that
* when assigning variables, the update is done when inserting
* to the maps, however, if removing a variable, we need to
* call this method. */
void update_minmaxll();
/** The method parses the string to recover a variable name
* and lag/lead ll. The variable name doesn't contain a lead/lag. */
virtual void parse_variable(const string &in, string &out, int &ll) const = 0;
public:
/** Return true if the str represents a double.*/
static bool is_string_constant(const string &str);
};
/** This class is a parent of all orderings of the dynamic atoms
* of variables which can appear before t, at t, or after t. It
* encapsulates the ordering, and the information about the number
* of static (appearing only at time t) predetermined (appearing
* before t and possibly at t), both (appearing before t and after
* t and possibly at t) and forward looking (appearing after t and
* possibly at t).
*
* The constructor takes a list of variable names. The class also
* provides mapping from the ordering of the variables in the list
* (outer) to the new ordering (at time t) and back.
*
* The user of the subclass must call do_ordering() after
* initialization.
*
* The class contains a few preimplemented methods for
* ordering. The class is used in this way: Make a subclass, and
* implement pure virtual do_ordering() by just plugging a
* preimplemented method, or plugging your own implementation. The
* method do_ordering() is called by the user after the constructor.
*/
class VarOrdering
{
protected:
/** Number of static variables. */
int n_stat;
/** Number of predetermined variables. */
int n_pred;
/** Number of both variables. */
int n_both;
/** Number of forward looking variables. */
int n_forw;
/** This is a set of tree indices corresponding to the
* variables at all times as they occur in the formulas. In
* fact, since this is used only for derivatives, the ordering
* of this vector is only important for ordering of the
* derivatives, in other contexts the ordering is not
* important, so it is rather a set of indices.*/
vector<int> der_atoms;
/** This maps tree index of the variable to the position in
* the row of the ordering. One should be careful with making
* space in the positions for variables not appearing at time
* t. For instance in the pred(t-1), both(t-1), stat(t),
* pred(t), both(t), forw(t), both(t+1), forw(t+1) ordering,
* the variables x(t-1), y(t-1), x(t+1), z(t-1), z(t), and
* z(t+1) having tree indices 6,5,4,3,2,1 will be ordered as
* follows: y(t-1), x(t-1), z(t-1), [y(t)], [x(t)], z(t),
* x(t+1), where a bracketed expresion means non-existent by
* occupying a space. The map thus will look as follows:
* {5→0, 6→1, 3→2, 2→5, 3→6}. Note that nothing is mapped
* to positions 3 and 4. */
map<int, int> positions;
/** This maps an ordering of the list of variables in
* constructor to the new ordering (at time t). The length is
* the number of variables. */
vector<int> outer2y;
/** This maps a new ordering to the ordering of the list of
* variables in constructor (at time t). The length is the
* number of variables. */
vector<int> y2outer;
/** This is just a reference for variable names to keep it
* from constructor to do_ordering() implementations. */
const vector<string> &varnames;
/** This is just a reference to atoms to keep it from
* constructor to do_ordering() implementations. */
const DynamicAtoms &atoms;
public:
/** This is an enum type for an ordering type implemented by
* do_general. */
enum ord_type { pbspbfbf, bfspbfpb };
/** Construct the ordering of the variables given by the names
* with their dynamic occurrences defined by the atoms. It
* calls the virtual method do_ordering which can be
* reimplemented. */
VarOrdering(const vector<string> &vnames, const DynamicAtoms &a)
: n_stat(0), n_pred(0), n_both(0), n_forw(0), varnames(vnames), atoms(a)
{
}
VarOrdering(const VarOrdering &vo, const vector<string> &vnames,
const DynamicAtoms &a);
VarOrdering(const VarOrdering &vo) = delete;
virtual std::unique_ptr<VarOrdering> clone(const vector<string> &vnames,
const DynamicAtoms &a) const = 0;
/** Destructor does nothing here. */
virtual ~VarOrdering() = default;
/** This is the method setting the ordering and the map. A
* subclass must reimplement it, possibly using a
* preimplemented ordering. This method must be called by the
* user after the class has been created. */
virtual void do_ordering() = 0;
/** Return number of static. */
int
nstat() const
{
return n_stat;
}
/** Return number of predetermined. */
int
npred() const
{
return n_pred;
}
/** Return number of both. */
int
nboth() const
{
return n_both;
}
/** Return number of forward looking. */
int
nforw() const
{
return n_forw;
}
/** Return the set of tree indices for derivatives. */
const vector<int> &
get_der_atoms() const
{
return der_atoms;
}
/** Return the y2outer. */
const vector<int> &
get_y2outer() const
{
return y2outer;
}
/** Return the outer2y. */
const vector<int> &
get_outer2y() const
{
return outer2y;
}
/** Query the atom given by the tree index. True is returned
* if the atom is one of the variables in the object. */
bool check(int t) const;
/** Return the position of the atom (nulary term) given by a
* tree index. It is a lookup to the map. If the atom cannot
* be found, the exception is raised. */
int get_pos_of(int t) const;
/** This returns a length of ordered row of atoms. In all
* cases so far, it does not depend on the ordering and it is
* as follows. */
int
length() const
{
return n_stat+2*n_pred+3*n_both+2*n_forw;
}
/** Debug print. */
void print() const;
protected:
/** This is a general ordering method which orders the
* variables by the given ordering ord_type. See documentation
* for respective do_ methods. */
void do_general(ord_type ordering);
/** This is a preimplemented ordering for do_ordering()
* method. It assumes that the variables appear only at time
* t-1, t, t+1. It orders the atoms as pred(t-1), both(t-1),
* stat(t), pred(t), both(t), forw(t), both(t+1),
* forw(t+1). It builds the der_atoms, the map of positions,
* as well as y2outer and outer2y. */
void
do_pbspbfbf()
{
do_general(pbspbfbf);
}
/** This is a preimplemented ordering for do_ordering()
* method. It assumes that the variables appear only at time
* t-1, t, t+1. It orders the atoms as both(t+1), forw(t+1),
* stat(t), pred(t), both(t), forw(t), pred(t-1),
* both(t-1). It builds the der_atoms, the map of positions,
* as well as y2outer and outer2y. */
void
do_bfspbfpb()
{
do_general(bfspbfpb);
}
/** This is a preimplemented ordering for do_ordering()
* method. It makes no assumptions about occurences of
* variables at different times. It orders the atoms with
* increasing time keeping the given ordering within one
* time. This implies that y2outer and outer2y will be
* identities. The der_atoms will be just a sequence of atoms
* from the least to the most time preserving the order of atoms
* within one time. */
void do_increasing_time();
};
};
#endif