]> www.ginac.de Git - ginac.git/blobdiff - ginac/indexed.cpp
Index renaming issues, sped up simplify_indexed, used defined NC-objects
[ginac.git] / ginac / indexed.cpp
index 3fad73f8eb22ae2e533f8b889ac247dafbba5a2e..64eba6d08cf1cccf6dbf455cbcce4c01356dd3e7 100644 (file)
@@ -3,7 +3,7 @@
  *  Implementation of GiNaC's indexed expressions. */
 
 /*
- *  GiNaC Copyright (C) 1999-2003 Johannes Gutenberg University Mainz, Germany
+ *  GiNaC Copyright (C) 1999-2005 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
@@ -17,7 +17,7 @@
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <iostream>
 #include "operators.h"
 #include "lst.h"
 #include "archive.h"
+#include "symbol.h"
 #include "utils.h"
+#include "integral.h"
+#include "matrix.h"
 
 namespace GiNaC {
 
@@ -48,7 +51,7 @@ GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(indexed, exprseq,
 // default constructor
 //////////
 
-indexed::indexed() : symtree(sy_none())
+indexed::indexed() : symtree(not_symmetric())
 {
        tinfo_key = TINFO_indexed;
 }
@@ -57,31 +60,31 @@ indexed::indexed() : symtree(sy_none())
 // other constructors
 //////////
 
-indexed::indexed(const ex & b) : inherited(b), symtree(sy_none())
+indexed::indexed(const ex & b) : inherited(b), symtree(not_symmetric())
 {
        tinfo_key = TINFO_indexed;
        validate();
 }
 
-indexed::indexed(const ex & b, const ex & i1) : inherited(b, i1), symtree(sy_none())
+indexed::indexed(const ex & b, const ex & i1) : inherited(b, i1), symtree(not_symmetric())
 {
        tinfo_key = TINFO_indexed;
        validate();
 }
 
-indexed::indexed(const ex & b, const ex & i1, const ex & i2) : inherited(b, i1, i2), symtree(sy_none())
+indexed::indexed(const ex & b, const ex & i1, const ex & i2) : inherited(b, i1, i2), symtree(not_symmetric())
 {
        tinfo_key = TINFO_indexed;
        validate();
 }
 
-indexed::indexed(const ex & b, const ex & i1, const ex & i2, const ex & i3) : inherited(b, i1, i2, i3), symtree(sy_none())
+indexed::indexed(const ex & b, const ex & i1, const ex & i2, const ex & i3) : inherited(b, i1, i2, i3), symtree(not_symmetric())
 {
        tinfo_key = TINFO_indexed;
        validate();
 }
 
-indexed::indexed(const ex & b, const ex & i1, const ex & i2, const ex & i3, const ex & i4) : inherited(b, i1, i2, i3, i4), symtree(sy_none())
+indexed::indexed(const ex & b, const ex & i1, const ex & i2, const ex & i3, const ex & i4) : inherited(b, i1, i2, i3, i4), symtree(not_symmetric())
 {
        tinfo_key = TINFO_indexed;
        validate();
@@ -105,7 +108,7 @@ indexed::indexed(const ex & b, const symmetry & symm, const ex & i1, const ex &
        validate();
 }
 
-indexed::indexed(const ex & b, const exvector & v) : inherited(b), symtree(sy_none())
+indexed::indexed(const ex & b, const exvector & v) : inherited(b), symtree(not_symmetric())
 {
        seq.insert(seq.end(), v.begin(), v.end());
        tinfo_key = TINFO_indexed;
@@ -129,7 +132,7 @@ indexed::indexed(const symmetry & symm, const exvector & v, bool discardable) :
        tinfo_key = TINFO_indexed;
 }
 
-indexed::indexed(const symmetry & symm, exvector * vp) : inherited(vp), symtree(symm)
+indexed::indexed(const symmetry & symm, std::auto_ptr<exvector> vp) : inherited(vp), symtree(symm)
 {
        tinfo_key = TINFO_indexed;
 }
@@ -152,7 +155,7 @@ indexed::indexed(const archive_node &n, lst &sym_lst) : inherited(n, sym_lst)
                                symtree = sy_anti();
                                break;
                        default:
-                               symtree = sy_none();
+                               symtree = not_symmetric();
                                break;
                }
                const_cast<symmetry &>(ex_to<symmetry>(symtree)).validate(seq.size() - 1);
@@ -237,7 +240,7 @@ void indexed::do_print_latex(const print_latex & c, unsigned level) const
 
 void indexed::do_print_tree(const print_tree & c, unsigned level) const
 {
-       c.s << std::string(level, ' ') << class_name()
+       c.s << std::string(level, ' ') << class_name() << " @" << this
            << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
            << ", " << seq.size()-1 << " indices"
            << ", symmetry=" << symtree << std::endl;
@@ -294,6 +297,9 @@ ex indexed::eval(int level) const
                return f * thiscontainer(v);
        }
 
+       if(this->tinfo()==TINFO_indexed && seq.size()==1)
+               return base;
+
        // Canonicalize indices according to the symmetry properties
        if (seq.size() > 2) {
                exvector v = seq;
@@ -316,29 +322,41 @@ ex indexed::thiscontainer(const exvector & v) const
        return indexed(ex_to<symmetry>(symtree), v);
 }
 
-ex indexed::thiscontainer(exvector * vp) const
+ex indexed::thiscontainer(std::auto_ptr<exvector> vp) const
 {
        return indexed(ex_to<symmetry>(symtree), vp);
 }
 
+unsigned indexed::return_type() const
+{
+       if(is_a<matrix>(op(0)))
+               return return_types::commutative;
+       else
+               return op(0).return_type();
+}
+
 ex indexed::expand(unsigned options) const
 {
        GINAC_ASSERT(seq.size() > 0);
 
-       if ((options & expand_options::expand_indexed) && is_exactly_a<add>(seq[0])) {
-
-               // expand_indexed expands (a+b).i -> a.i + b.i
-               const ex & base = seq[0];
-               ex sum = _ex0;
-               for (size_t i=0; i<base.nops(); i++) {
+       if (options & expand_options::expand_indexed) {
+               ex newbase = seq[0].expand(options);
+               if (is_exactly_a<add>(newbase)) {
+                       ex sum = _ex0;
+                       for (size_t i=0; i<newbase.nops(); i++) {
+                               exvector s = seq;
+                               s[0] = newbase.op(i);
+                               sum += thiscontainer(s).expand(options);
+                       }
+                       return sum;
+               }
+               if (!are_ex_trivially_equal(newbase, seq[0])) {
                        exvector s = seq;
-                       s[0] = base.op(i);
-                       sum += thiscontainer(s).expand();
+                       s[0] = newbase;
+                       return ex_to<indexed>(thiscontainer(s)).inherited::expand(options);
                }
-               return sum;
-
-       } else
-               return inherited::expand(options);
+       }
+       return inherited::expand(options);
 }
 
 //////////
@@ -495,10 +513,43 @@ exvector ncmul::get_free_indices() const
        return free_indices;
 }
 
+struct is_summation_idx : public std::unary_function<ex, bool> {
+       bool operator()(const ex & e)
+       {
+               return is_dummy_pair(e, e);
+       }
+};
+
 exvector power::get_free_indices() const
 {
-       // Return free indices of basis
-       return basis.get_free_indices();
+       // Get free indices of basis
+       exvector basis_indices = basis.get_free_indices();
+
+       if (exponent.info(info_flags::even)) {
+               // If the exponent is an even number, then any "free" index that
+               // forms a dummy pair with itself is actually a summation index
+               exvector really_free;
+               std::remove_copy_if(basis_indices.begin(), basis_indices.end(),
+                                   std::back_inserter(really_free), is_summation_idx());
+               return really_free;
+       } else
+               return basis_indices;
+}
+
+exvector integral::get_free_indices() const
+{
+       if (a.get_free_indices().size() || b.get_free_indices().size())
+               throw (std::runtime_error("integral::get_free_indices: boundary values should not have free indices"));
+       return f.get_free_indices();
+}
+
+template<class T> size_t number_of_type(const exvector&v)
+{
+       size_t number = 0;
+       for(exvector::const_iterator i=v.begin(); i!=v.end(); ++i)
+               if(is_exactly_a<T>(*i))
+                       ++number;
+       return number;
 }
 
 /** Rename dummy indices in an expression.
@@ -509,10 +560,10 @@ exvector power::get_free_indices() const
  *  @param global_dummy_indices The set of dummy indices that have appeared
  *    before and which we would like to use in "e", too. This gets updated
  *    by the function */
