manpagez: man pages & more
info ginac
Home | html | info | man
[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.5 Symbols

Symbolic indeterminates, or symbols for short, are for symbolic manipulation what atoms are for chemistry.

A typical symbol definition looks like this:

 
symbol x("x");

This definition actually contains three very different things:

Symbols have an explicit name, supplied as a string during construction, because in C++, variable names can't be used as values, and the C++ compiler throws them away during compilation.

It is possible to omit the symbol name in the definition:

 
symbol x;

In this case, GiNaC will assign the symbol an internal, unique name of the form symbolNNN. This won't affect the usability of the symbol but the output of your calculations will become more readable if you give your symbols sensible names (for intermediate expressions that are only used internally such anonymous symbols can be quite useful, however).

Now, here is one important property of GiNaC that differentiates it from other computer algebra programs you may have used: GiNaC does not use the names of symbols to tell them apart, but a (hidden) serial number that is unique for each newly created symbol object. If you want to use one and the same symbol in different places in your program, you must only create one symbol object and pass that around. If you create another symbol, even if it has the same name, GiNaC will treat it as a different indeterminate.

Observe:

 
ex f(int n)
{
    symbol x("x");
    return pow(x, n);
}

int main()
{
    symbol x("x");
    ex e = f(6);

    cout << e << endl;
     // prints "x^6" which looks right, but...

    cout << e.degree(x) << endl;
     // ...this doesn't work. The symbol "x" here is different from the one
     // in f() and in the expression returned by f(). Consequently, it
     // prints "0".
}

One possibility to ensure that f() and main() use the same symbol is to pass the symbol as an argument to f():

 
ex f(int n, const ex & x)
{
    return pow(x, n);
}

int main()
{
    symbol x("x");

    // Now, f() uses the same symbol.
    ex e = f(6, x);

    cout << e.degree(x) << endl;
     // prints "6", as expected
}

Another possibility would be to define a global symbol x that is used by both f() and main(). If you are using global symbols and multiple compilation units you must take special care, however. Suppose that you have a header file ‘globals.h’ in your program that defines a symbol x("x");. In this case, every unit that includes ‘globals.h’ would also get its own definition of x (because header files are just inlined into the source code by the C++ preprocessor), and hence you would again end up with multiple equally-named, but different, symbols. Instead, the ‘globals.h’ header should only contain a declaration like extern symbol x;, with the definition of x moved into a C++ source file such as ‘globals.cpp’.

A different approach to ensuring that symbols used in different parts of your program are identical is to create them with a factory function like this one:

 
const symbol & get_symbol(const string & s)
{
    static map<string, symbol> directory;
    map<string, symbol>::iterator i = directory.find(s);
    if (i != directory.end())
        return i->second;
    else
        return directory.insert(make_pair(s, symbol(s))).first->second;
}

This function returns one newly constructed symbol for each name that is passed in, and it returns the same symbol when called multiple times with the same name. Using this symbol factory, we can rewrite our example like this:

 
ex f(int n)
{
    return pow(get_symbol("x"), n);
}

int main()
{
    ex e = f(6);

    // Both calls of get_symbol("x") yield the same symbol.
    cout << e.degree(get_symbol("x")) << endl;
     // prints "6"
}

Instead of creating symbols from strings we could also have get_symbol() take, for example, an integer number as its argument. In this case, we would probably want to give the generated symbols names that include this number, which can be accomplished with the help of an ostringstream.

In general, if you're getting weird results from GiNaC such as an expression ‘x-x’ that is not simplified to zero, you should check your symbol definitions.

As we said, the names of symbols primarily serve for purposes of expression output. But there are actually two instances where GiNaC uses the names for identifying symbols: When constructing an expression from a string, and when recreating an expression from an archive (see section Input and output of expressions).

In addition to its name, a symbol may contain a special string that is used in LaTeX output:

 
symbol x("x", "\\Box");

This creates a symbol that is printed as "x" in normal output, but as "\Box" in LaTeX code (See section Input and output of expressions, for more information about the different output formats of expressions in GiNaC). GiNaC automatically creates proper LaTeX code for symbols having names of greek letters (‘alpha’, ‘mu’, etc.).

Symbols in GiNaC can't be assigned values. If you need to store results of calculations and give them a name, use C++ variables of type ex. If you want to replace a symbol in an expression with something else, you can invoke the expression's .subs() method (see section Substituting expressions).

By default, symbols are expected to stand in for complex values, i.e. they live in the complex domain. As a consequence, operations like complex conjugation, for example (see section Complex expressions), do not evaluate if applied to such symbols. Likewise log(exp(x)) does not evaluate to x, because of the unknown imaginary part of x. On the other hand, if you are sure that your symbols will hold only real values, you would like to have such functions evaluated. Therefore GiNaC allows you to specify the domain of the symbol. Instead of symbol x("x"); you can write realsymbol x("x"); to tell GiNaC that x stands in for real values.

Furthermore, it is also possible to declare a symbol as positive. This will, for instance, enable the automatic simplification of abs(x) into x. This is done by declaring the symbol as possymbol x("x");.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]
© manpagez.com 2000-2024
Individual documents may contain additional copyright information.