Next: , Previous: Methods and functions, Up: Methods and functions


5.1 Getting information about expressions

5.1.1 Checking expression types

Sometimes it's useful to check whether a given expression is a plain number, a sum, a polynomial with integer coefficients, or of some other specific type. GiNaC provides a couple of functions for this:

     bool is_a<T>(const ex & e);
     bool is_exactly_a<T>(const ex & e);
     bool ex::info(unsigned flag);
     unsigned ex::return_type() const;
     return_type_t ex::return_type_tinfo() const;

When the test made by is_a<T>() returns true, it is safe to call one of the functions ex_to<T>(), where T is one of the class names (See The class hierarchy, for a list of all classes). For example, assuming e is an ex:

     {
         ...
         if (is_a<numeric>(e))
             numeric n = ex_to<numeric>(e);
         ...
     }

is_a<T>(e) allows you to check whether the top-level object of an expression ‘e’ is an instance of the GiNaC class ‘T’ (See The class hierarchy, for a list of all classes). This is most useful, e.g., for checking whether an expression is a number, a sum, or a product:

     {
         symbol x("x");
         ex e1 = 42;
         ex e2 = 4*x - 3;
         is_a<numeric>(e1);  // true
         is_a<numeric>(e2);  // false
         is_a<add>(e1);      // false
         is_a<add>(e2);      // true
         is_a<mul>(e1);      // false
         is_a<mul>(e2);      // false
     }

In contrast, is_exactly_a<T>(e) allows you to check whether the top-level object of an expression ‘e’ is an instance of the GiNaC class ‘T’, not including parent classes.

The info() method is used for checking certain attributes of expressions. The possible values for the flag argument are defined in ginac/flags.h, the most important being explained in the following table:

Flag Returns true if the object is...
numeric ...a number (same as is_a<numeric>(...))
real ...a real number, symbol or constant (i.e. is not complex)
rational ...an exact rational number (integers are rational, too)
integer ...a (non-complex) integer
crational ...an exact (complex) rational number (such as 2/3+7/2*I)
cinteger ...a (complex) integer (such as 2-3*I)
positive ...not complex and greater than 0
negative ...not complex and less than 0
nonnegative ...not complex and greater than or equal to 0
posint ...an integer greater than 0
negint ...an integer less than 0
nonnegint ...an integer greater than or equal to 0
even ...an even integer
odd ...an odd integer
prime ...a prime integer (probabilistic primality test)
relation ...a relation (same as is_a<relational>(...))
relation_equal ...a == relation
relation_not_equal ...a != relation
relation_less ...a < relation
relation_less_or_equal ...a <= relation
relation_greater ...a > relation
relation_greater_or_equal ...a >= relation
symbol ...a symbol (same as is_a<symbol>(...))
list ...a list (same as is_a<lst>(...))
polynomial ...a polynomial (i.e. only consists of sums and products of numbers and symbols with positive integer powers)
integer_polynomial ...a polynomial with (non-complex) integer coefficients
cinteger_polynomial ...a polynomial with (possibly complex) integer coefficients (such as 2-3*I)
rational_polynomial ...a polynomial with (non-complex) rational coefficients
crational_polynomial ...a polynomial with (possibly complex) rational coefficients (such as 2/3+7/2*I)
rational_function ...a rational function (x+y, z/(x+y))
algebraic ...an algebraic object (sqrt(2), sqrt(x)-1)

To determine whether an expression is commutative or non-commutative and if so, with which other expressions it would commutate, you use the methods return_type() and return_type_tinfo(). See Non-commutative objects, for an explanation of these.

5.1.2 Accessing subexpressions

Many GiNaC classes, like add, mul, lst, and function, act as containers for subexpressions. For example, the subexpressions of a sum (an add object) are the individual terms, and the subexpressions of a function are the function's arguments.

GiNaC provides several ways of accessing subexpressions. The first way is to use the two methods

     size_t ex::nops();
     ex ex::op(size_t i);

nops() determines the number of subexpressions (operands) contained in the expression, while op(i) returns the i-th (0..nops()-1) subexpression. In the case of a power object, op(0) will return the basis and op(1) the exponent. For indexed objects, op(0) is the base expression and op(i), i>0 are the indices.

The second way to access subexpressions is via the STL-style random-access iterator class const_iterator and the methods

     const_iterator ex::begin();
     const_iterator ex::end();

begin() returns an iterator referring to the first subexpression; end() returns an iterator which is one-past the last subexpression. If the expression has no subexpressions, then begin() == end(). These iterators can also be used in conjunction with non-modifying STL algorithms.