-static ex rename_dummy_indices(const ex & e, exvector & global_dummy_indices, exvector & local_dummy_indices)
+template<class T> static ex rename_dummy_indices(const ex & e, exvector & global_dummy_indices, exvector & local_dummy_indices)
 {
-       size_t global_size = global_dummy_indices.size(),
-              local_size = local_dummy_indices.size();
+       size_t global_size = number_of_type<T>(global_dummy_indices),
+              local_size = number_of_type<T>(local_dummy_indices);
 
        // Any local dummy indices at all?
        if (local_size == 0)
@@ -526,7 +577,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(op0_is_equal(), *it)) == global_dummy_indices.end()) {
+                       if (is_exactly_a<T>(*it) && find_if(global_dummy_indices.begin(), global_dummy_indices.end(), bind2nd(idx_is_equal_ignore_dim(), *it)) == global_dummy_indices.end()) {
                                global_dummy_indices.push_back(*it);
                                global_size++;
                                remaining--;
@@ -544,11 +595,13 @@ static ex rename_dummy_indices(const ex & e, exvector & global_dummy_indices, ex
        exvector local_syms, global_syms;
        local_syms.reserve(local_size);
        global_syms.reserve(local_size);
-       for (size_t i=0; i<local_size; i++)
-               local_syms.push_back(local_dummy_indices[i].op(0));
+       for (size_t i=0; local_syms.size()!=local_size; i++)
+               if(is_exactly_a<T>(local_dummy_indices[i]))
+                       local_syms.push_back(local_dummy_indices[i].op(0));
        shaker_sort(local_syms.begin(), local_syms.end(), ex_is_less(), ex_swap());
-       for (size_t i=0; i<local_size; i++) // don't use more global symbols than necessary
-               global_syms.push_back(global_dummy_indices[i].op(0));
+       for (size_t i=0; global_syms.size()!=local_size; i++) // don't use more global symbols than necessary
+               if(is_exactly_a<T>(global_dummy_indices[i]))
+                       global_syms.push_back(global_dummy_indices[i].op(0));
        shaker_sort(global_syms.begin(), global_syms.end(), ex_is_less(), ex_swap());
 
        // Remove common indices
@@ -640,16 +693,15 @@ struct ex_base_is_less : public std::binary_function<ex, ex, bool> {
        }
 };
 
-/** Simplify product of indexed expressions (commutative, noncommutative and
- *  simple squares), return list of free indices. */
-ex simplify_indexed_product(const ex & e, exvector & free_indices, exvector & dummy_indices, const scalar_products & sp)
+/* An auxiliary function used by simplify_indexed() and expand_dummy_sum() 
+ * It returns an exvector of factors from the supplied product */
+static void product_to_exvector(const ex & e, exvector & v, bool & non_commutative)
 {
        // Remember whether the product was commutative or noncommutative
        // (because we chop it into factors and need to reassemble later)
-       bool non_commutative = is_exactly_a<ncmul>(e);
+       non_commutative = is_exactly_a<ncmul>(e);
 
        // Collect factors in an exvector, store squares twice
-       exvector v;
        v.reserve(e.nops() * 2);
 
        if (is_exactly_a<power>(e)) {
@@ -662,7 +714,7 @@ ex simplify_indexed_product(const ex & e, exvector & free_indices, exvector & du
                        ex f = e.op(i);
                        if (is_exactly_a<power>(f) && f.op(1).is_equal(_ex2)) {
                                v.push_back(f.op(0));
-                   v.push_back(f.op(0));
+                               v.push_back(f.op(0));
                        } else if (is_exactly_a<ncmul>(f)) {
                                // Noncommutative factor found, split it as well
                                non_commutative = true; // everything becomes noncommutative, ncmul will sort out the commutative factors later
@@ -672,6 +724,31 @@ ex simplify_indexed_product(const ex & e, exvector & free_indices, exvector & du
                                v.push_back(f);
                }
        }
+}
+
+template<class T> ex idx_symmetrization(const ex& r,const exvector& local_dummy_indices)
+{      exvector dummy_syms;
+       dummy_syms.reserve(r.nops());
+       for (exvector::const_iterator it = local_dummy_indices.begin(); it != local_dummy_indices.end(); ++it)
+                       if(is_exactly_a<T>(*it))
+                               dummy_syms.push_back(it->op(0));
+       if(dummy_syms.size() < 2)
+               return r;
+       ex q=symmetrize(r, dummy_syms);
+       return q;
+}
+
+/** Simplify product of indexed expressions (commutative, noncommutative and
+ *  simple squares), return list of free indices. */
+ex simplify_indexed_product(const ex & e, exvector & free_indices, exvector & dummy_indices, const scalar_products & sp)
+{
+       // Collect factors in an exvector
+       exvector v;
+
+       // Remember whether the product was commutative or noncommutative
+       // (because we chop it into factors and need to reassemble later)
+       bool non_commutative;
+       product_to_exvector(e, v, non_commutative);
 
        // Perform contractions
        bool something_changed = false;
@@ -821,19 +898,26 @@ contraction_done:
        // The result should be symmetric with respect to exchange of dummy
        // indices, so if the symmetrization vanishes, the whole expression is
        // zero. This detects things like eps.i.j.k * p.j * p.k = 0.
-       if (local_dummy_indices.size() >= 2) {
-               exvector dummy_syms;
-               dummy_syms.reserve(local_dummy_indices.size());
-               for (exvector::const_iterator it = local_dummy_indices.begin(); it != local_dummy_indices.end(); ++it)
-                       dummy_syms.push_back(it->op(0));
-               if (symmetrize(r, dummy_syms).is_zero()) {
-                       free_indices.clear();
-                       return _ex0;
-               }
+       ex q = idx_symmetrization<idx>(r, local_dummy_indices);
+       if (q.is_zero()) {
+               free_indices.clear();
+               return _ex0;
+       }
+       q = idx_symmetrization<varidx>(q, local_dummy_indices);
+       if (q.is_zero()) {
+               free_indices.clear();
+               return _ex0;
+       }
+       q = idx_symmetrization<spinidx>(q, local_dummy_indices);
+       if (q.is_zero()) {
+               free_indices.clear();
+               return _ex0;
        }
 
        // Dummy index renaming
-       r = rename_dummy_indices(r, dummy_indices, local_dummy_indices);
+       r = rename_dummy_indices<idx>(r, dummy_indices, local_dummy_indices);
+       r = rename_dummy_indices<varidx>(r, dummy_indices, local_dummy_indices);
+       r = rename_dummy_indices<spinidx>(r, dummy_indices, local_dummy_indices);
 
        // Product of indexed object with a scalar?
        if (is_exactly_a<mul>(r) && r.nops() == 2
@@ -900,6 +984,17 @@ public:
        }
 };
 
+bool hasindex(const ex &x, const ex &sym)
+{      
+       if(is_a<idx>(x) && x.op(0)==sym)
+               return true;
+       else
+               for(size_t i=0; i<x.nops(); ++i)
+                       if(hasindex(x.op(i), sym))
+                               return true;
+       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)
 {
@@ -928,7 +1023,10 @@ ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indi
                }
 
                // Rename the dummy indices
-               return rename_dummy_indices(e_expanded, dummy_indices, local_dummy_indices);
+               e_expanded = rename_dummy_indices<idx>(e_expanded, dummy_indices, local_dummy_indices);
+               e_expanded = rename_dummy_indices<varidx>(e_expanded, dummy_indices, local_dummy_indices);
+               e_expanded = rename_dummy_indices<spinidx>(e_expanded, dummy_indices, local_dummy_indices);
+               return e_expanded;
        }
 
        // Simplification of sum = sum of simplifications, check consistency of
@@ -972,18 +1070,19 @@ ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indi
                if (num_terms_orig < 2 || dummy_indices.size() < 2)
                        return sum;
 
-               // Yes, construct vector of all dummy index symbols
-               exvector dummy_syms;
-               dummy_syms.reserve(dummy_indices.size());
-               for (exvector::const_iterator it = dummy_indices.begin(); it != dummy_indices.end(); ++it)
-                       dummy_syms.push_back(it->op(0));
-
                // Chop the sum into terms and symmetrize each one over the dummy
                // indices
                std::vector<terminfo> terms;
                for (size_t i=0; i<sum.nops(); i++) {
                        const ex & term = sum.op(i);
-                       ex term_symm = symmetrize(term, dummy_syms);
+                       exvector dummy_indices_of_term;
+                       dummy_indices_of_term.reserve(dummy_indices.size());
+                       for(exvector::iterator i=dummy_indices.begin(); i!=dummy_indices.end(); ++i)
+                               if(hasindex(term,i->op(0)))
+                                       dummy_indices_of_term.push_back(*i);
+                       ex term_symm = idx_symmetrization<idx>(term, dummy_indices_of_term);
+                       term_symm = idx_symmetrization<varidx>(term_symm, dummy_indices_of_term);
+                       term_symm = idx_symmetrization<spinidx>(term_symm, dummy_indices_of_term);
                        if (term_symm.is_zero())
                                continue;
                        terms.push_back(terminfo(term, term_symm));
@@ -1107,8 +1206,9 @@ ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indi
  *  performs contraction of dummy indices where possible and checks whether
  *  the free indices in sums are consistent.
  *
+ *  @param options Simplification options (currently unused)
  *  @return simplified expression */
-ex ex::simplify_indexed() const
+ex ex::simplify_indexed(unsigned options) const
 {
        exvector free_indices, dummy_indices;
        scalar_products sp;
@@ -1121,8 +1221,9 @@ ex ex::simplify_indexed() const
  *  scalar products by known values if desired.
  *
  *  @param sp Scalar products to be replaced automatically
+ *  @param options Simplification options (currently unused)
  *  @return simplified expression */
-ex ex::simplify_indexed(const scalar_products & sp) const
+ex ex::simplify_indexed(const scalar_products & sp, unsigned options) const
 {
        exvector free_indices, dummy_indices;
        return GiNaC::simplify_indexed(*this, free_indices, dummy_indices, sp);
@@ -1247,4 +1348,126 @@ void scalar_products::debugprint() const
        }
 }
 
+/** Returns all dummy indices from the exvector */
+exvector get_all_dummy_indices(const ex & e)
+{
+       exvector p;
+       bool nc;
+       product_to_exvector(e, p, nc);
+       exvector::const_iterator ip = p.begin(), ipend = p.end();
+       exvector v, v1;
+       while (ip != ipend) {
+               if (is_a<indexed>(*ip)) {
+                       v1 = ex_to<indexed>(*ip).get_dummy_indices();
+                       v.insert(v.end(), v1.begin(), v1.end());
+                       exvector::const_iterator ip1 = ip+1;
+                       while (ip1 != ipend) {
+                               if (is_a<indexed>(*ip1)) {
+                                       v1 = ex_to<indexed>(*ip).get_dummy_indices(ex_to<indexed>(*ip1));
+                                       v.insert(v.end(), v1.begin(), v1.end());
+                               }
+                               ++ip1;
+                       }
+               }
+               ++ip;
+       }
+       return v;
+}
+
+ex rename_dummy_indices_uniquely(const exvector & va, const exvector & vb, const ex & b)
+{
+       exvector common_indices;
+       set_intersection(va.begin(), va.end(), vb.begin(), vb.end(), std::back_insert_iterator<exvector>(common_indices), ex_is_less());
+       if (common_indices.empty()) {
+               return b;
+       } else {
+               exvector new_indices, old_indices;
+               old_indices.reserve(2*common_indices.size());
+               new_indices.reserve(2*common_indices.size());
+               exvector::const_iterator ip = common_indices.begin(), ipend = common_indices.end();
+               while (ip != ipend) {
+                       ex newsym=(new symbol)->setflag(status_flags::dynallocated);
+                       ex newidx;
+                       if(is_exactly_a<spinidx>(*ip))
+                               newidx = (new spinidx(newsym, ex_to<spinidx>(*ip).get_dim(),
+                                               ex_to<spinidx>(*ip).is_covariant(),
+                                               ex_to<spinidx>(*ip).is_dotted()))
+                                       -> setflag(status_flags::dynallocated);
+                       else if (is_exactly_a<varidx>(*ip))
+                               newidx = (new varidx(newsym, ex_to<varidx>(*ip).get_dim(),
+                                               ex_to<varidx>(*ip).is_covariant()))
+                                       -> setflag(status_flags::dynallocated);
+                       else
+                               newidx = (new idx(newsym, ex_to<idx>(*ip).get_dim()))
+                                       -> setflag(status_flags::dynallocated);
+                       old_indices.push_back(*ip);
+                       new_indices.push_back(newidx);
+                       if(is_a<varidx>(*ip)) {
+                               old_indices.push_back(ex_to<varidx>(*ip).toggle_variance());
+                               new_indices.push_back(ex_to<varidx>(newidx).toggle_variance());
+                       }
+                       ++ip;
+               }
+               return b.subs(lst(old_indices.begin(), old_indices.end()), lst(new_indices.begin(), new_indices.end()), subs_options::no_pattern);
+       }
+}
+
+ex rename_dummy_indices_uniquely(const ex & a, const ex & b)
+{
+       exvector va = get_all_dummy_indices(a);
+       exvector vb = get_all_dummy_indices(b);
+       sort(va.begin(), va.end(), ex_is_less());
+       sort(vb.begin(), vb.end(), ex_is_less());
+       return rename_dummy_indices_uniquely(va, vb, b);
+}
+
+ex expand_dummy_sum(const ex & e, bool subs_idx)
+{
+       ex e_expanded = e.expand();
+       pointer_to_map_function_1arg<bool> fcn(expand_dummy_sum, subs_idx);
+       if (is_a<add>(e_expanded) || is_a<lst>(e_expanded) || is_a<matrix>(e_expanded)) {
+               return e_expanded.map(fcn);
+       } else if (is_a<ncmul>(e_expanded) || is_a<mul>(e_expanded) || is_a<power>(e_expanded)) {
+               exvector v = get_all_dummy_indices(e_expanded);
+               exvector::const_iterator it = v.begin(), itend = v.end();
+               while (it != itend) {
+                       varidx nu = ex_to<varidx>(*it);
+                       if (nu.is_dim_numeric()) {
+                               ex en = 0;
+                               for (int i=0; i < ex_to<numeric>(nu.get_dim()).to_int(); i++) {
+                                       if (is_a<varidx>(nu) && !subs_idx) {
+                                               en += e_expanded.subs(lst(nu == varidx(i, nu.get_dim(), true), nu.toggle_variance() == varidx(i, nu.get_dim())));
+                                       } else {
+                                               en += e_expanded.subs(lst(nu == idx(i, nu.get_dim()), nu.toggle_variance() == idx(i, nu.get_dim())));
+                                       }
+                               }
+                               return expand_dummy_sum(en, subs_idx);
+                       } 
+                       ++it;
+               }
+               return e;
+       } else if (is_a<indexed>(e_expanded)) {
+               exvector v = ex_to<indexed>(e_expanded).get_dummy_indices();
+               exvector::const_iterator it = v.begin(), itend = v.end();
+               while (it != itend) {
+                       varidx nu = ex_to<varidx>(*it);
+                       if (nu.is_dim_numeric()) {
+                               ex en = 0;
+                               for (int i=0; i < ex_to<numeric>(nu.get_dim()).to_int(); i++) {
+                                       if (is_a<varidx>(nu) && !subs_idx) {
+                                               en += e_expanded.subs(lst(nu == varidx(i, nu.get_dim(), true), nu.toggle_variance() == varidx(i, nu.get_dim())));
+                                       } else {
+                                               en += e_expanded.subs(lst(nu == idx(i, nu.get_dim()), nu.toggle_variance() == idx(i, nu.get_dim())));
+                                       }
+                               }
+                               return expand_dummy_sum(en, subs_idx);
+                       } 
+                       ++it;
+               }
+               return e;
+       } else {
+               return e;
+       }
+}
+
 } // namespace GiNaC