1
2 #include "Functional.hh"
3 #include "Cleanup.hh"
4 #include "Permutations.hh"
5 #include "MultiIndex.hh"
6 //#include "SympyCdb.hh"
7 #include "algorithms/evaluate.hh"
8 #include "algorithms/simplify.hh"
9 #include "algorithms/substitute.hh"
10 #include "properties/EpsilonTensor.hh"
11 #include "properties/PartialDerivative.hh"
12 #include "properties/Coordinate.hh"
13 #include "properties/Depends.hh"
14 #include "properties/Accent.hh"
15 #include <functional>
16
17 // #define DEBUG 1
18
19 using namespace cadabra;
20
evaluate(const Kernel & k,Ex & tr,const Ex & c,bool rhs,bool simplify)21 evaluate::evaluate(const Kernel& k, Ex& tr, const Ex& c, bool rhs, bool simplify)
22 : Algorithm(k, tr), components(c), only_rhs(rhs), call_sympy(simplify)
23 {
24 }
25
can_apply(iterator it)26 bool evaluate::can_apply(iterator it)
27 {
28 return tr.is_head(it); // only act at top level, we descend ourselves
29 }
30
is_scalar_function(iterator it) const31 bool evaluate::is_scalar_function(iterator it) const
32 {
33 if(*it->name=="\\pow" || *it->name=="\\exp" || *it->name=="\\sin" || *it->name=="\\cos" ) return true;
34 return false;
35 }
36
apply(iterator & it)37 Algorithm::result_t evaluate::apply(iterator& it)
38 {
39 result_t res=result_t::l_no_action;
40
41 // The first pass is top-down.
42
43
44 // The second pass is bottom-up. The general logic of the routines
45 // this calls is that, instead of looping over all possible index
46 // value sets, we look straight at the substitution rules, and
47 // check that these are required for some index values (this is
48 // where symmetry arguments should come in as well).
49 //
50 // The logic in Compare.cc helps us by matching component A_{t r}
51 // in the rule to an abstract tensor A_{m n} in the expression, storing
52 // the index name -> index value map.
53
54 it = cadabra::do_subtree(tr, it, [&](Ex::iterator walk) -> Ex::iterator {
55 #ifdef DEBUG
56 std::cerr << "evaluate at " << walk << std::endl;
57 #endif
58
59 if(*(walk->name)=="\\components") walk = handle_components(walk);
60 // FIXME: currently \pow is the only function for which we go straight up without
61 // evaluating. For this reason, its children do not get wrapped in a \components node
62 // in handle_factor. This needs to be extended to other function as well.
63 else if(is_scalar_function(walk))
64 {
65 unwrap_scalar_in_components_node(walk); // this is a scalar
66 return walk;
67 }
68 else if(is_component(walk)) return walk;
69 else if(*(walk->name)=="\\sum") walk = handle_sum(walk);
70 else if(*(walk->name)=="\\prod" || *(walk->name)=="\\wedge" || *(walk->name)=="\\frac")
71 walk = handle_prod(walk);
72 else
73 {
74 const PartialDerivative *pd = kernel.properties.get<PartialDerivative>(walk);
75 if(pd) walk = handle_derivative(walk);
76 else {
77 const EpsilonTensor *eps = kernel.properties.get<EpsilonTensor>(walk);
78 if(eps) {
79 walk = handle_epsilon(walk);
80 }
81 else if(*walk->name!="\\equals" && walk->is_index()==false) {
82 if(! (only_rhs && tr.is_head(walk)==false && ( *(tr.parent(walk)->name)=="\\equals" || *(tr.parent(walk)->name)=="\\arrow" ) && tr.index(walk)==0) ) {
83 index_map_t empty;
84 sibling_iterator tmp(walk);
85 #ifdef DEBUG
86 std::cerr << "handling factor" << std::endl;
87 std::cerr << *walk->name << std::endl;
88 #endif
89 walk = handle_factor(tmp, empty);
90 // std::cerr << "handling factor done" << std::endl;
91 }
92 }
93 }
94 }
95 return walk;
96 }
97 );
98
99 // Final cleanup, e.g. to reduce scalar expressions to proper
100 // scalars instead of 'components' nodes.
101
102 cleanup_dispatch_deep(kernel, tr);
103
104 return res;
105 }
106
is_component(iterator it) const107 bool evaluate::is_component(iterator it) const
108 {
109 // FIXME: The fact that this is called in the main loop above
110 // prevents any evaluation of tensorial expressions which appear
111 // inside components. Such things could in principle appear if, in
112 // an existing components node, a scalar was replaced with an
113 // object built from a tensor.
114
115 while(true) {
116 if(*it->name=="\\components") {
117 return true;
118 }
119 if(tr.is_head(it)==false)
120 it=tr.parent(it);
121 else
122 break;
123 }
124 return false;
125 }
126
handle_components(iterator it)127 Ex::iterator evaluate::handle_components(iterator it)
128 {
129 // This just cleans up component nodes. At the moment this means
130 // taking care of handling dummy pairs.
131
132 index_map_t ind_free, ind_dummy;
133 classify_indices(it, ind_free, ind_dummy);
134 if(ind_dummy.size()==0) return it;
135
136 // Wrap in a product, use handle_prod to sort out summation.
137 it = tr.wrap(it, str_node("\\prod"));
138 it = handle_prod(it);
139 return it;
140 }
141
handle_sum(iterator it)142 Ex::iterator evaluate::handle_sum(iterator it)
143 {
144 // std::cerr << "handle sum" << Ex(it) << std::endl;
145
146 // pre-scan: remove zero nodes from evaluate having processed
147 // nodes at lower level, and ensure child nodes are \component
148 // nodes.
149 sibling_iterator sib=tr.begin(it);
150 while(sib!=tr.end(it)) {
151 sibling_iterator nxt=sib;
152 ++nxt;
153
154 if(*sib->multiplier==0) { // zero terms can be removed
155 tr.erase(sib);
156 }
157 else if(is_component(sib)==false) {
158 index_map_t empty;
159 handle_factor(sib, empty);
160 }
161
162 sib=nxt;
163 }
164 if(tr.number_of_children(it)==0) {
165 node_zero(it);
166 return it;
167 }
168
169 index_map_t full_ind_free, full_ind_dummy;
170
171 // First find the values that all indices will need to take. We do not loop over
172 // them, but we need them in order to figure out which patterns in the rule can
173 // match to patterns in the expression.
174
175 classify_indices(it, full_ind_free, full_ind_dummy);
176 for(auto i: full_ind_free) {
177 // std::cerr << "finding prop for " << Ex(i.second) << std::endl;
178 const Indices *prop = kernel.properties.get<Indices>(i.second);
179 if(prop==0) {
180 const Coordinate *crd = kernel.properties.get<Coordinate>(i.second, true);
181 if(crd==0)
182 throw ArgumentException("evaluate: Index "+*(i.second->name)
183 +" does not have an Indices property.");
184 }
185
186 if(prop!=0 && prop->values.size()==0)
187 throw ArgumentException("evaluate: Do not know values of index "+*(i.second->name)+".");
188 }
189
190 // Iterate over all terms in the sum. These should be of two types: \component nodes,
191 // which we do not need to touch anymore, and nodes which have still not been
192 // evaluated. We send them all to handle_factor, which will return immediately on the
193 // first node type, and convert the second type to the first.
194
195 sib=tr.begin(it);
196 while(sib!=tr.end(it)) {
197 sibling_iterator nxt=sib;
198 ++nxt;
199 if(sib->is_zero())
200 sib=tr.erase(sib);
201 else {
202 handle_factor(sib, full_ind_free);
203 sib=nxt;
204 }
205 }
206
207 // Now all terms in the sum (which has its top node at 'it') are
208 // \component nodes. We need to merge these together into a single
209 // node.
210
211 auto sib1=tr.begin(it);
212 // merge_component_children(sib1);
213 auto sib2=sib1;
214 ++sib2;
215 while(sib2!=tr.end(it)) {
216 #ifdef DEBUG
217 std::cerr << "merging components " << Ex(sib1) << " and " << Ex(sib2) << std::endl;
218 #endif
219 merge_components(sib1, sib2);
220 sib2=tr.erase(sib2);
221 }
222 cleanup_components(sib1);
223
224 it=tr.flatten_and_erase(it);
225
226 return it;
227 }
228
handle_factor(sibling_iterator sib,const index_map_t & full_ind_free)229 Ex::iterator evaluate::handle_factor(sibling_iterator sib, const index_map_t& full_ind_free)
230 {
231 #ifdef DEBUG
232 std::cerr << "handle_factor " << Ex(sib) << std::endl;
233 #endif
234 if(*sib->name=="\\components") return sib;
235
236 // If this factor is an accent at the top level, descent further.
237 const Accent *acc = kernel.properties.get<Accent>(sib);
238 if(acc) {
239 auto deeper=tr.begin(sib);
240 handle_factor(deeper, full_ind_free);
241 // Put the accent on each of the components.
242 sibling_iterator cl = tr.end(deeper);
243 --cl;
244 cadabra::do_list(tr, cl, [&](Ex::iterator c) {
245 auto towrap = tr.child(c, 1);
246 tr.wrap(towrap, *sib);
247 return true;
248 });
249 //tr.print_recursive_treeform(std::cerr, sib);
250 // Move the component node up, outside the accent.
251 sib=tr.flatten(sib);
252 sib=tr.erase(sib);
253 //tr.print_recursive_treeform(std::cerr, sib);
254 return sib;
255 }
256
257 // We need to know for all indices whether they are free or dummy,
258 // in particular to handle internal contractions correctly.
259 index_map_t ind_free, ind_dummy;
260 classify_indices(sib, ind_free, ind_dummy);
261
262 // Pure scalar nodes need to be wrapped in a \component node to make life
263 // easier for the rest of the algorithm.
264 if(ind_free.size()==0 && ind_dummy.size()==0) {
265 if(!tr.is_head(sib) && *tr.parent(sib)->name!="\\pow") {
266 sib=wrap_scalar_in_components_node(sib);
267 #ifdef DEBUG
268 std::cerr << "wrapped scalar" << std::endl;
269 #endif
270 }
271 return sib;
272 }
273 // If the indices are all Coordinates, this is a scalar, not a tensor.
274 // It then needs simple wrapping just like a 'proper' scalar handed above.
275 if(ind_dummy.size()==0 && ind_free.size()!=0) {
276 bool all_coordinates=true;
277 for(auto& ind: ind_free) {
278 const Coordinate *crd = kernel.properties.get<Coordinate>(ind.second, true);
279 if(!crd) {
280 all_coordinates=false;
281 break;
282 }
283 }
284 if(all_coordinates) {
285 if(!tr.is_head(sib) && *tr.parent(sib)->name!="\\pow") {
286 sib=wrap_scalar_in_components_node(sib);
287 #ifdef DEBUG
288 std::cerr << "wrapped scalar with component derivatives" << std::endl;
289 #endif
290 }
291 return sib;
292 }
293 }
294
295 // Attempt to apply each component substitution rule on this term.
296 Ex repl("\\components");
297 for(auto& ind: ind_free)
298 repl.append_child(repl.begin(), ind.second);
299 // If there are no free indices, add an empty first child anyway,
300 // otherwise we need special cases in various other places.
301 auto vl = repl.append_child(repl.begin(), str_node("\\comma"));
302 bool has_acted=false;
303 cadabra::do_list(components, components.begin(), [&](Ex::iterator c) {
304 Ex rule(c);
305 Ex obj(sib);
306 // std::cerr << "attempting rule " << rule << " on " << obj << std::endl;
307 // rule is a single rule, we walk the list.
308 substitute subs(kernel, obj, rule);
309 subs.comparator.set_value_matches_index(true);
310 iterator oit=obj.begin();
311 if(subs.can_apply(oit)) {
312 has_acted=true;
313 // std::cerr << "can apply" << std::endl;
314 auto el = repl.append_child(vl, str_node("\\equals"));
315 auto il = repl.append_child(el, str_node("\\comma"));
316 auto fi = full_ind_free.begin();
317 // FIXME: need to do something sensible with indices on the lhs
318 // of rules which are not coordinates. You can have A_{m n} as expression,
319 // A_{0 0} -> r, A_{i j} -> \delta_{i j} as rule, but at the moment the
320 // second rule does not do the right thing.
321
322 // Store all free indices (not the dummies!) in the component node.
323 // If we have been passed an empty list of free indices (because the parent
324 // node is not a sum node), simply add all free index values in turn.
325 if(fi==full_ind_free.end()) {
326 // for(auto& r: subs.comparator.index_value_map) {
327 // repl.append_child(il, r.second.begin())->fl.parent_rel=str_node::p_none;
328 // }
329
330 fi=ind_free.begin();
331 while(fi!=ind_free.end()) {
332 for(auto& r: subs.comparator.index_value_map) {
333 if(fi->first == r.first) {
334 // std::cerr << "adding " << r.second.begin() << std::endl;
335 repl.append_child(il, r.second.begin())->fl.parent_rel=str_node::p_none;
336 break;
337 }
338 }
339 auto fiold(fi);
340 while(fi!=ind_free.end() && fiold->first==fi->first)
341 ++fi;
342 }
343 }
344 else {
345 while(fi!=full_ind_free.end()) {
346 for(auto& r: subs.comparator.index_value_map) {
347 if(fi->first == r.first) {
348 repl.append_child(il, r.second.begin())->fl.parent_rel=str_node::p_none;
349 break;
350 }
351 }
352 auto fiold(fi);
353 while(fi!=full_ind_free.end() && fiold->first==fi->first)
354 ++fi;
355 }
356 }
357 subs.apply(oit);
358 repl.append_child(el, obj.begin());
359
360 return true; // Cannot yet abort the do_list loop.
361 }
362 else {
363 // TRACE: There is no rule which matches this factor. This means that
364 // we want to keep all components?
365 }
366 return true;
367 });
368 if(!has_acted) {
369 // There was not a single rule which matched for this tensor. That's means
370 // that the user wants to keep the entire tensor (all components).
371 #ifdef DEBUG
372 std::cerr << "No single rule matched " << Ex(sib) << std::endl;
373 #endif
374 sib=dense_factor(sib, ind_free, ind_dummy);
375 }
376 else {
377 merge_component_children(repl.begin());
378
379 #ifdef DEBUG
380 std::cerr << "result now " << repl << std::endl;
381 #endif
382 sib = tr.move_ontop(iterator(sib), repl.begin());
383 }
384
385 return sib;
386 }
387
dense_factor(iterator it,const index_map_t & ind_free,const index_map_t & ind_dummy)388 Ex::iterator evaluate::dense_factor(iterator it, const index_map_t& ind_free, const index_map_t& ind_dummy)
389 {
390 if(ind_dummy.size()!=0)
391 throw RuntimeException("Cannot yet evaluate this expression.");
392
393 // For each index we need to iterate over all possible values, and generate a
394 // components node for it. This should be done 'on the fly' eventually, the way
395 // python treats 'map', but that will require wrapping all access to
396 // '\components' in a separate class.
397
398 index_position_map_t ind_pos_free;
399 fill_index_position_map(it, ind_free, ind_pos_free);
400
401 Ex comp("\\components");
402
403 auto fi = ind_free.begin();
404 //std::cerr << "dense factor with indices: ";
405 MultiIndex<Ex> mi;
406 while(fi!=ind_free.end()) {
407 comp.append_child(comp.begin(), fi->first.begin());
408 // Look up which values this index takes.
409 auto *id = kernel.properties.get<Indices>(fi->second);
410 std::vector<Ex> values;
411 if(!id || id->values.size()==0) {
412 // No index property known, or not known which values the index
413 // takes, so keep this index unexpanded.
414 auto val=Ex(fi->second);
415 val.begin()->fl.parent_rel=str_node::parent_rel_t::p_none;
416 values.push_back(val);
417 }
418 else {
419 for(const auto& ex: id->values)
420 values.push_back(ex);
421 }
422
423 mi.values.push_back(values);
424 ++fi;
425 }
426
427 auto comma=comp.append_child(comp.begin(), str_node("\\comma"));
428
429 // For each set of index values...
430 for(mi.start(); !mi.end(); ++mi) {
431 auto ivs = comp.append_child(comma, str_node("\\equals"));
432 auto ivsc = comp.append_child(ivs, str_node("\\comma"));
433 // ... add the values of the indices.
434 for(std::size_t i=0; i<mi.values.size(); ++i) {
435 comp.append_child(ivsc, mi[i].begin());
436 }
437 // ... then set the value of the tensor component.
438 auto repfac = comp.append_child(ivs, it);
439 fi = ind_free.begin();
440 size_t i=0;
441 while(fi!=ind_free.end()) {
442 auto il = begin_index(repfac);
443 auto num = ind_pos_free[fi->second];
444 il += num;
445 auto ii = iterator(il);
446 auto parent_rel = il->fl.parent_rel;
447 comp.replace(ii, mi[i].begin())->fl.parent_rel=parent_rel;
448 ++fi;
449 ++i;
450 }
451 }
452
453 #ifdef DEBUG
454 std::cerr << Ex(it) << std::endl;
455 #endif
456
457 it=tr.move_ontop(it, comp.begin());
458
459 return it;
460 }
461
merge_component_children(iterator it)462 void evaluate::merge_component_children(iterator it)
463 {
464 // Scan the entries of a single \components node for those
465 // which carry the same index value for the free indices.
466 // Such things can arise from e.g. A_{m} A_{m n} and the
467 // rule { A_{r}=3, A_{t}=5, A_{r t}=1, A_{t t}=2 }, which
468 // leads to two entries for the free index n=t.
469
470 // if(*it->name!="\\components")
471 // std::cerr << "*** " << *it->name << std::endl;
472 assert(*it->name=="\\components");
473
474 auto comma=tr.end(it);
475 --comma;
476
477 // if(*comma->name!="\\comma")
478 // std::cerr << "*** " << *comma->name << std::endl;
479 assert(*comma->name=="\\comma");
480
481 auto cv1=tr.begin(comma); // equals node
482 while(cv1!=tr.end(comma)) {
483 auto iv1=tr.begin(cv1); // index values \comma
484 auto cv2=cv1;
485 ++cv2;
486 while(cv2!=tr.end(comma)) {
487 auto iv2=tr.begin(cv2); // index values \comma
488 if(tr.equal_subtree(iv1, iv2)) {
489 // std::cerr << "merging " << Ex(iv1) << std::endl;
490 Ex::sibling_iterator tv1=iv1; // tensor component value
491 ++tv1;
492 Ex::sibling_iterator tv2=iv2;
493 ++tv2;
494 // std::cerr << "need to merge" << std::endl;
495 if(*tv1->name!="\\sum")
496 tv1=tr.wrap(tv1, str_node("\\sum"));
497 tr.append_child(tv1, tv2);
498 cv2=tr.erase(cv2);
499 }
500 else ++cv2;
501 }
502 ++cv1;
503 }
504 }
505
merge_components(iterator it1,iterator it2)506 void evaluate::merge_components(iterator it1, iterator it2)
507 {
508 // Merge two component nodes which come from two terms in a sum (so that
509 // we can be assured that the free indices match; they just may not be
510 // in the same order).
511
512 #ifdef DEBUG
513 std::cerr << "merge_components on " << Ex(it1) << " and " << Ex(it2) << std::endl;
514 #endif
515
516 assert(*it1->name=="\\components");
517 assert(*it2->name=="\\components");
518 sibling_iterator sib1=tr.end(it1);
519 --sib1;
520 sibling_iterator sib2=tr.end(it2);
521 --sib2;
522 assert(*sib1->name=="\\comma");
523 assert(*sib2->name=="\\comma");
524
525 // We cannot directly compare the lhs of the equals nodes of it1
526 // with the lhs of the equals node of it2, because the index order
527 // on the two components nodes may be different. We first have to
528 // ensure that the orders are the same (but only, of course) if we
529 // have anything to permutate in the first place.
530
531 if(*tr.begin(it1)->name!="\\comma") {
532 // Look at all indices on the two components nodes. Find
533 // the permutation that takes the indices on it2 and brings
534 // them in the order as they are on it1.
535 Perm perm;
536 perm.find(tr.begin(it2), sib2, tr.begin(it1), sib1);
537
538 // For each \equals node in the it2 comma node, permute
539 // the values so they agree with the index order on it1.
540 cadabra::do_list(tr, sib2, [&](Ex::iterator nd) {
541 // nd is an \equals node.
542 assert(*nd->name=="\\equals");
543 auto comma = tr.begin(nd);
544 assert(*comma->name=="\\comma");
545 perm.apply(tr.begin(comma), tr.end(comma));
546 return true;
547 });
548
549 #ifdef DEBUG
550 std::cerr << "permutations done" << std::endl;
551 #endif
552 }
553
554 // Now all index orders match and we can simply compare index value sets.
555
556 cadabra::do_list(tr, sib2, [&](Ex::iterator it2) {
557 assert(*it2->name=="\\equals");
558
559 auto lhs2 = tr.begin(it2);
560 auto found = cadabra::find_in_list(tr, sib1, [&](Ex::iterator it1) {
561
562 auto lhs1 = tr.begin(it1);
563 //std::cerr << "comparing " << *lhs1->name << " with " << *lhs2->name << std::endl;
564 if(tr.equal_subtree(lhs1, lhs2)) {
565 auto sum1=lhs1;
566 ++sum1;
567 auto sum2=lhs2;
568 ++sum2;
569 if(*sum1->name!="\\sum")
570 sum1=tr.wrap(sum1, str_node("\\sum"));
571 tr.append_child(sum1, sum2);
572 return iterator(sum1);
573 }
574
575 return tr.end();
576 });
577 if(found==tr.end()) {
578 tr.append_child(iterator(sib1), it2);
579 }
580 return true;
581 });
582
583
584 if(call_sympy)
585 simplify_components(it1);
586 }
587
cleanup_components(iterator it)588 void evaluate::cleanup_components(iterator it)
589 {
590 sibling_iterator sib=tr.end(it);
591 --sib;
592
593 cadabra::do_list(tr, sib, [&](Ex::iterator nd) {
594 auto iv=tr.begin(nd);
595 ++iv;
596 iterator p=iv;
597 cleanup_dispatch(kernel, tr, p);
598 return true;
599 });
600 }
601
handle_derivative(iterator it)602 Ex::iterator evaluate::handle_derivative(iterator it)
603 {
604 #ifdef DEBUG
605 std::cerr << "handle_derivative " << Ex(it) << std::endl;
606 #endif
607
608 // In order to figure out which components to keep, we need to do two things:
609 // expand into components the argument of the derivative, and then
610 // figure out the dependence of that argument on the various coordinates.
611 // There may be other orders (for e.g. situations where we want to keep entire
612 // components unevaluated), but that's for later when we have practical use cases.
613
614 sibling_iterator sib=tr.begin(it);
615 while(sib!=tr.end(it)) {
616 if(sib->is_index()==false) {
617 if(is_component(sib)==false) {
618 index_map_t empty;
619 // This really shouldn't be necessary; the way in which the
620 // top level 'apply' works, it should have rewritten the argument
621 // of the derivative into a \components node already.
622 sib=handle_factor(sib, empty);
623 }
624 break;
625 }
626 ++sib;
627 }
628 assert(sib!=tr.end(it));
629
630 // std::cerr << "after handle\n" << Ex(it) << std::endl;
631
632 index_map_t ind_free, ind_dummy;
633 classify_indices(it, ind_free, ind_dummy);
634
635 // Flag an error if a partial derivative has an upper index which
636 // is not position=free: this would require converting the index
637 // with a metric, and that should be done by the user using
638 // rewrite_indices.
639 //
640 // Also flag an error if there is more than one index type (we do
641 // not handle those cases yet).
642
643 auto fu = tr.begin(it);
644 const Indices *unique_indices=0;
645 while(fu!=tr.end(it)) {
646 if(fu->is_index()) {
647 const Indices *ind = kernel.properties.get<Indices>(fu);
648 if(ind!=unique_indices) {
649 if(unique_indices==0)
650 unique_indices=ind;
651 else
652 throw RuntimeException("All indices on a single derivative need to be of the same type.");
653 }
654
655 if(fu->fl.parent_rel==str_node::p_super) {
656 if(ind && ind->position_type!=Indices::free)
657 throw RuntimeException("All indices on derivatives need to be lowered first.");
658 }
659 }
660 ++fu;
661 }
662
663 // Figure out the positions of the index values in the components
664 // node inside the derivative which correspond to values of dummy
665 // indices (these necessarily have the other dummy on the
666 // derivative itself).
667 std::vector<std::pair<size_t, size_t>> dummy_positions;
668
669 decltype(ind_dummy.begin()) dumit[2];
670 dumit[0] = ind_dummy.begin();
671 while(dumit[0]!=ind_dummy.end()) {
672 dumit[1]=dumit[0];
673 ++dumit[1];
674 assert(dumit[1]!=ind_dummy.end());
675
676 bool on_component[2];
677 iterator parents[2];
678 for(int i=0; i<2; ++i) {
679 parents[i]=tr.parent(dumit[i]->second);
680 on_component[i]=*parents[i]->name=="\\components";
681 }
682
683 if(on_component[0]==false && on_component[1]==true)
684 dummy_positions.push_back(std::make_pair(tr.index(dumit[0]->second), tr.index(dumit[1]->second)));
685 else if(on_component[1]==false && on_component[0]==true)
686 dummy_positions.push_back(std::make_pair(tr.index(dumit[1]->second), tr.index(dumit[0]->second)));
687
688 ++dumit[0];
689 ++dumit[0];
690 }
691
692 // Walk all the index value sets of the \components node inside the
693 // \partial node. For each, determine the dependencies, and
694 // generate one element for each dependence.
695
696 sibling_iterator ivalues = tr.end(sib);
697 --ivalues;
698
699 size_t ni=number_of_direct_indices(it);
700
701 cadabra::do_list(tr, ivalues, [&](Ex::iterator iv) {
702 #ifdef DEBUG
703 std::cerr << "====" << std::endl;
704 std::cerr << Ex(iv) << std::endl;
705 #endif
706 // For each internal dummy set, keep track of the
707 // position in the permutation array where we generate
708 // its value.
709 std::map<Ex, int, tree_exact_less_for_indexmap_obj> d2p;
710
711 Ex_comparator comp(kernel.properties);
712
713 sibling_iterator rhs = tr.begin(iv);
714 ++rhs;
715 auto deps=dependencies(rhs);
716
717 if(deps.size()==0) {
718 pm->message("No dependencies for " + *rhs->name);
719 tr.erase(iv);
720 return true;
721 }
722
723 // If one of the dependencies is '\partial{#}', replace this will all the
724 // index values that can appear in the derivative.
725 auto dit = deps.begin();
726 while(dit!=deps.end()) {
727 comp.clear();
728 if(*(dit->begin()->name)=="\\partial") { // FIXME: do a proper full-tree comparison with '\partial{#}'.
729 // if(comp.equal_subtree("\partial{#}", *dit, Ex_comparator::useprops_t::never, true)==Ex_comparator::match_t::subtree_match) {
730 deps.erase(dit);
731 for(const auto& ival: unique_indices->values)
732 deps.insert(ival);
733 break;
734 }
735 ++dit;
736 }
737
738 // If the argument does not depend on anything, all derivatives
739 // would produce zero. Remove this \equals node from the tree.
740 if(deps.size()==0) {
741 pm->message("No relevant dependencies for " + *rhs->name);
742 tr.erase(iv);
743 return true;
744 }
745
746 // All indices on \partial can take any of the values of the
747 // dependencies, EXCEPT when the index is a dummy index OR
748 // when the index on the partial is a Coordinate. And of course
749 // we should check that the indices can actually take the
750 // values of the dependencies.
751 //
752 // In the 1st exceptional case, we firstly need to ensure
753 // that both indices in the dummy pair take the same value
754 // (this is done with d2p). Secondly, we need to ensure that
755 // if the second index sits on the argument, we only use the
756 // value of that index as given in the 'iv' list.
757 //
758 // In the 2nd exceptional case, we just need to determine if
759 // the particular derivative does not annihilate the argument.
760
761 // Need all combinations of values, with repetition (multiple
762 // pick) allowed.
763
764 combin::combinations<Ex> cb;
765 for(auto& obj: deps) {
766 #ifdef DEBUG
767 std::cerr << "dep " << obj << std::endl;
768 #endif
769 if(unique_indices) {
770 // std::cerr << "checking that deps are in values" << std::endl;
771 for(const auto& allowed: unique_indices->values) {
772 comp.clear();
773 if(comp.equal_subtree(allowed.begin(), obj.begin(), Ex_comparator::useprops_t::never, true)<=Ex_comparator::match_t::subtree_match) {
774 cb.original.push_back(obj);
775 break;
776 }
777 }
778 }
779 else
780 cb.original.push_back(obj);
781 }
782
783 if(cb.original.size()==0) {
784 // We may have had dependencies, but none of the index values can take those
785 // values. So all is zero.
786 tr.erase(iv);
787 return true;
788 }
789
790 cb.multiple_pick=true;
791 cb.block_length=1;
792 for(size_t n=0; n<ni; ++n) {
793 // If this child is a coordinate, take it out of the combinatorics
794 // of summing over index values (it's a single value).
795 if(kernel.properties.get<Coordinate>(tr.child(it, n), true)!=0)
796 continue;
797
798 Ex iname(tr.child(it,n)); // FIXME: does not handle Accented objects
799 if(ind_dummy.find(iname)!=ind_dummy.end()) {
800 // If this dummy has one leg on the argument of the derivative,
801 // take it out of the combinatorics, because its value will
802 // be fixed.
803 bool out=false;
804 for(auto& d: dummy_positions)
805 if(d.first==n) {
806 out=true;
807 break;
808 }
809 if(out) continue;
810
811 if(d2p.find(iname)!=d2p.end())
812 continue;
813 else {
814 d2p[iname]=cb.sublengths.size();
815 }
816 }
817 cb.sublengths.push_back(1);
818 }
819 if(cb.sublengths.size()>0) // only if not all indices are fixed
820 cb.permute();
821
822 #ifdef DEBUG
823 std::cerr << cb.size() << " permutations of indices" << std::endl;
824 #endif
825
826 // Note: indices on partial may be dummies, in which case the
827 // values cannot be arbitrary. This is a self-contraction,
828 // but cannot be caught by handle_factor because derivatives
829 // do not get handled by patterns directly, they get
830 // constructed by looking at dependencies.
831
832 // For each index value set we constructed for the indices on the
833 // derivative, create an entry in the \components node.
834
835 for(unsigned int i=0; i<cb.size() || cb.size()==0; ++i) {
836 // 'i' runs over all index combinations.
837 #ifdef DEBUG
838 std::cerr << "Index combination " << i << std::endl;
839 #endif
840 Ex eqcopy(iv);
841 auto lhs=eqcopy.begin(eqcopy.begin());
842 assert(*lhs->name=="\\comma");
843
844 if(cb.size()>0) {
845 #ifdef DEBUG
846 std::cerr << "Copying values of derivative indices" << std::endl;
847 #endif
848 // Setup the index values; simply copy from the cb array, but only
849 // if the indices are not internal dummy.
850 for(size_t j=0; j<cb[i].size(); ++j) {
851 auto fd = ind_dummy.find(Ex(tr.child(it, j)));
852 if(fd==ind_dummy.end()) {
853 eqcopy.append_child(iterator(lhs), cb[i][j].begin() );
854 }
855 }
856 }
857 auto rhs=lhs;
858 ++rhs;
859 multiplier_t mult=*rhs->multiplier;
860 one(rhs->multiplier);
861
862 // Wrap a '\\partial' node around the component value, and add the
863 // same index values as above to this node.
864 rhs=eqcopy.wrap(rhs, str_node("\\partial"));
865 multiply(rhs->multiplier, mult);
866 multiply(rhs->multiplier, *it->multiplier);
867 // auto pch=tr.begin(it);
868 // iterator arg=tr.begin(rhs);
869 for(size_t j=0, cb_j=0; j<ni; ++j) {
870 #ifdef DEBUG
871 std::cerr << j << " : ";
872 #endif
873 bool done=false;
874 for(auto& d: dummy_positions) {
875 if(d.first==j) {
876 // This index is forced to a value because it is a dummy of which the partner
877 // is fixed by the argument on which the derivative acts.
878 #ifdef DEBUG
879 std::cerr << "fixed" << std::endl;
880 #endif
881 eqcopy.insert_subtree(rhs.begin(), tr.child(lhs,d.second))->fl.parent_rel=str_node::p_sub;
882 done=true;
883 break;
884 }
885 }
886 // std::cerr << "testing index " << j << " of \n" << Ex(it) << std::endl;
887 if(kernel.properties.get<Coordinate>(tr.child(it, j), true)!=0) {
888 // std::cerr << "Coordinate, so need straight copy" << std::endl;
889 eqcopy.insert_subtree(rhs.begin(), tr.child(it,j))->fl.parent_rel=str_node::p_sub;
890 done=true;
891 }
892 if(!done) {
893 // If we get here, the index value was not fixed because it is part of an
894 // already fixed dummy pair. And it was not fixed because the index was a
895 // Coordinate.
896 size_t fromj=cb_j;
897 Ex iname(tr.child(it,j));
898 auto fi = d2p.find(iname);
899 if(fi!=d2p.end()) {
900 fromj = (*fi).second;
901 if(fromj == cb_j)
902 ++cb_j;
903 }
904 else {
905 ++cb_j;
906 }
907 // std::cerr << "cb: " << i << ", " << fromj << std::endl;
908 eqcopy.insert_subtree(rhs.begin(), cb[i][fromj].begin() )->fl.parent_rel=str_node::p_sub;
909 }
910 }
911 // std::cerr << "----" << std::endl;
912
913 // For all dummy pairs which have one index on the
914 // \components node inside the derivative, we need to
915 // remove the corresponding value from the components
916 // node.
917 std::vector<sibling_iterator> sibs_to_erase;
918 for(auto di: dummy_positions) {
919 sibs_to_erase.push_back(tr.child(lhs, di.second));
920 }
921 for(auto se: sibs_to_erase)
922 tr.erase(se);
923
924 // Now move this replacement expression into the tree.
925
926 // std::cerr << "Replacement now " << std::endl;
927 // std::cerr << eqcopy << std::endl;
928 tr.move_before(tr.begin(ivalues), eqcopy.begin());
929
930 if(cb.size()==0) break;
931 }
932
933 // Erase the original \equals entry (we generated a full replacement above).
934 tr.erase(iv);
935 return true;
936 });
937
938 #ifdef DEBUG
939 std::cerr << tr.number_of_children(ivalues) << " nonzero components in this derivative" << std::endl;
940 #endif
941 if(tr.number_of_children(ivalues)==0) {
942 // All components of the derivative evaluated to zero because
943 // there were no dependencies. Replace this derivative node with
944 // a zero and return;
945 node_zero(it);
946 return it;
947 }
948
949 one(it->multiplier);
950
951 #ifdef DEBUG
952 std::cerr << "now " << Ex(it) << std::endl;
953 #endif
954
955
956 // Now move the free (but not the internal dummy or Coordinate!)
957 // partial indices to the components node, and then unwrap the
958 // partial node.
959
960 auto pch=tr.begin(it);
961 for(size_t n=0; n<ni; ++n) {
962 sibling_iterator nxt=pch;
963 ++nxt;
964 if(ind_dummy.find(Ex(pch))!=ind_dummy.end()) {
965 tr.erase(pch);
966 }
967 else if(kernel.properties.get<Coordinate>(pch, true)!=0) {
968 tr.erase(pch);
969 }
970 else
971 tr.move_before(ivalues, pch);
972 pch=nxt;
973 }
974
975
976 // Remove indices from the components node which came from the
977 // argument and which are dummy.
978 it=tr.flatten_and_erase(it);
979 auto se = tr.begin(it);
980 while(se!=tr.end(it)) {
981 if(ind_dummy.find(Ex(se))!=ind_dummy.end())
982 se = tr.erase(se);
983 else
984 ++se;
985 }
986
987 #ifdef DEBUG
988 std::cerr << "after index move " << Ex(it) << std::endl;
989 #endif
990
991 merge_component_children(it);
992
993 #ifdef DEBUG
994 std::cerr << "after merge " << Ex(it) << std::endl;
995 #endif
996
997 if(call_sympy)
998 simplify_components(it);
999 // std::cerr << "then " << Ex(it) << std::endl;
1000
1001 return it;
1002 }
1003
handle_epsilon(iterator it)1004 Ex::iterator evaluate::handle_epsilon(iterator it)
1005 {
1006 Ex rep("\\components");
1007 // attach indices to components
1008 // figure out the index value ranges
1009 // generate permutations of 'r1 ... rn' and signs
1010 // fill components
1011 auto sib=tr.begin(it);
1012 while(sib!=tr.end(it)) {
1013 rep.append_child(rep.begin(), (iterator)sib);
1014 ++sib;
1015 }
1016 auto cvals = rep.append_child(rep.begin(), str_node("\\comma"));
1017
1018 sib=tr.begin(it);
1019 const Indices *ind = kernel.properties.get<Indices>(sib);
1020 if(ind==0)
1021 throw ArgumentException("No Indices property known for indices in EpsilonTensor.");
1022
1023 combin::combinations<Ex> cb;
1024 for(auto& val: ind->values)
1025 cb.original.push_back(val);
1026 cb.multiple_pick=false;
1027 cb.block_length=1;
1028 cb.set_unit_sublengths();
1029 cb.permute();
1030
1031 for(unsigned int i=0; i<cb.size(); ++i) {
1032 auto equals = rep.append_child(cvals, str_node("\\equals"));
1033 auto vcomma = rep.append_child(equals, str_node("\\comma"));
1034 for(unsigned int j=0; j<cb.original.size(); ++j) {
1035 // std::cerr << *(cb[i][j].begin()->multiplier) << " ";
1036 rep.append_child(vcomma, cb[i][j].begin());
1037 }
1038 auto one = rep.append_child(equals, str_node("1"));
1039 multiply(one->multiplier, cb.ordersign(i));
1040 // std::cerr << std::endl;
1041 }
1042
1043 it=tr.move_ontop(it, rep.begin());
1044 return it;
1045 }
1046
simplify_components(iterator it)1047 void evaluate::simplify_components(iterator it)
1048 {
1049 assert(*it->name=="\\components");
1050
1051 // Simplify the components of the now single \component node by
1052 // calling the scalar backend. We feed it the components
1053 // individually.
1054 sibling_iterator lst = tr.end(it);
1055 --lst;
1056
1057 cadabra::simplify simp(kernel, tr);
1058 simp.set_progress_monitor(pm);
1059
1060 cadabra::do_list(tr, lst, [&](Ex::iterator eqs) {
1061 assert(*eqs->name=="\\equals");
1062 auto rhs1 = tr.begin(eqs);
1063 ++rhs1;
1064 iterator nd=rhs1;
1065 if(pm) pm->group("scalar_backend");
1066 // std::cerr << "simplify at " << Ex(nd) << std::endl;
1067 simp.apply_generic(nd, false, false, 0);
1068 if(pm) pm->group();
1069
1070 if(nd->is_zero()) {
1071 // std::cerr << "component zero " << nd.node << std::endl;
1072 tr.erase(eqs);
1073 }
1074 else {
1075 // std::cerr << "component non-zero " << nd.node << std::endl;
1076 }
1077 return true;
1078 });
1079
1080 // Note: the 'erase' in the loop above may have left us with a
1081 // \components node with an empty list of component values. However,
1082 // since all logic expects to find a \component node, we do NOT yet
1083 // replace this with a scalar zero here.
1084 }
1085
dependencies(iterator it)1086 std::set<Ex, tree_exact_less_obj> evaluate::dependencies(iterator it)
1087 {
1088 tree_exact_less_obj comp(&kernel.properties);
1089 std::set<Ex, tree_exact_less_obj> ret(comp);
1090
1091 // Is this node a coordinate itself? If so, add it.
1092 const Coordinate *cd = kernel.properties.get<Coordinate>(it, true);
1093 if(cd) {
1094 Ex cpy(it);
1095 cpy.begin()->fl.bracket=str_node::b_none;
1096 cpy.begin()->fl.parent_rel=str_node::p_none;
1097 one(cpy.begin()->multiplier);
1098 ret.insert(cpy);
1099 }
1100
1101 // Determine explicit dependence on Coordinates, that is, collect
1102 // parent_rel=p_none arguments, and add them directly if they
1103 // carry a Coordinate property, or run the algorithm recursively
1104 // if not (to catch e.g. exp(F(r)) depending on 'r'.
1105
1106 cadabra::do_subtree(tr, it, [&](Ex::iterator nd) -> Ex::iterator {
1107 if(nd==it) return nd; // skip node itself, leads to indefinite recursion
1108 if(nd->fl.parent_rel==str_node::p_none)
1109 {
1110 const Coordinate *cd = kernel.properties.get<Coordinate>(nd, true);
1111 if(cd) {
1112 Ex cpy(nd);
1113 cpy.begin()->fl.bracket=str_node::b_none;
1114 cpy.begin()->fl.parent_rel=str_node::p_none;
1115 one(cpy.begin()->multiplier);
1116 ret.insert(cpy);
1117 }
1118 else {
1119 auto arg_deps=dependencies(nd);
1120 if(arg_deps.size()>0)
1121 for(const auto& new_dep: arg_deps)
1122 ret.insert(new_dep);
1123 }
1124 }
1125 return nd;
1126 });
1127
1128 // Determine implicit dependence via Depends.
1129 #ifdef DEBUG
1130 std::cerr << "deps for " << Ex(it) << std::endl;
1131 #endif
1132
1133 const DependsBase *dep = kernel.properties.get<DependsBase>(it, true);
1134 if(dep) {
1135 #ifdef DEBUG
1136 std::cerr << "implicit deps" << std::endl;
1137 #endif
1138 Ex deps(dep->dependencies(kernel, it));
1139 cadabra::do_list(deps, deps.begin(), [&](Ex::iterator nd) {
1140 Ex cpy(nd);
1141 cpy.begin()->fl.bracket=str_node::b_none;
1142 cpy.begin()->fl.parent_rel=str_node::p_none;
1143 ret.insert(cpy);
1144 return true;
1145 });
1146 #ifdef DEBUG
1147 for(auto& e: ret)
1148 std::cerr << e << std::endl;
1149 #endif
1150 }
1151
1152 return ret;
1153 }
1154
wrap_scalar_in_components_node(iterator sib)1155 Algorithm::iterator evaluate::wrap_scalar_in_components_node(iterator sib)
1156 {
1157 // FIXME: would be good if we could write this in a more readable form.
1158 auto eq=tr.wrap(sib, str_node("\\equals"));
1159 tr.prepend_child(eq, str_node("\\comma"));
1160 eq=tr.wrap(eq, str_node("\\comma"));
1161 sib=tr.wrap(eq, str_node("\\components"));
1162 return sib;
1163 }
1164
unwrap_scalar_in_components_node(iterator it)1165 void evaluate::unwrap_scalar_in_components_node(iterator it)
1166 {
1167 // To apply to a scalar function: remove all scalars wrapped in
1168 // components nodes and make them normal scalars again.
1169 auto sib=tr.begin(it);
1170 while(sib!=tr.end(it)) {
1171 if(*sib->name=="\\components") {
1172 iterator tmp=sib;
1173 ::cleanup_components(kernel, tr, tmp);
1174 }
1175 ++sib;
1176 }
1177 }
1178
handle_prod(iterator it)1179 Ex::iterator evaluate::handle_prod(iterator it)
1180 {
1181 // std::cerr << "handle_prod " << Ex(it) << std::endl;
1182
1183 std::string prod_name=*it->name;
1184
1185 // All factors are either \component nodes, pure scalar nodes, or nodes which still need replacing.
1186 // The handle_factor function takes care of the latter two.
1187
1188 sibling_iterator sib=tr.begin(it);
1189 while(sib!=tr.end(it)) {
1190 sibling_iterator nxt=sib;
1191 ++nxt;
1192
1193 if(*sib->multiplier==0) { // zero factors make the entire product zero.
1194 node_zero(it);
1195 return it;
1196 }
1197
1198 if(is_component(sib)==false) {
1199 index_map_t empty;
1200 handle_factor(sib, empty);
1201 }
1202
1203 sib=nxt;
1204 }
1205
1206 // TRACE: If a factor has not had a rule match, it will be left
1207 // un-evaluated here. So you get
1208 // X^{a} \component_{a}( 0=3, 2=-5 )
1209 // and then we fail lower down. What we could do is let
1210 // handle_factor write out such unevaluated expressions to
1211 // component ones. That's somewhat wasteful though.
1212
1213 #ifdef DEBUG
1214 std::cerr << "every factor a \\components:\n" << Ex(it) << std::endl;
1215 #endif
1216
1217 // Now every factor in the product is a \component node. The thing
1218 // is effectively a large sparse tensor product. We need to do the
1219 // sums over the dummy indices, turning this into a single
1220 // \component node.
1221
1222 index_map_t ind_free, ind_dummy;
1223 classify_indices(it, ind_free, ind_dummy);
1224
1225 auto di = ind_dummy.begin();
1226 // Since no factor can be a sum anymore, dummy indices always occur in pairs,
1227 // there is no need to account for anything more tricky. Every pair leads
1228 // to a sum.
1229 while(di!=ind_dummy.end()) {
1230 auto di2=di;
1231 ++di2;
1232 int num1 = tr.index(di->second);
1233 int num2 = tr.index(di2->second);
1234 // std::cerr << *(di->first.begin()->name)
1235 // << " is index " << num1 << " in first and index " << num2 << " in second node " << std::endl;
1236
1237 // three cases:
1238 // two factors, single index in common. Merge is simple.
1239 // two factors, more than one index in common. After merging this turns into:
1240 // single factor, self-contraction
1241
1242 auto cit1 = tr.parent(di->second);
1243 auto cit2 = tr.parent(di2->second);
1244
1245 // Are the components objects cit1, cit2 on which these indices sit the same one?
1246 if(cit1 != cit2) {
1247 // Walk through all components of the first tensor, and for each check whether
1248 // any of the components of the second tensor matches the value for this dummy
1249 // index.
1250 sibling_iterator sib1=tr.end(cit1);
1251 --sib1;
1252 sibling_iterator sib2=tr.end(cit2);
1253 --sib2;
1254
1255 // Move all indices of the second tensor to be indices of the first.
1256 sibling_iterator mv=tr.begin(cit2);
1257 while(mv!=sib2) {
1258 sibling_iterator nxt=mv;
1259 ++nxt;
1260 tr.move_before(sib1, mv);
1261 mv=nxt;
1262 }
1263
1264 cadabra::do_list(tr, sib1, [&](Ex::iterator it1) {
1265 if(*it1->name!="\\equals")
1266 std::cerr << *it->name << std::endl;
1267 assert(*it1->name=="\\equals");
1268 auto lhs1 = tr.begin(it1);
1269 auto ivalue1 = tr.begin(lhs1);
1270 ivalue1 += num1;
1271 cadabra::do_list(tr, sib2, [&](Ex::iterator it2) {
1272 assert(*it2->name=="\\equals");
1273 auto lhs2 = tr.begin(it2);
1274 auto ivalue2 = tr.begin(lhs2);
1275 ivalue2 += num2;
1276
1277 // Compare the two index values in the two tensors, only continue if
1278 // these are the same.
1279 // std::cerr << "comparing value " << *ivalue1->name << " with " << *ivalue2->name << std::endl;
1280 // std::cerr << " " << &(*ivalue1) << " vs " << &(*ivalue2) << std::endl;
1281 if(tr.equal_subtree(ivalue1,ivalue2)) {
1282 // Create new merged index value set.
1283 Ex ivs("\\equals");
1284 auto ivs_lhs = tr.append_child(ivs.begin(), str_node("\\comma"));
1285 auto ivs_rhs = tr.append_child(ivs.begin(), str_node(prod_name));
1286 auto ci = tr.begin(lhs1);
1287 int n=0;
1288 while(ci!=tr.end(lhs1)) {
1289 if(n!=num1)
1290 ivs.append_child(ivs_lhs, iterator(ci));
1291 ++ci;
1292 ++n;
1293 }
1294 ci = ivs.begin(lhs2);
1295 n=0;
1296 while(ci!=ivs.end(lhs2)) {
1297 if(n!=num2)
1298 ivs.append_child(ivs_lhs, iterator(ci));
1299 ++ci;
1300 ++n;
1301 }
1302 auto rhs1=lhs1;
1303 ++rhs1;
1304 ivs.append_child(ivs_rhs, iterator(rhs1));
1305 auto rhs2=lhs2;
1306 ++rhs2;
1307 ivs.append_child(ivs_rhs, iterator(rhs2));
1308 //std::cerr << "ivs_rhs = " << Ex(ivs_rhs) << std::endl;
1309 cleanup_dispatch_deep(kernel, ivs);
1310 // Insert this new index value set before sib1, so that it will not get used
1311 // inside the outer loop.
1312 tr.move_before(it1, ivs.begin());
1313 }
1314 return true;
1315 });
1316 // This index value set can now be erased as all
1317 // possible combinations have been considered.
1318 tr.erase(it1);
1319 return true;
1320 });
1321 // Remove the dummy indices from the index set of tensor 1.
1322 tr.erase(di->second);
1323 tr.erase(di2->second);
1324 // Tensor 2 can now be removed from the product as well, as all information is now
1325 // part of tensor 1.
1326 tr.erase(cit2);
1327 }
1328
1329 else {
1330 // Components objects cit1 and cit2 are actually the same. We just need to
1331 // do a single loop now, going over all index value sets and keeping those
1332 // for which the num1-th and num2-th value are identical.
1333
1334 sibling_iterator sib1=tr.end(cit1);
1335 --sib1;
1336
1337 cadabra::do_list(tr, sib1, [&](Ex::iterator it1) {
1338 assert(*it1->name=="\\equals");
1339 auto lhs = tr.begin(it1);
1340 auto ivalue1 = tr.begin(lhs);
1341 auto ivalue2 = ivalue1;
1342 ivalue1 += num1;
1343 ivalue2 += num2;
1344 if(tr.equal_subtree(ivalue1,ivalue2)) {
1345 tr.erase(ivalue1);
1346 tr.erase(ivalue2);
1347 }
1348 else {
1349 tr.erase(it1);
1350 }
1351 return true;
1352 }
1353 );
1354 tr.erase(di->second);
1355 tr.erase(di2->second);
1356 }
1357
1358 ++di;
1359 ++di;
1360 }
1361
1362 // TRACE: are we still ok here? Looks ok: one component node
1363 // with no indices.
1364 // std::cerr << "Before doing outer product:\n" << Ex(it) << std::endl;
1365
1366 // At this stage we have one or more components nodes in the product,
1367 // and we have collected all possible index value combinations.
1368 // We need to do an outer multiplication, merging all index names into
1369 // one, and computing tensor component values for all possible index values.
1370
1371 int n=tr.number_of_children(it);
1372 // std::cerr << "outer product:\n" << Ex(it) << std::endl;
1373 if(n>1) {
1374 //std::cerr << "merging" << std::endl;
1375 auto first=tr.begin(it); // component node
1376 auto other=first;
1377 ++other;
1378 auto fi=tr.end(first);
1379 --fi;
1380 // Add the free indices of 'other' to 'first'.
1381 while(other!=tr.end(it)) {
1382 auto oi=tr.begin(other);
1383 while(oi!=tr.end(other)) {
1384 if(oi->is_index()==false)
1385 break;
1386 tr.insert_subtree(fi, oi);
1387 ++oi;
1388 }
1389 ++other;
1390 }
1391
1392 // Now do an outer combination of all possible indexvalue/componentvalue
1393 // in the various component nodes.
1394 auto comma1=tr.end(first);
1395 --comma1;
1396 other=first;
1397 ++other;
1398 while(other!=tr.end(it)) {
1399 Ex newcomma("\\comma"); // List of index value combinations and associated component values
1400 auto comma2=tr.end(other);
1401 --comma2;
1402 assert(*comma1->name=="\\comma");
1403 assert(*comma2->name=="\\comma");
1404 auto eq1=tr.begin(comma1); // The \equals node
1405 while(eq1!=tr.end(comma1)) {
1406 auto eq2=tr.begin(comma2);
1407 while(eq2!=tr.end(comma2)) {
1408 // Collect all index values.
1409 auto neq = newcomma.append_child(newcomma.begin(), str_node("\\equals"));
1410 auto ncm = newcomma.append_child(neq, str_node("\\comma")); // List of index values
1411 auto iv=tr.begin(tr.begin(eq1));
1412 while(iv!=tr.end(tr.begin(eq1))) {
1413 newcomma.append_child(ncm, iterator(iv));
1414 ++iv;
1415 }
1416 iv=tr.begin(tr.begin(eq2));
1417 while(iv!=tr.end(tr.begin(eq2))) {
1418 newcomma.append_child(ncm, iterator(iv));
1419 ++iv;
1420 }
1421 // Multiply component values.
1422 Ex prod(*it->name);
1423 iv=tr.end(eq1);
1424 --iv;
1425 prod.append_child(prod.begin(), iterator(iv));
1426 iv=tr.end(eq2);
1427 --iv;
1428 prod.append_child(prod.begin(), iterator(iv));
1429 cleanup_dispatch_deep(kernel, prod);
1430 newcomma.append_child(neq, prod.begin());
1431 ++eq2;
1432 }
1433 ++eq1;
1434 }
1435 // Now replace the original comma1 node with newcomma, and re-iterate if there
1436 // are further factors in the tensor product.
1437 comma1 = tr.move_ontop(iterator(comma1), newcomma.begin());
1438 other=tr.erase(other);
1439 }
1440 // std::cerr << Ex(it) << std::endl;
1441 }
1442
1443 // At this stage, there should be only one factor in the product, which
1444 // should be a \components node. We do a cleanup, after which it should be
1445 // at the 'it' node.
1446
1447 assert(*it->name=="\\prod" || *it->name=="\\wedge" || *it->name=="\\frac");
1448 assert(tr.number_of_children(it)==1);
1449 assert(*tr.begin(it)->name=="\\components");
1450 tr.begin(it)->fl.bracket=it->fl.bracket;
1451 tr.begin(it)->fl.parent_rel=it->fl.parent_rel;
1452 tr.begin(it)->multiplier=it->multiplier;
1453 tr.flatten(it);
1454 it=tr.erase(it);
1455 push_down_multiplier(kernel, tr, it);
1456
1457 // iterator pr=tr.end();
1458 // if(tr.is_head(it)==false) {
1459 // pr=tr.parent(it);
1460 // std::cerr << "Tracing just before merge:\n " << Ex(pr) << std::endl;
1461 // }
1462
1463 merge_component_children(it);
1464
1465 // if(pr!=tr.end())
1466 // std::cerr << "after component merge:\n " << Ex(pr) << std::endl;
1467
1468 // cleanup_dispatch(kernel, tr, it);
1469 // if(pr!=tr.end())
1470 // std::cerr << "And afterwards:\n " << Ex(pr) << std::endl;
1471
1472 if(*it->name!="\\components") {
1473 // The result is a scalar. Because we are expected to return
1474 // a \components node, we wrap this scalar again.
1475 // std::cerr << "wrapping scalar" << std::endl;
1476 it=wrap_scalar_in_components_node(it);
1477 // std::cerr << Ex(it) << std::endl;
1478 }
1479
1480 // else {
1481 // // We may have duplicate index value entries; merge them.
1482 // merge_component_children(it);
1483 // }
1484
1485 // Use sympy to simplify components.
1486 if(call_sympy)
1487 simplify_components(it);
1488 //std::cerr << "simplified:\n" << Ex(it) << std::endl;
1489
1490 return it;
1491 }
1492