GiNaC 1.8.8
indexed.cpp
Go to the documentation of this file.
1
5/*
6 * GiNaC Copyright (C) 1999-2025 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#include "indexed.h"
24#include "idx.h"
25#include "add.h"
26#include "mul.h"
27#include "ncmul.h"
28#include "power.h"
29#include "relational.h"
30#include "symmetry.h"
31#include "operators.h"
32#include "lst.h"
33#include "archive.h"
34#include "symbol.h"
35#include "utils.h"
36#include "integral.h"
37#include "matrix.h"
38#include "inifcns.h"
39
40#include <algorithm>
41#include <iostream>
42#include <limits>
43#include <sstream>
44#include <stdexcept>
45
46namespace GiNaC {
47
50 print_func<print_latex>(&indexed::do_print_latex).
51 print_func<print_tree>(&indexed::do_print_tree))
52
53
54// default constructor
56
58{
59}
60
62// other constructors
64
65indexed::indexed(const ex & b) : inherited{b}, symtree(not_symmetric())
66{
67 validate();
68}
69
70indexed::indexed(const ex & b, const ex & i1) : inherited{b, i1}, symtree(not_symmetric())
71{
72 validate();
73}
74
75indexed::indexed(const ex & b, const ex & i1, const ex & i2) : inherited{b, i1, i2}, symtree(not_symmetric())
76{
77 validate();
78}
79
80indexed::indexed(const ex & b, const ex & i1, const ex & i2, const ex & i3) : inherited{b, i1, i2, i3}, symtree(not_symmetric())
81{
82 validate();
83}
84
85indexed::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())
86{
87 validate();
88}
89
90indexed::indexed(const ex & b, const symmetry & symm, const ex & i1, const ex & i2) : inherited{b, i1, i2}, symtree(symm)
91{
92 validate();
93}
94
95indexed::indexed(const ex & b, const symmetry & symm, const ex & i1, const ex & i2, const ex & i3) : inherited{b, i1, i2, i3}, symtree(symm)
96{
97 validate();
98}
99
100indexed::indexed(const ex & b, const symmetry & symm, const ex & i1, const ex & i2, const ex & i3, const ex & i4) : inherited{b, i1, i2, i3, i4}, symtree(symm)
101{
102 validate();
103}
104
105indexed::indexed(const ex & b, const exvector & v) : inherited{b}, symtree(not_symmetric())
106{
107 seq.insert(seq.end(), v.begin(), v.end());
108 validate();
109}
110
111indexed::indexed(const ex & b, const symmetry & symm, const exvector & v) : inherited{b}, symtree(symm)
112{
113 seq.insert(seq.end(), v.begin(), v.end());
114 validate();
115}
116
117indexed::indexed(const symmetry & symm, const exprseq & es) : inherited(es), symtree(symm)
118{
119}
120
121indexed::indexed(const symmetry & symm, const exvector & v) : inherited(v), symtree(symm)
122{
123}
124
125indexed::indexed(const symmetry & symm, exvector && v) : inherited(std::move(v)), symtree(symm)
126{
127}
128
130// archiving
132
134{
135 inherited::read_archive(n, sym_lst);
136 if (!n.find_ex("symmetry", symtree, sym_lst)) {
137 // GiNaC versions <= 0.9.0 had an unsigned "symmetry" property
138 unsigned symm = 0;
139 n.find_unsigned("symmetry", symm);
140 switch (symm) {
141 case 1:
142 symtree = sy_symm();
143 break;
144 case 2:
145 symtree = sy_anti();
146 break;
147 default:
149 break;
150 }
151 const_cast<symmetry &>(ex_to<symmetry>(symtree)).validate(seq.size() - 1);
152 }
153}
155
157{
158 inherited::archive(n);
159 n.add_ex("symmetry", symtree);
160}
161
163// functions overriding virtual functions from base classes
165
166void indexed::printindices(const print_context & c, unsigned level) const
167{
168 if (seq.size() > 1) {
169
170 auto it = seq.begin() + 1, itend = seq.end();
171
172 if (is_a<print_latex>(c)) {
173
174 // TeX output: group by variance
175 bool first = true;
176 bool covariant = true;
177
178 while (it != itend) {
179 bool cur_covariant = (is_a<varidx>(*it) ? ex_to<varidx>(*it).is_covariant() : true);
180 if (first || cur_covariant != covariant) { // Variance changed
181 // The empty {} prevents indices from ending up on top of each other
182 if (!first)
183 c.s << "}{}";
184 covariant = cur_covariant;
185 if (covariant)
186 c.s << "_{";
187 else
188 c.s << "^{";
189 }
190 it->print(c, level);
191 c.s << " ";
192 first = false;
193 it++;
194 }
195 c.s << "}";
196
197 } else {
198
199 // Ordinary output
200 while (it != itend) {
201 it->print(c, level);
202 it++;
203 }
204 }
205 }
206}
207
208void indexed::print_indexed(const print_context & c, const char *openbrace, const char *closebrace, unsigned level) const
209{
210 if (precedence() <= level)
211 c.s << openbrace << '(';
212 c.s << openbrace;
213 seq[0].print(c, precedence());
214 c.s << closebrace;
215 printindices(c, level);
216 if (precedence() <= level)
217 c.s << ')' << closebrace;
218}
219
220void indexed::do_print(const print_context & c, unsigned level) const
221{
222 print_indexed(c, "", "", level);
223}
224
225void indexed::do_print_latex(const print_latex & c, unsigned level) const
226{
227 print_indexed(c, "{", "}", level);
228}
229
230void indexed::do_print_tree(const print_tree & c, unsigned level) const
231{
232 c.s << std::string(level, ' ') << class_name() << " @" << this
233 << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
234 << ", " << seq.size()-1 << " indices"
235 << ", symmetry=" << symtree << std::endl;
236 seq[0].print(c, level + c.delta_indent);
237 printindices(c, level + c.delta_indent);
238}
239
240bool indexed::info(unsigned inf) const
241{
242 if (inf == info_flags::indexed) return true;
243 if (inf == info_flags::has_indices) return seq.size() > 1;
244 return inherited::info(inf);
245}
246
247bool indexed::all_index_values_are(unsigned inf) const
248{
249 // No indices? Then no property can be fulfilled
250 if (seq.size() < 2)
251 return false;
252
253 // Check all indices
254 return find_if(seq.begin() + 1, seq.end(),
255 [inf](const ex & e) { return !(ex_to<idx>(e).get_value().info(inf)); }) == seq.end();
256}
257
258int indexed::compare_same_type(const basic & other) const
259{
260 GINAC_ASSERT(is_a<indexed>(other));
261 return inherited::compare_same_type(other);
262}
263
265{
266 const ex &base = seq[0];
267
268 // If the base object is 0, the whole object is 0
269 if (base.is_zero())
270 return _ex0;
271
272 // If the base object is a product, pull out the numeric factor
273 if (is_exactly_a<mul>(base) && is_exactly_a<numeric>(base.op(base.nops() - 1))) {
274 exvector v(seq);
275 ex f = ex_to<numeric>(base.op(base.nops() - 1));
276 v[0] = seq[0] / f;
277 return f * thiscontainer(v);
278 }
279
280 if((typeid(*this) == typeid(indexed)) && seq.size()==1)
281 return base;
282
283 // Canonicalize indices according to the symmetry properties
284 if (seq.size() > 2) {
285 exvector v = seq;
286 GINAC_ASSERT(is_exactly_a<symmetry>(symtree));
287 int sig = canonicalize(v.begin() + 1, ex_to<symmetry>(symtree));
288 if (sig != std::numeric_limits<int>::max()) {
289 // Something has changed while sorting indices, more evaluations later
290 if (sig == 0)
291 return _ex0;
292 return ex(sig) * thiscontainer(v);
293 }
294 }
295
296 // Let the class of the base object perform additional evaluations
297 return ex_to<basic>(base).eval_indexed(*this);
298}
299
301{
302 if(op(0).info(info_flags::real))
303 return *this;
304 return real_part_function(*this).hold();
305}
306
308{
309 if(op(0).info(info_flags::real))
310 return 0;
311 return imag_part_function(*this).hold();
312}
313
315{
316 return indexed(ex_to<symmetry>(symtree), v);
317}
318
320{
321 return indexed(ex_to<symmetry>(symtree), std::move(v));
322}
323
324unsigned indexed::return_type() const
325{
326 if(is_a<matrix>(op(0)))
328 else
329 return op(0).return_type();
330}
331
333{
334 GINAC_ASSERT(seq.size() > 0);
335
337 ex newbase = seq[0].expand(options);
338 if (is_exactly_a<add>(newbase)) {
339 ex sum = _ex0;
340 for (size_t i=0; i<newbase.nops(); i++) {
341 exvector s = seq;
342 s[0] = newbase.op(i);
343 sum += thiscontainer(s).expand(options);
344 }
345 return sum;
346 }
347 if (!are_ex_trivially_equal(newbase, seq[0])) {
348 exvector s = seq;
349 s[0] = newbase;
350 return ex_to<indexed>(thiscontainer(s)).inherited::expand(options);
351 }
352 }
353 return inherited::expand(options);
354}
355
357// virtual functions which can be overridden by derived classes
359
360// none
361
363// non-virtual functions in this class
365
370{
371 GINAC_ASSERT(seq.size() > 0);
372 auto it = seq.begin() + 1, itend = seq.end();
373 while (it != itend) {
374 if (!is_a<idx>(*it))
375 throw(std::invalid_argument("indices of indexed object must be of type idx"));
376 it++;
377 }
378
379 if (!symtree.is_zero()) {
380 if (!is_exactly_a<symmetry>(symtree))
381 throw(std::invalid_argument("symmetry of indexed object must be of type symmetry"));
382 const_cast<symmetry &>(ex_to<symmetry>(symtree)).validate(seq.size() - 1);
383 }
384}
385
390{
391 return _ex0;
392}
393
395// global functions
397
399 bool operator() (const ex &lh, const ex &rh) const
400 {
401 if (lh.is_equal(rh))
402 return true;
403 else
404 try {
405 // Replacing the dimension might cause an error (e.g. with
406 // index classes that only work in a fixed number of dimensions)
407 return lh.is_equal(ex_to<idx>(rh).replace_dim(ex_to<idx>(lh).get_dim()));
408 } catch (...) {
409 return false;
410 }
411 }
412};
413
415static bool indices_consistent(const exvector & v1, const exvector & v2)
416{
417 // Number of indices must be the same
418 if (v1.size() != v2.size())
419 return false;
420
421 return equal(v1.begin(), v1.end(), v2.begin(), idx_is_equal_ignore_dim());
422}
423
425{
426 GINAC_ASSERT(seq.size() >= 1);
427 return exvector(seq.begin() + 1, seq.end());
428}
429
431{
432 exvector free_indices, dummy_indices;
433 find_free_and_dummy(seq.begin() + 1, seq.end(), free_indices, dummy_indices);
434 return dummy_indices;
435}
436
438{
439 exvector indices = get_free_indices();
440 exvector other_indices = other.get_free_indices();
441 indices.insert(indices.end(), other_indices.begin(), other_indices.end());
442 exvector dummy_indices;
443 find_dummy_indices(indices, dummy_indices);
444 return dummy_indices;
445}
446
447bool indexed::has_dummy_index_for(const ex & i) const
448{
449 auto it = seq.begin() + 1, itend = seq.end();
450 while (it != itend) {
451 if (is_dummy_pair(*it, i))
452 return true;
453 it++;
454 }
455 return false;
456}
457
459{
460 exvector free_indices, dummy_indices;
461 find_free_and_dummy(seq.begin() + 1, seq.end(), free_indices, dummy_indices);
462 return free_indices;
463}
464
466{
467 exvector free_indices;
468 for (size_t i=0; i<nops(); i++) {
469 if (i == 0)
470 free_indices = op(i).get_free_indices();
471 else {
472 exvector free_indices_of_term = op(i).get_free_indices();
473 if (!indices_consistent(free_indices, free_indices_of_term))
474 throw (std::runtime_error("add::get_free_indices: inconsistent indices in sum"));
475 }
476 }
477 return free_indices;
478}
479
481{
482 // Concatenate free indices of all factors
483 exvector un;
484 for (size_t i=0; i<nops(); i++) {
485 exvector free_indices_of_factor = op(i).get_free_indices();
486 un.insert(un.end(), free_indices_of_factor.begin(), free_indices_of_factor.end());
487 }
488
489 // And remove the dummy indices
490 exvector free_indices, dummy_indices;
491 find_free_and_dummy(un, free_indices, dummy_indices);
492 return free_indices;
493}
494
496{
497 // Concatenate free indices of all factors
498 exvector un;
499 for (size_t i=0; i<nops(); i++) {
500 exvector free_indices_of_factor = op(i).get_free_indices();
501 un.insert(un.end(), free_indices_of_factor.begin(), free_indices_of_factor.end());
502 }
503
504 // And remove the dummy indices
505 exvector free_indices, dummy_indices;
506 find_free_and_dummy(un, free_indices, dummy_indices);
507 return free_indices;
508}
509
511 bool operator()(const ex & e)
512 {
513 return is_dummy_pair(e, e);
514 }
515};
516
518{
519 if (a.get_free_indices().size() || b.get_free_indices().size())
520 throw (std::runtime_error("integral::get_free_indices: boundary values should not have free indices"));
521 return f.get_free_indices();
522}
523
524template<class T> size_t number_of_type(const exvector&v)
525{
526 size_t number = 0;
527 for (auto & it : v)
528 if (is_exactly_a<T>(it))
529 ++number;
530 return number;
531}
532
541template<class T> static ex rename_dummy_indices(const ex & e, exvector & global_dummy_indices, exvector & local_dummy_indices)
542{
543 size_t global_size = number_of_type<T>(global_dummy_indices),
544 local_size = number_of_type<T>(local_dummy_indices);
545
546 // Any local dummy indices at all?
547 if (local_size == 0)
548 return e;
549
550 if (global_size < local_size) {
551
552 // More local indices than we encountered before, add the new ones
553 // to the global set
554 size_t old_global_size = global_size;
555 int remaining = local_size - global_size;
556 auto it = local_dummy_indices.begin(), itend = local_dummy_indices.end();
557 while (it != itend && remaining > 0) {
558 if (is_exactly_a<T>(*it) &&
559 find_if(global_dummy_indices.begin(), global_dummy_indices.end(),
560 [it](const ex &lh) { return idx_is_equal_ignore_dim()(lh, *it); }) == global_dummy_indices.end()) {
561 global_dummy_indices.push_back(*it);
562 global_size++;
563 remaining--;
564 }
565 it++;
566 }
567
568 // If this is the first set of local indices, do nothing
569 if (old_global_size == 0)
570 return e;
571 }
572 GINAC_ASSERT(local_size <= global_size);
573
574 // Construct vectors of index symbols
575 exvector local_syms, global_syms;
576 local_syms.reserve(local_size);
577 global_syms.reserve(local_size);
578 for (size_t i=0; local_syms.size()!=local_size; i++)
579 if(is_exactly_a<T>(local_dummy_indices[i]))
580 local_syms.push_back(local_dummy_indices[i].op(0));
581 shaker_sort(local_syms.begin(), local_syms.end(), ex_is_less(), ex_swap());
582 for (size_t i=0; global_syms.size()!=local_size; i++) // don't use more global symbols than necessary
583 if(is_exactly_a<T>(global_dummy_indices[i]))
584 global_syms.push_back(global_dummy_indices[i].op(0));
585 shaker_sort(global_syms.begin(), global_syms.end(), ex_is_less(), ex_swap());
586
587 // Remove common indices
588 exvector local_uniq, global_uniq;
589 set_difference(local_syms.begin(), local_syms.end(), global_syms.begin(), global_syms.end(), std::back_insert_iterator<exvector>(local_uniq), ex_is_less());
590 set_difference(global_syms.begin(), global_syms.end(), local_syms.begin(), local_syms.end(), std::back_insert_iterator<exvector>(global_uniq), ex_is_less());
591
592 // Replace remaining non-common local index symbols by global ones
593 if (local_uniq.empty())
594 return e;
595 else {
596 while (global_uniq.size() > local_uniq.size())
597 global_uniq.pop_back();
598 return e.subs(lst(local_uniq.begin(), local_uniq.end()), lst(global_uniq.begin(), global_uniq.end()), subs_options::no_pattern);
599 }
600}
601
603static void find_variant_indices(const exvector & v, exvector & variant_indices)
604{
605 exvector::const_iterator it1, itend;
606 for (it1 = v.begin(), itend = v.end(); it1 != itend; ++it1) {
607 if (is_exactly_a<varidx>(*it1))
608 variant_indices.push_back(*it1);
609 }
610}
611
619bool reposition_dummy_indices(ex & e, exvector & variant_dummy_indices, exvector & moved_indices)
620{
621 bool something_changed = false;
622
623 // Find dummy symbols that occur twice in the same indexed object.
624 exvector local_var_dummies;
625 local_var_dummies.reserve(e.nops()/2);
626 for (size_t i=1; i<e.nops(); ++i) {
627 if (!is_a<varidx>(e.op(i)))
628 continue;
629 for (size_t j=i+1; j<e.nops(); ++j) {
630 if (is_dummy_pair(e.op(i), e.op(j))) {
631 local_var_dummies.push_back(e.op(i));
632 for (auto k = variant_dummy_indices.begin(); k!=variant_dummy_indices.end(); ++k) {
633 if (e.op(i).op(0) == k->op(0)) {
634 variant_dummy_indices.erase(k);
635 break;
636 }
637 }
638 break;
639 }
640 }
641 }
642
643 // In the case where a dummy symbol occurs twice in the same indexed object
644 // we try all possibilities of raising/lowering and keep the least one in
645 // the sense of ex_is_less.
646 ex optimal_e = e;
647 size_t numpossibs = 1 << local_var_dummies.size();
648 for (size_t i=0; i<numpossibs; ++i) {
649 ex try_e = e;
650 for (size_t j=0; j<local_var_dummies.size(); ++j) {
651 exmap m;
652 if (1<<j & i) {
653 ex curr_idx = local_var_dummies[j];
654 ex curr_toggle = ex_to<varidx>(curr_idx).toggle_variance();
655 m[curr_idx] = curr_toggle;
656 m[curr_toggle] = curr_idx;
657 }
658 try_e = e.subs(m, subs_options::no_pattern);
659 }
660 if(ex_is_less()(try_e, optimal_e))
661 { optimal_e = try_e;
662 something_changed = true;
663 }
664 }
665 e = optimal_e;
666
667 if (!is_a<indexed>(e))
668 return true;
669
670 exvector seq = ex_to<indexed>(e).seq;
671
672 // If a dummy index is encountered for the first time in the
673 // product, pull it up, otherwise, pull it down
674 for (auto it2 = seq.begin()+1, it2end = seq.end(); it2 != it2end; ++it2) {
675 if (!is_exactly_a<varidx>(*it2))
676 continue;
677
678 exvector::iterator vit, vitend;
679 for (vit = variant_dummy_indices.begin(), vitend = variant_dummy_indices.end(); vit != vitend; ++vit) {
680 if (it2->op(0).is_equal(vit->op(0))) {
681 if (ex_to<varidx>(*it2).is_covariant()) {
682 /*
683 * N.B. we don't want to use
684 *
685 * e = e.subs(lst{
686 * *it2 == ex_to<varidx>(*it2).toggle_variance(),
687 * ex_to<varidx>(*it2).toggle_variance() == *it2
688 * }, subs_options::no_pattern);
689 *
690 * since this can trigger non-trivial repositioning of indices,
691 * e.g. due to non-trivial symmetry properties of e, thus
692 * invalidating iterators
693 */
694 *it2 = ex_to<varidx>(*it2).toggle_variance();
695 something_changed = true;
696 }
697 moved_indices.push_back(*vit);
698 variant_dummy_indices.erase(vit);
699 goto next_index;
700 }
701 }
702
703 for (vit = moved_indices.begin(), vitend = moved_indices.end(); vit != vitend; ++vit) {
704 if (it2->op(0).is_equal(vit->op(0))) {
705 if (ex_to<varidx>(*it2).is_contravariant()) {
706 *it2 = ex_to<varidx>(*it2).toggle_variance();
707 something_changed = true;
708 }
709 goto next_index;
710 }
711 }
712
713next_index: ;
714 }
715
716 if (something_changed)
717 e = ex_to<indexed>(e).thiscontainer(seq);
718
719 return something_changed;
720}
721
722/* Ordering that only compares the base expressions of indexed objects. */
724 bool operator() (const ex &lh, const ex &rh) const
725 {
726 return (is_a<indexed>(lh) ? lh.op(0) : lh).compare(is_a<indexed>(rh) ? rh.op(0) : rh) < 0;
727 }
728};
729
730/* An auxiliary function used by simplify_indexed() and expand_dummy_sum()
731 * It returns an exvector of factors from the supplied product */
732static void product_to_exvector(const ex & e, exvector & v, bool & non_commutative)
733{
734 // Remember whether the product was commutative or noncommutative
735 // (because we chop it into factors and need to reassemble later)
736 non_commutative = is_exactly_a<ncmul>(e);
737
738 // Collect factors in an exvector, store squares twice
739 v.reserve(e.nops() * 2);
740
741 if (is_exactly_a<power>(e)) {
742 // We only get called for simple squares, split a^2 -> a*a
744 v.push_back(e.op(0));
745 v.push_back(e.op(0));
746 } else {
747 for (size_t i=0; i<e.nops(); i++) {
748 ex f = e.op(i);
749 if (is_exactly_a<power>(f) && f.op(1).is_equal(_ex2)) {
750 v.push_back(f.op(0));
751 v.push_back(f.op(0));
752 } else if (is_exactly_a<ncmul>(f)) {
753 // Noncommutative factor found, split it as well
754 non_commutative = true; // everything becomes noncommutative, ncmul will sort out the commutative factors later
755 for (size_t j=0; j<f.nops(); j++)
756 v.push_back(f.op(j));
757 } else
758 v.push_back(f);
759 }
760 }
761}
762
763template<class T> ex idx_symmetrization(const ex& r,const exvector& local_dummy_indices)
764{ exvector dummy_syms;
765 dummy_syms.reserve(r.nops());
766 for (auto & it : local_dummy_indices)
767 if(is_exactly_a<T>(it))
768 dummy_syms.push_back(it.op(0));
769 if(dummy_syms.size() < 2)
770 return r;
771 ex q=symmetrize(r, dummy_syms);
772 return q;
773}
774
775// Forward declaration needed in absence of friend injection, C.f. [namespace.memdef]:
776ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indices, const scalar_products & sp);
777
780ex simplify_indexed_product(const ex & e, exvector & free_indices, exvector & dummy_indices, const scalar_products & sp)
781{
782 // Collect factors in an exvector
783 exvector v;
784
785 // Remember whether the product was commutative or noncommutative
786 // (because we chop it into factors and need to reassemble later)
787 bool non_commutative;
788 product_to_exvector(e, v, non_commutative);
789
790 // Perform contractions
791 bool something_changed = false;
792 bool has_nonsymmetric = false;
793 GINAC_ASSERT(v.size() > 1);
794 exvector::iterator it1, itend = v.end(), next_to_last = itend - 1;
795 for (it1 = v.begin(); it1 != next_to_last; it1++) {
796
797try_again:
798 if (!is_a<indexed>(*it1))
799 continue;
800
801 bool first_noncommutative = (it1->return_type() != return_types::commutative);
802 bool first_nonsymmetric = ex_to<symmetry>(ex_to<indexed>(*it1).get_symmetry()).has_nonsymmetric();
803
804 // Indexed factor found, get free indices and look for contraction
805 // candidates
806 exvector free1, dummy1;
807 find_free_and_dummy(ex_to<indexed>(*it1).seq.begin() + 1, ex_to<indexed>(*it1).seq.end(), free1, dummy1);
808
809 exvector::iterator it2;
810 for (it2 = it1 + 1; it2 != itend; it2++) {
811
812 if (!is_a<indexed>(*it2))
813 continue;
814
815 bool second_noncommutative = (it2->return_type() != return_types::commutative);
816
817 // Find free indices of second factor and merge them with free
818 // indices of first factor
819 exvector un;
820 find_free_and_dummy(ex_to<indexed>(*it2).seq.begin() + 1, ex_to<indexed>(*it2).seq.end(), un, dummy1);
821 un.insert(un.end(), free1.begin(), free1.end());
822
823 // Check whether the two factors share dummy indices
824 exvector free, dummy;
825 find_free_and_dummy(un, free, dummy);
826 size_t num_dummies = dummy.size();
827 if (num_dummies == 0)
828 continue;
829
830 // At least one dummy index, is it a defined scalar product?
831 bool contracted = false;
832 if (free.empty() && it1->nops()==2 && it2->nops()==2) {
833
834 ex dim = minimal_dim(
835 ex_to<idx>(it1->op(1)).get_dim(),
836 ex_to<idx>(it2->op(1)).get_dim()
837 );
838
839 // User-defined scalar product?
840 if (sp.is_defined(*it1, *it2, dim)) {
841
842 // Yes, substitute it
843 *it1 = sp.evaluate(*it1, *it2, dim);
844 *it2 = _ex1;
845 goto contraction_done;
846 }
847 }
848
849 // Try to contract the first one with the second one
850 contracted = ex_to<basic>(it1->op(0)).contract_with(it1, it2, v);
851 if (!contracted) {
852
853 // That didn't work; maybe the second object knows how to
854 // contract itself with the first one
855 contracted = ex_to<basic>(it2->op(0)).contract_with(it2, it1, v);
856 }
857 if (contracted) {
858contraction_done:
859 if (first_noncommutative || second_noncommutative
860 || is_exactly_a<add>(*it1) || is_exactly_a<add>(*it2)
861 || is_exactly_a<mul>(*it1) || is_exactly_a<mul>(*it2)
862 || is_exactly_a<ncmul>(*it1) || is_exactly_a<ncmul>(*it2)) {
863
864 // One of the factors became a sum or product:
865 // re-expand expression and run again
866 // Non-commutative products are always re-expanded to give
867 // eval_ncmul() the chance to re-order and canonicalize
868 // the product
869 bool is_a_product = (is_exactly_a<mul>(*it1) || is_exactly_a<ncmul>(*it1)) &&
870 (is_exactly_a<mul>(*it2) || is_exactly_a<ncmul>(*it2));
871 ex r = (non_commutative ? ex(ncmul(std::move(v))) : ex(mul(std::move(v))));
872
873 // If new expression is a product we can call this function again,
874 // otherwise we need to pass argument to simplify_indexed() to be expanded
875 if (is_a_product)
876 return simplify_indexed_product(r, free_indices, dummy_indices, sp);
877 else
878 return simplify_indexed(r, free_indices, dummy_indices, sp);
879 }
880
881 // Both objects may have new indices now or they might
882 // even not be indexed objects any more, so we have to
883 // start over
884 something_changed = true;
885 goto try_again;
886 }
887 else if (!has_nonsymmetric &&
888 (first_nonsymmetric ||
889 ex_to<symmetry>(ex_to<indexed>(*it2).get_symmetry()).has_nonsymmetric())) {
890 has_nonsymmetric = true;
891 }
892 }
893 }
894
895 // Find free indices (concatenate them all and call find_free_and_dummy())
896 // and all dummy indices that appear
897 exvector un, individual_dummy_indices;
898 for (it1 = v.begin(), itend = v.end(); it1 != itend; ++it1) {
899 exvector free_indices_of_factor;
900 if (is_a<indexed>(*it1)) {
901 exvector dummy_indices_of_factor;
902 find_free_and_dummy(ex_to<indexed>(*it1).seq.begin() + 1, ex_to<indexed>(*it1).seq.end(), free_indices_of_factor, dummy_indices_of_factor);
903 individual_dummy_indices.insert(individual_dummy_indices.end(), dummy_indices_of_factor.begin(), dummy_indices_of_factor.end());
904 } else
905 free_indices_of_factor = it1->get_free_indices();
906 un.insert(un.end(), free_indices_of_factor.begin(), free_indices_of_factor.end());
907 }
908 exvector local_dummy_indices;
909 find_free_and_dummy(un, free_indices, local_dummy_indices);
910 local_dummy_indices.insert(local_dummy_indices.end(), individual_dummy_indices.begin(), individual_dummy_indices.end());
911
912 // Filter out the dummy indices with variance
913 exvector variant_dummy_indices;
914 find_variant_indices(local_dummy_indices, variant_dummy_indices);
915
916 // Any indices with variance present at all?
917 if (!variant_dummy_indices.empty()) {
918
919 // Yes, bring the product into a canonical order that only depends on
920 // the base expressions of indexed objects
921 if (!non_commutative)
922 std::sort(v.begin(), v.end(), ex_base_is_less());
923
924 exvector moved_indices;
925
926 // Iterate over all indexed objects in the product
927 for (it1 = v.begin(), itend = v.end(); it1 != itend; ++it1) {
928 if (!is_a<indexed>(*it1))
929 continue;
930
931 if (reposition_dummy_indices(*it1, variant_dummy_indices, moved_indices))
932 something_changed = true;
933 }
934 }
935
936 ex r;
937 if (something_changed)
938 r = non_commutative ? ex(ncmul(std::move(v))) : ex(mul(std::move(v)));
939 else
940 r = e;
941
942 // The result should be symmetric with respect to exchange of dummy
943 // indices, so if the symmetrization vanishes, the whole expression is
944 // zero. This detects things like eps.i.j.k * p.j * p.k = 0.
945 if (has_nonsymmetric) {
946 ex q = idx_symmetrization<idx>(r, local_dummy_indices);
947 if (q.is_zero()) {
948 free_indices.clear();
949 return _ex0;
950 }
951 q = idx_symmetrization<varidx>(q, local_dummy_indices);
952 if (q.is_zero()) {
953 free_indices.clear();
954 return _ex0;
955 }
956 q = idx_symmetrization<spinidx>(q, local_dummy_indices);
957 if (q.is_zero()) {
958 free_indices.clear();
959 return _ex0;
960 }
961 }
962
963 // Dummy index renaming
964 r = rename_dummy_indices<idx>(r, dummy_indices, local_dummy_indices);
965 r = rename_dummy_indices<varidx>(r, dummy_indices, local_dummy_indices);
966 r = rename_dummy_indices<spinidx>(r, dummy_indices, local_dummy_indices);
967
968 // Product of indexed object with a scalar?
969 if (is_exactly_a<mul>(r) && r.nops() == 2
970 && is_exactly_a<numeric>(r.op(1)) && is_a<indexed>(r.op(0)))
971 return ex_to<basic>(r.op(0).op(0)).scalar_mul_indexed(r.op(0), ex_to<numeric>(r.op(1)));
972 else
973 return r;
974}
975
978class terminfo {
979public:
980 terminfo(const ex & orig_, const ex & symm_) : orig(orig_), symm(symm_) {}
981
984};
985
987public:
988 bool operator() (const terminfo & ti1, const terminfo & ti2) const
989 {
990 return (ti1.symm.compare(ti2.symm) < 0);
991 }
992};
993
996class symminfo {
997public:
998 symminfo() : num(0) {}
999
1000 symminfo(const ex & symmterm_, const ex & orig_, size_t num_) : orig(orig_), num(num_)
1001 {
1002 if (is_exactly_a<mul>(symmterm_) && is_exactly_a<numeric>(symmterm_.op(symmterm_.nops()-1))) {
1003 coeff = symmterm_.op(symmterm_.nops()-1);
1004 symmterm = symmterm_ / coeff;
1005 } else {
1006 coeff = 1;
1007 symmterm = symmterm_;
1008 }
1009 }
1010
1014 size_t num;
1015};
1016
1018public:
1019 bool operator() (const symminfo & si1, const symminfo & si2) const
1020 {
1021 return (si1.symmterm.compare(si2.symmterm) < 0);
1022 }
1023};
1024
1026public:
1027 bool operator() (const symminfo & si1, const symminfo & si2) const
1028 {
1029 return (si1.orig.compare(si2.orig) < 0);
1030 }
1031};
1032
1033bool hasindex(const ex &x, const ex &sym)
1034{
1035 if(is_a<idx>(x) && x.op(0)==sym)
1036 return true;
1037 else
1038 for(size_t i=0; i<x.nops(); ++i)
1039 if(hasindex(x.op(i), sym))
1040 return true;
1041 return false;
1042}
1043
1045ex simplify_indexed(const ex & e, exvector & free_indices, exvector & dummy_indices, const scalar_products & sp)
1046{
1047 // Expand the expression
1048 ex e_expanded = e.expand();
1049
1050 // Simplification of single indexed object: just find the free indices
1051 // and perform dummy index renaming/repositioning
1052 if (is_a<indexed>(e_expanded)) {
1053
1054 // Find the dummy indices
1055 const indexed &i = ex_to<indexed>(e_expanded);
1056 exvector local_dummy_indices;
1057 find_free_and_dummy(i.seq.begin() + 1, i.seq.end(), free_indices, local_dummy_indices);
1058
1059 // Filter out the dummy indices with variance
1060 exvector variant_dummy_indices;
1061 find_variant_indices(local_dummy_indices, variant_dummy_indices);
1062
1063 // Any indices with variance present at all?
1064 if (!variant_dummy_indices.empty()) {
1065
1066 // Yes, reposition them
1067 exvector moved_indices;
1068 reposition_dummy_indices(e_expanded, variant_dummy_indices, moved_indices);
1069 }
1070
1071 // Rename the dummy indices
1072 e_expanded = rename_dummy_indices<idx>(e_expanded, dummy_indices, local_dummy_indices);
1073 e_expanded = rename_dummy_indices<varidx>(e_expanded, dummy_indices, local_dummy_indices);
1074 e_expanded = rename_dummy_indices<spinidx>(e_expanded, dummy_indices, local_dummy_indices);
1075 return e_expanded;
1076 }
1077
1078 // Simplification of sum = sum of simplifications, check consistency of
1079 // free indices in each term
1080 if (is_exactly_a<add>(e_expanded)) {
1081 bool first = true;
1082 ex sum;
1083 free_indices.clear();
1084
1085 for (size_t i=0; i<e_expanded.nops(); i++) {
1086 exvector free_indices_of_term;
1087 ex term = simplify_indexed(e_expanded.op(i), free_indices_of_term, dummy_indices, sp);
1088 if (!term.is_zero()) {
1089 if (first) {
1090 free_indices = free_indices_of_term;
1091 sum = term;
1092 first = false;
1093 } else {
1094 if (!indices_consistent(free_indices, free_indices_of_term)) {
1095 std::ostringstream s;
1096 s << "simplify_indexed: inconsistent indices in sum: ";
1097 s << exprseq(free_indices) << " vs. " << exprseq(free_indices_of_term);
1098 throw (std::runtime_error(s.str()));
1099 }
1100 if (is_a<indexed>(sum) && is_a<indexed>(term))
1101 sum = ex_to<basic>(sum.op(0)).add_indexed(sum, term);
1102 else
1103 sum += term;
1104 }
1105 }
1106 }
1107
1108 // If the sum turns out to be zero, we are finished
1109 if (sum.is_zero()) {
1110 free_indices.clear();
1111 return sum;
1112 }
1113
1114 // More than one term and more than one dummy index?
1115 size_t num_terms_orig = (is_exactly_a<add>(sum) ? sum.nops() : 1);
1116 if (num_terms_orig < 2 || dummy_indices.size() < 2)
1117 return sum;
1118
1119 // Chop the sum into terms and symmetrize each one over the dummy
1120 // indices
1121 std::vector<terminfo> terms;
1122 for (size_t i=0; i<sum.nops(); i++) {
1123 const ex & term = sum.op(i);
1124 exvector dummy_indices_of_term;
1125 dummy_indices_of_term.reserve(dummy_indices.size());
1126 for (auto & i : dummy_indices)
1127 if (hasindex(term,i.op(0)))
1128 dummy_indices_of_term.push_back(i);
1129 ex term_symm = idx_symmetrization<idx>(term, dummy_indices_of_term);
1130 term_symm = idx_symmetrization<varidx>(term_symm, dummy_indices_of_term);
1131 term_symm = idx_symmetrization<spinidx>(term_symm, dummy_indices_of_term);
1132 if (term_symm.is_zero())
1133 continue;
1134 terms.push_back(terminfo(term, term_symm));
1135 }
1136
1137 // Sort by symmetrized terms
1138 std::sort(terms.begin(), terms.end(), terminfo_is_less());
1139
1140 // Combine equal symmetrized terms
1141 std::vector<terminfo> terms_pass2;
1142 for (std::vector<terminfo>::const_iterator i=terms.begin(); i!=terms.end(); ) {
1143 size_t num = 1;
1144 auto j = i + 1;
1145 while (j != terms.end() && j->symm == i->symm) {
1146 num++;
1147 j++;
1148 }
1149 terms_pass2.push_back(terminfo(i->orig * num, i->symm * num));
1150 i = j;
1151 }
1152
1153 // If there is only one term left, we are finished
1154 if (terms_pass2.size() == 1)
1155 return terms_pass2[0].orig;
1156
1157 // Chop the symmetrized terms into subterms
1158 std::vector<symminfo> sy;
1159 for (auto & i : terms_pass2) {
1160 if (is_exactly_a<add>(i.symm)) {
1161 size_t num = i.symm.nops();
1162 for (size_t j=0; j<num; j++)
1163 sy.push_back(symminfo(i.symm.op(j), i.orig, num));
1164 } else
1165 sy.push_back(symminfo(i.symm, i.orig, 1));
1166 }
1167
1168 // Sort by symmetrized subterms
1169 std::sort(sy.begin(), sy.end(), symminfo_is_less_by_symmterm());
1170
1171 // Combine equal symmetrized subterms
1172 std::vector<symminfo> sy_pass2;
1173 exvector result;
1174 for (auto i=sy.begin(); i!=sy.end(); ) {
1175
1176 // Combine equal terms
1177 auto j = i + 1;
1178 if (j != sy.end() && j->symmterm == i->symmterm) {
1179
1180 // More than one term, collect the coefficients
1181 ex coeff = i->coeff;
1182 while (j != sy.end() && j->symmterm == i->symmterm) {
1183 coeff += j->coeff;
1184 j++;
1185 }
1186
1187 // Add combined term to result
1188 if (!coeff.is_zero())
1189 result.push_back(coeff * i->symmterm);
1190
1191 } else {
1192
1193 // Single term, store for second pass
1194 sy_pass2.push_back(*i);
1195 }
1196
1197 i = j;
1198 }
1199
1200 // Were there any remaining terms that didn't get combined?
1201 if (sy_pass2.size() > 0) {
1202
1203 // Yes, sort by their original terms
1204 std::sort(sy_pass2.begin(), sy_pass2.end(), symminfo_is_less_by_orig());
1205
1206 for (std::vector<symminfo>::const_iterator i=sy_pass2.begin(); i!=sy_pass2.end(); ) {
1207
1208 // How many symmetrized terms of this original term are left?
1209 size_t num = 1;
1210 auto j = i + 1;
1211 while (j != sy_pass2.end() && j->orig == i->orig) {
1212 num++;
1213 j++;
1214 }
1215
1216 if (num == i->num) {
1217
1218 // All terms left, then add the original term to the result
1219 result.push_back(i->orig);
1220
1221 } else {
1222
1223 // Some terms were combined with others, add up the remaining symmetrized terms
1224 std::vector<symminfo>::const_iterator k;
1225 for (k=i; k!=j; k++)
1226 result.push_back(k->coeff * k->symmterm);
1227 }
1228
1229 i = j;
1230 }
1231 }
1232
1233 // Add all resulting terms
1234 ex sum_symm = dynallocate<add>(result);
1235 if (sum_symm.is_zero())
1236 free_indices.clear();
1237 return sum_symm;
1238 }
1239
1240 // Simplification of products
1241 if (is_exactly_a<mul>(e_expanded)
1242 || is_exactly_a<ncmul>(e_expanded)
1243 || (is_exactly_a<power>(e_expanded) && is_a<indexed>(e_expanded.op(0)) && e_expanded.op(1).is_equal(_ex2)))
1244 return simplify_indexed_product(e_expanded, free_indices, dummy_indices, sp);
1245
1246 // Cannot do anything
1247 free_indices.clear();
1248 return e_expanded;
1249}
1250
1258{
1259 exvector free_indices, dummy_indices;
1260 scalar_products sp;
1261 return GiNaC::simplify_indexed(*this, free_indices, dummy_indices, sp);
1262}
1263
1273{
1274 exvector free_indices, dummy_indices;
1275 return GiNaC::simplify_indexed(*this, free_indices, dummy_indices, sp);
1276}
1277
1280{
1281 return GiNaC::symmetrize(*this, get_free_indices());
1282}
1283
1286{
1287 return GiNaC::antisymmetrize(*this, get_free_indices());
1288}
1289
1292{
1294}
1295
1297// helper classes
1299
1300spmapkey::spmapkey(const ex & v1_, const ex & v2_, const ex & dim_) : dim(dim_)
1301{
1302 // If indexed, extract base objects
1303 ex s1 = is_a<indexed>(v1_) ? v1_.op(0) : v1_;
1304 ex s2 = is_a<indexed>(v2_) ? v2_.op(0) : v2_;
1305
1306 // Enforce canonical order in pair
1307 if (s1.compare(s2) > 0) {
1308 v1 = s2;
1309 v2 = s1;
1310 } else {
1311 v1 = s1;
1312 v2 = s2;
1313 }
1314}
1315
1316bool spmapkey::operator==(const spmapkey &other) const
1317{
1318 if (!v1.is_equal(other.v1))
1319 return false;
1320 if (!v2.is_equal(other.v2))
1321 return false;
1322 if (is_a<wildcard>(dim) || is_a<wildcard>(other.dim))
1323 return true;
1324 else
1325 return dim.is_equal(other.dim);
1326}
1327
1328bool spmapkey::operator<(const spmapkey &other) const
1329{
1330 int cmp = v1.compare(other.v1);
1331 if (cmp)
1332 return cmp < 0;
1333 cmp = v2.compare(other.v2);
1334 if (cmp)
1335 return cmp < 0;
1336
1337 // Objects are equal, now check dimensions
1338 if (is_a<wildcard>(dim) || is_a<wildcard>(other.dim))
1339 return false;
1340 else
1341 return dim.compare(other.dim) < 0;
1342}
1343
1345{
1346 std::cerr << "(" << v1 << "," << v2 << "," << dim << ")";
1347}
1348
1349void scalar_products::add(const ex & v1, const ex & v2, const ex & sp)
1350{
1351 spm[spmapkey(v1, v2)] = sp;
1352}
1353
1354void scalar_products::add(const ex & v1, const ex & v2, const ex & dim, const ex & sp)
1355{
1356 spm[spmapkey(v1, v2, dim)] = sp;
1357}
1358
1359void scalar_products::add_vectors(const lst & l, const ex & dim)
1360{
1361 // Add all possible pairs of products
1362 for (auto & it1 : l)
1363 for (auto & it2 : l)
1364 add(it1, it2, it1 * it2);
1365}
1366
1368{
1369 spm.clear();
1370}
1371
1373bool scalar_products::is_defined(const ex & v1, const ex & v2, const ex & dim) const
1374{
1375 return spm.find(spmapkey(v1, v2, dim)) != spm.end();
1376}
1377
1379ex scalar_products::evaluate(const ex & v1, const ex & v2, const ex & dim) const
1380{
1381 return spm.find(spmapkey(v1, v2, dim))->second;
1382}
1383
1385{
1386 std::cerr << "map size=" << spm.size() << std::endl;
1387 for (auto & it : spm) {
1388 const spmapkey & k = it.first;
1389 std::cerr << "item key=";
1390 k.debugprint();
1391 std::cerr << ", value=" << it.second << std::endl;
1392 }
1393}
1394
1396{
1397 if (is_a<indexed>(e))
1398 return ex_to<indexed>(e).get_dummy_indices();
1399 else if (is_a<power>(e) && e.op(1)==2) {
1400 return e.op(0).get_free_indices();
1401 }
1402 else if (is_a<mul>(e) || is_a<ncmul>(e)) {
1403 exvector dummies;
1404 exvector free_indices;
1405 for (std::size_t i = 0; i < e.nops(); ++i) {
1406 exvector dummies_of_factor = get_all_dummy_indices_safely(e.op(i));
1407 dummies.insert(dummies.end(), dummies_of_factor.begin(),
1408 dummies_of_factor.end());
1409 exvector free_of_factor = e.op(i).get_free_indices();
1410 free_indices.insert(free_indices.begin(), free_of_factor.begin(),
1411 free_of_factor.end());
1412 }
1413 exvector free_out, dummy_out;
1414 find_free_and_dummy(free_indices.begin(), free_indices.end(), free_out,
1415 dummy_out);
1416 dummies.insert(dummies.end(), dummy_out.begin(), dummy_out.end());
1417 return dummies;
1418 }
1419 else if(is_a<add>(e)) {
1420 exvector result;
1421 for(std::size_t i = 0; i < e.nops(); ++i) {
1422 exvector dummies_of_term = get_all_dummy_indices_safely(e.op(i));
1423 sort(dummies_of_term.begin(), dummies_of_term.end());
1424 exvector new_vec;
1425 set_union(result.begin(), result.end(), dummies_of_term.begin(),
1426 dummies_of_term.end(), std::back_inserter<exvector>(new_vec),
1427 ex_is_less());
1428 result.swap(new_vec);
1429 }
1430 return result;
1431 }
1432 return exvector();
1433}
1434
1437{
1438 exvector p;
1439 bool nc;
1440 product_to_exvector(e, p, nc);
1441 auto ip = p.begin(), ipend = p.end();
1442 exvector v, v1;
1443 while (ip != ipend) {
1444 if (is_a<indexed>(*ip)) {
1445 v1 = ex_to<indexed>(*ip).get_dummy_indices();
1446 v.insert(v.end(), v1.begin(), v1.end());
1447 auto ip1 = ip + 1;
1448 while (ip1 != ipend) {
1449 if (is_a<indexed>(*ip1)) {
1450 v1 = ex_to<indexed>(*ip).get_dummy_indices(ex_to<indexed>(*ip1));
1451 v.insert(v.end(), v1.begin(), v1.end());
1452 }
1453 ++ip1;
1454 }
1455 }
1456 ++ip;
1457 }
1458 return v;
1459}
1460
1462{
1463 exvector common_indices;
1464 set_intersection(va.begin(), va.end(), vb.begin(), vb.end(), std::back_insert_iterator<exvector>(common_indices), ex_is_less());
1465 if (common_indices.empty()) {
1466 return lst{lst{}, lst{}};
1467 } else {
1468 exvector new_indices, old_indices;
1469 old_indices.reserve(2*common_indices.size());
1470 new_indices.reserve(2*common_indices.size());
1471 exvector::const_iterator ip = common_indices.begin(), ipend = common_indices.end();
1472 while (ip != ipend) {
1473 ex newsym = dynallocate<symbol>();
1474 ex newidx;
1475 if(is_exactly_a<spinidx>(*ip))
1476 newidx = dynallocate<spinidx>(newsym, ex_to<spinidx>(*ip).get_dim(),
1477 ex_to<spinidx>(*ip).is_covariant(),
1478 ex_to<spinidx>(*ip).is_dotted());
1479 else if (is_exactly_a<varidx>(*ip))
1480 newidx = dynallocate<varidx>(newsym, ex_to<varidx>(*ip).get_dim(),
1481 ex_to<varidx>(*ip).is_covariant());
1482 else
1483 newidx = dynallocate<idx>(newsym, ex_to<idx>(*ip).get_dim());
1484 old_indices.push_back(*ip);
1485 new_indices.push_back(newidx);
1486 if(is_a<varidx>(*ip)) {
1487 old_indices.push_back(ex_to<varidx>(*ip).toggle_variance());
1488 new_indices.push_back(ex_to<varidx>(newidx).toggle_variance());
1489 }
1490 ++ip;
1491 }
1492 return lst{lst(old_indices.begin(), old_indices.end()), lst(new_indices.begin(), new_indices.end())};
1493 }
1494}
1495
1496ex rename_dummy_indices_uniquely(const exvector & va, const exvector & vb, const ex & b)
1497{
1498 lst indices_subs = rename_dummy_indices_uniquely(va, vb);
1499 return (indices_subs.op(0).nops()>0 ? b.subs(ex_to<lst>(indices_subs.op(0)), ex_to<lst>(indices_subs.op(1)), subs_options::no_pattern|subs_options::no_index_renaming) : b);
1500}
1501
1503{
1505 if (va.size() > 0) {
1507 if (vb.size() > 0) {
1508 sort(va.begin(), va.end(), ex_is_less());
1509 sort(vb.begin(), vb.end(), ex_is_less());
1510 lst indices_subs = rename_dummy_indices_uniquely(va, vb);
1511 if (indices_subs.op(0).nops() > 0)
1512 return b.subs(ex_to<lst>(indices_subs.op(0)), ex_to<lst>(indices_subs.op(1)), subs_options::no_pattern|subs_options::no_index_renaming);
1513 }
1514 }
1515 return b;
1516}
1517
1518ex rename_dummy_indices_uniquely(exvector & va, const ex & b, bool modify_va)
1519{
1520 if (va.size() > 0) {
1522 if (vb.size() > 0) {
1523 sort(vb.begin(), vb.end(), ex_is_less());
1524 lst indices_subs = rename_dummy_indices_uniquely(va, vb);
1525 if (indices_subs.op(0).nops() > 0) {
1526 if (modify_va) {
1527 for (auto & i : ex_to<lst>(indices_subs.op(1)))
1528 va.push_back(i);
1529 exvector uncommon_indices;
1530 set_difference(vb.begin(), vb.end(), indices_subs.op(0).begin(), indices_subs.op(0).end(), std::back_insert_iterator<exvector>(uncommon_indices), ex_is_less());
1531 for (auto & ip : uncommon_indices)
1532 va.push_back(ip);
1533 sort(va.begin(), va.end(), ex_is_less());
1534 }
1535 return b.subs(ex_to<lst>(indices_subs.op(0)), ex_to<lst>(indices_subs.op(1)), subs_options::no_pattern|subs_options::no_index_renaming);
1536 }
1537 }
1538 }
1539 return b;
1540}
1541
1542ex expand_dummy_sum(const ex & e, bool subs_idx)
1543{
1544 ex e_expanded = e.expand();
1546 if (is_a<add>(e_expanded) || is_a<lst>(e_expanded) || is_a<matrix>(e_expanded)) {
1547 return e_expanded.map(fcn);
1548 } else if (is_a<ncmul>(e_expanded) || is_a<mul>(e_expanded) || is_a<power>(e_expanded) || is_a<indexed>(e_expanded)) {
1549 exvector v;
1550 if (is_a<indexed>(e_expanded))
1551 v = ex_to<indexed>(e_expanded).get_dummy_indices();
1552 else
1553 v = get_all_dummy_indices(e_expanded);
1554 ex result = e_expanded;
1555 for (const auto & nu : v) {
1556 if (ex_to<idx>(nu).get_dim().info(info_flags::nonnegint)) {
1557 int idim = ex_to<numeric>(ex_to<idx>(nu).get_dim()).to_int();
1558 ex en = 0;
1559 for (int i=0; i < idim; i++) {
1560 if (subs_idx && is_a<varidx>(nu)) {
1561 ex other = ex_to<varidx>(nu).toggle_variance();
1562 en += result.subs(lst{
1563 nu == idx(i, idim),
1564 other == idx(i, idim)
1565 });
1566 } else {
1567 en += result.subs( nu.op(0) == i );
1568 }
1569 }
1570 result = en;
1571 }
1572 }
1573 return result;
1574 } else {
1575 return e;
1576 }
1577}
1578
1579} // namespace GiNaC
Interface to GiNaC's sums of expressions.
Archiving of GiNaC expressions.
#define GINAC_ASSERT(X)
Assertion macro for checking invariances.
Definition assertion.h:33
Sum of expressions.
Definition add.h:32
exvector get_free_indices() const override
Return a vector containing the free indices of an expression.
Definition indexed.cpp:465
This class stores all properties needed to record/retrieve the state of one object of class basic (or...
Definition archive.h:49
This class is the ABC (abstract base class) of GiNaC's class hierarchy.
Definition basic.h:105
unsigned hashvalue
hash value
Definition basic.h:303
unsigned flags
of type status_flags
Definition basic.h:302
virtual int compare_same_type(const basic &other) const
Returns order relation between two objects of same type.
Definition basic.cpp:719
Wrapper template for making GiNaC classes out of STL containers.
Definition container.h:73
size_t nops() const override
Number of operands/members.
Definition container.h:118
ex op(size_t i) const override
Return operand/member at position i.
Definition container.h:295
Lightweight wrapper for GiNaC's symbolic objects.
Definition ex.h:73
ex map(map_function &f) const
Definition ex.h:163
const_iterator begin() const noexcept
Definition ex.h:663
exvector get_free_indices() const
Definition ex.h:207
bool find(const ex &pattern, exset &found) const
Find all occurrences of a pattern.
Definition ex.cpp:106
ex expand(unsigned options=0) const
Expand an expression.
Definition ex.cpp:74
bool is_equal(const ex &other) const
Definition ex.h:346
ex simplify_indexed(unsigned options=0) const
Simplify/canonicalize expression containing indexed objects.
Definition indexed.cpp:1257
ex symmetrize_cyclic() const
Symmetrize expression by cyclic permutation over its free indices.
Definition indexed.cpp:1291
unsigned return_type() const
Definition ex.h:231
const_iterator end() const noexcept
Definition ex.h:668
size_t nops() const
Definition ex.h:136
ex subs(const exmap &m, unsigned options=0) const
Definition ex.h:842
int compare(const ex &other) const
Definition ex.h:323
ex symmetrize() const
Symmetrize expression over its free indices.
Definition indexed.cpp:1279
bool is_zero() const
Definition ex.h:214
void print(const print_context &c, unsigned level=0) const
Print expression to stream.
Definition ex.cpp:55
ex op(size_t i) const
Definition ex.h:137
ex coeff(const ex &s, int n=1) const
Definition ex.h:176
ex antisymmetrize() const
Antisymmetrize expression over its free indices.
Definition indexed.cpp:1285
size_t nops() const override
Number of operands/members.
ex op(size_t i) const override
Return operand/member at position i.
@ expand_indexed
expands (a+b).i to a.i+b.i
Definition flags.h:32
This class holds one index of an indexed object.
Definition idx.h:36
This class holds an indexed expression.
Definition indexed.h:40
bool all_index_values_are(unsigned inf) const
Check whether all index values have a certain property.
Definition indexed.cpp:247
ex thiscontainer(const exvector &v) const override
Definition indexed.cpp:314
unsigned precedence() const override
Return relative operator precedence (for parenthezing output).
Definition indexed.h:136
void printindices(const print_context &c, unsigned level) const
Definition indexed.cpp:166
exvector get_free_indices() const override
Return a vector containing the free indices of an expression.
Definition indexed.cpp:458
void archive(archive_node &n) const override
Save (a.k.a.
Definition indexed.cpp:156
void do_print(const print_context &c, unsigned level) const
Definition indexed.cpp:220
ex expand(unsigned options=0) const override
Expand expression, i.e.
Definition indexed.cpp:332
void do_print_latex(const print_latex &c, unsigned level) const
Definition indexed.cpp:225
void read_archive(const archive_node &n, lst &syms) override
Read (a.k.a.
Definition indexed.cpp:133
void do_print_tree(const print_tree &c, unsigned level) const
Definition indexed.cpp:230
ex derivative(const symbol &s) const override
Implementation of ex::diff() for an indexed object always returns 0.
Definition indexed.cpp:389
bool info(unsigned inf) const override
Information about the object.
Definition indexed.cpp:240
ex eval() const override
Perform automatic non-interruptive term rewriting rules.
Definition indexed.cpp:264
indexed(const ex &b)
Construct indexed object with no index.
Definition indexed.cpp:65
ex real_part() const override
Definition indexed.cpp:300
void validate() const
Check whether all indices are of class idx and validate the symmetry tree.
Definition indexed.cpp:369
ex symtree
Index symmetry (tree of symmetry objects)
Definition indexed.h:192
exvector get_indices() const
Return a vector containing the object's indices.
Definition indexed.cpp:424
exvector get_dummy_indices() const
Return a vector containing the dummy indices of the object, if any.
Definition indexed.cpp:430
void print_indexed(const print_context &c, const char *openbrace, const char *closebrace, unsigned level) const
Definition indexed.cpp:208
bool has_dummy_index_for(const ex &i) const
Check whether the object has an index that forms a dummy index pair with a given index.
Definition indexed.cpp:447
unsigned return_type() const override
Definition indexed.cpp:324
ex imag_part() const override
Definition indexed.cpp:307
exvector get_free_indices() const override
Return a vector containing the free indices of an expression.
Definition indexed.cpp:517
Product of expressions.
Definition mul.h:32
exvector get_free_indices() const override
Return a vector containing the free indices of an expression.
Definition indexed.cpp:480
Non-commutative product of expressions.
Definition ncmul.h:33
exvector get_free_indices() const override
Return a vector containing the free indices of an expression.
Definition indexed.cpp:495
Base class for print_contexts.
Definition print.h:102
Context for latex-parsable output.
Definition print.h:122
Context for tree-like output for debugging.
Definition print.h:146
Helper class for storing information about known scalar products which are to be automatically replac...
Definition indexed.h:217
bool is_defined(const ex &v1, const ex &v2, const ex &dim) const
Check whether scalar product pair is defined.
Definition indexed.cpp:1373
void debugprint() const
Definition indexed.cpp:1384
ex evaluate(const ex &v1, const ex &v2, const ex &dim) const
Return value of defined scalar product pair.
Definition indexed.cpp:1379
void add(const ex &v1, const ex &v2, const ex &sp)
Register scalar product pair and its value.
Definition indexed.cpp:1349
void clear()
Clear all registered scalar products.
Definition indexed.cpp:1367
void add_vectors(const lst &l, const ex &dim=wild())
Register list of vectors.
Definition indexed.cpp:1359
void debugprint() const
Definition indexed.cpp:1344
bool operator<(const spmapkey &other) const
Definition indexed.cpp:1328
bool operator==(const spmapkey &other) const
Definition indexed.cpp:1316
@ no_pattern
disable pattern matching
Definition flags.h:51
Basic CAS symbol.
Definition symbol.h:39
This class describes the symmetry of a group of indices.
Definition symmetry.h:39
bool operator()(const symminfo &si1, const symminfo &si2) const
Definition indexed.cpp:1027
bool operator()(const symminfo &si1, const symminfo &si2) const
Definition indexed.cpp:1019
This structure stores the individual symmetrized terms obtained during the simplification of sums.
Definition indexed.cpp:996
ex coeff
coefficient of symmetrized term
Definition indexed.cpp:1012
ex symmterm
symmetrized term
Definition indexed.cpp:1011
ex orig
original term
Definition indexed.cpp:1013
size_t num
how many symmetrized terms resulted from the original term
Definition indexed.cpp:1014
symminfo(const ex &symmterm_, const ex &orig_, size_t num_)
Definition indexed.cpp:1000
bool operator()(const terminfo &ti1, const terminfo &ti2) const
Definition indexed.cpp:988
This structure stores the original and symmetrized versions of terms obtained during the simplificati...
Definition indexed.cpp:978
terminfo(const ex &orig_, const ex &symm_)
Definition indexed.cpp:980
ex symm
symmetrized term
Definition indexed.cpp:983
ex orig
original term
Definition indexed.cpp:982
unsigned options
Definition factor.cpp:2474
vector< int > k
Definition factor.cpp:1435
size_t n
Definition factor.cpp:1432
size_t c
Definition factor.cpp:757
ex x
Definition factor.cpp:1610
size_t r
Definition factor.cpp:757
mvec m
Definition factor.cpp:758
Interface to GiNaC's indices.
Interface to GiNaC's indexed expressions.
Interface to GiNaC's initially known functions.
Interface to GiNaC's symbolic integral.
Definition of GiNaC's lst.
Interface to symbolic matrices.
Interface to GiNaC's products of expressions.
Definition add.cpp:36
static bool indices_consistent(const exvector &v1, const exvector &v2)
Check whether two sorted index vectors are consistent (i.e.
Definition indexed.cpp:415
ex idx_symmetrization(const ex &r, const exvector &local_dummy_indices)
Definition indexed.cpp:763
const ex _ex2
Definition utils.cpp:389
ex minimal_dim(const ex &dim1, const ex &dim2)
Return the minimum of two index dimensions.
Definition idx.cpp:560
const symmetry & not_symmetric()
Definition symmetry.cpp:349
container< std::list > lst
Definition lst.h:32
std::map< ex, ex, ex_is_less > exmap
Definition basic.h:50
symmetry sy_symm()
Definition symmetry.h:121
ex symmetrize(const ex &thisex)
Definition ex.h:809
static void find_variant_indices(const exvector &v, exvector &variant_indices)
Given a set of indices, extract those of class varidx.
Definition indexed.cpp:603
const ex _ex1
Definition utils.cpp:385
bool are_ex_trivially_equal(const ex &e1, const ex &e2)
Compare two objects of class quickly without doing a deep tree traversal.
Definition ex.h:700
bool reposition_dummy_indices(ex &e, exvector &variant_dummy_indices, exvector &moved_indices)
Raise/lower dummy indices in a single indexed objects to canonicalize their variance.
Definition indexed.cpp:619
static ex rename_dummy_indices(const ex &e, exvector &global_dummy_indices, exvector &local_dummy_indices)
Rename dummy indices in an expression.
Definition indexed.cpp:541
ex simplify_indexed(const ex &thisex, unsigned options=0)
Definition ex.h:803
bool is_dummy_pair(const idx &i1, const idx &i2)
Check whether two indices form a dummy pair.
Definition idx.cpp:501
print_func< print_context >(&varidx::do_print). print_func< print_latex >(&varidx
Definition idx.cpp:44
symmetry sy_anti()
Definition symmetry.h:126
ex simplify_indexed_product(const ex &e, exvector &free_indices, exvector &dummy_indices, const scalar_products &sp)
Simplify product of indexed expressions (commutative, noncommutative and simple squares),...
Definition indexed.cpp:780
ex antisymmetrize(const ex &thisex)
Definition ex.h:815
ex op(const ex &thisex, size_t i)
Definition ex.h:827
void shaker_sort(It first, It last, Cmp comp, Swap swapit)
Definition utils.h:193
ex coeff(const ex &thisex, const ex &s, int n=1)
Definition ex.h:758
static ex symm(const ex &e, exvector::const_iterator first, exvector::const_iterator last, bool asymmetric)
Definition symmetry.cpp:484
int canonicalize(exvector::iterator v, const symmetry &symm)
Canonicalize the order of elements of an expression vector, according to the symmetry properties defi...
Definition symmetry.cpp:437
static void product_to_exvector(const ex &e, exvector &v, bool &non_commutative)
Definition indexed.cpp:732
size_t number_of_type(const exvector &v)
Definition indexed.cpp:524
void find_dummy_indices(const exvector &v, exvector &out_dummy)
Given a vector of indices, find the dummy indices.
Definition idx.h:245
lst rename_dummy_indices_uniquely(const exvector &va, const exvector &vb)
Similar to above, where va and vb are the same and the return value is a list of two lists for substi...
Definition indexed.cpp:1461
ex symmetrize_cyclic(const ex &thisex)
Definition ex.h:821
GINAC_IMPLEMENT_REGISTERED_CLASS_OPT_T(lst, basic, print_func< print_context >(&lst::do_print). print_func< print_tree >(&lst::do_print_tree)) template<> bool lst GINAC_BIND_UNARCHIVER(lst)
Specialization of container::info() for lst.
Definition lst.cpp:42
const ex _ex0
Definition utils.cpp:369
void find_free_and_dummy(exvector::const_iterator it, exvector::const_iterator itend, exvector &out_free, exvector &out_dummy)
Given a vector of indices, split them into two vectors, one containing the free indices,...
Definition idx.cpp:520
ex expand_dummy_sum(const ex &e, bool subs_idx)
This function returns the given expression with expanded sums for all dummy index summations,...
Definition indexed.cpp:1542
std::vector< ex > exvector
Definition basic.h:48
bool hasindex(const ex &x, const ex &sym)
Definition indexed.cpp:1033
exvector get_all_dummy_indices(const ex &e)
Returns all dummy indices from the exvector.
Definition indexed.cpp:1436
container< std::vector > exprseq
Definition exprseq.h:32
exvector get_all_dummy_indices_safely(const ex &e)
More reliable version of the form.
Definition indexed.cpp:1395
Definition ex.h:988
Interface to GiNaC's non-commutative products of expressions.
Interface to GiNaC's overloaded operators.
Interface to GiNaC's symbolic exponentiation (basis^exponent).
#define GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(classname, supername, options)
Macro for inclusion in the implementation of each registered class.
Definition registrar.h:184
Interface to relations between expressions.
bool operator()(const ex &lh, const ex &rh) const
Definition indexed.cpp:724
bool operator()(const ex &lh, const ex &rh) const
Definition indexed.cpp:399
bool operator()(const ex &e)
Definition indexed.cpp:511
Interface to GiNaC's symbolic objects.
Interface to GiNaC's symmetry definitions.
Interface to several small and furry utilities needed within GiNaC but not of any interest to the use...

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.