Next: , Previous: Solving linear systems of equations, Up: Methods and functions


5.15 Input and output of expressions

5.15.1 Expression output

Expressions can simply be written to any stream:

     {
         symbol x("x");
         ex e = 4.5*I+pow(x,2)*3/2;
         cout << e << endl;    // prints '4.5*I+3/2*x^2'
         // ...

The default output format is identical to the ginsh input syntax and to that used by most computer algebra systems, but not directly pastable into a GiNaC C++ program (note that in the above example, pow(x,2) is printed as ‘x^2’).

It is possible to print expressions in a number of different formats with a set of stream manipulators;

     std::ostream & dflt(std::ostream & os);
     std::ostream & latex(std::ostream & os);
     std::ostream & tree(std::ostream & os);
     std::ostream & csrc(std::ostream & os);
     std::ostream & csrc_float(std::ostream & os);
     std::ostream & csrc_double(std::ostream & os);
     std::ostream & csrc_cl_N(std::ostream & os);
     std::ostream & index_dimensions(std::ostream & os);
     std::ostream & no_index_dimensions(std::ostream & os);

The tree, latex and csrc formats are also available in ginsh via the print(), print_latex() and print_csrc() functions, respectively.

All manipulators affect the stream state permanently. To reset the output format to the default, use the dflt manipulator:

         // ...
         cout << latex;            // all output to cout will be in LaTeX format from
                                   // now on
         cout << e << endl;        // prints '4.5 i+\frac{3}{2} x^{2}'
         cout << sin(x/2) << endl; // prints '\sin(\frac{1}{2} x)'
         cout << dflt;             // revert to default output format
         cout << e << endl;        // prints '4.5*I+3/2*x^2'
         // ...

If you don't want to affect the format of the stream you're working with, you can output to a temporary ostringstream like this:

         // ...
         ostringstream s;
         s << latex << e;         // format of cout remains unchanged
         cout << s.str() << endl; // prints '4.5 i+\frac{3}{2} x^{2}'
         // ...

The csrc (an alias for csrc_double), csrc_float, csrc_double and csrc_cl_N manipulators set the output to a format that can be directly used in a C or C++ program. The three possible formats select the data types used for numbers (csrc_cl_N uses the classes provided by the CLN library):

         // ...
         cout << "f = " << csrc_float << e << ";\n";
         cout << "d = " << csrc_double << e << ";\n";
         cout << "n = " << csrc_cl_N << e << ";\n";
         // ...

The above example will produce (note the x^2 being converted to x*x):

     f = (3.0/2.0)*(x*x)+std::complex<float>(0.0,4.5000000e+00);
     d = (3.0/2.0)*(x*x)+std::complex<double>(0.0,4.5000000000000000e+00);
     n = cln::cl_RA("3/2")*(x*x)+cln::complex(cln::cl_I("0"),cln::cl_F("4.5_17"));

The tree manipulator allows dumping the internal structure of an expression for debugging purposes:

         // ...
         cout << tree << e;
     }

produces

     add, hash=0x0, flags=0x3, nops=2
         power, hash=0x0, flags=0x3, nops=2
             x (symbol), serial=0, hash=0xc8d5bcdd, flags=0xf
             2 (numeric), hash=0x6526b0fa, flags=0xf
         3/2 (numeric), hash=0xf9828fbd, flags=0xf
         -----
         overall_coeff
         4.5L0i (numeric), hash=0xa40a97e0, flags=0xf
         =====

The latex output format is for LaTeX parsing in mathematical mode. It is rather similar to the default format but provides some braces needed by LaTeX for delimiting boxes and also converts some common objects to conventional LaTeX names. It is possible to give symbols a special name for LaTeX output by supplying it as a second argument to the symbol constructor.

For example, the code snippet

     {
         symbol x("x", "\\circ");
         ex e = lgamma(x).series(x==0,3);
         cout << latex << e << endl;
     }

will print

         {(-\ln(\circ))}+{(-\gamma_E)} \circ+{(\frac{1}{12} \pi^{2})} \circ^{2}
         +\mathcal{O}(\circ^{3})

