// Copyright (C) 2005, Ondra Kamenik // $Id: formula_parser.cpp 2268 2008-11-22 10:38:03Z michel $ #include "utils/cc/pascal_triangle.h" #include "utils/cc/exception.h" #include "parser_exception.h" #include "location.h" #include "formula_parser.h" #include "formula_tab.hh" #include using namespace ogp; extern location_type fmla_lloc; FormulaParser::FormulaParser(const FormulaParser& fp, Atoms& a) : otree(fp.otree), atoms(a), formulas(fp.formulas), ders() { // create derivatives for (unsigned int i = 0; i < fp.ders.size(); i++) ders.push_back(new FormulaDerivatives(*(fp.ders[i]))); } FormulaParser::~FormulaParser() { destroy_derivatives(); } void FormulaParser::differentiate(int max_order) { destroy_derivatives(); vector vars; vars = atoms.variables(); for (unsigned int i = 0; i < formulas.size(); i++) ders.push_back(new FormulaDerivatives(otree, vars, formulas[i], max_order)); } const FormulaDerivatives& FormulaParser::derivatives(int i) const { if (i < (int)ders.size()) return *(ders[i]); else throw ogu::Exception(__FILE__,__LINE__, "Wrong formula index in FormulaParser::derivatives"); return *(ders[0]); // just because of compiler } void FormulaParser::add_formula(int t) { formulas.push_back(t); } int FormulaParser::add_binary(code_t code, int t1, int t2) { return otree.add_binary(code, t1, t2); } int FormulaParser::add_unary(code_t code, int t) { return otree.add_unary(code, t); } int FormulaParser::add_nulary(const char* str) { int t = -1; try { t = atoms.check(str); } catch (const ParserException& e) { throw ParserException(e, fmla_lloc.off); } if (t == -1) { t = otree.add_nulary(); atoms.assign(str, t); } return t; } void FormulaParser::add_subst_formulas(const map& subst, const FormulaParser& fp) { for (int i = 0; i < fp.nformulas(); i++) { int f = add_substitution(fp.formula(i), subst, fp); add_formula(f); } } void FormulaParser::substitute_formulas(const map& smap) { for (int i = 0; i < nformulas(); i++) { // make substitution and replace the formula for it int f = add_substitution(formulas[i], smap); formulas[i] = f; // update the derivatives if any if (i < (int)ders.size() && ders[i]) { int order = ders[i]->get_order(); delete ders[i]; ders[i] = new FormulaDerivatives(otree, atoms.variables(), formulas[i], order); } } } /** Global symbols for passing info to parser. */ FormulaParser* fparser; /** The declarations of functions defined in formula_ll.cc and * formula_tab.cc generated from formula.lex and formula.y */ void* fmla__scan_buffer(char*, size_t); void fmla__destroy_buffer(void*); int fmla_parse(); extern location_type fmla_lloc; /** This makes own copy of provided data, sets the buffer for the * parser with fmla_scan_buffer, and launches fmla_parse(). Note that * the pointer returned from fmla_scan_buffer must be freed at the * end. */ void FormulaParser::parse(int length, const char* stream) { char* buffer = new char[length+2]; strncpy(buffer, stream, length); buffer[length] = '\0'; buffer[length+1] = '\0'; fmla_lloc.off = 0; fmla_lloc.ll = 0; void* p = fmla__scan_buffer(buffer, (unsigned int)length+2); fparser = this; fmla_parse(); delete [] buffer; fmla__destroy_buffer(p); } void FormulaParser::error(const char* mes) const { throw ParserException(mes, fmla_lloc.off); } int FormulaParser::last_formula() const { int res = -1; for (unsigned int i = 0; i < formulas.size(); i++) if (res < formulas[i]) res = formulas[i]; return std::max(res, otree.get_last_nulary()); } int FormulaParser::pop_last_formula() { if (formulas.size() == 0) return -1; int t = formulas.back(); if (formulas.size() == ders.size()) { delete ders.back(); ders.pop_back(); } formulas.pop_back(); return t; } void FormulaParser::print() const { atoms.print(); for (unsigned int i = 0; i < formulas.size(); i++) { printf("formula %d:\n", formulas[i]); otree.print_operation(formulas[i]); } for (unsigned int i = 0; i < ders.size(); i++) { printf("derivatives for the formula %d:\n", formulas[i]); ders[i]->print(otree); } } void FormulaParser::destroy_derivatives() { while (ders.size() > 0) { delete ders.back(); ders.pop_back(); } } /** This constructor makes a vector of indices for formulas * corresponding to derivatives of the given formula. The formula is * supposed to belong to the provided tree, the created derivatives * are added to the tree. * * The algorithm is as follows. todo: update description of the * algorithm */ FormulaDerivatives::FormulaDerivatives(OperationTree& otree, const vector& vars, int f, int max_order) : nvar(vars.size()), order(max_order) { FoldMultiIndex fmi_zero(nvar); tder.push_back(f); indices.push_back(fmi_zero); unsigned int last_order_beg = 0; unsigned int last_order_end = tder.size(); for (int k = 1; k <= order; k++) { // interval = last_order_end) break; } // build ind2der map for (unsigned int i = 0; i < indices.size(); i++) ind2der.insert(Tfmiintmap::value_type(indices[i], i)); } FormulaDerivatives::FormulaDerivatives(const FormulaDerivatives& fd) : tder(fd.tder), indices(fd.indices), ind2der(fd.ind2der), nvar(fd.nvar), order(fd.order) { } int FormulaDerivatives::derivative(const FoldMultiIndex& mi) const { if (mi.order() > order) throw ogu::Exception(__FILE__,__LINE__, "Wrong order of multi-index in FormulaDerivatives::derivative"); if (mi.nv() != nvar) throw ogu::Exception(__FILE__,__LINE__, "Wrong multi-index variables in FormulaDerivatives::derivative"); Tfmiintmap::const_iterator it = ind2der.find(mi); if (it == ind2der.end()) return OperationTree::zero; else return tder[(*it).second]; } void FormulaDerivatives::print(const OperationTree& otree) const { for (Tfmiintmap::const_iterator it = ind2der.begin(); it != ind2der.end(); ++it) { printf("derivative "); (*it).first.print(); printf(" is formula %d\n", tder[(*it).second]); otree.print_operation(tder[(*it).second]); } } void FormulaCustomEvaluator::eval(const AtomValues& av, FormulaEvalLoader& loader) { etree.reset_all(); av.setValues(etree); for (unsigned int i = 0; i < terms.size(); i++) { double res = etree.eval(terms[i]); loader.load((int)i, res); } } FoldMultiIndex::FoldMultiIndex(int nv) : nvar(nv), ord(0), data(new int[ord]) { } FoldMultiIndex::FoldMultiIndex(int nv, int ordd, int ii) : nvar(nv), ord(ordd), data(new int[ord]) { for (int i = 0; i < ord; i++) data[i] = ii; } /** Note that a monotone sequence mapped by monotone mapping yields a * monotone sequence. */ FoldMultiIndex::FoldMultiIndex(int nv, const FoldMultiIndex& mi, const vector& mp) : nvar(nv), ord(mi.ord), data(new int[ord]) { for (int i = 0; i < ord; i++) { if (i < ord-1 && mp[i+1] < mp[i]) throw ogu::Exception(__FILE__,__LINE__, "Mapping not monotone in FoldMultiIndex constructor"); if (mp[mi[i]] >= nv || mp[mi[i]] < 0) throw ogu::Exception(__FILE__,__LINE__, "Mapping out of bounds in FoldMultiIndex constructor"); data[i] = mp[mi[i]]; } } FoldMultiIndex::FoldMultiIndex(const FoldMultiIndex& fmi, int new_orders) : nvar(fmi.nvar), ord(fmi.ord+new_orders), data(new int[ord]) { memcpy(data, fmi.data, fmi.ord*sizeof(int)); int new_item = (fmi.ord > 0)? fmi.data[fmi.ord-1] : 0; for (int i = fmi.ord; i < ord; i++) { data[i] = new_item; } } FoldMultiIndex::FoldMultiIndex(const FoldMultiIndex& fmi) : nvar(fmi.nvar), ord(fmi.ord), data(new int[fmi.ord]) { memcpy(data, fmi.data, ord*sizeof(int)); } const FoldMultiIndex& FoldMultiIndex::operator=(const FoldMultiIndex& fmi) { if (ord != fmi.ord) { delete [] data; data = new int[fmi.ord]; } ord = fmi.ord; nvar = fmi.nvar; memcpy(data, fmi.data, ord*sizeof(int)); return *this; } bool FoldMultiIndex::operator<(const FoldMultiIndex& fmi) const { if (nvar != fmi.nvar) ogu::Exception(__FILE__,__LINE__, "Different nvar in FoldMultiIndex::operator<"); if (ord < fmi.ord) return true; if (ord > fmi.ord) return false; int i = 0; while (i < ord && data[i] == fmi.data[i]) i++; if (i == ord) return false; else return data[i] < fmi.data[i]; } bool FoldMultiIndex::operator==(const FoldMultiIndex& fmi) const { bool res = true; res = res && (nvar == fmi.nvar) && (ord == fmi.ord); if (res) for (int i = 0; i < ord; i++) if (data[i] != fmi.data[i]) return false; return res; } void FoldMultiIndex::increment() { if (ord == 0) return; int k = ord-1; data[k]++; while (k > 0 && data[k] == nvar) { data[k] = 0; data[--k]++; } for (int kk = 1; kk < ord; kk++) if (data[kk-1] > data[kk]) data[kk] = data[kk-1]; } // For description of an algorithm for calculation of folded offset, // see Tensor Library Documentation, Ondra Kamenik, 2005, description // of FTensor::getOffsetRecurse(). int FoldMultiIndex::offset() const { // make copy for the recursions int* tmp = new int[ord]; for (int i = 0; i < ord; i++) tmp[i] = data[i]; // call the recursive algorithm int res = offset_recurse(tmp, ord, nvar); delete [] tmp; return res; } void FoldMultiIndex::print() const { printf("["); for (int i = 0; i < ord; i++) printf("%d ", data[i]); printf("]"); } int FoldMultiIndex::offset_recurse(int* data, int len, int nv) { if (len == 0) return 0; // calculate length of initial constant indices int prefix = 1; while (prefix < len && data[0] == data[prefix]) prefix++; int m = data[0]; int s1 = ptriang.noverk(nv+len-1, len) - ptriang.noverk(nv-m+len-1,len); // cancel m from the rest of the sequence for (int i = prefix; i < len; i++) data[i] -= m; // calculate offset of the remaining sequence int s2 = offset_recurse(data+prefix, len-prefix, nv-m); // return the sum return s1+s2; } bool ltfmi::operator()(const FoldMultiIndex& i1, const FoldMultiIndex& i2) const { return i1 < i2; } FormulaDerEvaluator::FormulaDerEvaluator(const FormulaParser& fp) : etree(fp.otree, -1) { for (unsigned int i = 0; i < fp.ders.size(); i++) ders.push_back((const FormulaDerivatives*)(fp.ders[i])); der_atoms = fp.atoms.variables(); } void FormulaDerEvaluator::eval(const AtomValues& av, FormulaDerEvalLoader& loader, int order) { if (ders.size() == 0) return; int maxorder = ders[0]->order; if (order > maxorder) throw ogu::Exception(__FILE__,__LINE__, "Wrong order in FormulaDerEvaluator::eval"); etree.reset_all(); av.setValues(etree); int* vars = new int[order]; for (unsigned int i = 0; i < ders.size(); i++) { for (FormulaDerivatives::Tfmiintmap::const_iterator it = ders[i]->ind2der.begin(); it != ders[i]->ind2der.end(); ++it) { const FoldMultiIndex& mi = (*it).first; if (mi.order() == order) { // set vars from multiindex mi and variables for (int k = 0; k < order; k++) vars[k] = der_atoms[mi[k]]; // evaluate double res = etree.eval(ders[i]->tder[(*it).second]); // load loader.load(i, order, vars, res); } } } delete [] vars; } void FormulaDerEvaluator::eval(const vector& mp, const AtomValues& av, FormulaDerEvalLoader& loader, int order) { etree.reset_all(); av.setValues(etree); int nvar_glob = der_atoms.size(); int nvar = mp.size(); int* vars = new int[order]; for (unsigned int i = 0; i < ders.size(); i++) { FoldMultiIndex mi(nvar, order); do { // find index of the derivative in the tensor FoldMultiIndex mi_glob(nvar_glob, mi, mp); int der = ders[i]->derivative(mi_glob); if (der != OperationTree::zero) { // set vars from the global multiindex for (int k = 0; k < order; k++) vars[k] = der_atoms[mi_glob[k]]; // evaluate derivative double res = etree.eval(der); // load loader.load(i, order, vars, res); } mi.increment(); } while (! mi.past_the_end()); } delete [] vars; } // Local Variables: // mode:C++ // End: