]> www.ginac.de Git - ginac.git/blobdiff - ginac/indexed.cpp
dummy index renamer ignores index dimensions
[ginac.git] / ginac / indexed.cpp
index dcd6d9510d6d0ac688802373f30f7d93e0f07362..5efd3083adc7d020162b265c2dad8130713d49b2 100644 (file)
@@ -3,7 +3,7 @@
  *  Implementation of GiNaC's indexed expressions. */
 
 /*
- *  GiNaC Copyright (C) 1999-2002 Johannes Gutenberg University Mainz, Germany
+ *  GiNaC Copyright (C) 1999-2003 Johannes Gutenberg University Mainz, Germany
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
  */
 
 #include <iostream>
+#include <sstream>
 #include <stdexcept>
 
 #include "indexed.h"
@@ -179,7 +180,7 @@ void indexed::print(const print_context & c, unsigned level) const
 {
        GINAC_ASSERT(seq.size() > 0);
 
-       if (is_of_type(c, print_tree)) {
+       if (is_a<print_tree>(c)) {
 
                c.s << std::string(level, ' ') << class_name()
                    << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
@@ -191,21 +192,19 @@ void indexed::print(const print_context & c, unsigned level) const
 
        } else {
 
-               bool is_tex = is_of_type(c, print_latex);
+               bool is_tex = is_a<print_latex>(c);
                const ex & base = seq[0];
-               bool need_parens = is_ex_exactly_of_type(base, add) || is_ex_exactly_of_type(base, mul)
-                               || is_ex_exactly_of_type(base, ncmul) || is_ex_exactly_of_type(base, power)
-                               || is_ex_of_type(base, indexed);
+
+               if (precedence() <= level)
+                       c.s << (is_tex ? "{(" : "(");
                if (is_tex)
                        c.s << "{";
-               if (need_parens)
-                       c.s << "(";
-               base.print(c);
-               if (need_parens)
-                       c.s << ")";
+               base.print(c, precedence());
                if (is_tex)
                        c.s << "}";
                printindices(c, level);
+               if (precedence() <= level)
+                       c.s << (is_tex ? ")}" : ")");
        }
 }
 
@@ -321,7 +320,7 @@ void indexed::printindices(const print_context & c, unsigned level) const
 
                exvector::const_iterator it=seq.begin() + 1, itend = seq.end();
 
-               if (is_of_type(c, print_latex)) {
+               if (is_a<print_latex>(c)) {
 
                        // TeX output: group by variance
                        bool first = true;
@@ -389,6 +388,22 @@ ex indexed::derivative(const symbol & s) const
 // global functions
 //////////
 
+struct idx_is_equal_ignore_dim : public std::binary_function<ex, ex, bool> {
+       bool operator() (const ex &lh, const ex &rh) const
+       {
+               if (lh.is_equal(rh))
+                       return true;
+               else
+                       try {
+                               // Replacing the dimension might cause an error (e.g. with
+                               // index classes that only work in a fixed number of dimensions)
+                               return lh.is_equal(ex_to<idx>(rh).replace_dim(ex_to<idx>(lh).get_dim()));
+                       } catch (...) {
+                               return false;
+                       }
+       }
+};
+
 /** Check whether two sorted index vectors are consistent (i.e. equal). */
 static bool indices_consistent(const exvector & v1, const exvector & v2)
 {
@@ -396,7 +411,7 @@ static bool indices_consistent(const exvector & v1, const exvector & v2)
        if (v1.size() != v2.size())
                return false;
 
-       return equal(v1.begin(), v1.end(), v2.begin(), ex_is_equal());
+       return equal(v1.begin(), v1.end(), v2.begin(), idx_is_equal_ignore_dim());
 }
 
 exvector indexed::get_indices(void) const
@@ -516,7 +531,7 @@ static ex rename_dummy_indices(const ex & e, exvector & global_dummy_indices, ex
                int remaining = local_size - global_size;
                exvector::const_iterator it = local_dummy_indices.begin(), itend = local_dummy_indices.end();
                while (it != itend && remaining > 0) {
-                       if (find_if(global_dummy_indices.begin(), global_dummy_indices.end(), bind2nd(ex_is_equal(), *it)) == global_dummy_indices.end()) {
+                       if (find_if(global_dummy_indices.begin(), global_dummy_indices.end(), bind2nd(op0_is_equal(), *it)) == global_dummy_indices.end()) {
                                global_dummy_indices.push_back(*it);
                                global_size++;
                                remaining--;
@@ -813,6 +828,63 @@ contraction_done:
                return r;
 }
 
+/** This structure stores the original and symmetrized versions of terms
+ *  obtained during the simplification of sums. */
+class terminfo {
+public:
+       terminfo(const ex & orig_, const ex & symm_) : orig(orig_), symm(symm_) {}
+
+       ex orig; /**< original term */
+       ex symm; /**< symmtrized term */
+};
+
+class terminfo_is_less {
+public:
+       bool operator() (const terminfo & ti1, const terminfo & ti2) const
+       {
+               return (ti1.symm.compare(ti2.symm) < 0);
+       }
+};
+
+/** This structure stores the individual symmetrized terms obtained during
+ *  the simplification of sums. */
+class symminfo {
+public:
+       symminfo() : num(0) {}
+
+       symminfo(const ex & symmterm_, const ex & orig_, unsigned num_) : orig(orig_), num(num_)
+       {
+               if (is_exactly_a<mul>(symmterm_) && is_exactly_a<numeric>(symmterm_.op(symmterm_.nops()-1))) {
+                       coeff = symmterm_.op(symmterm_.nops()-1);
+                       symmterm = symmterm_ / coeff;
+               } else {
+                       coeff = 1;
+                       symmterm = symmterm_;
+               }
+       }
+
+       ex symmterm;  /**< symmetrized term */
+       ex coeff;     /**< coefficient of symmetrized term */
+       ex orig;      /**< original term */
+       unsigned num; /**< how many symmetrized terms resulted from the original term */
+};
+
+class symminfo_is_less_by_symmterm {
+public:
+       bool operator() (const symminfo & si1, const symminfo & si2) const
+       {
+               return (si1.symmterm.compare(si2.symmterm) < 0);
+       }
+};
+
+class symminfo_is_less_by_orig {
+public:
+       bool operator() (const symminfo & si1, const symminfo & si2) const
+       {
+               return (si1.orig.compare(si2.orig) < 0);
+       }
+};
+
 /** Simplify indexed expression, return list of free indices. */
 ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indices, const scalar_products & sp)
 {
@@ -848,7 +920,7 @@ ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indi
        // free indices in each term
        if (is_ex_exactly_of_type(e_expanded, add)) {
                bool first = true;
-               ex sum = _ex0;
+               ex sum;
                free_indices.clear();
 
                for (unsigned i=0; i<e_expanded.nops(); i++) {
@@ -860,8 +932,12 @@ ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indi
                                        sum = term;
                                        first = false;
                                } else {
-                                       if (!indices_consistent(free_indices, free_indices_of_term))
-                                               throw (std::runtime_error("simplify_indexed: inconsistent indices in sum"));
+                                       if (!indices_consistent(free_indices, free_indices_of_term)) {
+                                               std::ostringstream s;
+                                               s << "simplify_indexed: inconsistent indices in sum: ";
+                                               s << exprseq(free_indices) << " vs. " << exprseq(free_indices_of_term);
+                                               throw (std::runtime_error(s.str()));
+                                       }
                                        if (is_ex_of_type(sum, indexed) && is_ex_of_type(term, indexed))
                                                sum = ex_to<basic>(sum.op(0)).add_indexed(sum, term);
                                        else
@@ -870,7 +946,134 @@ ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indi
                        }
                }
 
-               return sum;
+               // If the sum turns out to be zero, we are finished
+               if (sum.is_zero()) {
+                       free_indices.clear();
+                       return sum;
+               }
+
+               // More than one term and more than one dummy index?
+               int num_terms_orig = (is_exactly_a<add>(sum) ? sum.nops() : 1);
+               if (num_terms_orig < 2 || dummy_indices.size() < 2)
+                       return sum;
+
+               // Yes, construct list of all dummy index symbols
+               lst dummy_syms;
+               for (int i=0; i<dummy_indices.size(); i++)
+                       dummy_syms.append(dummy_indices[i].op(0));
+
+               // Chop the sum into terms and symmetrize each one over the dummy
+               // indices
+               std::vector<terminfo> terms;
+               for (unsigned i=0; i<sum.nops(); i++) {
+                       const ex & term = sum.op(i);
+                       ex term_symm = term.symmetrize(dummy_syms);
+                       if (term_symm.is_zero())
+                               continue;
+                       terms.push_back(terminfo(term, term_symm));
+               }
+
+               // Sort by symmetrized terms
+               std::sort(terms.begin(), terms.end(), terminfo_is_less());
+
+               // Combine equal symmetrized terms
+               std::vector<terminfo> terms_pass2;
+               for (std::vector<terminfo>::const_iterator i=terms.begin(); i!=terms.end(); ) {
+                       unsigned num = 1;
+                       std::vector<terminfo>::const_iterator j = i + 1;
+                       while (j != terms.end() && j->symm == i->symm) {
+                               num++;
+                               j++;
+                       }
+                       terms_pass2.push_back(terminfo(i->orig * num, i->symm * num));
+                       i = j;
+               }
+
+               // If there is only one term left, we are finished
+               if (terms_pass2.size() == 1)
+                       return terms_pass2[0].orig;
+
+               // Chop the symmetrized terms into subterms
+               std::vector<symminfo> sy;
+               for (std::vector<terminfo>::const_iterator i=terms_pass2.begin(); i!=terms_pass2.end(); ++i) {
+                       if (is_exactly_a<add>(i->symm)) {
+                               unsigned num = i->symm.nops();
+                               for (unsigned j=0; j<num; j++)
+                                       sy.push_back(symminfo(i->symm.op(j), i->orig, num));
+                       } else
+                               sy.push_back(symminfo(i->symm, i->orig, 1));
+               }
+
+               // Sort by symmetrized subterms
+               std::sort(sy.begin(), sy.end(), symminfo_is_less_by_symmterm());
+
+               // Combine equal symmetrized subterms
+               std::vector<symminfo> sy_pass2;
+               exvector result;
+               for (std::vector<symminfo>::const_iterator i=sy.begin(); i!=sy.end(); ) {
+
+                       // Combine equal terms
+                       std::vector<symminfo>::const_iterator j = i + 1;
+                       if (j != sy.end() && j->symmterm == i->symmterm) {
+
+                               // More than one term, collect the coefficients
+                               ex coeff = i->coeff;
+                               while (j != sy.end() && j->symmterm == i->symmterm) {
+                                       coeff += j->coeff;
+                                       j++;
+                               }
+
+                               // Add combined term to result
+                               if (!coeff.is_zero())
+                                       result.push_back(coeff * i->symmterm);
+
+                       } else {
+
+                               // Single term, store for second pass
+                               sy_pass2.push_back(*i);
+                       }
+
+                       i = j;
+               }
+
+               // Were there any remaining terms that didn't get combined?
+               if (sy_pass2.size() > 0) {
+
+                       // Yes, sort by their original terms
+                       std::sort(sy_pass2.begin(), sy_pass2.end(), symminfo_is_less_by_orig());
+
+                       for (std::vector<symminfo>::const_iterator i=sy_pass2.begin(); i!=sy_pass2.end(); ) {
+
+                               // How many symmetrized terms of this original term are left?
+                               unsigned num = 1;
+                               std::vector<symminfo>::const_iterator j = i + 1;
+                               while (j != sy_pass2.end() && j->orig == i->orig) {
+                                       num++;
+                                       j++;
+                               }
+
+                               if (num == i->num) {
+
+                                       // All terms left, then add the original term to the result
+                                       result.push_back(i->orig);
+
+                               } else {
+
+                                       // Some terms were combined with others, add up the remaining symmetrized terms
+                                       std::vector<symminfo>::const_iterator k;
+                                       for (k=i; k!=j; k++)
+                                               result.push_back(k->coeff * k->symmterm);
+                               }
+
+                               i = j;
+                       }
+               }
+
+               // Add all resulting terms
+               ex sum_symm = (new add(result))->setflag(status_flags::dynallocated);
+               if (sum_symm.is_zero())
+                       free_indices.clear();
+               return sum_symm;
        }
 
        // Simplification of products