Index dimensions are normally hidden in the output. To make them visible, use the index_dimensions manipulator. The dimensions will be written in square brackets behind each index value in the default and LaTeX output formats:

     {
         symbol x("x"), y("y");
         varidx mu(symbol("mu"), 4), nu(symbol("nu"), 4);
         ex e = indexed(x, mu) * indexed(y, nu);
     
         cout << e << endl;
          // prints 'x~mu*y~nu'
         cout << index_dimensions << e << endl;
          // prints 'x~mu[4]*y~nu[4]'
         cout << no_index_dimensions << e << endl;
          // prints 'x~mu*y~nu'
     }

If you need any fancy special output format, e.g. for interfacing GiNaC with other algebra systems or for producing code for different programming languages, you can always traverse the expression tree yourself:

     static void my_print(const ex & e)
     {
         if (is_a<function>(e))
             cout << ex_to<function>(e).get_name();
         else
             cout << ex_to<basic>(e).class_name();
         cout << "(";
         size_t n = e.nops();
         if (n)
             for (size_t i=0; i<n; i++) {
                 my_print(e.op(i));
                 if (i != n-1)
                     cout << ",";
             }
         else
             cout << e;
         cout << ")";
     }
     
     int main()
     {
         my_print(pow(3, x) - 2 * sin(y / Pi)); cout << endl;
         return 0;
     }

This will produce

     add(power(numeric(3),symbol(x)),mul(sin(mul(power(constant(Pi),numeric(-1)),
     symbol(y))),numeric(-2)))

If you need an output format that makes it possible to accurately reconstruct an expression by feeding the output to a suitable parser or object factory, you should consider storing the expression in an archive object and reading the object properties from there. See the section on archiving for more information.

5.15.2 Expression input

GiNaC provides no way to directly read an expression from a stream because you will usually want the user to be able to enter something like ‘2*x+sin(y)’ and have the ‘x’ and ‘y’ correspond to the symbols x and y you defined in your program and there is no way to specify the desired symbols to the >> stream input operator.

Instead, GiNaC lets you read an expression from a stream or a string, specifying the mapping between the input strings and symbols to be used:

     {
         symbol x, y;
         symtab table;
         table["x"] = x;
         table["y"] = y;
         parser reader(table);
         ex e = reader("2*x+sin(y)");
     }

The input syntax is the same as that used by ginsh and the stream output operator <<. Matching between the input strings and expressions is given by ‘table’. The ‘table’ in this example instructs GiNaC to substitute any input substring “x” with symbol x. Likewise, the substring “y” will be replaced with symbol y. It's also possible to map input (sub)strings to arbitrary expressions:

     {
         symbol x, y;
         symtab table;
         table["x"] = x+log(y)+1;
         parser reader(table);
         ex e = reader("5*x^3 - x^2");
         // e = 5*(x+log(y)+1)^3 + (x+log(y)+1)^2
     }

If no mapping is specified for a particular string GiNaC will create a symbol with corresponding name. Later on you can obtain all parser generated symbols with get_syms() method:

     {
         parser reader;
         ex e = reader("2*x+sin(y)");
         symtab table = reader.get_syms();
         symbol x = reader["x"];
         symbol y = reader["y"];
     }

Sometimes you might want to prevent GiNaC from inserting these extra symbols (for example, you want treat an unexpected string in the input as an error).

     {
     	symtab table;
     	table["x"] = symbol();
     	parser reader(table);
     	parser.strict = true;
     	ex e;
     	try {
     		e = reader("2*x+sin(y)");
     	} catch (parse_error& err) {
     		cerr << err.what() << endl;
     		// prints "unknown symbol "y" in the input"
     	}
     }

With this parser, it's also easy to implement interactive GiNaC programs:

     #include <iostream>
     #include <string>
     #include <stdexcept>
     #include <ginac/ginac.h>
     using namespace std;
     using namespace GiNaC;
     
     int main()
     {
     	cout << "Enter an expression containing 'x': " << flush;
     	parser reader;
     
     	try {
     		ex e = reader(cin);
     		symtab table = reader.get_syms();
     		symbol x = table.find("x") != table.end() ?
     			   ex_to<symbol>(table["x"]) : symbol("x");
     		cout << "The derivative of " << e << " with respect to x is ";
     		cout << e.diff(x) << "." << endl;
     	} catch (exception &p) {
     		cerr << p.what() << endl;
     	}
     }

