[GiNaC-list] Expression order changing

Sheplyakov Alexei varg at theor.jinr.ru
Tue Jun 5 15:37:55 CEST 2007


Hello!

On Mon, Jun 04, 2007 at 10:33:21AM +0200, Martin Sandve Alnæs wrote:
> Hi,
> when generating code from ginac expressions (with csrc), the order of
> the printed expressions will vary depending on previous operations in
> the program, in particular the previous construction of symbols. I
> understand this is because ginac uses an internal symbol id counter of
> some kind.
> 
> Is it possible to "reset" this counter, or somehow make sure a set of
> newly created symbols keep the same relative order independent of
> previous operations in the program?

You could create custom printing function (or print_context) which does
not care of symbol ordering. Here is what I use:

#include <iostream>
#include <ginac/ginac.h>
using namespace GiNaC;
#define unlikely(cond) __builtin_expect((cond), 0)

/**
 * @brief transform multivariate polynomial into an exmap
 * @param e exporession to operate on
 * @param l list of variables
 * @param do_expand -- expand expression before transforming it.
 * Set this to false if you know in advance that e is expanded w.r.t.
 * all variables in l. Useful for processing polynomials of the form
 * x_1^j_1...x_k^j_k(a_i+b_i+c_i...)^n_i.
 * BEWARE: if the initial expression is not expanded in variables 
 * listed in l, the result is complete nonsense.
 */
const exmap collect_vargs(const ex& e, const lst& l, const bool do_expand)
{
  static const ex _ex1(1);
  static const ex _ex0(0);
  ex x;
  exmap cmap = exmap();
  if (unlikely(!do_expand))
    x = e;
  else
    x = e.expand();

  if (unlikely(! is_a<add>(x))) {
    ex key = _ex1;
    ex pre_coeff = x;
    for (lst::const_iterator li=l.begin(); li!=l.end(); ++li) {
      int cexp = pre_coeff.degree(*li);
      pre_coeff = pre_coeff.coeff(*li, cexp);
      key *= pow(*li, cexp);
    }
    cmap.insert(exmap::value_type(key, pre_coeff));
  } else {

    for (const_iterator xi=x.begin(); xi!=x.end(); ++xi) {
      ex key = _ex1;
      ex pre_coeff = *xi;
      for (lst::const_iterator li=l.begin(); li!=l.end(); ++li) {
        int cexp = pre_coeff.degree(*li);
        pre_coeff = pre_coeff.coeff(*li, cexp);
        key *= pow(*li, cexp);
      }
      exmap::iterator ci = cmap.find(key);
      if (ci != cmap.end())
        ci->second += pre_coeff;
      else
        cmap.insert(exmap::value_type(key, pre_coeff));
    }
  }

  return cmap;
}

#define MARK \
  (std::cout << std::endl << __FUNCTION__ << ":" << __LINE__ << std::endl)

void example0() {
  MARK;
  symbol x("x"), y("y"), a("a"), b("b"), c("c");
  ex e = pow(x, 2)*a + x*y*pow(b+c, 10); 
  exmap em = collect_vargs(e, lst(x, y), false);
  // N.B. e is already expanded in x, y
  for (exmap::const_iterator ii=em.begin(); ii!=em.end(); ++ ii)
    std::cout << ii->first << " => " << ii->second << std::endl;
  // prints
  // x^2 => a
  // x*y => (a+b)^10
}

/**
 * @brief Print monomial in a human-readable form
 * @param l user-specified ordering, a list of expressions
 * @param e expression to print, should be a monomial (w.r.t. l)
 *
 */
