* 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
*/
#include <iostream>
+#include <sstream>
#include <stdexcept>
#include "indexed.h"
{
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
} 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 ? ")}" : ")");
}
}
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;
// 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)
{
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
/** Rename dummy indices in an expression.
*
- * @param e Expression to be worked on
+ * @param e Expression to work on
* @param local_dummy_indices The set of dummy indices that appear in the
* expression "e"
* @param global_dummy_indices The set of dummy indices that have appeared
}
}
+/** Given a set of indices, extract those of class varidx. */
+static void find_variant_indices(const exvector & v, exvector & variant_indices)
+{
+ exvector::const_iterator it1, itend;
+ for (it1 = v.begin(), itend = v.end(); it1 != itend; ++it1) {
+ if (is_exactly_a<varidx>(*it1))
+ variant_indices.push_back(*it1);
+ }
+}
+
+/** Raise/lower dummy indices in a single indexed objects to canonicalize their
+ * variance.
+ *
+ * @param e Object to work on
+ * @param variant_dummy_indices The set of indices that might need repositioning (will be changed by this function)
+ * @param moved_indices The set of indices that have been repositioned (will be changed by this function)
+ * @return true if 'e' was changed */
+bool reposition_dummy_indices(ex & e, exvector & variant_dummy_indices, exvector & moved_indices)
+{
+ bool something_changed = false;
+
+ // If a dummy index is encountered for the first time in the
+ // product, pull it up, otherwise, pull it down
+ exvector::const_iterator it2, it2start, it2end;
+ for (it2start = ex_to<indexed>(e).seq.begin(), it2end = ex_to<indexed>(e).seq.end(), it2 = it2start + 1; it2 != it2end; ++it2) {
+ if (!is_exactly_a<varidx>(*it2))
+ continue;
+
+ exvector::iterator vit, vitend;
+ for (vit = variant_dummy_indices.begin(), vitend = variant_dummy_indices.end(); vit != vitend; ++vit) {
+ if (it2->op(0).is_equal(vit->op(0))) {
+ if (ex_to<varidx>(*it2).is_covariant()) {
+ e = e.subs(lst(
+ *it2 == ex_to<varidx>(*it2).toggle_variance(),
+ ex_to<varidx>(*it2).toggle_variance() == *it2
+ ));
+ something_changed = true;
+ it2 = ex_to<indexed>(e).seq.begin() + (it2 - it2start);
+ it2start = ex_to<indexed>(e).seq.begin();
+ it2end = ex_to<indexed>(e).seq.end();
+ }
+ moved_indices.push_back(*vit);
+ variant_dummy_indices.erase(vit);
+ goto next_index;
+ }
+ }
+
+ for (vit = moved_indices.begin(), vitend = moved_indices.end(); vit != vitend; ++vit) {
+ if (it2->op(0).is_equal(vit->op(0))) {
+ if (ex_to<varidx>(*it2).is_contravariant()) {
+ e = e.subs(*it2 == ex_to<varidx>(*it2).toggle_variance());
+ something_changed = true;
+ it2 = ex_to<indexed>(e).seq.begin() + (it2 - it2start);
+ it2start = ex_to<indexed>(e).seq.begin();
+ it2end = ex_to<indexed>(e).seq.end();
+ }
+ goto next_index;
+ }
+ }
+
+next_index: ;
+ }
+
+ return something_changed;
+}
+
/* Ordering that only compares the base expressions of indexed objects. */
struct ex_base_is_less : public std::binary_function<ex, ex, bool> {
bool operator() (const ex &lh, const ex &rh) const
// Filter out the dummy indices with variance
exvector variant_dummy_indices;
- for (it1 = local_dummy_indices.begin(), itend = local_dummy_indices.end(); it1 != itend; ++it1) {
- if (is_exactly_a<varidx>(*it1))
- variant_dummy_indices.push_back(*it1);
- }
+ find_variant_indices(local_dummy_indices, variant_dummy_indices);
// Any indices with variance present at all?
if (!variant_dummy_indices.empty()) {
if (!is_ex_of_type(*it1, indexed))
continue;
- ex new_it1;
- bool it1_dirty = false; // It this is true, then new_it1 holds a new value for *it1
-
- // If a dummy index is encountered for the first time in the
- // product, pull it up, otherwise, pull it down
- exvector::iterator it2, it2end;
- for (it2 = const_cast<indexed &>(ex_to<indexed>(*it1)).seq.begin(), it2end = const_cast<indexed &>(ex_to<indexed>(*it1)).seq.end(); it2 != it2end; ++it2) {
- if (!is_exactly_a<varidx>(*it2))
- continue;
-
- exvector::iterator vit, vitend;
- for (vit = variant_dummy_indices.begin(), vitend = variant_dummy_indices.end(); vit != vitend; ++vit) {
- if (it2->op(0).is_equal(vit->op(0))) {
- if (ex_to<varidx>(*it2).is_covariant()) {
- new_it1 = (it1_dirty ? new_it1 : *it1).subs(*it2 == ex_to<varidx>(*it2).toggle_variance());
- it1_dirty = true;
- something_changed = true;
- }
- moved_indices.push_back(*vit);
- variant_dummy_indices.erase(vit);
- goto next_index;
- }
- }
-
- for (vit = moved_indices.begin(), vitend = moved_indices.end(); vit != vitend; ++vit) {
- if (it2->op(0).is_equal(vit->op(0))) {
- if (ex_to<varidx>(*it2).is_contravariant()) {
- new_it1 = (it1_dirty ? new_it1 : *it1).subs(*it2 == ex_to<varidx>(*it2).toggle_variance());
- it1_dirty = true;
- something_changed = true;
- }
- goto next_index;
- }
- }
-
-next_index: ;
- }
-
- if (it1_dirty)
- *it1 = new_it1;
+ if (reposition_dummy_indices(*it1, variant_dummy_indices, moved_indices))
+ something_changed = true;
}
}
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)
{
ex e_expanded = e.expand();
// Simplification of single indexed object: just find the free indices
- // and perform dummy index renaming
+ // and perform dummy index renaming/repositioning
if (is_ex_of_type(e_expanded, indexed)) {
+
+ // Find the dummy indices
const indexed &i = ex_to<indexed>(e_expanded);
exvector local_dummy_indices;
find_free_and_dummy(i.seq.begin() + 1, i.seq.end(), free_indices, local_dummy_indices);
+
+ // Filter out the dummy indices with variance
+ exvector variant_dummy_indices;
+ find_variant_indices(local_dummy_indices, variant_dummy_indices);
+
+ // Any indices with variance present at all?
+ if (!variant_dummy_indices.empty()) {
+
+ // Yes, reposition them
+ exvector moved_indices;
+ reposition_dummy_indices(e_expanded, variant_dummy_indices, moved_indices);
+ }
+
+ // Rename the dummy indices
return rename_dummy_indices(e_expanded, dummy_indices, local_dummy_indices);
}
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
}
}
+ // 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_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));
+
+ // 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;
+ }
+
+ // 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;
}