5.15.3 Compiling expressions to C function pointers

Numerical evaluation of algebraic expressions is seamlessly integrated into GiNaC by help of the CLN library. While CLN allows for very fast arbitrary precision numerics, which is more than sufficient for most users, sometimes only the speed of built-in floating point numbers is fast enough, e.g. for Monte Carlo integration. The only viable option then is the following: print the expression in C syntax format, manually add necessary C code, compile that program and run is as a separate application. This is not only cumbersome and involves a lot of manual intervention, but it also separates the algebraic and the numerical evaluation into different execution stages.

GiNaC offers a couple of functions that help to avoid these inconveniences and problems. The functions automatically perform the printing of a GiNaC expression and the subsequent compiling of its associated C code. The created object code is then dynamically linked to the currently running program. A function pointer to the C function that performs the numerical evaluation is returned and can be used instantly. This all happens automatically, no user intervention is needed.

The following example demonstrates the use of compile_ex:

         // ...
         symbol x("x");
         ex myexpr = sin(x) / x;
     
         FUNCP_1P fp;
         compile_ex(myexpr, x, fp);
     
         cout << fp(3.2) << endl;
         // ...

The function compile_ex is called with the expression to be compiled and its only free variable x. Upon successful completion the third parameter contains a valid function pointer to the corresponding C code module. If called like in the last line only built-in double precision numerics is involved.

The function pointer has to be defined in advance. GiNaC offers three function pointer types at the moment:

         typedef double (*FUNCP_1P) (double);
         typedef double (*FUNCP_2P) (double, double);
         typedef void (*FUNCP_CUBA) (const int*, const double[], const int*, double[]);

