]> www.ginac.de Git - ginac.git/blob - ginac/indexed.cpp
- subs() can be used to substitute functions, tensors and indexed objects
[ginac.git] / ginac / indexed.cpp
1 /** @file indexed.cpp
2  *
3  *  Implementation of GiNaC's indexed expressions. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2001 Johannes Gutenberg University Mainz, Germany
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <stdexcept>
24
25 #include "indexed.h"
26 #include "idx.h"
27 #include "add.h"
28 #include "mul.h"
29 #include "ncmul.h"
30 #include "power.h"
31 #include "lst.h"
32 #include "archive.h"
33 #include "utils.h"
34 #include "debugmsg.h"
35
36 namespace GiNaC {
37
38 GINAC_IMPLEMENT_REGISTERED_CLASS(indexed, exprseq)
39
40 //////////
41 // default constructor, destructor, copy constructor assignment operator and helpers
42 //////////
43
44 indexed::indexed() : symmetry(unknown)
45 {
46         debugmsg("indexed default constructor", LOGLEVEL_CONSTRUCT);
47         tinfo_key = TINFO_indexed;
48 }
49
50 void indexed::copy(const indexed & other)
51 {
52         inherited::copy(other);
53         symmetry = other.symmetry;
54 }
55
56 DEFAULT_DESTROY(indexed)
57
58 //////////
59 // other constructors
60 //////////
61
62 indexed::indexed(const ex & b) : inherited(b), symmetry(unknown)
63 {
64         debugmsg("indexed constructor from ex", LOGLEVEL_CONSTRUCT);
65         tinfo_key = TINFO_indexed;
66         assert_all_indices_of_type_idx();
67 }
68
69 indexed::indexed(const ex & b, const ex & i1) : inherited(b, i1), symmetry(unknown)
70 {
71         debugmsg("indexed constructor from ex,ex", LOGLEVEL_CONSTRUCT);
72         tinfo_key = TINFO_indexed;
73         assert_all_indices_of_type_idx();
74 }
75
76 indexed::indexed(const ex & b, const ex & i1, const ex & i2) : inherited(b, i1, i2), symmetry(unknown)
77 {
78         debugmsg("indexed constructor from ex,ex,ex", LOGLEVEL_CONSTRUCT);
79         tinfo_key = TINFO_indexed;
80         assert_all_indices_of_type_idx();
81 }
82
83 indexed::indexed(const ex & b, const ex & i1, const ex & i2, const ex & i3) : inherited(b, i1, i2, i3), symmetry(unknown)
84 {
85         debugmsg("indexed constructor from ex,ex,ex,ex", LOGLEVEL_CONSTRUCT);
86         tinfo_key = TINFO_indexed;
87         assert_all_indices_of_type_idx();
88 }
89
90 indexed::indexed(const ex & b, const ex & i1, const ex & i2, const ex & i3, const ex & i4) : inherited(b, i1, i2, i3, i4), symmetry(unknown)
91 {
92         debugmsg("indexed constructor from ex,ex,ex,ex,ex", LOGLEVEL_CONSTRUCT);
93         tinfo_key = TINFO_indexed;
94         assert_all_indices_of_type_idx();
95 }
96
97 indexed::indexed(const ex & b, symmetry_type symm, const ex & i1, const ex & i2) : inherited(b, i1, i2), symmetry(symm)
98 {
99         debugmsg("indexed constructor from ex,symmetry,ex,ex", LOGLEVEL_CONSTRUCT);
100         tinfo_key = TINFO_indexed;
101         assert_all_indices_of_type_idx();
102 }
103
104 indexed::indexed(const ex & b, symmetry_type symm, const ex & i1, const ex & i2, const ex & i3) : inherited(b, i1, i2, i3), symmetry(symm)
105 {
106         debugmsg("indexed constructor from ex,symmetry,ex,ex,ex", LOGLEVEL_CONSTRUCT);
107         tinfo_key = TINFO_indexed;
108         assert_all_indices_of_type_idx();
109 }
110
111 indexed::indexed(const ex & b, symmetry_type symm, const ex & i1, const ex & i2, const ex & i3, const ex & i4) : inherited(b, i1, i2, i3, i4), symmetry(symm)
112 {
113         debugmsg("indexed constructor from ex,symmetry,ex,ex,ex,ex", LOGLEVEL_CONSTRUCT);
114         tinfo_key = TINFO_indexed;
115         assert_all_indices_of_type_idx();
116 }
117
118 indexed::indexed(const ex & b, const exvector & v) : inherited(b), symmetry(unknown)
119 {
120         debugmsg("indexed constructor from ex,exvector", LOGLEVEL_CONSTRUCT);
121         seq.insert(seq.end(), v.begin(), v.end());
122         tinfo_key = TINFO_indexed;
123         assert_all_indices_of_type_idx();
124 }
125
126 indexed::indexed(const ex & b, symmetry_type symm, const exvector & v) : inherited(b), symmetry(symm)
127 {
128         debugmsg("indexed constructor from ex,symmetry,exvector", LOGLEVEL_CONSTRUCT);
129         seq.insert(seq.end(), v.begin(), v.end());
130         tinfo_key = TINFO_indexed;
131         assert_all_indices_of_type_idx();
132 }
133
134 indexed::indexed(symmetry_type symm, const exprseq & es) : inherited(es), symmetry(symm)
135 {
136         debugmsg("indexed constructor from symmetry,exprseq", LOGLEVEL_CONSTRUCT);
137         tinfo_key = TINFO_indexed;
138         assert_all_indices_of_type_idx();
139 }
140
141 indexed::indexed(symmetry_type symm, const exvector & v, bool discardable) : inherited(v, discardable), symmetry(symm)
142 {
143         debugmsg("indexed constructor from symmetry,exvector", LOGLEVEL_CONSTRUCT);
144         tinfo_key = TINFO_indexed;
145         assert_all_indices_of_type_idx();
146 }
147
148 indexed::indexed(symmetry_type symm, exvector * vp) : inherited(vp), symmetry(symm)
149 {
150         debugmsg("indexed constructor from symmetry,exvector *", LOGLEVEL_CONSTRUCT);
151         tinfo_key = TINFO_indexed;
152         assert_all_indices_of_type_idx();
153 }
154
155 //////////
156 // archiving
157 //////////
158
159 indexed::indexed(const archive_node &n, const lst &sym_lst) : inherited(n, sym_lst)
160 {
161         debugmsg("indexed constructor from archive_node", LOGLEVEL_CONSTRUCT);
162         unsigned int symm;
163         if (!(n.find_unsigned("symmetry", symm)))
164                 throw (std::runtime_error("unknown indexed symmetry type in archive"));
165 }
166
167 void indexed::archive(archive_node &n) const
168 {
169         inherited::archive(n);
170         n.add_unsigned("symmetry", symmetry);
171 }
172
173 DEFAULT_UNARCHIVE(indexed)
174
175 //////////
176 // functions overriding virtual functions from bases classes
177 //////////
178
179 void indexed::printraw(std::ostream & os) const
180 {
181         debugmsg("indexed printraw", LOGLEVEL_PRINT);
182         GINAC_ASSERT(seq.size() > 0);
183
184         os << class_name() << "(";
185         seq[0].printraw(os);
186         os << ",indices=";
187         printrawindices(os);
188         os << ",hash=" << hashvalue << ",flags=" << flags << ")";
189 }
190
191 void indexed::printtree(std::ostream & os, unsigned indent) const
192 {
193         debugmsg("indexed printtree", LOGLEVEL_PRINT);
194         GINAC_ASSERT(seq.size() > 0);
195
196         os << std::string(indent, ' ') << class_name() << ", " << seq.size()-1 << " indices";
197         os << ",hash=" << hashvalue << ",flags=" << flags << std::endl;
198         printtreeindices(os, indent);
199 }
200
201 void indexed::print(std::ostream & os, unsigned upper_precedence) const
202 {
203         debugmsg("indexed print", LOGLEVEL_PRINT);
204         GINAC_ASSERT(seq.size() > 0);
205
206         const ex & base = seq[0];
207         bool need_parens = is_ex_exactly_of_type(base, add) || is_ex_exactly_of_type(base, mul)
208                         || is_ex_exactly_of_type(base, ncmul) || is_ex_exactly_of_type(base, power);
209         if (need_parens)
210                 os << "(";
211         os << base;
212         if (need_parens)
213                 os << ")";
214         printindices(os);
215 }
216
217 bool indexed::info(unsigned inf) const
218 {
219         if (inf == info_flags::indexed) return true;
220         if (inf == info_flags::has_indices) return seq.size() > 1;
221         return inherited::info(inf);
222 }
223
224 bool indexed::all_index_values_are(unsigned inf) const
225 {
226         // No indices? Then no property can be fulfilled
227         if (seq.size() < 2)
228                 return false;
229
230         // Check all indices
231         exvector::const_iterator it = seq.begin() + 1, itend = seq.end();
232         while (it != itend) {
233                 GINAC_ASSERT(is_ex_of_type(*it, idx));
234                 if (!ex_to_idx(*it).get_value().info(inf))
235                         return false;
236                 it++;
237         }
238         return true;
239 }
240
241 int indexed::compare_same_type(const basic & other) const
242 {
243         GINAC_ASSERT(is_of_type(other, indexed));
244         return inherited::compare_same_type(other);
245 }
246
247 // The main difference between sort_index_vector() and canonicalize_indices()
248 // is that the latter takes the symmetry of the object into account. Once we
249 // implement mixed symmetries, canonicalize_indices() will only be able to
250 // reorder index pairs with known symmetry properties, while sort_index_vector()
251 // always sorts the whole vector.
252
253 /** Bring a vector of indices into a canonic order (don't care about the
254  *  symmetry of the objects carrying the indices). Dummy indices will lie
255  *  next to each other after the sorting.
256  *
257  *  @param v Index vector to be sorted */
258 static void sort_index_vector(exvector &v)
259 {
260         // Nothing to sort if less than 2 elements
261         if (v.size() < 2)
262                 return;
263
264         // Simple bubble sort algorithm should be sufficient for the small
265         // number of indices expected
266         exvector::iterator it1 = v.begin(), itend = v.end(), next_to_last_idx = itend - 1;
267         while (it1 != next_to_last_idx) {
268                 exvector::iterator it2 = it1 + 1;
269                 while (it2 != itend) {
270                         if (it1->compare(*it2) > 0)
271                                 it1->swap(*it2);
272                         it2++;
273                 }
274                 it1++;
275         }
276 }
277
278 /** Bring a vector of indices into a canonic order. This operation only makes
279  *  sense if the object carrying these indices is either symmetric or totally
280  *  antisymmetric with respect to the indices.
281  *
282  *  @param itbegin Start of index vector
283  *  @param itend End of index vector
284  *  @param antisymm Whether the object is antisymmetric
285  *  @return the sign introduced by the reordering of the indices if the object
286  *          is antisymmetric (or 0 if two equal indices are encountered). For
287  *          symmetric objects, this is always +1. If the index vector was
288  *          already in a canonic order this function returns INT_MAX. */
289 static int canonicalize_indices(exvector::iterator itbegin, exvector::iterator itend, bool antisymm)
290 {
291         bool something_changed = false;
292         int sig = 1;
293
294         // Simple bubble sort algorithm should be sufficient for the small
295         // number of indices expected
296         exvector::iterator it1 = itbegin, next_to_last_idx = itend - 1;
297         while (it1 != next_to_last_idx) {
298                 exvector::iterator it2 = it1 + 1;
299                 while (it2 != itend) {
300                         int cmpval = it1->compare(*it2);
301                         if (cmpval == 1) {
302                                 it1->swap(*it2);
303                                 something_changed = true;
304                                 if (antisymm)
305                                         sig = -sig;
306                         } else if (cmpval == 0 && antisymm) {
307                                 something_changed = true;
308                                 sig = 0;
309                         }
310                         it2++;
311                 }
312                 it1++;
313         }
314
315         return something_changed ? sig : INT_MAX;
316 }
317
318 ex indexed::eval(int level) const
319 {
320         // First evaluate children, then we will end up here again
321         if (level > 1)
322                 return indexed(symmetry, evalchildren(level));
323
324         const ex &base = seq[0];
325
326         // If the base object is 0, the whole object is 0
327         if (base.is_zero())
328                 return _ex0();
329
330         // If the base object is a product, pull out the numeric factor
331         if (is_ex_exactly_of_type(base, mul) && is_ex_exactly_of_type(base.op(base.nops() - 1), numeric)) {
332                 exvector v = seq;
333                 ex f = ex_to_numeric(base.op(base.nops() - 1));
334                 v[0] = seq[0] / f;
335                 return f * thisexprseq(v);
336         }
337
338         // Canonicalize indices according to the symmetry properties
339         if (seq.size() > 2 && (symmetry != unknown && symmetry != mixed)) {
340                 exvector v = seq;
341                 int sig = canonicalize_indices(v.begin() + 1, v.end(), symmetry == antisymmetric);
342                 if (sig != INT_MAX) {
343                         // Something has changed while sorting indices, more evaluations later
344                         if (sig == 0)
345                                 return _ex0();
346                         return ex(sig) * thisexprseq(v);
347                 }
348         }
349
350         // Let the class of the base object perform additional evaluations
351         return base.bp->eval_indexed(*this);
352 }
353
354 int indexed::degree(const ex & s) const
355 {
356         return is_equal(*s.bp) ? 1 : 0;
357 }
358
359 int indexed::ldegree(const ex & s) const
360 {
361         return is_equal(*s.bp) ? 1 : 0;
362 }
363
364 ex indexed::coeff(const ex & s, int n) const
365 {
366         if (is_equal(*s.bp))
367                 return n==1 ? _ex1() : _ex0();
368         else
369                 return n==0 ? ex(*this) : _ex0();
370 }
371
372 ex indexed::subs(const lst & ls, const lst & lr) const
373 {
374         GINAC_ASSERT(ls.nops() == lr.nops());
375
376         for (unsigned i=0; i<ls.nops(); i++) {
377                 if (is_ex_of_type(ls.op(i), indexed) &&
378                     compare_same_type(ex_to_indexed(ls.op(i)))==0)
379                         return lr.op(i);
380         }
381
382         return inherited::subs(ls, lr);
383 }
384
385 ex indexed::thisexprseq(const exvector & v) const
386 {
387         return indexed(symmetry, v);
388 }
389
390 ex indexed::thisexprseq(exvector * vp) const
391 {
392         return indexed(symmetry, vp);
393 }
394
395 ex indexed::expand(unsigned options) const
396 {
397         GINAC_ASSERT(seq.size() > 0);
398
399         if ((options & expand_options::expand_indexed) && is_ex_exactly_of_type(seq[0], add)) {
400
401                 // expand_indexed expands (a+b).i -> a.i + b.i
402                 const ex & base = seq[0];
403                 ex sum = _ex0();
404                 for (unsigned i=0; i<base.nops(); i++) {
405                         exvector s = seq;
406                         s[0] = base.op(i);
407                         sum += thisexprseq(s).expand();
408                 }
409                 return sum;
410
411         } else
412                 return inherited::expand(options);
413 }
414
415 //////////
416 // virtual functions which can be overridden by derived classes
417 //////////
418
419 // none
420
421 //////////
422 // non-virtual functions in this class
423 //////////
424
425 void indexed::printrawindices(std::ostream & os) const
426 {
427         if (seq.size() > 1) {
428                 exvector::const_iterator it=seq.begin() + 1, itend = seq.end();
429                 while (it != itend) {
430                         it->printraw(os);
431                         it++;
432                         if (it != itend)
433                                 os << ",";
434                 }
435         }
436 }
437
438 void indexed::printtreeindices(std::ostream & os, unsigned indent) const
439 {
440         if (seq.size() > 1) {
441                 exvector::const_iterator it=seq.begin() + 1, itend = seq.end();
442                 while (it != itend) {
443                         os << std::string(indent + delta_indent, ' ');
444                         it->printraw(os);
445                         os << std::endl;
446                         it++;
447                 }
448         }
449 }
450
451 void indexed::printindices(std::ostream & os) const
452 {
453         if (seq.size() > 1) {
454                 exvector::const_iterator it=seq.begin() + 1, itend = seq.end();
455                 while (it != itend) {
456                         it->print(os);
457                         it++;
458                 }
459         }
460 }
461
462 /** Check whether all indices are of class idx. This function is used
463  *  internally to make sure that all constructed indexed objects really
464  *  carry indices and not some other classes. */
465 void indexed::assert_all_indices_of_type_idx(void) const
466 {
467         GINAC_ASSERT(seq.size() > 0);
468         exvector::const_iterator it = seq.begin() + 1, itend = seq.end();
469         while (it != itend) {
470                 if (!is_ex_of_type(*it, idx))
471                         throw(std::invalid_argument("indices of indexed object must be of type idx"));
472                 it++;
473         }
474 }
475
476 //////////
477 // global functions
478 //////////
479
480 /** Check whether two sorted index vectors are consistent (i.e. equal). */
481 static bool indices_consistent(const exvector & v1, const exvector & v2)
482 {
483         // Number of indices must be the same
484         if (v1.size() != v2.size())
485                 return false;
486
487         // And also the indices themselves
488         exvector::const_iterator ait = v1.begin(), aitend = v1.end(),
489                                  bit = v2.begin(), bitend = v2.end();
490         while (ait != aitend) {
491                 if (!ait->is_equal(*bit))
492                         return false;
493                 ait++; bit++;
494         }
495         return true;
496 }
497
498 exvector indexed::get_indices(void) const
499 {
500         GINAC_ASSERT(seq.size() >= 1);
501         return exvector(seq.begin() + 1, seq.end());
502 }
503
504 exvector indexed::get_dummy_indices(void) const
505 {
506         exvector free_indices, dummy_indices;
507         find_free_and_dummy(seq.begin() + 1, seq.end(), free_indices, dummy_indices);
508         return dummy_indices;
509 }
510
511 exvector indexed::get_dummy_indices(const indexed & other) const
512 {
513         exvector indices = get_free_indices();
514         exvector other_indices = other.get_free_indices();
515         indices.insert(indices.end(), other_indices.begin(), other_indices.end());
516         exvector dummy_indices;
517         find_dummy_indices(indices, dummy_indices);
518         return dummy_indices;
519 }
520
521 exvector indexed::get_free_indices(void) const
522 {
523         exvector free_indices, dummy_indices;
524         find_free_and_dummy(seq.begin() + 1, seq.end(), free_indices, dummy_indices);
525         return free_indices;
526 }
527
528 exvector add::get_free_indices(void) const
529 {
530         exvector free_indices;
531         for (unsigned i=0; i<nops(); i++) {
532                 if (i == 0)
533                         free_indices = op(i).get_free_indices();
534                 else {
535                         exvector free_indices_of_term = op(i).get_free_indices();
536                         if (!indices_consistent(free_indices, free_indices_of_term))
537                                 throw (std::runtime_error("add::get_free_indices: inconsistent indices in sum"));
538                 }
539         }
540         return free_indices;
541 }
542
543 exvector mul::get_free_indices(void) const
544 {
545         // Concatenate free indices of all factors
546         exvector un;
547         for (unsigned i=0; i<nops(); i++) {
548                 exvector free_indices_of_factor = op(i).get_free_indices();
549                 un.insert(un.end(), free_indices_of_factor.begin(), free_indices_of_factor.end());
550         }
551
552         // And remove the dummy indices
553         exvector free_indices, dummy_indices;
554         find_free_and_dummy(un, free_indices, dummy_indices);
555         return free_indices;
556 }
557
558 exvector ncmul::get_free_indices(void) const
559 {
560         // Concatenate free indices of all factors
561         exvector un;
562         for (unsigned i=0; i<nops(); i++) {
563                 exvector free_indices_of_factor = op(i).get_free_indices();
564                 un.insert(un.end(), free_indices_of_factor.begin(), free_indices_of_factor.end());
565         }
566
567         // And remove the dummy indices
568         exvector free_indices, dummy_indices;
569         find_free_and_dummy(un, free_indices, dummy_indices);
570         return free_indices;
571 }
572
573 exvector power::get_free_indices(void) const
574 {
575         // Return free indices of basis
576         return basis.get_free_indices();
577 }
578
579 /** Simplify product of indexed expressions (commutative, noncommutative and
580  *  simple squares), return list of free indices. */
581 ex simplify_indexed_product(const ex & e, exvector & free_indices, const scalar_products & sp)
582 {
583         // Remember whether the product was commutative or noncommutative
584         // (because we chop it into factors and need to reassemble later)
585         bool non_commutative = is_ex_exactly_of_type(e, ncmul);
586
587         // Collect factors in an exvector, store squares twice
588         exvector v;
589         v.reserve(e.nops() * 2);
590
591         if (is_ex_exactly_of_type(e, power)) {
592                 // We only get called for simple squares, split a^2 -> a*a
593                 GINAC_ASSERT(e.op(1).is_equal(_ex2()));
594                 v.push_back(e.op(0));
595                 v.push_back(e.op(0));
596         } else {
597                 for (int i=0; i<e.nops(); i++) {
598                         ex f = e.op(i);
599                         if (is_ex_exactly_of_type(f, power) && f.op(1).is_equal(_ex2())) {
600                                 v.push_back(f.op(0));
601                     v.push_back(f.op(0));
602                         } else if (is_ex_exactly_of_type(f, ncmul)) {
603                                 // Noncommutative factor found, split it as well
604                                 non_commutative = true; // everything becomes noncommutative, ncmul will sort out the commutative factors later
605                                 for (int j=0; j<f.nops(); j++)
606                                         v.push_back(f.op(j));
607                         } else
608                                 v.push_back(f);
609                 }
610         }
611
612         // Perform contractions
613         bool something_changed = false;
614         GINAC_ASSERT(v.size() > 1);
615         exvector::iterator it1, itend = v.end(), next_to_last = itend - 1;
616         for (it1 = v.begin(); it1 != next_to_last; it1++) {
617
618 try_again:
619                 if (!is_ex_of_type(*it1, indexed))
620                         continue;
621
622                 // Indexed factor found, get free indices and look for contraction
623                 // candidates
624                 exvector free1, dummy1;
625                 find_free_and_dummy(ex_to_indexed(*it1).seq.begin() + 1, ex_to_indexed(*it1).seq.end(), free1, dummy1);
626
627                 exvector::iterator it2;
628                 for (it2 = it1 + 1; it2 != itend; it2++) {
629
630                         if (!is_ex_of_type(*it2, indexed))
631                                 continue;
632
633                         // Find free indices of second factor and merge them with free
634                         // indices of first factor
635                         exvector un;
636                         find_free_and_dummy(ex_to_indexed(*it2).seq.begin() + 1, ex_to_indexed(*it2).seq.end(), un, dummy1);
637                         un.insert(un.end(), free1.begin(), free1.end());
638
639                         // Check whether the two factors share dummy indices
640                         exvector free, dummy;
641                         find_free_and_dummy(un, free, dummy);
642                         if (dummy.size() == 0)
643                                 continue;
644
645                         // At least one dummy index, is it a defined scalar product?
646                         bool contracted = false;
647                         if (free.size() == 0) {
648                                 if (sp.is_defined(*it1, *it2)) {
649                                         *it1 = sp.evaluate(*it1, *it2);
650                                         *it2 = _ex1();
651                                         goto contraction_done;
652                                 }
653                         }
654
655                         // Contraction of symmetric with antisymmetric object is zero
656                         if ((ex_to_indexed(*it1).symmetry == indexed::symmetric &&
657                              ex_to_indexed(*it2).symmetry == indexed::antisymmetric
658                           || ex_to_indexed(*it1).symmetry == indexed::antisymmetric &&
659                              ex_to_indexed(*it2).symmetry == indexed::symmetric)
660                          && dummy.size() > 1) {
661                                 free_indices.clear();
662                                 return _ex0();
663                         }
664
665                         // Try to contract the first one with the second one
666                         contracted = it1->op(0).bp->contract_with(it1, it2, v);
667                         if (!contracted) {
668
669                                 // That didn't work; maybe the second object knows how to
670                                 // contract itself with the first one
671                                 contracted = it2->op(0).bp->contract_with(it2, it1, v);
672                         }
673                         if (contracted) {
674 contraction_done:
675                                 if (is_ex_exactly_of_type(*it1, add) || is_ex_exactly_of_type(*it2, add)
676                                  || is_ex_exactly_of_type(*it1, mul) || is_ex_exactly_of_type(*it2, mul)) {
677
678                                         // One of the factors became a sum or product:
679                                         // re-expand expression and run again
680                                         ex r = non_commutative ? ex(ncmul(v)) : ex(mul(v));
681                                         return simplify_indexed(r, free_indices, sp);
682                                 }
683
684                                 // Both objects may have new indices now or they might
685                                 // even not be indexed objects any more, so we have to
686                                 // start over
687                                 something_changed = true;
688                                 goto try_again;
689                         }
690                 }
691         }
692
693         // Find free indices (concatenate them all and call find_free_and_dummy())
694         exvector un, dummy_indices;
695         it1 = v.begin(); itend = v.end();
696         while (it1 != itend) {
697                 exvector free_indices_of_factor = it1->get_free_indices();
698                 un.insert(un.end(), free_indices_of_factor.begin(), free_indices_of_factor.end());
699                 it1++;
700         }
701         find_free_and_dummy(un, free_indices, dummy_indices);
702
703         ex r;
704         if (something_changed)
705                 r = non_commutative ? ex(ncmul(v)) : ex(mul(v));
706         else
707                 r = e;
708
709         // Product of indexed object with a scalar?
710         if (is_ex_exactly_of_type(r, mul) && r.nops() == 2
711          && is_ex_exactly_of_type(r.op(1), numeric) && is_ex_of_type(r.op(0), indexed))
712                 return r.op(0).op(0).bp->scalar_mul_indexed(r.op(0), ex_to_numeric(r.op(1)));
713         else
714                 return r;
715 }
716
717 /** Simplify indexed expression, return list of free indices. */
718 ex simplify_indexed(const ex & e, exvector & free_indices, const scalar_products & sp)
719 {
720         // Expand the expression
721         ex e_expanded = e.expand();
722
723         // Simplification of single indexed object: just find the free indices
724         if (is_ex_of_type(e_expanded, indexed)) {
725                 const indexed &i = ex_to_indexed(e_expanded);
726                 exvector dummy_indices;
727                 find_free_and_dummy(i.seq.begin() + 1, i.seq.end(), free_indices, dummy_indices);
728                 return e_expanded;
729         }
730
731         // Simplification of sum = sum of simplifications, check consistency of
732         // free indices in each term
733         if (is_ex_exactly_of_type(e_expanded, add)) {
734                 bool first = true;
735                 ex sum = _ex0();
736                 free_indices.clear();
737
738                 for (unsigned i=0; i<e_expanded.nops(); i++) {
739                         exvector free_indices_of_term;
740                         ex term = simplify_indexed(e_expanded.op(i), free_indices_of_term, sp);
741                         if (!term.is_zero()) {
742                                 if (first) {
743                                         free_indices = free_indices_of_term;
744                                         sum = term;
745                                         first = false;
746                                 } else {
747                                         if (!indices_consistent(free_indices, free_indices_of_term))
748                                                 throw (std::runtime_error("simplify_indexed: inconsistent indices in sum"));
749                                         if (is_ex_of_type(sum, indexed) && is_ex_of_type(term, indexed))
750                                                 sum = sum.op(0).bp->add_indexed(sum, term);
751                                         else
752                                                 sum += term;
753                                 }
754                         }
755                 }
756
757                 return sum;
758         }
759
760         // Simplification of products
761         if (is_ex_exactly_of_type(e_expanded, mul)
762          || is_ex_exactly_of_type(e_expanded, ncmul)
763          || (is_ex_exactly_of_type(e_expanded, power) && is_ex_of_type(e_expanded.op(0), indexed) && e_expanded.op(1).is_equal(_ex2())))
764                 return simplify_indexed_product(e_expanded, free_indices, sp);
765
766         // Cannot do anything
767         free_indices.clear();
768         return e_expanded;
769 }
770
771 ex simplify_indexed(const ex & e)
772 {
773         exvector free_indices;
774         scalar_products sp;
775         return simplify_indexed(e, free_indices, sp);
776 }
777
778 ex simplify_indexed(const ex & e, const scalar_products & sp)
779 {
780         exvector free_indices;
781         return simplify_indexed(e, free_indices, sp);
782 }
783
784 //////////
785 // helper classes
786 //////////
787
788 void scalar_products::add(const ex & v1, const ex & v2, const ex & sp)
789 {
790         spm[make_key(v1, v2)] = sp;
791 }
792
793 void scalar_products::clear(void)
794 {
795         spm.clear();
796 }
797
798 /** Check whether scalar product pair is defined. */
799 bool scalar_products::is_defined(const ex & v1, const ex & v2) const
800 {
801         return spm.find(make_key(v1, v2)) != spm.end();
802 }
803
804 /** Return value of defined scalar product pair. */
805 ex scalar_products::evaluate(const ex & v1, const ex & v2) const
806 {
807         return spm.find(make_key(v1, v2))->second;
808 }
809
810 void scalar_products::debugprint(void) const
811 {
812         std::cerr << "map size=" << spm.size() << std::endl;
813         for (spmap::const_iterator cit=spm.begin(); cit!=spm.end(); ++cit) {
814                 const spmapkey & k = cit->first;
815                 std::cerr << "item key=(" << k.first << "," << k.second;
816                 std::cerr << "), value=" << cit->second << std::endl;
817         }
818 }
819
820 /** Make key from object pair. */
821 spmapkey scalar_products::make_key(const ex & v1, const ex & v2)
822 {
823         // If indexed, extract base objects
824         ex s1 = is_ex_of_type(v1, indexed) ? v1.op(0) : v1;
825         ex s2 = is_ex_of_type(v2, indexed) ? v2.op(0) : v2;
826
827         // Enforce canonical order in pair
828         if (s1.compare(s2) > 0)
829                 return spmapkey(s2, s1);
830         else
831                 return spmapkey(s1, s2);
832 }
833
834 } // namespace GiNaC