1 /*
2
3 Cadabra: a field-theory motivated computer algebra system.
4 Copyright (C) 2001-2015 Kasper Peeters <kasper.peeters@phi-sci.com>
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation, either version 3 of the
9 License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include <stddef.h>
22 #include "Algorithm.hh"
23 #include "Storage.hh"
24 #include "Props.hh"
25 #include "Cleanup.hh"
26 #include <typeinfo>
27 #include <boost/version.hpp>
28 #if BOOST_VERSION > 105500
29 #include <boost/core/demangle.hpp>
30 #endif
31 //#include <boost/stacktrace.hpp>
32
33 #include "properties/Accent.hh"
34 #include "properties/Derivative.hh"
35 #include "properties/Indices.hh"
36 #include "properties/Coordinate.hh"
37 #include "properties/Symbol.hh"
38 #include "properties/Trace.hh"
39 #include "properties/DependsBase.hh"
40
41 #include <sstream>
42
43 //#define DEBUG
44
45 using namespace cadabra;
46
Algorithm(const Kernel & k,Ex & tr_)47 Algorithm::Algorithm(const Kernel& k, Ex& tr_)
48 : IndexClassifier(k),
49 interrupted(false),
50 number_of_calls(0), number_of_modifications(0),
51 suppress_normal_output(false),
52 discard_command_node(false),
53 tr(tr_),
54 pm(0),
55 traverse_ldots(false)
56 {
57 }
58
~Algorithm()59 Algorithm::~Algorithm()
60 {
61 }
62
set_progress_monitor(ProgressMonitor * pm_)63 void Algorithm::set_progress_monitor(ProgressMonitor *pm_)
64 {
65 pm=pm_;
66 }
67
apply_pre_order(bool repeat)68 Algorithm::result_t Algorithm::apply_pre_order(bool repeat)
69 {
70 #if BOOST_VERSION > 105500
71 if(pm)
72 pm->group(boost::core::demangle(typeid(*this).name()).c_str());
73 #else
74 if(pm)
75 pm->group(typeid(*this).name());
76 #endif
77
78 result_t ret=result_t::l_no_action;
79 Ex::iterator start=tr.begin();
80 while(start!=tr.end()) {
81 if(traverse_ldots || !tr.is_hidden(start)) {
82 if(start->is_index()==false) {
83 auto aor=apply_once(start);
84 if(aor==result_t::l_applied || aor==result_t::l_applied_no_new_dummies) {
85 ret=result_t::l_applied;
86 // Need to cleanup on the entire tree above us.
87
88 if(!repeat) {
89 start.skip_children();
90 ++start;
91 }
92 }
93 else ++start;
94 }
95 else ++start;
96 }
97 else {
98 ++start;
99 }
100 }
101
102 cleanup_dispatch_deep(kernel, tr);
103
104 if(pm) pm->group();
105 return ret;
106 }
107
apply_generic(bool deep,bool repeat,unsigned int depth)108 Algorithm::result_t Algorithm::apply_generic(bool deep, bool repeat, unsigned int depth)
109 {
110 auto it = tr.begin();
111 return apply_generic(it, deep, repeat, depth);
112 }
113
apply_generic(Ex::iterator & it,bool deep,bool repeat,unsigned int depth)114 Algorithm::result_t Algorithm::apply_generic(Ex::iterator& it, bool deep, bool repeat, unsigned int depth)
115 {
116 #if BOOST_VERSION > 105500
117 if(pm)
118 pm->group(boost::core::demangle(typeid(*this).name()).c_str());
119 #else
120 if(pm)
121 pm->group(typeid(*this).name());
122 #endif
123
124 result_t ret=result_t::l_no_action;
125
126 Ex::fixed_depth_iterator start=tr.begin_fixed(it, depth, false);
127 #ifdef DEBUG
128 std::cerr << "apply_generic at " << it.node << " " << *it->name << " " << *start->name << std::endl;
129 #endif
130
131 while(tr.is_valid(start)) {
132 // std::cerr << "evaluate main loop at " << *start->name << std::endl;
133 #ifdef DEBUG
134 std::cerr << "main loop for " << typeid(*this).name() << ":\n" << Ex(start) << std::endl;
135 #endif
136
137 result_t thisret=result_t::l_no_action;
138 Ex::iterator enter(start);
139 Ex::fixed_depth_iterator next(start);
140 ++next;
141 // if(tr.is_valid(next))
142 // std::cerr << "next = " << *next->name << std::endl;
143 do {
144 // std::cout << "apply at " << *enter->name << std::endl;
145 bool work_is_topnode=(enter==it);
146 if(deep && depth==0)
147 thisret = apply_deep(enter);
148 else
149 thisret = apply_once(enter);
150
151 if(work_is_topnode)
152 it=enter;
153
154 // FIXME: handle l_error or remove
155 if(thisret==result_t::l_applied || thisret==result_t::l_applied_no_new_dummies)
156 ret=result_t::l_applied;
157 }
158 while(depth==0 && repeat && (thisret==result_t::l_applied || thisret==result_t::l_applied_no_new_dummies));
159
160 if(depth==0) {
161 // std::cerr << "break " << std::endl;
162 break;
163 }
164 else {
165 // std::cerr << "no break " << std::endl;
166 }
167 start=next;
168 }
169
170 // std::cerr << "pre-exit node " << it.node << std::endl;
171
172 // If we are acting at fixed depth, we will not have gone up in the
173 // tree, so missed one cleanup action. Do it now.
174 if(depth>0) {
175 Ex::fixed_depth_iterator start=tr.begin_fixed(it, depth-1, false);
176 while(tr.is_valid(start)) {
177 Ex::iterator work=start;
178 ++start;
179 bool cpy=false;
180 if(work==it) cpy=true;
181 cleanup_dispatch(kernel, tr, work);
182 if(cpy) it=work;
183 }
184 }
185
186 // std::cerr << "exit node " << it.node << std::endl;
187
188 // if(tr.is_valid(it)) {
189 // std::cerr << "exit " << *it->name << std::endl;
190 // std::cerr << "exit apply_generic\n" << Ex(it) << std::endl;
191 // }
192
193 if(pm) pm->group();
194 return ret;
195 }
196
apply_once(Ex::iterator & it)197 Algorithm::result_t Algorithm::apply_once(Ex::iterator& it)
198 {
199 // std::cerr << "=== apply_once ===" << std::endl;
200 if(traverse_ldots || !tr.is_hidden(it)) {
201 if(can_apply(it)) {
202 result_t res=apply(it);
203 // std::cerr << "apply algorithm at " << *it->name << std::endl;
204 if(res==result_t::l_applied || res==result_t::l_applied_no_new_dummies) {
205 cleanup_dispatch(kernel, tr, it);
206 return res;
207 }
208 }
209 }
210
211 return result_t::l_no_action;
212 }
213
apply_deep(Ex::iterator & it)214 Algorithm::result_t Algorithm::apply_deep(Ex::iterator& it)
215 {
216 // This recursive algorithm walks the tree depth-first
217 // (parent-after-child). The algorithm is applied on each node if
218 // can_apply returns true. When the iterator goes up one level
219 // (i.e. from a child to a parent), and any changes have been made
220 // so far at the child level level, cleanup and simplification
221 // routines will be called. The only nodes that can be removed from
222 // the tree are nodes at a lower level than the simplification
223 // node.
224
225 // std::cout << "=== apply_deep ===" << std::endl;
226 // tr.print_recursive_treeform(std::cout, it);
227
228 post_order_iterator current=it;
229 current.descend_all();
230 post_order_iterator last=it;
231 int deepest_action = -1;
232 // std::cout << "apply_deep: it = " << *it->name << std::endl;
233 bool stop_after_this_one=false;
234 result_t some_changes_somewhere=result_t::l_no_action;
235
236 for(;;) {
237 // std::cout << "reached " << *current->name << std::endl;
238 #ifdef DEBUG
239 std::cout << "apply_deep " << typeid(*this).name() << ": current = " << *current->name << std::endl;
240 #endif
241
242 if(current.node==last.node) {
243 // std::cout << "stop after this one" << std::endl;
244 stop_after_this_one=true;
245 }
246
247 if(deepest_action > tr.depth(current)) {
248 #ifdef DEBUG
249 std::cerr << "simplify; we are at " << *(current->name) << std::endl;
250 #endif
251 iterator work=current;
252 bool work_is_topnode=(work==it);
253 cleanup_dispatch(kernel, tr, work);
254 current=work;
255 if(work_is_topnode)
256 it=work;
257 #ifdef DEBUG
258 std::cerr << "current now " << *(current->name) << std::endl;
259 tr.print_recursive_treeform(std::cerr, current);
260 #endif
261 deepest_action = tr.depth(current); // needs to propagate upwards
262 }
263
264 if((traverse_ldots || !tr.is_hidden(current)) && can_apply(current)) {
265 #ifdef DEBUG
266 std::cout << "acting at " << *current->name << std::endl;
267 #endif
268 iterator work=current;
269 post_order_iterator next(current);
270 ++next;
271 bool work_is_topnode=(work==it);
272 result_t res = apply(work);
273 if(res==Algorithm::result_t::l_applied || res==Algorithm::result_t::l_applied_no_new_dummies) {
274 some_changes_somewhere=result_t::l_applied;
275 if(res==Algorithm::result_t::l_applied) {
276 // std::cerr << "rename replacement on " << work << std::endl;
277 rename_replacement_dummies(work, true);
278 }
279 deepest_action=tr.depth(work);
280 // If we got a zero at 'work', we need to propagate this up the tree and
281 // then restart our post-order traversal such that everything that has
282 // been removed from the tree by this zero will no longer be considered.
283 if(*work->multiplier==0) {
284 #ifdef DEBUG
285 std::cerr << "propagate zero up the tree" << std::endl;
286 tr.print_recursive_treeform(std::cerr, it);
287 #endif
288 post_order_iterator moved_next=work;
289 propagate_zeroes(moved_next, it);
290 #ifdef DEBUG
291 tr.print_recursive_treeform(std::cerr, it);
292 std::cerr << Ex(it) << std::endl;
293 #endif
294 next=moved_next;
295 }
296
297 // The 'work' iterator can now point to a new node. If we were acting at the
298 // top node, we need to propagate the change in 'work' to 'it' so the caller
299 // knows where the new top node is.
300 if(work_is_topnode)
301 it=work;
302 }
303 // The algorithm may have replaced the 'work' node, so instead of walking from
304 // there, we continue at the node which was next in line before we called 'apply'.
305 current=next;
306 }
307 else {
308 ++current;
309 }
310
311 if(stop_after_this_one)
312 break;
313
314 }
315
316 #ifdef DEBUG
317 std::cerr << "recursive end **" << std::endl;
318 #endif
319
320 return some_changes_somewhere;
321 }
322
propagate_zeroes(post_order_iterator & it,const iterator & topnode)323 void Algorithm::propagate_zeroes(post_order_iterator& it, const iterator& topnode)
324 {
325 assert(*it->multiplier==0);
326 if(it==topnode) return;
327 if(tr.is_head(it)) return;
328
329 iterator walk=tr.parent(it);
330 #ifdef DEBUG
331 std::cerr << "propagate_zeroes at " << *walk->name << std::endl;
332 #endif
333 // if(!tr.is_valid(walk))
334 // return;
335
336 const Derivative *der=kernel.properties.get<Derivative>(walk);
337 const Trace *trace=kernel.properties.get<Trace>(walk);
338 if(*walk->name=="\\prod" || der || trace) {
339 if(der && it->is_index()) return;
340 walk->multiplier=rat_set.insert(0).first;
341 it=walk;
342 propagate_zeroes(it, topnode);
343 // Removing happens in the next step.
344 }
345 else if(*walk->name=="\\pow") {
346 if(tr.index(it)==0) { // the argument
347 walk->multiplier=rat_set.insert(0).first;
348 it=walk;
349 propagate_zeroes(it, topnode);
350 }
351 else { // the exponent
352 rset_t::iterator rem=walk->multiplier;
353 tr.erase(it);
354 tr.flatten(walk);
355 it=tr.erase(walk);
356 node_one(it);
357 it->multiplier=rem;
358 }
359 }
360 else if(*walk->name=="\\sum") {
361 if(tr.number_of_children(walk)>2) {
362 if(tr.is_valid(tr.next_sibling(it))) {
363 it=tr.erase(it);
364 it.descend_all();
365 }
366 else {
367 iterator ret=tr.parent(it);
368 tr.erase(it);
369 it=ret;
370 }
371 }
372 else {
373 // If the sum is the top node, we cannot flatten it because
374 // we are not allowed to invalidate the topnode iterator
375 if(walk==topnode) {
376 #ifdef DEBUG
377 std::cerr << "\\sum at top, cannot flatten" << std::endl;
378 #endif
379 // it=tr.next_sibling(it); // Added but wrong?
380 return;
381 }
382
383 tr.erase(it);
384 iterator singlearg=tr.begin(walk);
385 if(singlearg!=tr.end(walk)) {
386 singlearg->fl.bracket=walk->fl.bracket; // to remove brackets of the sum
387 if(*tr.parent(walk)->name=="\\prod") {
388 multiply(tr.parent(walk)->multiplier, *singlearg->multiplier);
389 cadabra::one(singlearg->multiplier);
390 }
391 }
392 tr.flatten(walk);
393 it=tr.erase(walk);
394 if(*it->name=="\\prod" && *tr.parent(it)->name=="\\prod") {
395 tr.flatten(it);
396 it=tr.erase(it);
397 }
398 }
399 }
400 else {
401 iterator nn=tr.insert_after(it, str_node("1"));
402 nn->fl.parent_rel=it->fl.parent_rel;
403 nn->fl.bracket=it->fl.bracket;
404 it=tr.erase(it);
405 zero(it->multiplier);
406 }
407
408 return;
409 }
410
pushup_multiplier(iterator it)411 void Algorithm::pushup_multiplier(iterator it)
412 {
413 if(!tr.is_valid(it)) return;
414 if(*it->multiplier!=1) {
415 if(*it->name=="\\sum") {
416 // txtout << "SUM" << std::endl;
417 sibling_iterator sib=tr.begin(it);
418 while(sib!=tr.end(it)) {
419 multiply(sib->multiplier, *it->multiplier);
420 // txtout << "going up" << std::endl;
421 if(tr.is_head(it)==false)
422 pushup_multiplier(tr.parent(it));
423 // txtout << "back and back up" << std::endl;
424 pushup_multiplier(sib);
425 // txtout << "back" << std::endl;
426 ++sib;
427 }
428 ::one(it->multiplier);
429 }
430 else {
431 // txtout << "PUSHUP: " << *it->name << std::endl;
432 if(tr.is_head(it)==false) {
433 // txtout << "test propinherit" << std::endl;
434 // iterator tmp=tr.parent(it);
435 // tmp not always valid?!? This one crashes hard with a loop!?!
436 // txtout << " of " << *tmp->name << std::endl;
437 const PropertyInherit *pin=kernel.properties.get<PropertyInherit>(tr.parent(it));
438 if(pin || *(tr.parent(it)->name)=="\\prod") {
439 multiply(tr.parent(it)->multiplier, *it->multiplier);
440 ::one(it->multiplier); // moved up, was at end of block, correct?
441 // txtout << "going up" << std::endl;
442 pushup_multiplier(tr.parent(it));
443 // txtout << "back" << std::endl;
444 }
445 // else txtout << "not relevant" << std::endl;
446 }
447 }
448 }
449 }
450
node_zero(iterator it)451 void Algorithm::node_zero(iterator it)
452 {
453 ::zero(it->multiplier);
454 tr.erase_children(it);
455 it->name=name_set.insert("1").first;
456 }
457
node_one(iterator it)458 void Algorithm::node_one(iterator it)
459 {
460 ::one(it->multiplier);
461 tr.erase_children(it);
462 it->name=name_set.insert("1").first;
463 }
464
node_integer(iterator it,int num)465 void Algorithm::node_integer(iterator it, int num)
466 {
467 ::one(it->multiplier);
468 tr.erase_children(it);
469 it->name=name_set.insert("1").first;
470 ::multiply(it->multiplier, num);
471 }
472
index_parity(iterator it) const473 int Algorithm::index_parity(iterator it) const
474 {
475 sibling_iterator frst=tr.begin(tr.parent(it));
476 sibling_iterator fnd(it);
477 int sgn=1;
478 while(frst!=fnd) {
479 sgn=-sgn;
480 ++frst;
481 }
482 return sgn;
483 }
484
485
number_of_indices(iterator it)486 unsigned int Algorithm::number_of_indices(iterator it)
487 {
488 unsigned int res=0;
489 index_iterator indit=begin_index(it);
490 while(indit!=end_index(it)) {
491 ++res;
492 ++indit;
493 }
494 return res;
495 }
496
get_index_set_name(iterator it) const497 std::string Algorithm::get_index_set_name(iterator it) const
498 {
499 const Indices *ind=kernel.properties.get<Indices>(it, true);
500 if(ind) {
501 return ind->set_name;
502 // TODO: The logic was once as below, but it is no longer clear to
503 // me why that would ever make sense.
504 // if(ind->parent_name!="") return ind->parent_name;
505 // else return ind->set_name;
506 }
507 else return " undeclared";
508 }
509
begin_index(iterator it) const510 index_iterator Algorithm::begin_index(iterator it) const
511 {
512 return index_iterator::begin(kernel.properties, it);
513 }
514
end_index(iterator it) const515 index_iterator Algorithm::end_index(iterator it) const
516 {
517 return index_iterator::end(kernel.properties, it);
518 }
519
520
521
522
check_index_consistency(iterator it) const523 bool Algorithm::check_index_consistency(iterator it) const
524 {
525 index_map_t ind_free, ind_dummy;
526 classify_indices(it,ind_free,ind_dummy);
527 return true;
528 }
529
check_degree_consistency(iterator) const530 bool Algorithm::check_degree_consistency(iterator ) const
531 {
532 return true; // FIXME: this needs to be implemented.
533 }
534
check_consistency(iterator it) const535 bool Algorithm::check_consistency(iterator it) const
536 {
537 Stopwatch w1;
538 w1.start();
539 // debugout << "checking consistency ... " << std::flush;
540 assert(tr.is_head(it)==false);
541 // iterator entry=it;
542 iterator end=it;
543 end.skip_children();
544 ++end;
545 while(it!=end) {
546 if(interrupted)
547 throw InterruptionException("check_consistency");
548
549 if(*it->name=="\\sum") {
550 if(*it->multiplier!=1)
551 throw ConsistencyException("Found \\sum node with non-unit multiplier.");
552 else if(Ex::number_of_children(it)<2)
553 throw ConsistencyException("Found a \\sum node with 0 or 1 child nodes.");
554 else {
555 sibling_iterator sumch=it.begin();
556 str_node::bracket_t firstbracket=sumch->fl.bracket;
557 while(*sumch->name=="\\sum" || *sumch->name=="\\prod") {
558 ++sumch;
559 if(sumch==it.end()) break;
560 else firstbracket=sumch->fl.bracket;
561 }
562 sumch=it.begin();
563 while(sumch!=it.end()) {
564 if(*sumch->name!="\\sum" && *sumch->name!="\\prod") {
565 if(sumch->fl.bracket!=firstbracket)
566 throw ConsistencyException("Found a \\sum node with different brackets on its children.");
567 }
568 // else if(*sumch->name=="\\sum") {
569 // sibling_iterator sumchch=sumch.begin();
570 // while(sumchch!=sumch.end()) {
571 // if(sumchch->fl.bracket==str_node::b_none) {
572 // tr.print_recursive_treeform(debugout, entry);
573 // throw ConsistencyException("Found a sum node with \\sum child without bracketed children.");
574 // }
575 // ++sumchch;
576 // }
577 // }
578 ++sumch;
579 }
580 }
581 }
582 else if(*it->name=="\\prod") {
583 if(Ex::number_of_children(it)<=1)
584 throw ConsistencyException("Found \\prod node with only 0 or 1 children.");
585 sibling_iterator ch=it.begin();
586 str_node::bracket_t firstbracket=ch->fl.bracket;
587 while(*ch->name=="\\sum" || *ch->name=="\\prod") {
588 ++ch;
589 if(ch==it.end()) break;
590 else firstbracket=ch->fl.bracket;
591 }
592 ch=it.begin();
593 while(ch!=it.end()) {
594 if(*ch->name!="\\prod" && *ch->name!="\\sum") {
595 if(ch->fl.bracket!=firstbracket)
596 throw ConsistencyException("Found \\prod node with different brackets on its children.");
597 }
598 if(*ch->multiplier!=1) {
599 throw ConsistencyException("Found \\prod child with non-unit multiplier.");
600 // debugout << "node name " << *ch->name << ", multiplier " << *ch->multiplier << std::endl;
601 // inconsistent=true;
602 // break;
603 }
604 ++ch;
605 }
606 }
607 else if(*it->name=="\\sequence") {
608 if(Ex::number_of_children(it)!=2)
609 throw ConsistencyException("Found \\sequence node with incorrect (non-2) number of children.");
610 }
611 ++it;
612 }
613
614 w1.stop();
615 // debugout << "checking done..." << w1 << std::endl;
616 return true;
617 }
618
report_progress(const std::string &,int,int,int count)619 void Algorithm::report_progress(const std::string&, int, int, int count)
620 {
621 // bool display=false;
622 // if(count==2) display=true;
623 // else {
624 // if(report_progress_stopwatch.stopped()) {
625 // display=true;
626 // report_progress_stopwatch.start();
627 // }
628 // else {
629 // if(report_progress_stopwatch.seconds()>0 || report_progress_stopwatch.useconds()>300000L) {
630 // display=true;
631 // report_progress_stopwatch.reset();
632 // }
633 // }
634 // }
635
636 // if(display) { // prevents updates at a rate of more than one per second
637 // if(eo->output_format==Ex_output::out_xcadabra) {
638 // txtout << "<progress>" << std::endl
639 // << str << std::endl
640 // << todo << std::endl
641 // << done << std::endl
642 // << count << std::endl
643 // << "</progress>" << std::endl;
644 // }
645 // else {
646 // if(count==2)
647 // txtout << str << " (" << done << " of " << todo << " completed)" << std::endl;
648 // }
649 // }
650 }
651
rename_replacement_dummies(iterator two,bool still_inside_algo)652 bool Algorithm::rename_replacement_dummies(iterator two, bool still_inside_algo)
653 {
654 #ifdef DEBUG
655 std::cerr << "renaming in " << two << std::endl;
656 #endif
657 // std::cout << "full story " << *two->name << std::endl;
658 // print_classify_indices(two);
659 // std::cout << "replacement" << std::endl;
660 // print_classify_indices(std::cout, two);
661
662 index_map_t ind_free, ind_dummy;
663 index_map_t ind_free_full, ind_dummy_full;
664
665 if(false && still_inside_algo) {
666 if(tr.is_head(two)==false)
667 classify_indices_up(tr.parent(two), ind_free_full, ind_dummy_full);
668 }
669 else {
670 classify_indices_up(two, ind_free_full, ind_dummy_full); // the indices in everything except the replacement
671 }
672 classify_indices(two, ind_free, ind_dummy); // the indices in the replacement subtree
673 #ifdef DEBUG
674 std::cerr << "dummies of " << *two->name << std::endl;
675 for(auto& ii: ind_dummy)
676 std::cerr << ii.first << std::endl;
677 std::cerr << "free indices above us" << std::endl;
678 for(auto& ii: ind_free_full)
679 std::cerr << ii.first << std::endl;
680 std::cerr << "dummy indices above us" << std::endl;
681 for(auto& ii: ind_dummy_full)
682 std::cerr << ii.first << std::endl;
683 #endif
684
685 index_map_t must_be_empty;
686 index_map_t newly_generated;
687
688 // Catch double index pairs
689 determine_intersection(ind_dummy_full, ind_dummy, must_be_empty);
690 index_map_t::iterator it=must_be_empty.begin();
691 while(it!=must_be_empty.end()) {
692 #ifdef DEBUG
693 std::cerr << "double index pair" << std::endl;
694 #endif
695 Ex the_key=(*it).first;
696 const Indices *dums=kernel.properties.get<Indices>(it->second, true);
697 if(!dums)
698 throw ConsistencyException("Failed to find dummy property for $"+*it->second->name+"$ while renaming dummies.");
699 // txtout << "failed to find dummy property for " << *it->second->name << std::endl;
700 assert(dums);
701 Ex relabel
702 =get_dummy(dums, &ind_dummy_full, &ind_dummy, &ind_free_full, &ind_free, &newly_generated);
703 newly_generated.insert(index_map_t::value_type(Ex(relabel),(*it).second));
704 // txtout << " renamed to " << *relabel << std::endl;
705 do {
706 tr.replace_index((*it).second, relabel.begin(), true);
707 // (*it).second->name=relabel;
708 ++it;
709 }
710 while(it!=must_be_empty.end() && tree_exact_equal(&kernel.properties, (*it).first,the_key, 1, true, -2, true));
711 }
712
713 // Catch triple indices (two cases: dummy pair in replacement, free index elsewhere and
714 // dummy elsewhere, free index in replacement)
715 must_be_empty.clear();
716 // newly_generated.clear(); // DO NOT ERASE, IDIOT!
717
718 determine_intersection(ind_free_full, ind_dummy, must_be_empty);
719 //for(auto& ii: must_be_empty) {
720 // std::cerr << ii.first << std::endl;
721 // }
722 it=must_be_empty.begin();
723 while(it!=must_be_empty.end()) {
724 //std::cerr << "triple index pair " << it->first << std::endl;
725 Ex the_key=(*it).first;
726 const Indices *dums=kernel.properties.get<Indices>(it->second, true);
727 if(!dums)
728 throw ConsistencyException("Failed to find dummy property for $"+*it->second->name+"$ while renaming dummies.");
729 assert(dums);
730 Ex relabel
731 =get_dummy(dums, &ind_dummy_full, &ind_dummy, &ind_free_full, &ind_free, &newly_generated);
732 relabel.begin()->fl.parent_rel=it->second->fl.parent_rel;
733 newly_generated.insert(index_map_t::value_type(relabel,(*it).second));
734 do {
735 tr.replace_index((*it).second, relabel.begin(), true);
736 ++it;
737 }
738 while(it!=must_be_empty.end() && tree_exact_equal(&kernel.properties, (*it).first,the_key, 1, true, -2, true));
739 }
740
741 must_be_empty.clear();
742 // newly_generated.clear();
743 determine_intersection(ind_free, ind_dummy_full, must_be_empty);
744 it=must_be_empty.begin();
745 while(it!=must_be_empty.end()) {
746 // std::cerr << "triple index pair 2" << std::endl;
747 Ex the_key=(*it).first;
748 const Indices *dums=kernel.properties.get<Indices>(it->second, true);
749 if(!dums)
750 throw ConsistencyException("Failed to find dummy property for $"+*it->second->name+"$ while renaming dummies.");
751 assert(dums);
752 Ex relabel
753 =get_dummy(dums, &ind_dummy_full, &ind_dummy, &ind_free_full, &ind_free, &newly_generated);
754 relabel.begin()->fl.parent_rel=it->second->fl.parent_rel;
755 newly_generated.insert(index_map_t::value_type(relabel,(*it).second));
756 do {
757 tr.replace_index((*it).second, relabel.begin(), true);
758 ++it;
759 }
760 while(it!=must_be_empty.end() && tree_exact_equal(&kernel.properties, (*it).first,the_key, 1, true, -2, true));
761 }
762
763 return true;
764 }
765
766
767
contains(sibling_iterator from,sibling_iterator to,sibling_iterator arg)768 bool Algorithm::contains(sibling_iterator from, sibling_iterator to, sibling_iterator arg)
769 {
770 while(from!=to) {
771 if(from->name==arg->name) return true;
772 ++from;
773 }
774 return false;
775 }
776
find_arg_superset(range_vector_t & ran,sibling_iterator it)777 Algorithm::range_vector_t::iterator Algorithm::find_arg_superset(range_vector_t& ran,
778 sibling_iterator it)
779 {
780 sibling_iterator nxt=it;
781 ++nxt;
782 return find_arg_superset(ran, it, nxt);
783 }
784
785 //void Algorithm::find_argument_lists(range_vector_t& ran, bool only_comma_lists) const
786 // {
787 // sibling_iterator argit=args_begin();
788 // while(argit!=args_end()) {
789 // if(*argit->name=="\\comma") {
790 // ran.push_back(range_t(tr.begin(argit), tr.end(argit)));
791 // }
792 // else if(!only_comma_lists) {
793 // sibling_iterator argnxt=argit; ++argnxt;
794 // ran.push_back(range_t(argit, argnxt));
795 // }
796 // ++argit;
797 // }
798 // }
799
800 template<class Iter>
find_arg_superset(range_vector_t & ran,Iter st,Iter nd)801 Algorithm::range_vector_t::iterator Algorithm::find_arg_superset(range_vector_t& ran, Iter st, Iter nd)
802 {
803 range_vector_t::iterator ranit=ran.begin();
804 while(ranit!=ran.end()) {
805 sibling_iterator findthese=st;
806 bool contained=true;
807 while(findthese!=nd) {
808 if(contains((*ranit).first, (*ranit).second, findthese)) {
809 ++findthese;
810 }
811 else {
812 contained=false;
813 break;
814 }
815 }
816 if(contained) return ranit;
817 ++ranit;
818 }
819 return ran.end();
820 }
821
is_termlike(iterator it)822 bool Algorithm::is_termlike(iterator it)
823 {
824 if(*it->name=="\\equals") return false;
825
826 if(!is_factorlike(it))
827 if(*it->name!="\\sum")
828 if(it->fl.parent_rel==str_node::p_none)
829 return true;
830
831 return false;
832 }
833
is_factorlike(iterator it)834 bool Algorithm::is_factorlike(iterator it)
835 {
836 if(Ex::is_head(it)) return false;
837 if(*Ex::parent(it)->name=="\\prod")
838 return true;
839 return false;
840 }
841
842 // This only returns true if the indicated node is a single non-reserved node (non-prod, non-sum, ...)
843 // at the top level of an expression (real top, top of equation lhs/rhs, top of integral argument, ...).
844
is_single_term(iterator it)845 bool Algorithm::is_single_term(iterator it)
846 {
847 if(*it->name!="\\prod" && *it->name!="\\sum" && *it->name!="\\asymimplicit"
848 && *it->name!="\\comma" && *it->name!="\\equals" && *it->name!="\\arrow") {
849
850 if(tr.is_head(it) || *tr.parent(it)->name=="\\equals" || *tr.parent(it)->name=="\\int") return true;
851 else if(*tr.parent(it)->name=="\\sum")
852 return true;
853 else if(*tr.parent(it)->name!="\\prod" && it->fl.parent_rel==str_node::parent_rel_t::p_none
854 && kernel.properties.get<Accent>(tr.parent(it))==0 ) {
855 #ifdef DEBUG
856 std::cerr << "Found single term in " << tr.parent(it) << std::endl;
857 #endif
858 return true;
859 }
860 }
861 return false;
862 }
863
is_nonprod_factor_in_prod(iterator it)864 bool Algorithm::is_nonprod_factor_in_prod(iterator it)
865 {
866 if(*it->name!="\\prod" && *it->name!="\\sum" && *it->name!="\\asymimplicit" && *it->name!="\\comma"
867 && *it->name!="\\equals") {
868 try {
869 if(tr.is_head(it)==false && *tr.parent(it)->name=="\\prod")
870 return true;
871 }
872 catch(navigation_error& ex) {
873 // no parent, ignore
874 }
875 // else return true;
876 }
877 return false;
878 }
879
prod_wrap_single_term(iterator & it)880 bool Algorithm::prod_wrap_single_term(iterator& it)
881 {
882 if(is_single_term(it)) {
883 force_node_wrap(it, "\\prod");
884 return true;
885 }
886 else return false;
887 }
888
sum_wrap_single_term(iterator & it)889 bool Algorithm::sum_wrap_single_term(iterator& it)
890 {
891 if(is_single_term(it)) {
892 force_node_wrap(it, "\\sum");
893 return true;
894 }
895 else return false;
896 }
897
force_node_wrap(iterator & it,std::string nm)898 void Algorithm::force_node_wrap(iterator& it, std::string nm)
899 {
900 iterator prodnode=tr.insert(it, str_node(nm));
901 sibling_iterator fr=it, to=it;
902 ++to;
903 tr.reparent(prodnode, fr, to);
904 prodnode->fl.bracket=it->fl.bracket;
905 it->fl.bracket=str_node::b_none;
906 if(nm!="\\sum") { // multipliers should sit on terms in a sum
907 prodnode->multiplier=it->multiplier;
908 one(it->multiplier);
909 }
910 it=prodnode;
911 }
912
prod_unwrap_single_term(iterator & it)913 bool Algorithm::prod_unwrap_single_term(iterator& it)
914 {
915 if((*it->name)=="\\prod") {
916 if(tr.number_of_children(it)==1) {
917 multiply(tr.begin(it)->multiplier, *it->multiplier);
918 tr.begin(it)->fl.bracket=it->fl.bracket;
919 tr.begin(it)->multiplier=it->multiplier;
920 tr.flatten(it);
921 it=tr.erase(it);
922 return true;
923 }
924 }
925 return false;
926 }
927
sum_unwrap_single_term(iterator & it)928 bool Algorithm::sum_unwrap_single_term(iterator& it)
929 {
930 if((*it->name)=="\\sum") {
931 if(tr.number_of_children(it)==1) {
932 multiply(tr.begin(it)->multiplier, *it->multiplier);
933 tr.begin(it)->fl.bracket=it->fl.bracket;
934 tr.begin(it)->multiplier=it->multiplier;
935 tr.flatten(it);
936 it=tr.erase(it);
937 return true;
938 }
939 }
940 return false;
941 }
942
separated_by_derivative(iterator i1,iterator i2,iterator check_dependence) const943 bool Algorithm::separated_by_derivative(iterator i1, iterator i2, iterator check_dependence) const
944 {
945 iterator lca = tr.lowest_common_ancestor(i1, i2);
946
947 // Walk up the tree from the first node until the LCA, flag any derivatives
948 // with which we do not commute.
949
950 struct {
951 bool operator()(const Kernel& kernel, Ex& tr, iterator walk, iterator lca, iterator check_dependence)
952 {
953 const Properties& pr=kernel.properties;
954 do {
955 walk=Ex::parent(walk);
956 if(walk == lca) break;
957 const Derivative *der=pr.get<Derivative>(walk);
958 if(der) {
959 if(tr.is_valid(check_dependence) ) {
960 const DependsBase *dep = pr.get<DependsBase>(check_dependence);
961 if(dep) {
962 Ex deps=dep->dependencies(kernel, check_dependence);
963 sibling_iterator depobjs=deps.begin(deps.begin());
964 while(depobjs!=deps.end(deps.begin())) {
965 if(walk->name == depobjs->name) {
966 return true;
967 }
968 else {
969 // compare all indices
970 sibling_iterator indit=tr.begin(walk);
971 while(indit!=tr.end(walk)) {
972 if(indit->is_index()) {
973 if(subtree_exact_equal(&pr, indit, depobjs))
974 return true;
975 }
976 ++indit;
977 }
978 }
979 ++depobjs;
980 }
981 return false; // Dependence found but not relevant here.
982 }
983 else return false; // No dependence property found at all.
984 }
985 else return true; // Should not check for dependence.
986 }
987 }
988 while(walk != lca);
989 return false;
990 }
991 } one_run;
992
993 if(one_run(kernel, tr, i1, lca, check_dependence)) return true;
994 if(one_run(kernel, tr, i2, lca, check_dependence)) return true;
995
996 return false;
997 }
998
999
1000 // bool Algorithm::cleanup_anomalous_products(Ex& tr, Ex::iterator& it)
1001 // {
1002 // if(*(it->name)=="\\prod") {
1003 // if(tr.number_of_children(it)==0) {
1004 // it->name=name_set.insert("1").first;
1005 // return true;
1006 // }
1007 // else if(tr.number_of_children(it)==1) {
1008 // tr.begin(it)->fl.bracket=it->fl.bracket;
1009 // tr.begin(it)->multiplier=it->multiplier;
1010 // tr.flatten(it);
1011 // Ex::iterator tmp=tr.erase(it);
1012 // // txtout << "HERRE?" << std::endl;
1013 // pushup_multiplier(tmp);
1014 // it=tmp;
1015 // return true;
1016 // }
1017 // }
1018 // return false;
1019 // }
1020 //
1021
locate_single_object(Ex::iterator obj_to_find,Ex::iterator st,Ex::iterator nd,std::vector<unsigned int> & store)1022 unsigned int Algorithm::locate_single_object(Ex::iterator obj_to_find,
1023 Ex::iterator st, Ex::iterator nd,
1024 std::vector<unsigned int>& store)
1025 {
1026 unsigned int count=0;
1027 unsigned int index=0;
1028 while(st!=nd) {
1029 Ex::iterator it1=st;
1030 it1.skip_children();
1031 ++it1;
1032 if(tr.equal(st, it1, obj_to_find, Algorithm::compare_)) {
1033 ++count;
1034 store.push_back(index);
1035 }
1036 ++st;
1037 ++index;
1038 }
1039 return count;
1040 }
1041
locate_object_set(const Ex & objs,Ex::iterator st,Ex::iterator nd,std::vector<unsigned int> & store)1042 bool Algorithm::locate_object_set(const Ex& objs,
1043 Ex::iterator st, Ex::iterator nd,
1044 std::vector<unsigned int>& store)
1045 {
1046 // Locate the objects in which to symmetrise. We use an integer
1047 // index (offset wrt. 'st') rather than an iterator because the
1048 // latter only apply to a single tree, not to its copies.
1049
1050 // We accept only a tree with a \comma node at the top.
1051 Ex::iterator top=objs.begin();
1052 if(*top->name!="\\comma")
1053 top = objs.begin(objs.begin());
1054
1055 assert(*top->name=="\\comma");
1056
1057 Ex::sibling_iterator fst=objs.begin(top);
1058 Ex::sibling_iterator fnd=objs.end(top);
1059 while(fst!=fnd) {
1060 Ex::iterator aim=fst;
1061 if((*aim->name)=="\\comma") {
1062 // Objects can themselves be lists of other objects (for instance
1063 // when we want to symmetrise in index pairs).
1064 if(locate_object_set(aim, st, nd, store)==false)
1065 return false;
1066 }
1067 else {
1068 if((*aim->name).size()==0 && tr.number_of_children(aim)==1)
1069 aim=tr.begin(aim);
1070 if(locate_single_object(aim, st, nd, store)!=1)
1071 return false;
1072 }
1073 ++fst;
1074 }
1075 return true;
1076 }
1077
1078
1079 namespace cadabra {
1080
1081 // static functions
1082
number_of_indices(const Properties & pr,iterator it)1083 unsigned int Algorithm::number_of_indices(const Properties& pr, iterator it)
1084 {
1085 unsigned int res=0;
1086 index_iterator indit=index_iterator::begin(pr, it);
1087 while(indit!=index_iterator::end(pr, it)) {
1088 ++res;
1089 ++indit;
1090 }
1091 return res;
1092 }
1093
number_of_direct_indices(iterator it)1094 unsigned int Algorithm::number_of_direct_indices(iterator it)
1095 {
1096 unsigned int res=0;
1097 sibling_iterator sib=Ex::begin(it);
1098 while(sib!=Ex::end(it)) {
1099 if(sib->fl.parent_rel==str_node::p_sub || sib->fl.parent_rel==str_node::p_super)
1100 ++res;
1101 ++sib;
1102 }
1103 return res;
1104 }
1105
less_without_numbers(nset_t::iterator it1,nset_t::iterator it2)1106 bool Algorithm::less_without_numbers(nset_t::iterator it1, nset_t::iterator it2)
1107 {
1108 std::string::const_iterator ch1=(*it1).begin();
1109 std::string::const_iterator ch2=(*it2).begin();
1110
1111 while(ch1!=(*it1).end() && ch2!=(*it2).end()) {
1112 if(isdigit(*ch1)) return true; // bla1 < blaq
1113 if(isdigit(*ch2)) return false; // blaa !< bla1
1114 if(*ch1>=*ch2) return false;
1115 ++ch1;
1116 ++ch2;
1117 }
1118 if(ch1==(*it1).end()) {
1119 if(ch2==(*it2).end())
1120 return false;
1121 else
1122 return true;
1123 }
1124 return false;
1125 }
1126
equal_without_numbers(nset_t::iterator it1,nset_t::iterator it2)1127 bool Algorithm::equal_without_numbers(nset_t::iterator it1, nset_t::iterator it2)
1128 {
1129 std::string::const_iterator ch1=(*it1).begin();
1130 std::string::const_iterator ch2=(*it2).begin();
1131
1132 while(ch1!=(*it1).end() && ch2!=(*it2).end()) {
1133 if(isdigit(*ch1)) {
1134 if(isdigit(*ch2))
1135 return true;
1136 else
1137 return false;
1138 }
1139 if(*ch1!=*ch2) return false;
1140 ++ch1;
1141 ++ch2;
1142 }
1143 if(ch1==(*it1).end()) {
1144 if(ch2==(*it2).end())
1145 return true;
1146 else
1147 return false;
1148 }
1149 return false;
1150 }
1151
compare_(const str_node & one,const str_node & two)1152 bool Algorithm::compare_(const str_node& one, const str_node& two)
1153 {
1154 // If the obj->name is empty, this means that we look for a tree with
1155 // anything as root, but the required index structure in obj. This
1156 // requires a slightly different 'equal_to' (one that always matches
1157 // an empty node with a non-empty node).
1158
1159 if(/* one.fl.bracket!=two.fl.bracket || */ one.fl.parent_rel!=two.fl.parent_rel)
1160 return false;
1161
1162 if((*two.name).size()==0)
1163 return true;
1164 else if(one.name==two.name)
1165 return true;
1166 return false;
1167 }
1168
1169 }
1170