FUNCP_2P allows for two variables in the expression. FUNCP_CUBA is the correct type to be used with the CUBA library (http://www.feynarts.de/cuba) for numerical integrations. The details for the parameters of FUNCP_CUBA are explained in the CUBA manual.

For every function pointer type there is a matching compile_ex available:

         void compile_ex(const ex& expr, const symbol& sym, FUNCP_1P& fp,
                         const std::string filename = "");
         void compile_ex(const ex& expr, const symbol& sym1, const symbol& sym2,
                         FUNCP_2P& fp, const std::string filename = "");
         void compile_ex(const lst& exprs, const lst& syms, FUNCP_CUBA& fp,
                         const std::string filename = "");

When the last parameter filename is not supplied, compile_ex will choose a unique random name for the intermediate source and object files it produces. On program termination these files will be deleted. If one wishes to keep the C code and the object files, one can supply the filename parameter. The intermediate files will use that filename and will not be deleted.

link_ex is a function that allows to dynamically link an existing object file and to make it available via a function pointer. This is useful if you have already used compile_ex on an expression and want to avoid the compilation step to be performed over and over again when you restart your program. The precondition for this is of course, that you have chosen a filename when you did call compile_ex. For every above mentioned function pointer type there exists a corresponding link_ex function:

         void link_ex(const std::string filename, FUNCP_1P& fp);
         void link_ex(const std::string filename, FUNCP_2P& fp);
         void link_ex(const std::string filename, FUNCP_CUBA& fp);

The complete filename (including the suffix .so) of the object file has to be supplied.

The function

         void unlink_ex(const std::string filename);

is supplied for the rare cases when one wishes to close the dynamically linked object files directly and have the intermediate files (only if filename has not been given) deleted. Normally one doesn't need this function, because all the clean-up will be done automatically upon (regular) program termination.

All the described functions will throw an exception in case they cannot perform correctly, like for example when writing the file or starting the compiler fails. Since internally the same printing methods as described in section csrc printing are used, only functions and objects that are available in standard C will compile successfully (that excludes polylogarithms for example at the moment). Another precondition for success is, of course, that it must be possible to evaluate the expression numerically. No free variables despite the ones supplied to compile_ex should appear in the expression.

compile_ex uses the shell script ginac-excompiler to start the C compiler and produce the object files. This shell script comes with GiNaC and will be installed together with GiNaC in the configured $PREFIX/bin directory.

5.15.4 Archiving

GiNaC allows creating archives of expressions which can be stored to or retrieved from files. To create an archive, you declare an object of class archive and archive expressions in it, giving each expression a unique name:

     #include <fstream>
     using namespace std;
     #include <ginac/ginac.h>
     using namespace GiNaC;
     
     int main()
     {
         symbol x("x"), y("y"), z("z");
     
         ex foo = sin(x + 2*y) + 3*z + 41;
         ex bar = foo + 1;
     
         archive a;
         a.archive_ex(foo, "foo");
         a.archive_ex(bar, "the second one");
         // ...

The archive can then be written to a file:

         // ...
         ofstream out("foobar.gar");
         out << a;
         out.close();
         // ...

The file foobar.gar contains all information that is needed to reconstruct the expressions foo and bar.

The tool viewgar that comes with GiNaC can be used to view the contents of GiNaC archive files:

     $ viewgar foobar.gar
     foo = 41+sin(x+2*y)+3*z
     the second one = 42+sin(x+2*y)+3*z

The point of writing archive files is of course that they can later be read in again:

         // ...
         archive a2;
         ifstream in("foobar.gar");
         in >> a2;
         // ...

And the stored expressions can be retrieved by their name:

         // ...
         lst syms;
         syms = x, y;
     
         ex ex1 = a2.unarchive_ex(syms, "foo");
         ex ex2 = a2.unarchive_ex(syms, "the second one");
     
         cout << ex1 << endl;              // prints "41+sin(x+2*y)+3*z"
         cout << ex2 << endl;              // prints "42+sin(x+2*y)+3*z"
         cout << ex1.subs(x == 2) << endl; // prints "41+sin(2+2*y)+3*z"
     }

Note that you have to supply a list of the symbols which are to be inserted in the expressions. Symbols in archives are stored by their name only and if you don't specify which symbols you have, unarchiving the expression will create new symbols with that name. E.g. if you hadn't included x in the syms list above, the ex1.subs(x == 2) statement would have had no effect because the x in ex1 would have been a different symbol than the x which was defined at the beginning of the program, although both would appear as ‘x’ when printed.

You can also use the information stored in an archive object to output expressions in a format suitable for exact reconstruction. The archive and archive_node classes have a couple of member functions that let you access the stored properties:

     static void my_print2(const archive_node & n)
     {
         string class_name;
         n.find_string("class", class_name);
         cout << class_name << "(";
     
         archive_node::propinfovector p;
         n.get_properties(p);
     
         size_t num = p.size();
         for (size_t i=0; i<num; i++) {
             const string &name = p[i].name;
             if (name == "class")
                 continue;
             cout << name << "=";
     
             unsigned count = p[i].count;
             if (count > 1)
                 cout << "{";
     
             for (unsigned j=0; j<count; j++) {
                 switch (p[i].type) {
                     case archive_node::PTYPE_BOOL: {
                         bool x;
                         n.find_bool(name, x, j);
                         cout << (x ? "true" : "false");
                         break;
                     }
                     case archive_node::PTYPE_UNSIGNED: {
                         unsigned x;
                         n.find_unsigned(name, x, j);
                         cout << x;
                         break;
                     }
                     case archive_node::PTYPE_STRING: {
                         string x;
                         n.find_string(name, x, j);
                         cout << '\"' << x << '\"';
                         break;
                     }
                     case archive_node::PTYPE_NODE: {
                         const archive_node &x = n.find_ex_node(name, j);
                         my_print2(x);
                         break;
                     }
                 }
     
                 if (j != count-1)
                     cout << ",";
             }
     
             if (count > 1)
                 cout << "}";
     
             if (i != num-1)
                 cout << ",";
         }
     
         cout << ")";
     }
     
     int main()
     {
         ex e = pow(2, x) - y;
         archive ar(e, "e");
         my_print2(ar.get_top_node(0)); cout << endl;
         return 0;
     }

This will produce:

     add(rest={power(basis=numeric(number="2"),exponent=symbol(name="x")),
     symbol(name="y")},coeff={numeric(number="1"),numeric(number="-1")},
     overall_coeff=numeric(number="0"))

Be warned, however, that the set of properties and their meaning for each class may change between GiNaC versions.