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