inline void print_monomial_ordered(std::ostream& s, const ex& e, const lst& l)
{
  static const ex _ex1(1);
  if (! (is_a<mul>(e) || is_a<power>(e))) {
    s << e;
    return;
  }
  // split the monomial into a vector...
  exvector ev;
  ex cmp = _ex1;
  for (lst::const_iterator li=l.begin(); li!=l.end(); ++li) {
    int cexp = e.degree(*li);
    ex tmp = pow(*li, cexp);
    if (tmp.is_equal(_ex1))
      continue;

    ev.push_back(tmp);
    cmp *= tmp;
    // N.B.: vector will be automagically sorted. 

    if (cmp.is_equal(e))
      break;
  }

  // ...and print out the vector
  size_t nops = ev.size();
  size_t count = 0;
  for (exvector::const_iterator vi=ev.begin(); vi!=ev.end(); ++vi) {
    bool need_parens = false;
    if (is_a<add>(*vi))
      need_parens = true;

    if (need_parens)
      s << '(';
    s << *vi;
    if (need_parens)
      s << ')';

    ++count;
    if (count != nops) 
      s << '*';
  }
}

void example1() {
  MARK;
  symbol y("y"),z("z");
   // shuffle symbols
  for (unsigned i=0; i < 100; i++) {
    symbol* xxx = new symbol("xxx");
    // do something stupid
    xxx->set_name("foo");
    delete xxx;
  }
  symbol x("x");
  ex e = pow(x, 2)*pow(z,3)*y;
  print_monomial_ordered(std::cout, e, lst(x, y, z));
  // prints
  // x^2*y*z^3
  // not  y*z^3*x^2, z^3*x^2*y, etc.

  std::cout << std::endl;
}


/**
 * @brief print collected @var{add} object one term per line
 */
inline void print_by_one(std::ostream& s, const exmap& e, const lst& l) {
  for (exmap::const_iterator ei=e.begin(); ei != e.end(); ++ei)
  {
    s << "+(";
    print_monomial_ordered(s, ei->first, l);
    s << ")*(" << ei->second << ")" << std::endl;
  }
}

void example2() {
  MARK;
  symbol x("x"), y("y"), a("a"), b("b"), c("c");
  ex e = pow(x, 2)*a + x*y*pow(b+c, 10); 
  lst l(x, y);
  exmap em = collect_vargs(e, l, false);
  // N.B. e is already expanded in x, y, hense `false' argument 
  // to collect_vargs
  print_by_one(std::cout, em, l); 
  // prints
  //
  // +(x^2)*(a)
  // +(x*y)*((b+c)^10)
}

/**
 * @brief print multivariate polynomial one term per line
 *
 * The order of terms is still random, but expressions are *look* much
 * better. Pipe through sort(1) to get even better result.
 *
 * @param s destination 
 * @param e expression to print 
 * @param l list of variables
 * @param need_expand (default: true) -- expand expression before processing.
 * This is the safe choice, but sometimes it spoils nice properties of 
 * the input expression (e.g., partial factorization). Set to false only if
 * you know in advance that the expression (e) is already expanded (w.r.t. l).
 * You have been warned.
 *
 * @todo: find out how to determine this in a fast and reliable way.
 */
void print_add_by_one(std::ostream& s, const ex& e, const lst& l, 
    const bool need_expand=true)
{
  exmap em = collect_vargs(e, l, need_expand);
  // N.B.: .normal() makes coefficients much "simpler" (I know in
  // advance that some denominators in my expressions *must* cancel).
  // But in general case this might be just a waste of time. So don't
  // hesitate to comment out these 3 lines:
  for (exmap::iterator i=em.begin(); i!=em.end(); ++i)
    i->second = collect_common_factors(i->second.normal());

  print_by_one(s, em, l);
}

void example3() {
  MARK;
  symbol x("x"), y("y"), a("a"), b("b"), c("c");
  ex e = pow(x, 2)*a + x*y*pow(b+c, 10); 
  // N.B. It is simple to see that e is already expanded in x, y,
  // hense `false' argument to collect_vargs is safe.
  print_add_by_one(std::cout, e, lst(x,y), false);
  // prints
  // +(x^2)*(a)
  // +(x*y)*((b+c)^10)
}

int main(int argc, char** argv) {
  example0();
  example1();
  example2();
  example3();
  return 0;
}



Best regards,
 Alexei

-- 
All science is either physics or stamp collecting.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 827 bytes
Desc: Digital signature
Url : http://www.cebix.net/pipermail/ginac-list/attachments/20070605/3612bdf8/attachment.pgp


More information about the GiNaC-list mailing list