]> www.ginac.de Git - ginac.git/blobdiff - ginac/indexed.cpp
- prepared for 1.0.13 release
[ginac.git] / ginac / indexed.cpp
index d9b2f474fc8266e7e96a87ee483c1f5eace86352..9b4092e6cd4336f1cdbc343c7595157be1566611 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"
@@ -387,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)
 {
@@ -394,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
@@ -811,6 +828,66 @@ contraction_done:
                return r;
 }
 
+/** This structure stores the original and symmetrized versions of terms
+ *  obtained during the simplification of sums. */
+class symminfo {
+public:
+       symminfo() : num(0) {}
+       ~symminfo() {}
+
+       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
+       {
+               int comp = si1.symmterm.compare(si2.symmterm);
+               if (comp < 0) return true;
+#if 0
+               if (comp > 0) return false;
+               comp = si1.orig.compare(si2.orig);
+               if (comp < 0) return true;
+               if (comp > 0) return false;
+               comp = si1.coeff.compare(si2.coeff);
+               if (comp < 0) return true;
+#endif
+               return false;
+       }
+};
+
+class symminfo_is_less_by_orig {
+public:
+       bool operator() (const symminfo & si1, const symminfo & si2) const
+       {
+               int comp = si1.orig.compare(si2.orig);
+               if (comp < 0) return true;
+#if 0
+               if (comp > 0) return false;
+               comp = si1.symmterm.compare(si2.symmterm);
+               if (comp < 0) return true;
+               if (comp > 0) return false;
+               comp = si1.coeff.compare(si2.coeff);
+               if (comp < 0) return true;
+#endif
+               return false;
+       }
+};
+
 /** Simplify indexed expression, return list of free indices. */
 ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indices, const scalar_products & sp)
 {
@@ -858,8 +935,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
@@ -868,25 +949,105 @@ ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indi
                        }
                }
 
+               // If the sum turns out to be zero, we are finished
                if (sum.is_zero()) {
                        free_indices.clear();
                        return sum;
                }
 
                // Symmetrizing over the dummy indices may cancel terms
-               int num_terms_orig = (is_a<add>(sum) ? sum.nops() : 1);
+               int num_terms_orig = (is_exactly_a<add>(sum) ? sum.nops() : 1);
                if (num_terms_orig > 1 && dummy_indices.size() >= 2) {
+
+                       // 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));
-                       ex sum_symm = sum.symmetrize(dummy_syms);
-                       if (sum_symm.is_zero()) {
-                               free_indices.clear();
-                               return _ex0;
+
+                       // Symmetrize each term separately and store the resulting
+                       // terms in a list of symminfo structures
+                       std::vector<symminfo> v;
+                       for (int i=0; i<sum.nops(); i++) {
+                               ex sum_symm = sum.op(i).symmetrize(dummy_syms);
+                               if (sum_symm.is_zero())
+                                       continue;
+                               if (is_exactly_a<add>(sum_symm))
+                                       for (int j=0; j<sum_symm.nops(); j++)
+                                               v.push_back(symminfo(sum_symm.op(j), sum.op(i), sum_symm.nops()));
+                               else
+                                       v.push_back(symminfo(sum_symm, sum.op(i), 1));
+                       }
+
+                       // Now add up all the unsymmetrized versions of the terms that
+                       // did not cancel out in the symmetrization
+                       exvector result;
+                       std::sort(v.begin(), v.end(), symminfo_is_less_by_symmterm());
+
+                       std::vector<symminfo> v_pass2;
+                       for (std::vector<symminfo>::const_iterator i=v.begin(); i!=v.end(); ) {
+
+                               // Combine equal terms
+                               std::vector<symminfo>::const_iterator j = i + 1;
+                               if (j != v.end() && j->symmterm == i->symmterm) {
+
+                                       // More than one term, collect the coefficients
+                                       ex coeff = i->coeff;
+                                       while (j != v.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
+                                       v_pass2.push_back(*i);
+                               }
+
+                               i = j;
                        }
-                       int num_terms = (is_a<add>(sum_symm) ? sum_symm.nops() : 1);
-                       if (num_terms < num_terms_orig)
-                               return sum_symm;
+
+                       // Were there any remaining terms that didn't get combined?
+                       if (v_pass2.size() > 0) {
+
+                               // Yes, sort them by their original term
+                               std::sort(v_pass2.begin(), v_pass2.end(), symminfo_is_less_by_orig());
+
+                               for (std::vector<symminfo>::const_iterator i=v_pass2.begin(); i!=v_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 != v_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;
                }
 
                return sum;