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.
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;
}
}
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.
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.