/** *************************************************************************** * @file GinacPrint.cc * * Source file for a class that prints GiNaC expressions in a form that * is efficient to compile and efficient to run. The main work this * class does is identifying shared sub-expressions to generate code * that only computes them once. * * See GinacPrint.hh for details on usage. * * Copyright 2010 * * National Robotics Engineering Center, Carnegie Mellon University * 10 40th Street, Pittsburgh, PA 15201 * www.rec.ri.cmu.edu * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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. * * This notice must appear in all copies of this file and its derivatives. * * History of Significant Contributions (don't put commit logs here): * 2010-05-20 Doug Baker, cape1232@yahoo.com: Created. * *************************************************************************** */ #include #include #include "GinacPrint.hh" namespace nrec { // Implementation note. GiNaC expressions do not have unique // identifiers. (ex::gethash() is not unique.) Therefore, // there is no way to know if we have seen an expression before // until we print it and look at what we printed. This means // that we have to traverse the expression tree for every // expression every time we see it. // // So this code is very inefficient. However, it is still valuable // since it results in being able to output C++ code that is // efficient. This code could be much improved and sped up if we // had unique identifiers for nodes in the expression tree. // // The author used this on a problem for which GiNaC would print // about 50MB of C++ code. GinacPrint takes on the order of 5 // minutes to print the expressions for that problem. When I tried // using ex::gethash() as the unique identifier for each expression // node (which is wrong, but comes very close to being correct), it // was so fast as to be almost instantaneous. void GinacPrint:: parse(ex const& e) { for (const_postorder_iterator it = e.postorder_begin(); it != e.postorder_end(); it++) { std::string expression = this->print(*it); if (m_symbols[expression] == "") { // If we don't have a symbol for this expression yet, make one. std::string newSymbol = this->genSym(); m_symbols[expression] = newSymbol; m_declarations.push_back(std::make_pair(newSymbol, expression)); } } } std::string GinacPrint:: symbol(ex const& e) { std::string expression = this->print(e); return m_symbols[expression]; } void GinacPrint:: setType(const std::string &newType) { m_datatype=(newType.empty())?"double":newType; } const std::string & GinacPrint:: setType() const { return m_datatype; } std::string GinacPrint:: declarations() { std::ostringstream dStream; for (size_t dd=0; dd< m_declarations.size(); dd++) { dStream << " " << m_datatype.c_str() << " " << m_declarations[dd].first << " = " << m_declarations[dd].second << ";" << std::endl; } return dStream.str(); } std::string GinacPrint:: genSym() { std::ostringstream s; s << "e" << m_serialNumber++; return s.str(); } // This prints the symbols for the operands for the expression e, // separated by the given operator, op. // YOU MUST HAVE ALREADY PARSED EXPRESSION e, or else this->symbol() // will return "" for any sub-expression that hasn't yet been parsed. std::string GinacPrint:: printOps(ex const& e, std::string const& op) { std::ostringstream stream; size_t n = e.nops(); if (n) { for (size_t i=0; isymbol(e.op(i)); if (i != n-1) stream << op; } } else { stream << e; } return stream.str(); } // This can only be used on an expression that has already been // parsed, or on a leaf expression. std::string GinacPrint:: print(ex const& e) { std::ostringstream stream; std::string name; if (is_a(e)) name = ex_to(e).get_name(); else name = ex_to(e).class_name(); // For symbols and numbers, just print them directly. if (name == "numeric") stream << e; else if (name == "symbol") stream << e; // For infix operations, print the operands separated by the appropriate // operator. else if (name == "add") stream << printOps(e, "+"); else if (name == "mul") stream << printOps(e, "*"); // For functions that don't use the same name as C/C++, // handle them specifically. else if (name == "power") stream << "pow(" << printOps(e, ",") << ")"; // For all other functions, just use the given name. // If you run across a function whose name differs from the C/C++ one // you will get a compilation error, and will need to add a special // case like "power" above. else stream << name << "(" << printOps(e, ",") << ")"; return stream.str(); } } // end namespace nrec