Here is an example that (non-recursively) prints the subexpressions of a given expression in three different ways:

     {
         ex e = ...
     
         // with nops()/op()
         for (size_t i = 0; i != e.nops(); ++i)
             cout << e.op(i) << endl;
     
         // with iterators
         for (const_iterator i = e.begin(); i != e.end(); ++i)
             cout << *i << endl;
     
         // with iterators and STL copy()
         std::copy(e.begin(), e.end(), std::ostream_iterator<ex>(cout, "\n"));
     }

op()/nops() and const_iterator only access an expression's immediate children. GiNaC provides two additional iterator classes, const_preorder_iterator and const_postorder_iterator, that iterate over all objects in an expression tree, in preorder or postorder, respectively. They are STL-style forward iterators, and are created with the methods

     const_preorder_iterator ex::preorder_begin();
     const_preorder_iterator ex::preorder_end();
     const_postorder_iterator ex::postorder_begin();
     const_postorder_iterator ex::postorder_end();

The following example illustrates the differences between const_iterator, const_preorder_iterator, and const_postorder_iterator:

     {
         symbol A("A"), B("B"), C("C");
         ex e = lst(lst(A, B), C);
     
         std::copy(e.begin(), e.end(),
                   std::ostream_iterator<ex>(cout, "\n"));
         // {A,B}
         // C
     
         std::copy(e.preorder_begin(), e.preorder_end(),
                   std::ostream_iterator<ex>(cout, "\n"));
         // {{A,B},C}
         // {A,B}
         // A
         // B
         // C
     
         std::copy(e.postorder_begin(), e.postorder_end(),
                   std::ostream_iterator<ex>(cout, "\n"));
         // A
         // B
         // {A,B}
         // C
         // {{A,B},C}
     }

Finally, the left-hand side and right-hand side expressions of objects of class relational (and only of these) can also be accessed with the methods

     ex ex::lhs();
     ex ex::rhs();

5.1.3 Comparing expressions

Expressions can be compared with the usual C++ relational operators like ==, >, and < but if the expressions contain symbols, the result is usually not determinable and the result will be false, except in the case of the != operator. You should also be aware that GiNaC will only do the most trivial test for equality (subtracting both expressions), so something like (pow(x,2)+x)/x==x+1 will return false.

Actually, if you construct an expression like a == b, this will be represented by an object of the relational class (see Relations) which is not evaluated until (explicitly or implicitly) cast to a bool.

There are also two methods

     bool ex::is_equal(const ex & other);
     bool ex::is_zero();

for checking whether one expression is equal to another, or equal to zero, respectively. See also the method ex::is_zero_matrix(), see Matrices.

5.1.4 Ordering expressions

Sometimes it is necessary to establish a mathematically well-defined ordering on a set of arbitrary expressions, for example to use expressions as keys in a std::map<> container, or to bring a vector of expressions into a canonical order (which is done internally by GiNaC for sums and products).

The operators <, > etc. described in the last section cannot be used for this, as they don't implement an ordering relation in the mathematical sense. In particular, they are not guaranteed to be antisymmetric: if ‘a’ and ‘b’ are different expressions, and a < b yields false, then b < a doesn't necessarily yield true.

By default, STL classes and algorithms use the < and == operators to compare objects, which are unsuitable for expressions, but GiNaC provides two functors that can be supplied as proper binary comparison predicates to the STL:

     class ex_is_less : public std::binary_function<ex, ex, bool> {
     public:
         bool operator()(const ex &lh, const ex &rh) const;
     };
     
     class ex_is_equal : public std::binary_function<ex, ex, bool> {
     public:
         bool operator()(const ex &lh, const ex &rh) const;
     };

For example, to define a map that maps expressions to strings you have to use

     std::map<ex, std::string, ex_is_less> myMap;

Omitting the ex_is_less template parameter will introduce spurious bugs because the map operates improperly.

Other examples for the use of the functors:

     std::vector<ex> v;
     // fill vector
     ...
     
     // sort vector
     std::sort(v.begin(), v.end(), ex_is_less());
     
     // count the number of expressions equal to '1'
     unsigned num_ones = std::count_if(v.begin(), v.end(),
                                       std::bind2nd(ex_is_equal(), 1));

The implementation of ex_is_less uses the member function

     int ex::compare(const ex & other) const;

which returns 0 if *this and other are equal, -1 if *this sorts before other, and 1 if *this sorts after other.