1
2 #include <typeinfo>
3
4 #include "Compare.hh"
5 #include "Algorithm.hh" // FIXME: only needed because index_iterator is in there
6 #include <sstream>
7 #include <regex>
8 #include "properties/Indices.hh"
9 #include "properties/Coordinate.hh"
10 #include "properties/ImplicitIndex.hh"
11 #include "properties/SelfCommutingBehaviour.hh"
12 #include "properties/CommutingAsSum.hh"
13 #include "properties/CommutingAsProduct.hh"
14 #include "properties/CommutingBehaviour.hh"
15 #include "properties/DifferentialForm.hh"
16 #include "properties/DiracBar.hh"
17 #include "properties/Integer.hh"
18 #include "properties/SortOrder.hh"
19
20 // In order to enable/disable debug output, also flip the swith in 'report' below.
21 // #define DEBUG(ln) ln
22 #define DEBUG(ln)
23
24 namespace cadabra {
25
26 int Ex_comparator::offset=0;
27
subtree_compare(const Properties * properties,Ex::iterator one,Ex::iterator two,int mod_prel,bool,int compare_multiplier,bool literal_wildcards)28 int subtree_compare(const Properties *properties,
29 Ex::iterator one, Ex::iterator two,
30 int mod_prel, bool, int compare_multiplier, bool literal_wildcards)
31 {
32 // std::cerr << "comparing " << Ex(one) << " with " << Ex(two) << " " << mod_prel << ", " << checksets << std::endl;
33
34 // The logic is to compare successive aspects of the two objects, returning a
35 // no-match code if a difference is found at a particular level, or continuing
36 // further down the line if there still is a match.
37
38 // Compare multipliers. Skip this step if one of the objects is a rational and the
39 // other not, as in that case we are matching values to symbols.
40 if( one->is_rational()==two->is_rational() ) {
41 if(compare_multiplier==-2 && !two->is_name_wildcard() && !one->is_name_wildcard())
42 if(one->multiplier != two->multiplier) {
43 if(*one->multiplier < *two->multiplier) return 2;
44 else return -2;
45 }
46 }
47
48 int mult=2;
49 // Handle object wildcards and comparison
50 if(!literal_wildcards) {
51 if(one->is_object_wildcard() || two->is_object_wildcard())
52 return 0;
53 }
54
55 // Handle mismatching node names.
56 if(one->name!=two->name) {
57 // std::cerr << *one->name << " != " << *two->name << std::endl;
58 if(literal_wildcards) {
59 if(*one->name < *two->name) return mult;
60 else return -mult;
61 }
62
63 DEBUG( std::cerr << "auto? " << one->is_autodeclare_wildcard() << ", " << two->is_numbered_symbol() << std::endl; );
64 if( (one->is_autodeclare_wildcard() && two->is_numbered_symbol()) ||
65 (two->is_autodeclare_wildcard() && one->is_numbered_symbol()) ) {
66 if( one->name_only() != two->name_only() ) {
67 if(*one->name < *two->name) return mult;
68 else return -mult;
69 }
70 }
71 else if( one->is_name_wildcard()==false && two->is_name_wildcard()==false ) {
72 if(*one->name < *two->name) return mult;
73 else return -mult;
74 }
75
76 DEBUG( std::cerr << "match despite difference: " << *one->name << " and " << *two->name << std::endl; );
77 }
78
79 // Compare parent relations.
80 if(mod_prel<=-2) {
81 str_node::parent_rel_t p1=one->fl.parent_rel;
82 str_node::parent_rel_t p2=two->fl.parent_rel;
83 if(p1!=p2) {
84 return (p1<p2)?2:-2;
85 }
86 }
87
88 // Now turn to the child nodes. Before comparing them directly, first compare
89 // the number of children, taking into account range wildcards.
90 int numch1=Ex::number_of_children(one);
91 int numch2=Ex::number_of_children(two);
92
93 // FIXME: handle range wildcards as in Props.cc
94
95 if(numch1!=numch2) {
96 if(numch1<numch2) return 2;
97 else return -2;
98 }
99
100 // Compare actual children. We run through this twice: first
101 // consider all non-index children, then all index children. This
102 // is because we want products of tensors which differ only by
103 // index position to be sorted by the names of the tensors first,
104 // not first by their index positions. Otherwise canonicalise cannot
105 // do its raising/lowering job.
106
107 bool do_indices=false;
108 int remember_ret=0;
109 if(mod_prel==0) mod_prel=-2;
110 else if(mod_prel>0) --mod_prel;
111 if(compare_multiplier==0) compare_multiplier=-2;
112 else if(compare_multiplier>0) --compare_multiplier;
113
114 for(;;) {
115 Ex::sibling_iterator sib1=one.begin(), sib2=two.begin();
116
117 while(sib1!=one.end()) {
118 if(sib1->is_index() == do_indices) {
119 int ret=subtree_compare(properties, sib1,sib2, mod_prel, true /* checksets */,
120 compare_multiplier, literal_wildcards);
121 // std::cerr << "result " << ret << std::endl;
122 if(abs(ret)>1)
123 return ret/abs(ret)*mult;
124 if(ret!=0 && remember_ret==0)
125 remember_ret=ret;
126 }
127 ++sib1;
128 ++sib2;
129 }
130
131 if(remember_ret!=0) break;
132
133 if(!do_indices) do_indices=true;
134 else break;
135 }
136
137 return remember_ret;
138 }
139
tree_less(const Properties * properties,const Ex & one,const Ex & two,int mod_prel,bool checksets,int compare_multiplier)140 bool tree_less(const Properties* properties, const Ex& one, const Ex& two, int mod_prel, bool checksets, int compare_multiplier)
141 {
142 return subtree_less(properties, one.begin(), two.begin(), mod_prel, checksets, compare_multiplier);
143 }
144
tree_equal(const Properties * properties,const Ex & one,const Ex & two,int mod_prel,bool checksets,int compare_multiplier)145 bool tree_equal(const Properties* properties, const Ex& one, const Ex& two, int mod_prel, bool checksets, int compare_multiplier)
146 {
147 return subtree_equal(properties, one.begin(), two.begin(), mod_prel, checksets, compare_multiplier);
148 }
149
tree_exact_less(const Properties * properties,const Ex & one,const Ex & two,int mod_prel,bool checksets,int compare_multiplier,bool literal_wildcards)150 bool tree_exact_less(const Properties* properties, const Ex& one, const Ex& two, int mod_prel, bool checksets, int compare_multiplier, bool literal_wildcards)
151 {
152 return subtree_exact_less(properties, one.begin(), two.begin(), mod_prel, checksets, compare_multiplier, literal_wildcards);
153 }
154
tree_exact_equal(const Properties * properties,const Ex & one,const Ex & two,int mod_prel,bool checksets,int compare_multiplier,bool literal_wildcards)155 bool tree_exact_equal(const Properties* properties, const Ex& one, const Ex& two, int mod_prel, bool checksets, int compare_multiplier, bool literal_wildcards)
156 {
157 return subtree_exact_equal(properties, one.begin(), two.begin(), mod_prel, checksets, compare_multiplier, literal_wildcards);
158 }
159
subtree_less(const Properties * properties,Ex::iterator one,Ex::iterator two,int mod_prel,bool checksets,int compare_multiplier)160 bool subtree_less(const Properties* properties, Ex::iterator one, Ex::iterator two, int mod_prel, bool checksets, int compare_multiplier)
161 {
162 int cmp=subtree_compare(properties, one, two, mod_prel, checksets, compare_multiplier);
163 if(cmp==2) return true;
164 return false;
165 }
166
subtree_equal(const Properties * properties,Ex::iterator one,Ex::iterator two,int mod_prel,bool checksets,int compare_multiplier)167 bool subtree_equal(const Properties* properties, Ex::iterator one, Ex::iterator two, int mod_prel, bool checksets, int compare_multiplier)
168 {
169 int cmp=subtree_compare(properties, one, two, mod_prel, checksets, compare_multiplier);
170 if(abs(cmp)<=1) return true;
171 return false;
172 }
173
subtree_exact_less(const Properties * properties,Ex::iterator one,Ex::iterator two,int mod_prel,bool checksets,int compare_multiplier,bool literal_wildcards)174 bool subtree_exact_less(const Properties* properties, Ex::iterator one, Ex::iterator two, int mod_prel, bool checksets, int compare_multiplier, bool literal_wildcards)
175 {
176 int cmp=subtree_compare(properties, one, two, mod_prel, checksets, compare_multiplier, literal_wildcards);
177 //std::cerr << "comparing " << Ex(one) << " with " << Ex(two) << " = " << cmp << std::endl;
178 if(cmp>0) return true;
179 return false;
180 }
181
subtree_exact_equal(const Properties * properties,Ex::iterator one,Ex::iterator two,int mod_prel,bool checksets,int compare_multiplier,bool literal_wildcards)182 bool subtree_exact_equal(const Properties* properties, Ex::iterator one, Ex::iterator two, int mod_prel, bool checksets, int compare_multiplier, bool literal_wildcards)
183 {
184 int cmp=subtree_compare(properties, one, two, mod_prel, checksets, compare_multiplier, literal_wildcards);
185 // std::cout << *one->name << " == " << *two->name << " : " << cmp << std::endl;
186 if(cmp==0) return true;
187 return false;
188 }
189
tree_less_obj(const Properties * k)190 tree_less_obj::tree_less_obj(const Properties* k)
191 : properties(k)
192 {
193 }
194
operator ()(const Ex & one,const Ex & two) const195 bool tree_less_obj::operator()(const Ex& one, const Ex& two) const
196 {
197 return tree_less(properties, one, two);
198 }
199
tree_less_modprel_obj(const Properties * k)200 tree_less_modprel_obj::tree_less_modprel_obj(const Properties* k)
201 : properties(k)
202 {
203 }
204
operator ()(const Ex & one,const Ex & two) const205 bool tree_less_modprel_obj::operator()(const Ex& one, const Ex& two) const
206 {
207 return tree_less(properties, one, two, 0);
208 }
209
tree_equal_obj(const Properties * k)210 tree_equal_obj::tree_equal_obj(const Properties* k)
211 : properties(k)
212 {
213 }
214
operator ()(const Ex & one,const Ex & two) const215 bool tree_equal_obj::operator()(const Ex& one, const Ex& two) const
216 {
217 return tree_equal(properties, one, two);
218 }
219
tree_exact_less_obj(const Properties * p)220 tree_exact_less_obj::tree_exact_less_obj(const Properties *p)
221 : properties(p)
222 {
223 }
224
operator ()(const Ex & one,const Ex & two) const225 bool tree_exact_less_obj::operator()(const Ex& one, const Ex& two) const
226 {
227 return tree_exact_less(properties, one, two);
228 }
229
tree_exact_less_no_wildcards_obj()230 tree_exact_less_no_wildcards_obj::tree_exact_less_no_wildcards_obj()
231 {
232 properties=0;
233 }
234
operator ()(const Ex & one,const Ex & two) const235 bool tree_exact_less_no_wildcards_obj::operator()(const Ex& one, const Ex& two) const
236 {
237 return tree_exact_less(properties, one, two, -2, true, 0, true);
238 }
239
operator ()(const Ex & one,const Ex & two) const240 bool tree_exact_less_no_wildcards_mod_prel_obj::operator()(const Ex& one, const Ex& two) const
241 {
242 return tree_exact_less(properties, one, two, 0, true, -2, true);
243 }
244
operator ()(const Ex & one,const Ex & two) const245 bool tree_exact_equal_obj::operator()(const Ex& one, const Ex& two) const
246 {
247 return tree_exact_equal(properties, one, two);
248 }
249
tree_exact_less_mod_prel_obj(const Properties * p)250 tree_exact_less_mod_prel_obj::tree_exact_less_mod_prel_obj(const Properties *p)
251 : properties(p)
252 {
253 }
254
operator ()(const Ex & one,const Ex & two) const255 bool tree_exact_less_mod_prel_obj::operator()(const Ex& one, const Ex& two) const
256 {
257 return tree_exact_less(properties, one, two, 0, true, -2, true);
258 }
259
operator ()(const Ex & one,const Ex & two) const260 bool tree_exact_equal_mod_prel_obj::operator()(const Ex& one, const Ex& two) const
261 {
262 return tree_exact_equal(properties, one, two, 0, true, -2, true);
263 }
264
operator ()(const Ex & one,const Ex & two) const265 bool tree_exact_less_for_indexmap_obj::operator()(const Ex& one, const Ex& two) const
266 {
267 return tree_exact_less(0, one, two, -2 /* mod_prel, was 0 */, true, -2 /* compare_multiplier, was 0 */, true);
268 }
269
270 //bool operator==(const Ex& first, const Ex& second)
271 // {
272 // return tree_exact_equal(properties, first, second, 0, true, -2, true);
273 // }
274 //
275
clear()276 void Ex_comparator::clear()
277 {
278 replacement_map.clear();
279 subtree_replacement_map.clear();
280 index_value_map.clear();
281 factor_locations.clear();
282 factor_moving_signs.clear();
283 }
284
match_subtree(const Ex & tr,Ex::iterator i1,Ex::iterator i2,Ex::iterator conditions)285 Ex_comparator::match_t Ex_comparator::match_subtree(const Ex& tr, Ex::iterator i1, Ex::iterator i2, Ex::iterator conditions)
286 {
287 auto ret = equal_subtree(i1, i2);
288 if(ret==match_t::subtree_match || ret==match_t::node_match) {
289 if(conditions==tr.end()) return ret;
290 std::string error;
291 if(satisfies_conditions(conditions, error))
292 return ret;
293 else
294 return match_t::no_match_greater; // FIXME: is that the right thing to do?
295 }
296 else return ret;
297 }
298
equal_subtree(Ex::iterator i1,Ex::iterator i2,useprops_t use_props,bool ignore_parent_rel)299 Ex_comparator::match_t Ex_comparator::equal_subtree(Ex::iterator i1, Ex::iterator i2,
300 useprops_t use_props, bool ignore_parent_rel)
301 {
302 DEBUG( std::cerr << "equal_subtree with use_props = " << use_props << std::endl; );
303 ++offset;
304
305 Ex::sibling_iterator i1end(i1);
306 Ex::sibling_iterator i2end(i2);
307 ++i1end;
308 ++i2end;
309
310 // When measuring depth (to determine if we need to use
311 // properties or not), things like \dot{\alpha} should give the
312 // same depth for \dot and for \alpha, because \dot is an
313 // Accent. We use a predicate function to take care of this.
314 auto depth_predicate = [this,use_props](const Ex::iterator& i) {
315 if(use_props!=useprops_t::not_at_top) return true;
316 const Accent *accent = properties.get<Accent>(i);
317 return (accent==0);
318 };
319
320 bool first_call=true;
321 match_t worst_mismatch=match_t::subtree_match;
322 Ex::iterator rememberi1=i1;
323
324 while(i1!=i1end && i2!=i2end) {
325 useprops_t up = use_props;
326 auto dist = Ex::distance(rememberi1, i1, depth_predicate);
327 if(use_props==useprops_t::not_at_top) {
328 if(dist!=0) up=useprops_t::always;
329 else up=useprops_t::never;
330 }
331 bool ip = ignore_parent_rel && (dist==0);
332 match_t mm=compare(i1, i2, first_call, up, ip);
333 // DEBUG( std::cerr << "COMPARE " << *i1->name << ", " << *i2->name << " = " << static_cast<int>(mm) << std::endl; )
334 first_call=false;
335 switch(mm) {
336 case match_t::no_match_indexpos_less:
337 case match_t::no_match_indexpos_greater:
338 worst_mismatch=mm;
339 // FIXME: at the moment skipping children is the right thing to do
340 // as we are assuming that the no_match_indexpos_... is the worst
341 // thing that could have happened for this child subtree. See
342 // also the FIXME below.
343 i1.skip_children();
344 i2.skip_children();
345 break;
346 case match_t::no_match_less:
347 case match_t::no_match_greater:
348 // As soon as we get a mismatch, return.
349 return report(mm);
350 case match_t::node_match: {
351 size_t num1=Ex::number_of_children(i1);
352 size_t num2=Ex::number_of_children(i2);
353
354 if(num1==1 && i1.begin()->is_range_wildcard()) {
355 // std::cerr << "comparing " << *i1->name << " with " << *i2->name << " " << num1 << " " << num2 << std::endl;
356 return match_t::subtree_match;
357 }
358
359 // TODO: this is where we should decide what to do with
360 // nodes which have sibling wildcards. Make a
361 // 'compare_siblings'. We also need a
362 // siblings_replacement_map in which we can store a
363 // sibling range for a given sibling wildcard symbol,
364 // e.g. a... -> i j k. This matching routine should walk
365 // to the first non-range object in the pattern, and match
366 // that. Once it is matched, it should store the resulting
367 // map for any range object to the left. Then continue,
368 // recursively, finding the second non-range object, and
369 // again storing the range object map. Once a no-match
370 // comes back, pop the match objects from the stack and
371 // try to find the non-range object to the right of the match
372 // reported earlier.
373
374 // This slightly oversearches (we could keep track of how
375 // many non-range objects are still to be matched in order
376 // to restrict how far to the right a search should go), but in
377 // practise this is probably not relevant (and can always be added).
378
379 if(num1 < num2) return report(match_t::no_match_less);
380 else if(num1 > num2) return report(match_t::no_match_greater);
381 break;
382 }
383 case match_t::match_index_less:
384 case match_t::match_index_greater:
385 // If we have a match but different index names, remember this
386 // mismatch, because it will determine our total result value.
387 if(worst_mismatch==match_t::subtree_match)
388 worst_mismatch=mm;
389 // If indices by type but not name, we do not need to go
390 // further down the tree. E.g. W_{\hat{\theta_1}} vs
391 // W_{\hat{\theta_2}} compared at the index position.
392 // The 'compare' has done a comparison of the entire
393 // tree of the indices, no need to go down.
394 i1.skip_children();
395 i2.skip_children();
396 break;
397 case match_t::subtree_match:
398 // If a match of the entire subtrees at i1 and i2 has been found,
399 // we do not need to go down the child nodes of i1 and i2 anymore.
400 i1.skip_children();
401 i2.skip_children();
402 break;
403 }
404 // Continue walking the tree downwards.
405 DEBUG( std::cerr << "one down" << std::endl; );
406 ++i1;
407 ++i2;
408 }
409 DEBUG( std::cerr << "equal_subtree done" << std::endl; );
410
411 return report(worst_mismatch);
412 }
413
Ex_comparator(const Properties & k)414 Ex_comparator::Ex_comparator(const Properties& k)
415 : properties(k), value_matches_index(false)
416 {
417 }
418
set_value_matches_index(bool v)419 void Ex_comparator::set_value_matches_index(bool v)
420 {
421 value_matches_index=v;
422 }
423
tab() const424 std::string Ex_comparator::tab() const
425 {
426 std::string ret;
427 for(int i=0; i<offset; ++i)
428 ret+=" ";
429 return ret;
430 }
431
report(Ex_comparator::match_t r) const432 Ex_comparator::match_t Ex_comparator::report(Ex_comparator::match_t r) const
433 {
434 return r;
435
436 std::cerr << tab() << "result = ";
437 switch(r) {
438 case match_t::node_match:
439 std::cerr << "node_match";
440 break;
441 case match_t::subtree_match:
442 std::cerr << "subtree_match";
443 break;
444 case match_t::match_index_less:
445 std::cerr << "match_index_less";
446 break;
447 case match_t::match_index_greater:
448 std::cerr << "match_index_greater";
449 break;
450 case match_t::no_match_indexpos_less:
451 std::cerr << "no_match_indexpos_less";
452 break;
453 case match_t::no_match_indexpos_greater:
454 std::cerr << "no_match_indexpos_greater";
455 break;
456 case match_t::no_match_less:
457 std::cerr << "no_match_less";
458 break;
459 case match_t::no_match_greater:
460 std::cerr << "no_match_greater";
461 break;
462 }
463 std::cerr << std::endl;
464 --offset;
465 return r;
466 }
467
compare(const Ex::iterator & one,const Ex::iterator & two,bool nobrackets,useprops_t use_props,bool ignore_parent_rel)468 Ex_comparator::match_t Ex_comparator::compare(const Ex::iterator& one,
469 const Ex::iterator& two,
470 bool nobrackets,
471 useprops_t use_props, bool ignore_parent_rel)
472 {
473 ++offset;
474
475 // nobrackets also implies 'no multiplier', i.e. 'toplevel'.
476 // 'one' is the substitute pattern, 'two' the expression under consideration.
477
478 DEBUG( std::cerr << tab() << "matching " << Ex(one) << tab() << "to " << Ex(two) << tab() << "using props = " << use_props << ", ignore_parent_rel = " << ignore_parent_rel << std::endl; );
479
480 if(nobrackets==false && one->fl.bracket != two->fl.bracket)
481 return report( (one->fl.bracket < two->fl.bracket)?match_t::no_match_less:match_t::no_match_greater );
482
483 // Determine whether we are dealing with one of the pattern types.
484 bool pattern=false;
485 bool objectpattern=false;
486 bool implicit_pattern=false; // anything in _{..} or ^{..} that is not an integer or coordinate
487 bool is_index=false;
488 bool is_sibling_pattern=false;
489 bool is_coordinate=false;
490 bool is_number=false;
491
492 if(one->fl.bracket==str_node::b_none && one->is_index() )
493 is_index=true;
494 if(one->is_name_wildcard())
495 pattern=true;
496 else if(one->is_object_wildcard())
497 objectpattern=true;
498 else if(one->is_siblings_wildcard())
499 is_sibling_pattern=true;
500 else if(is_index && one->is_integer()==false) {
501 // Things in _{..} or ^{..} are either indices (implicit patterns) or coordinates.
502 const Coordinate *cdn1=0;
503 if(use_props==useprops_t::always) {
504 DEBUG( std::cerr << tab() << "is " << *one->name << " a coordinate?" << std::endl; );
505 cdn1=properties.get<Coordinate>(one, true);
506 DEBUG( std::cerr << tab() << cdn1 << std::endl; );
507 }
508
509 if(cdn1==0)
510 implicit_pattern=true;
511 else
512 is_coordinate=true;
513 }
514 else if(one->is_integer())
515 is_number=true;
516
517 // Various cases to be distinguished now:
518 // - match index pattern to object
519 // - match object pattern to object
520 // - match coordinate to index
521 // - everything else, which does not involve patterns/wildcards
522
523 if(pattern || implicit_pattern) {
524
525 // It is possible to match integers to implicit patterns (indices), provided
526 // we know that the given index can take the found numerical value.
527 // See basic.cdb/test26 for a simple example.
528 // If we have a proper 'question mark' pattern, we always match to integers.
529
530 if(two->is_rational() && implicit_pattern) {
531 // Determine whether 'one' can take the value 'two'.
532 //std::cerr << "**** can one take value two " << use_props << std::endl;
533 const Integer *ip = 0;
534 if(use_props==useprops_t::always) {
535 DEBUG( std::cerr << tab() << "is " << *one->name << " an integer?" << std::endl; );
536 ip = properties.get<Integer>(one, true); // 'true' to ignore parent rel.
537 DEBUG( std::cerr << tab() << ip << std::endl; );
538 }
539
540 if(ip==0) return report(match_t::no_match_less);
541
542 bool lower_bdy=true, upper_bdy=true;
543 multiplier_t from, to;
544 if(ip->from.begin()==ip->from.end()) lower_bdy=false;
545 else {
546 if(!ip->from.begin()->is_rational()) return report(match_t::no_match_less);
547 from = *ip->from.begin()->multiplier;
548 }
549 if(ip->to.begin()==ip->to.end()) upper_bdy=false;
550 else {
551 if(!ip->to.begin()->is_rational()) return report(match_t::no_match_less);
552 to = *ip->to.begin()->multiplier;
553 }
554 if((lower_bdy && *two->multiplier < from) || (upper_bdy && *two->multiplier > to))
555 return report(match_t::no_match_less);
556
557 // std::cerr << tab() << Ex(one) << tab() << "can take value " << *two->multiplier << std::endl;
558 }
559
560 // We want to search the replacement map for replacement rules which we have
561 // constructed earlier, and discard the current match if it conflicts those
562 // rules. This is to make sure that e.g. a pattern k1_a k2_a does not match
563 // an expression k1_c k2_d.
564 //
565 // In order to ensure that a replacement rule for a lower index is also
566 // triggering a rule for an upper index, we simply store both rules (see
567 // below) so that searching for rules can remain simple.
568
569 replacement_map_t::iterator loc=replacement_map.find(one);
570
571 bool tested_full=true;
572
573 // If this is a pattern with a non-zero number of children,
574 // also search the pattern without the children (but not if
575 // this node is an Accent, because then the child nodes are
576 // very relevant).
577 if(loc == replacement_map.end() && Ex::number_of_children(one)!=0) {
578 DEBUG( std::cerr << tab() << "**** not found, trying without child nodes" << std::endl; );
579 if(properties.get<Accent>(one)==0 ) {
580 Ex tmp1(one);
581 tmp1.erase_children(tmp1.begin());
582 loc = replacement_map.find(tmp1);
583 tested_full=false;
584 }
585 }
586
587 if(loc!=replacement_map.end()) {
588 // We constructed a replacement rule for this node already at an earlier
589 // stage. Need to make sure that that rule is consistent with what we
590 // found now.
591
592 int cmp;
593
594 // If this is an index/pattern, try to match the whole index/pattern.
595 if(tested_full)
596 cmp=subtree_compare(&properties, (*loc).second.begin(), two, -2);
597 else {
598 Ex tmp2(two);
599 tmp2.erase_children(tmp2.begin());
600 cmp=subtree_compare(&properties, (*loc).second.begin(), tmp2.begin(), -2);
601 }
602 DEBUG(std::cerr << " pattern " << two
603 << " should be " << (*loc).second.begin()
604 << " because that's what " << one
605 << " was set to previously; result " << cmp << std::endl; );
606
607 if(cmp==0) return report(match_t::subtree_match);
608 else if(cmp>0) return report(match_t::no_match_less);
609 else return report(match_t::no_match_greater);
610 }
611 else {
612 // This index/pattern was not encountered earlier. If this node is an index,
613 // check that the index types in pattern and object agree (if known,
614 // otherwise assume they match).
615 // If two is a rational, we have already checked that one can take this value.
616
617 if(two->is_index() && !one->is_index() && !one->is_range_wildcard())
618 return report(match_t::no_match_indexpos_greater);
619
620 if(one->is_index()) {
621 DEBUG( std::cerr << tab() << "object one is index" << std::endl; );
622
623 const Indices *t1=0;
624 const Indices *t2=0;
625 if(use_props==useprops_t::always) {
626 DEBUG( std::cerr << tab() << "is " << one << " an index?" << std::endl; );
627 t1=properties.get<Indices>(one, false);
628 DEBUG( std::cerr << tab() << "found for one: " << t1 << std::endl; );
629 if(two->is_rational()==false) {
630 DEBUG( std::cerr << tab() << "is " << two << " an index?" << std::endl; );
631 t2=properties.get<Indices>(two, false);
632 DEBUG( std::cerr << tab() << t2 << std::endl; );
633 // It is still possible that t2 is a Coordinate and
634 // t1 an Index which can take the value of the
635 // coordinate. This happens when 'm' is an index
636 // taking values {t,...}, you declare X^{m} to have
637 // a property and then later ask if X^{t} has that
638 // property. In that case X^{m} is object 1 and
639 // X^{t} object 2, and we end up here (NOT in the
640 // branch with both exchanged, which happens when
641 // evaluate tries to determine if a rule for X^{t}
642 // applies to an expression X^{m}).
643 if(t1!=0 && t2!=t1) {
644 auto ivals = std::find_if(t1->values.begin(), t1->values.end(),
645 [&](const Ex& a) {
646 if(subtree_compare(&properties, a.begin(), two, 0)==0) return true;
647 else return false;
648 });
649 if(ivals!=t1->values.end())
650 t2=t1;
651 }
652 }
653 else {
654 t2=t1; // We already know 'one' can take the value 'two', so in a sense t2 is in the same set as t1.
655 DEBUG( std::cerr << tab() << two << " is rational" << std::endl; );
656 }
657 }
658
659 // Check parent rel if a) there is no Indices property for the indices, b) the index positions
660 // are not free. Effectively this means that indices without property info get treated as
661 // fixed-position indices.
662
663 // FIXME: this is not entirely correct. If the indexpos does not match, going deeper may
664 // still reveal that the mismatch is even worse.
665
666 if(!ignore_parent_rel)
667 if(t1==0 || t2==0 || (t1->position_type!=Indices::free && t2->position_type!=Indices::free))
668 if(one->fl.parent_rel != two->fl.parent_rel) {
669 DEBUG( std::cerr << tab() << "parent_rels not the same" << std::endl;);
670 return report( (one->fl.parent_rel < two->fl.parent_rel)
671 ?match_t::no_match_indexpos_less:match_t::no_match_indexpos_greater );
672 }
673
674 // If both indices have no Indices property, compare them by name and pretend they are
675 // both in the same Indices set.
676
677 if(two->is_rational()==false && !(t1==0 && t2==0)) {
678 if( (t1 || t2) && implicit_pattern ) {
679 if(t1 && t2) {
680 if((*t1).set_name != (*t2).set_name) {
681 if((*t1).set_name < (*t2).set_name) return report(match_t::no_match_less);
682 else return report(match_t::no_match_greater);
683 }
684 }
685 else {
686 // If we get here, 'one' or 'two' has an Indices property, and
687 // the other one doesn't. So we do not know how to compare them,
688 // except by name. Note that we should return something which is
689 // symmetric if 'two' and 'one' had been exchanged!
690 int dc = subtree_compare(&properties, one, two, 0);
691 if(dc<0) return report(match_t::no_match_less);
692 else return report(match_t::no_match_greater);
693 }
694 }
695 }
696 }
697
698 // See the documentation of substitute::can_apply for details about
699 // how the replacement_map is supposed to work. In general, if we want
700 // that e.g a found _{z} also leads to a replacement rule for (z), or
701 // if we want that a found _{z} also leads to a replacement for ^{z},
702 // this needs to be added to the replacement map explicitly.
703
704 DEBUG( std::cerr << "adding " << one << " -> " << two << " to replacement map " << std::endl; );
705 replacement_map[one]=two;
706
707 // if this is an index, also store the pattern with the parent_rel flipped
708
709 if(one->is_index()) {
710 Ex cmptree1(one);
711 Ex cmptree2(two);
712 cmptree1.begin()->flip_parent_rel();
713 if(two->is_index())
714 cmptree2.begin()->flip_parent_rel();
715 replacement_map[cmptree1]=cmptree2;
716 }
717
718 // if this is a pattern and the pattern has a non-zero number of children,
719 // also add the pattern without the children
720 if(Ex::number_of_children(one)!=0) {
721 Ex tmp1(one), tmp2(two);
722 tmp1.erase_children(tmp1.begin());
723 tmp2.erase_children(tmp2.begin());
724 replacement_map[tmp1]=tmp2;
725 }
726 // and if this is a pattern also insert the one without the parent_rel
727 if( /* one->is_name_wildcard() && */ one->is_index()) {
728 //std::cerr << "storing pattern without pattern rel " << replacement_map.size() << std::endl;
729 Ex tmp1(one), tmp2(two);
730 tmp1.begin()->fl.parent_rel=str_node::p_none;
731 tmp2.begin()->fl.parent_rel=str_node::p_none;
732 // We do not want this pattern to be present already.
733 auto fnd=replacement_map.find(tmp1);
734 if(fnd!=replacement_map.end()) {
735 throw InternalError("Attempting to duplicate replacement rule.");
736 }
737 // assert(replacement_map.find(tmp1)!=replacement_map.end());
738 // std::cerr << "storing " << Ex(tmp1) << " -> " << Ex(tmp2) << std::endl;
739 replacement_map[tmp1]=tmp2;
740 }
741
742 DEBUG( std::cerr << "Replacement map is now:" << std::endl;
743 for(auto& rule: replacement_map)
744 std::cerr << "* " << rule.first << " -> " << rule.second << std::endl;
745 );
746
747 }
748
749 // Return a match of the appropriate type. If we are comparing indices and
750 // they matched because they are from the same set but do not have the same
751 // name, we still need to let the caller know about this.
752 if(is_index) {
753 int xc = subtree_compare(0, one, two, ignore_parent_rel?(-1):(-2));
754 if(xc==0) return report(match_t::subtree_match);
755 if(xc>0) return report(match_t::match_index_less);
756 return report(match_t::match_index_greater);
757 }
758 else return report(match_t::node_match);
759 }
760 else if(objectpattern) {
761 // Even for object patterns, if we do not ignore the parent rel, we need to test them.
762 if(!ignore_parent_rel)
763 if( one->fl.parent_rel != two->fl.parent_rel )
764 return report( (one->fl.parent_rel < two->fl.parent_rel)
765 ?match_t::no_match_indexpos_less:match_t::no_match_indexpos_greater );
766
767 subtree_replacement_map_t::iterator loc=subtree_replacement_map.find(one->name);
768 if(loc!=subtree_replacement_map.end()) {
769 return report(equal_subtree((*loc).second,two,use_props));
770 }
771 else subtree_replacement_map[one->name]=two;
772
773 return report(match_t::subtree_match);
774 }
775 else if(is_coordinate || is_number) { // Check if the coordinate can come from an index.
776 const Indices *t2=0;
777 if(use_props==useprops_t::always) {
778 DEBUG( std::cerr << tab() << "is " << *two->name << " an index?" << std::endl; );
779 t2=properties.get<Indices>(two, true);
780 DEBUG( std::cerr << tab() << t2 << std::endl; );
781 }
782
783 if(value_matches_index && t2) {
784 // std::cerr << "coordinate " << *one->name << " versus index " << *two->name << std::endl;
785 // If the 'two' index type is fixed or independent, ensure
786 // that the parent relation matches!
787
788 if(!ignore_parent_rel)
789 if(t2->position_type==Indices::fixed || t2->position_type==Indices::independent) {
790 if( one->fl.parent_rel != two->fl.parent_rel ) {
791 DEBUG( std::cerr << tab() << "parent_rels not the same" << std::endl;);
792 return report( (one->fl.parent_rel < two->fl.parent_rel)
793 ?match_t::no_match_indexpos_less:match_t::no_match_indexpos_greater );
794 }
795 }
796
797 // Look through values attribute of Indices object to see if the 'two' index
798 // can take the 'one' value.
799
800 auto ivals = std::find_if(t2->values.begin(), t2->values.end(),
801 [&](const Ex& a) {
802 if(subtree_compare(&properties, a.begin(), one, 0)==0) return true;
803 else return false;
804 });
805 if(ivals!=t2->values.end()) {
806 // Verify that the 'two' index has not already been matched to a value
807 // different from 'one'.
808 Ex t1(two), t2(two), o1(one), o2(one);
809 t2.begin()->flip_parent_rel();
810 o2.begin()->flip_parent_rel();
811 auto prev1 = index_value_map.find(t1);
812 auto prev2 = index_value_map.find(t2);
813 if(prev1!=index_value_map.end() && ! (prev1->second==o1) ) {
814 // std::cerr << "Previously 1 " << Ex(two) << " was " << Ex(prev1->second) << std::endl;
815 return report(match_t::no_match_less);
816 }
817 if(prev2!=index_value_map.end() && ! (prev2->second==o2) ) {
818 // std::cerr << "Previously 2 " << Ex(two) << " was " << Ex(prev2->second) << std::endl;
819 return report(match_t::no_match_less);
820 }
821
822 index_value_map[two]=one;
823 return report(match_t::node_match);
824 }
825 else {
826 // The index 'one' is not known to be able to take value 'two'. So this is not
827 // a match. Compare lexographically; this should be symmetric if one and two had
828 // been exchanged.
829 int dc = subtree_compare(&properties, one, two, 0);
830 if(dc<0) return report(match_t::no_match_less);
831 else return report(match_t::no_match_greater);
832 }
833 }
834 else {
835 // What's left is the possibility that both indices are Coordinates, in which case they
836 // need to match exactly.
837 int cmp=subtree_compare(&properties, one, two, -2);
838 if(cmp==0) return report(match_t::subtree_match);
839 else if(cmp>0) return report(match_t::no_match_less);
840 else return report(match_t::no_match_greater);
841 }
842 }
843 else { // object is not dummy nor objectpattern nor coordinate
844
845 if(one->is_rational() && two->is_rational()) {
846 if(one->multiplier!=two->multiplier) {
847 if(*one->multiplier < *two->multiplier) return report(match_t::no_match_less);
848 else return report(match_t::no_match_greater);
849 }
850 else {
851 // Equal numerical factors with different parent rel _never_ match, because
852 // we cannot determine if index raising/lowering is allowed if we do not
853 // know the bundle type.
854 if(!ignore_parent_rel)
855 if(one->fl.parent_rel != two->fl.parent_rel)
856 return report( (one->fl.parent_rel < two->fl.parent_rel)
857 ?match_t::no_match_indexpos_less:match_t::no_match_indexpos_greater );
858 }
859 }
860
861 if(name_match_with_autodeclare(one, two)) {
862 if(nobrackets || (one->multiplier == two->multiplier) ) {
863 if(ignore_parent_rel || one->fl.parent_rel==two->fl.parent_rel) return report( match_t::node_match );
864 report( (one->fl.parent_rel < two->fl.parent_rel)
865 ?match_t::no_match_indexpos_less:match_t::no_match_indexpos_greater );
866 }
867
868 if(*one->multiplier < *two->multiplier) return report(match_t::no_match_less);
869 else return report(match_t::no_match_greater);
870 }
871 else if( ((one->is_autodeclare_wildcard() && two->is_numbered_symbol()) ||
872 (two->is_autodeclare_wildcard() && one->is_numbered_symbol()) ) && one->name_only()==two->name_only() ) {
873 return report(match_t::node_match);
874 }
875 else {
876 if( *one->name < *two->name ) return report(match_t::no_match_less);
877 else return report(match_t::no_match_greater);
878 }
879 }
880
881 assert(1==0); // should never be reached
882
883 return report(match_t::no_match_less);
884 }
885
name_match_with_autodeclare(Ex::sibling_iterator one,Ex::sibling_iterator two) const886 bool Ex_comparator::name_match_with_autodeclare(Ex::sibling_iterator one, Ex::sibling_iterator two) const
887 {
888 if(one->name==two->name) return true;
889 if((one->is_autodeclare_wildcard() && two->is_numbered_symbol()) ||
890 (two->is_autodeclare_wildcard() && one->is_numbered_symbol()) ) {
891 if( one->name_only()==two->name_only() )
892 return true;
893 }
894 return false;
895 }
896
match_subproduct(const Ex & tr,Ex::sibling_iterator lhs,Ex::sibling_iterator tofind,Ex::sibling_iterator st,Ex::iterator conditions)897 Ex_comparator::match_t Ex_comparator::match_subproduct(const Ex& tr,
898 Ex::sibling_iterator lhs,
899 Ex::sibling_iterator tofind,
900 Ex::sibling_iterator st,
901 Ex::iterator conditions)
902 {
903 replacement_map_t backup_replacements(replacement_map);
904 subtree_replacement_map_t backup_subtree_replacements(subtree_replacement_map);
905
906 // 'Start' iterates over all factors, trying to find 'tofind'. It may happen that the
907 // first match is such that the entire subproduct matches, but it may be that we have
908 // to iterate 'start' over more factors (typically when non-commutative objects are
909 // concerned).
910
911 Ex::sibling_iterator start=st.begin();
912 while(start!=st.end()) {
913
914 // The factor 'tofind' can only be matched against a factor in the subproduct if we
915 // have not already previously matched part of the lhs to this factor. We check that first.
916
917 if(std::find(factor_locations.begin(), factor_locations.end(), start)==factor_locations.end()) {
918
919 // Compare this factor with 'tofind'.
920
921 auto match = equal_subtree(tofind, start);
922 if(match==match_t::subtree_match || match==match_t::match_index_less || match==match_t::match_index_greater) {
923
924 // The factor has been found. Verify that it can be moved
925 // next to the previous factor, if applicable (nontrivial
926 // if factors do not commute).
927
928 int sign=1;
929 if(factor_locations.size()>0) {
930 DEBUG(std::cerr << "--- can move? ---" << std::endl; );
931 // Determining the sign is non-trivial to do step-by step.
932 // Consider an expression
933 // A B C D E
934 // and a pattern
935 // D A B
936 // (D, A, B anti-commuting). A can be moved to the right of D
937 // picking up two signs, and B can moved to the right of A with
938 // no sign.
939 Ex_comparator comparator(properties);
940 sign=comparator.can_move_adjacent(st, factor_locations, start);
941 DEBUG(std::cerr << "--- done can move ---" << sign << std::endl; );
942 }
943 if(sign==0) { // object found, but we cannot move it in the right order
944 replacement_map=backup_replacements;
945 subtree_replacement_map=backup_subtree_replacements;
946 }
947 else {
948 factor_locations.push_back(start);
949 factor_moving_signs.push_back(sign);
950
951 Ex::sibling_iterator nxt=tofind;
952 ++nxt;
953 if(nxt!=lhs.end()) {
954 match_t res=match_subproduct(tr, lhs, nxt, st, conditions);
955 if(res==match_t::subtree_match) return res;
956 else {
957 // txtout << tofind.node << "found factor useless " << start.node << std::endl;
958 factor_locations.pop_back();
959 factor_moving_signs.pop_back();
960 replacement_map=backup_replacements;
961 subtree_replacement_map=backup_subtree_replacements;
962 }
963 }
964 else {
965 // Found all factors in sub-product, now check the conditions.
966 std::string error;
967 if(conditions==tr.end()) return match_t::subtree_match;
968 if(satisfies_conditions(conditions, error)) {
969 return match_t::subtree_match;
970 }
971 else {
972 factor_locations.pop_back();
973 factor_moving_signs.pop_back();
974 replacement_map=backup_replacements;
975 subtree_replacement_map=backup_subtree_replacements;
976 }
977 }
978 }
979 }
980 else {
981 // txtout << tofind.node << "does not match" << std::endl;
982 replacement_map=backup_replacements;
983 subtree_replacement_map=backup_subtree_replacements;
984 }
985 }
986 ++start;
987 }
988 return match_t::no_match_less; // FIXME not entirely true
989 }
990
match_subsum(const Ex & tr,Ex::sibling_iterator lhs,Ex::sibling_iterator tofind,Ex::sibling_iterator st,Ex::iterator conditions)991 Ex_comparator::match_t Ex_comparator::match_subsum(const Ex& tr,
992 Ex::sibling_iterator lhs,
993 Ex::sibling_iterator tofind,
994 Ex::sibling_iterator st,
995 Ex::iterator conditions)
996 {
997 replacement_map_t backup_replacements(replacement_map);
998 subtree_replacement_map_t backup_subtree_replacements(subtree_replacement_map);
999
1000 // 'Start' iterates over all terms, trying to find 'tofind'. It may happen that the
1001 // first match is such that the entire sub-sum matches, but it may be that we have
1002 // to iterate 'start' over more terms (typically when relative factors of terms
1003 // do not match).
1004
1005 Ex::sibling_iterator start=st.begin();
1006 while(start!=st.end()) {
1007
1008 // The term 'tofind' can only be matched against a term in the sub-sum if we
1009 // have not already previously matched part of the lhs to this term. We check that first.
1010
1011 if(std::find(factor_locations.begin(), factor_locations.end(), start)==factor_locations.end()) {
1012
1013 // Compare this factor with 'tofind'.
1014
1015 auto match = equal_subtree(tofind, start);
1016 if(match==match_t::subtree_match || match==match_t::match_index_less || match==match_t::match_index_greater) {
1017
1018 // The term has been found. If this is not the 1st term, verify
1019 // that the ratio of its multiplier to the multiplier of the search term
1020 // agrees with that ratio for the first term.
1021
1022 if(tr.index(tofind)==0) {
1023 term_ratio = (*tofind->multiplier)/(*start->multiplier);
1024 }
1025 auto this_ratio = (*tofind->multiplier)/(*start->multiplier);
1026 if(this_ratio!=term_ratio) {
1027 replacement_map=backup_replacements;
1028 subtree_replacement_map=backup_subtree_replacements;
1029 }
1030 else {
1031 factor_locations.push_back(start);
1032
1033 Ex::sibling_iterator nxt=tofind;
1034 ++nxt;
1035 if(nxt!=lhs.end()) {
1036 match_t res=match_subsum(tr, lhs, nxt, st, conditions);
1037 if(res==match_t::subtree_match) return res;
1038 else {
1039 factor_locations.pop_back();
1040 replacement_map=backup_replacements;
1041 subtree_replacement_map=backup_subtree_replacements;
1042 }
1043 }
1044 else {
1045 // Found all factors in sub-product, now check the conditions.
1046 std::string error;
1047 if(conditions==tr.end()) return match_t::subtree_match;
1048 if(satisfies_conditions(conditions, error)) {
1049 return match_t::subtree_match;
1050 }
1051 else {
1052 factor_locations.pop_back();
1053 replacement_map=backup_replacements;
1054 subtree_replacement_map=backup_subtree_replacements;
1055 }
1056 }
1057 }
1058 }
1059 else {
1060 // txtout << tofind.node << "does not match" << std::endl;
1061 replacement_map=backup_replacements;
1062 subtree_replacement_map=backup_subtree_replacements;
1063 }
1064 }
1065 ++start;
1066 }
1067 return match_t::no_match_less; // FIXME not entirely true
1068 }
1069
1070 //Ex_comparator::match_t Ex_comparator::match_subsum(const Ex& tr,
1071 // Ex::sibling_iterator lhs,
1072 // Ex::sibling_iterator tofind,
1073 // Ex::sibling_iterator st,
1074 // Ex::iterator conditions)
1075 // {
1076 // // 'Start' iterates over all terms, trying to find 'tofind'.
1077 //
1078 // Ex::sibling_iterator start=st.begin();
1079 // while(start!=st.end()) {
1080 //
1081 // // The term 'tofind' can only be matched against a term in the
1082 // // subsum if we have not already previously matched part of the
1083 // // lhs to this factor. We check that first.
1084 //
1085 // if(std::find(factor_locations.begin(), factor_locations.end(), start)==factor_locations.end()) {
1086 //
1087 // // Compare this term with 'tofind'.
1088 //
1089 // auto match = equal_subtree(tofind, start);
1090 // if(match==match_t::subtree_match || match==match_t::match_index_less || match==match_t::match_index_greater) {
1091 // factor_locations.push_back(start);
1092 //
1093 // Ex::sibling_iterator nxt=tofind;
1094 // ++nxt;
1095 // if(nxt!=lhs.end()) {
1096 // match_t res=match_subsum(tr, lhs, nxt, st, conditions);
1097 // return res;
1098 // }
1099 // else {
1100 // // Found all factors in sub-product.
1101 // auto sib=lhs.begin();
1102 // for(size_t i=0; i<factor_locations.size(); ++i) {
1103 // std::cerr << *factor_locations[i]->multiplier << " (vs " << *sib->multiplier << "), ";
1104 // ++sib;
1105 // }
1106 // std::cerr << std::endl;
1107 //
1108 // // Now check the conditions.
1109 // std::string error;
1110 // if(conditions==tr.end()) return match_t::subtree_match;
1111 // if(satisfies_conditions(conditions, error)) {
1112 // return match_t::subtree_match;
1113 // }
1114 // }
1115 // }
1116 // }
1117 // ++start;
1118 // }
1119 // return match_t::no_match_less;
1120 // }
1121
1122
can_move_adjacent(Ex::iterator prod,Ex::sibling_iterator one,Ex::sibling_iterator two,bool fix_one)1123 int Ex_comparator::can_move_adjacent(Ex::iterator prod,
1124 Ex::sibling_iterator one, Ex::sibling_iterator two, bool fix_one)
1125 {
1126 assert(Ex::parent(one)==Ex::parent(two));
1127 assert(Ex::parent(one)==prod);
1128
1129 // Make sure that 'one' points to the object which occurs first in 'prod'.
1130 bool onefirst=false;
1131 Ex::sibling_iterator probe=one;
1132 while(probe!=prod.end()) {
1133 if(probe==two) {
1134 onefirst=true;
1135 break;
1136 }
1137 ++probe;
1138 }
1139 int sign=1;
1140 if(!onefirst) {
1141 std::swap(one,two);
1142 auto es=equal_subtree(one,two);
1143 sign*=can_swap(one,two,es);
1144 // txtout << "swapping one and two: " << sign << std::endl;
1145 }
1146
1147 if(sign!=0) {
1148 // Loop over all pair flips which are necessary to move one to the left of two.
1149 // Keep one fixed if fix_one is true.
1150
1151 if(fix_one) {
1152 probe=two;
1153 --probe;
1154 while(probe!=one) {
1155 auto es=equal_subtree(two,probe);
1156 // std::cerr << "swapping " << Ex(two) << " and " << Ex(probe) << std::endl;
1157 sign*=can_swap(two,probe,es);
1158 if(sign==0) break;
1159 --probe;
1160 }
1161 }
1162 else {
1163 probe=one;
1164 ++probe;
1165 while(probe!=two) {
1166 assert(probe!=prod.end());
1167 auto es=equal_subtree(one,probe);
1168 // std::cerr << "swapping " << Ex(one) << " and " << Ex(probe) << std::endl;
1169 sign*=can_swap(one,probe,es);
1170 if(sign==0) break;
1171 ++probe;
1172 }
1173 }
1174 }
1175 return sign;
1176 }
1177
can_move_to_front(Ex & tr,Ex::iterator prod,Ex::sibling_iterator one)1178 int Ex_comparator::can_move_to_front(Ex& tr, Ex::iterator prod, Ex::sibling_iterator one)
1179 {
1180 // Insert a dummy, determine whether we can move
1181 // next to the dummy, then erase the dummy again.
1182 auto dummy = tr.prepend_child(prod, str_node("dummy"));
1183 int sign=can_move_adjacent(prod, dummy, one, true);
1184 tr.erase(dummy);
1185 return sign;
1186 }
1187
can_swap_components(Ex::iterator one,Ex::iterator two,match_t subtree_comparison)1188 int Ex_comparator::can_swap_components(Ex::iterator one, Ex::iterator two, match_t subtree_comparison)
1189 {
1190 int tmp;
1191 auto impi=properties.get_with_pattern<ImplicitIndex>(one, tmp, "");
1192 if(impi.first) {
1193 if(impi.first->explicit_form.size()>0) {
1194 one=impi.first->explicit_form.begin();
1195 }
1196 }
1197 impi=properties.get_with_pattern<ImplicitIndex>(two, tmp, "");
1198 if(impi.first) {
1199 if(impi.first->explicit_form.size()>0) {
1200 two=impi.first->explicit_form.begin();
1201 }
1202 }
1203 return can_swap(one, two, subtree_comparison, true);
1204 }
1205
1206 // Determine the sign required to move the last factor in 'factors' to
1207 // the right of the first. If moving left, do not count signs from
1208 // moving past any of the other factors. If moving right, do count
1209 // those signs. In effect, the logic is that we count signs as if
1210 // all other factors have already been moved to the right of the first.
1211
can_move_adjacent(Ex::iterator prod,const std::vector<Ex::sibling_iterator> & factors,Ex::sibling_iterator i2)1212 int Ex_comparator::can_move_adjacent(Ex::iterator prod, const std::vector<Ex::sibling_iterator>& factors, Ex::sibling_iterator i2)
1213 {
1214 if(factors.size()==0) return 1;
1215
1216 Ex::sibling_iterator i1=factors[0];
1217
1218 // Determine whether to move right or left.
1219 bool move_right=true;
1220 Ex::sibling_iterator probe=i1;
1221 while(probe!=prod.end()) {
1222 if(probe==i2) {
1223 move_right=false;
1224 break;
1225 }
1226 ++probe;
1227 }
1228
1229 // Move through all factors.
1230 probe=i2;
1231 int sign=1;
1232 do {
1233 if(move_right) ++probe;
1234 else --probe;
1235 if(probe==i1)
1236 break;
1237
1238 if(std::find(factors.begin(), factors.end(), probe)!=factors.end())
1239 continue;
1240 auto es=equal_subtree(i2, probe);
1241 sign*=can_swap(i2, probe, es);
1242
1243 }
1244 while(sign!=0);
1245
1246 if(move_right) {
1247 // When moving right, all factors which we have already ordered
1248 // sit to the right of the moving one, so we need to swap with them
1249 // all.
1250 auto f=factors.begin();
1251 while(f!=factors.end()) {
1252 auto es=equal_subtree(i2, *f);
1253 sign*=can_swap(i2, *f, es);
1254 ++f;
1255 }
1256 }
1257
1258 return sign;
1259 }
1260
1261
1262 // Should obj and obj+1 be swapped, according to the SortOrder
1263 // properties?
1264 //
should_swap(Ex::iterator obj,match_t subtree_comparison)1265 bool Ex_comparator::should_swap(Ex::iterator obj, match_t subtree_comparison)
1266 {
1267 Ex::sibling_iterator one=obj, two=obj;
1268 ++two;
1269
1270 // If the two objects are the same up to index names, we do not care
1271 // whether it sits in a SortOrder list; just compare alpha.
1272 if(subtree_comparison==match_t::match_index_less) return false;
1273 if(subtree_comparison==match_t::match_index_greater) return true;
1274
1275 // Find a SortOrder property which contains both one and two.
1276 int num1, num2;
1277 const SortOrder *so1=properties.get<SortOrder>(one,num1);
1278 const SortOrder *so2=properties.get<SortOrder>(two,num2);
1279
1280 if(so1==0 || so2==0 || so1!=so2) {
1281 // std::cerr << "No sort order between " << Ex(one) << " and " << Ex(two);
1282 report(subtree_comparison);
1283 // std::cerr << std::endl;
1284 // No explicit sort order known; use alpha sort.
1285 if(subtree_comparison==match_t::subtree_match) return false;
1286 if(subtree_comparison==match_t::no_match_less) return false;
1287 if(subtree_comparison==match_t::no_match_greater) return true;
1288 return false;
1289 }
1290
1291 assert(so1==so2);
1292 return num1>num2;
1293 }
1294
1295 // Various tests about whether two non-elementary objects can be swapped.
1296 //
can_swap_prod_obj(Ex::iterator prod,Ex::iterator obj,bool ignore_implicit_indices)1297 int Ex_comparator::can_swap_prod_obj(Ex::iterator prod, Ex::iterator obj,
1298 bool ignore_implicit_indices)
1299 {
1300 // std::cout << "prod_obj " << *prod->name << " " << *obj->name << std::endl;
1301 // Warning: no check is made that prod is actually a product!
1302 int sign=1;
1303 Ex::sibling_iterator sib=prod.begin();
1304 while(sib!=prod.end()) {
1305 const Indices *ind1=properties.get<Indices>(sib, true);
1306 const Indices *ind2=properties.get<Indices>(obj, true);
1307 if(! (ind1!=0 && ind2!=0) ) { // If both objects are actually real indices,
1308 // then we do not include their commutativity property
1309 // in the sign. This is because the routines that use
1310 // can_swap_prod_obj all test for such index-index
1311 // swaps separately.
1312 auto es=equal_subtree(sib, obj);
1313 // std::cout << " " << *sib->name << " " << *obj->name << " " << es << std::endl;
1314 sign*=can_swap(sib, obj, es, ignore_implicit_indices);
1315 if(sign==0) break;
1316 }
1317 ++sib;
1318 }
1319 return sign;
1320 }
1321
can_swap_prod_prod(Ex::iterator prod1,Ex::iterator prod2,bool ignore_implicit_indices)1322 int Ex_comparator::can_swap_prod_prod(Ex::iterator prod1, Ex::iterator prod2,
1323 bool ignore_implicit_indices)
1324 {
1325 // std::cout << "prod_prod " << *prod1->name << " " << *prod2->name;
1326 // Warning: no check is made that prod1,2 are actually products!
1327 int sign=1;
1328 Ex::sibling_iterator sib=prod2.begin();
1329 while(sib!=prod2.end()) {
1330 sign*=can_swap_prod_obj(prod1, sib, ignore_implicit_indices);
1331 if(sign==0) break;
1332 ++sib;
1333 }
1334 // std::cout << " -> " << sign << std::endl;
1335 return sign;
1336 }
1337
can_swap_sum_obj(Ex::iterator sum,Ex::iterator obj,bool ignore_implicit_indices)1338 int Ex_comparator::can_swap_sum_obj(Ex::iterator sum, Ex::iterator obj,
1339 bool ignore_implicit_indices)
1340 {
1341 // Warning: no check is made that sum is actually a sum!
1342 int sofar=2;
1343 Ex::sibling_iterator sib=sum.begin();
1344 while(sib!=sum.end()) {
1345 auto es=equal_subtree(sib, obj);
1346 int thissign=can_swap(sib, obj, es, ignore_implicit_indices);
1347 if(sofar==2) sofar=thissign;
1348 else if(thissign!=sofar) {
1349 sofar=0;
1350 break;
1351 }
1352 ++sib;
1353 }
1354 return sofar;
1355 }
1356
can_swap_prod_sum(Ex::iterator prod,Ex::iterator sum,bool ignore_implicit_indices)1357 int Ex_comparator::can_swap_prod_sum(Ex::iterator prod, Ex::iterator sum,
1358 bool ignore_implicit_indices)
1359 {
1360 // Warning: no check is made that sum is actually a sum or prod is a prod!
1361 int sign=1;
1362 Ex::sibling_iterator sib=prod.begin();
1363 while(sib!=prod.end()) {
1364 // const Indices *ind=kernel.properties->get<Indices>(sib);
1365 // if(ind==0) {
1366 sign*=can_swap_sum_obj(sum, sib, ignore_implicit_indices);
1367 if(sign==0) break;
1368 // }
1369 ++sib;
1370 }
1371 return sign;
1372 }
1373
can_swap_sum_sum(Ex::iterator sum1,Ex::iterator sum2,bool ignore_implicit_indices)1374 int Ex_comparator::can_swap_sum_sum(Ex::iterator sum1, Ex::iterator sum2,
1375 bool ignore_implicit_indices)
1376 {
1377 int sofar=2;
1378 Ex::sibling_iterator sib=sum1.begin();
1379 while(sib!=sum1.end()) {
1380 int thissign=can_swap_sum_obj(sum2, sib, ignore_implicit_indices);
1381 if(sofar==2) sofar=thissign;
1382 else if(thissign!=sofar) {
1383 sofar=0;
1384 break;
1385 }
1386 ++sib;
1387 }
1388 return sofar;
1389 }
1390
can_swap_ilist_ilist(Ex::iterator obj1,Ex::iterator obj2)1391 int Ex_comparator::can_swap_ilist_ilist(Ex::iterator obj1, Ex::iterator obj2)
1392 {
1393 int sign=1;
1394
1395 index_iterator it1=index_iterator::begin(properties, obj1);
1396 while(it1!=index_iterator::end(properties, obj1)) {
1397 index_iterator it2=index_iterator::begin(properties, obj2);
1398 while(it2!=index_iterator::end(properties, obj2)) {
1399 // Only deal with real indices here, i.e. those carrying an Indices property.
1400 const Indices *ind1=properties.get<Indices>(it1, true);
1401 const Indices *ind2=properties.get<Indices>(it2, true);
1402 if(ind1!=0 && ind2!=0) {
1403 const CommutingBehaviour *com1 =properties.get<CommutingBehaviour>(it1, true);
1404 const CommutingBehaviour *com2 =properties.get<CommutingBehaviour>(it2, true);
1405
1406 if(com1!=0 && com1 == com2)
1407 sign *= com1->sign();
1408
1409 if(sign==0) break;
1410 }
1411 ++it2;
1412 }
1413 if(sign==0) break;
1414 ++it1;
1415 }
1416
1417 return sign;
1418 }
1419
can_swap_different_indexsets(Ex::iterator obj1,Ex::iterator obj2)1420 bool Ex_comparator::can_swap_different_indexsets(Ex::iterator obj1, Ex::iterator obj2)
1421 {
1422 std::set<const Indices *> index_sets1;
1423 // std::cerr << "Are " << obj1 << " and " << obj2 << " swappable?" << std::endl;
1424
1425 index_iterator it1=index_iterator::begin(properties, obj1);
1426 while(it1!=index_iterator::end(properties, obj1)) {
1427 auto ind = properties.get<Indices>(it1, true);
1428 if(!ind) return false;
1429 index_sets1.insert(ind);
1430 ++it1;
1431 }
1432 index_iterator it2=index_iterator::begin(properties, obj2);
1433 while(it2!=index_iterator::end(properties, obj2)) {
1434 auto ind = properties.get<Indices>(it2, true);
1435 if(!ind) return false;
1436 if(index_sets1.find(ind)!=index_sets1.end()) {
1437 // std::cerr << "NO" << std::endl;
1438 return false;
1439 }
1440 ++it2;
1441 }
1442 // std::cerr << "YES" << std::endl;
1443 return true;
1444 }
1445
can_swap(Ex::iterator one,Ex::iterator two,match_t,bool ignore_implicit_indices)1446 int Ex_comparator::can_swap(Ex::iterator one, Ex::iterator two, match_t,
1447 bool ignore_implicit_indices)
1448 {
1449 // std::cerr << "can_swap " << *one->name << " " << *two->name << " " << ignore_implicit_indices << std::endl;
1450
1451 // Explicitly declared commutation behaviour goes first.
1452 const CommutingBehaviour *com = properties.get<CommutingBehaviour>(one, two, true);
1453 if(com)
1454 return com->sign();
1455
1456
1457 // If both objects have implicit indices, we cannot swap the
1458 // objects because that would re-order the index line. The sole
1459 // exception is when these indices are explicitly stated to be in
1460 // different sets.
1461
1462 const ImplicitIndex *ii1 = properties.get<ImplicitIndex>(one);
1463 const ImplicitIndex *ii2 = properties.get<ImplicitIndex>(two);
1464 if(!ignore_implicit_indices) {
1465 if(ii1) {
1466 if(ii1->explicit_form.size()==0) {
1467 if(ii2) return 0; // nothing known about explicit form
1468 }
1469 else one=ii1->explicit_form.begin();
1470 }
1471 if(ii2) {
1472 if(ii2->explicit_form.size()==0) {
1473 if(ii1) return 0; // nothing known about explicit form
1474 }
1475 else two=ii2->explicit_form.begin();
1476 }
1477 // Check that indices in one and two are in mutually exclusive sets.
1478 if(ii1 && ii2)
1479 if(!can_swap_different_indexsets(one, two))
1480 return false;
1481 }
1482
1483 // Differential forms in a product cannot be moved through each
1484 // other except when the degree of one of them is zero. In a wedge
1485 // product, we can move them and potentially pick up a sign.
1486
1487 const DifferentialFormBase *df1 = properties.get<DifferentialFormBase>(one);
1488 const DifferentialFormBase *df2 = properties.get<DifferentialFormBase>(two);
1489
1490 if(df1 && df2) {
1491 if(df1->degree(properties,one).begin()->is_zero() || df2->degree(properties,two).begin()->is_zero())
1492 return 1;
1493 else {
1494 if(Ex::is_head(one) || *(Ex::parent(one)->name)=="\\wedge") {
1495 if(df1->degree(properties, one).is_rational()==false ||
1496 df2->degree(properties, two).is_rational()==false)
1497 return 0; // Cannot yet order forms with non-numerical degrees.
1498 long d1 = to_long(df1->degree(properties, one).to_rational());
1499 long d2 = to_long(df2->degree(properties, two).to_rational());
1500 if( (d1*d2) % 2 == 1) return -1;
1501 return 1;
1502 }
1503 }
1504 }
1505
1506 // Do we need to use Self* properties?
1507 const SelfCommutingBehaviour *sc1 =properties.get<SelfCommutingBehaviour>(one, true);
1508 const SelfCommutingBehaviour *sc2 =properties.get<SelfCommutingBehaviour>(two, true);
1509 if( (sc1!=0 && sc1==sc2) )
1510 return sc1->sign();
1511
1512 // One or both of the objects are not in an explicit list. So now comes the generic
1513 // part. The first step is to look at all explicit indices of the two objects and determine
1514 // their commutativity.
1515 // Note: this does not yet look at arguments (non-index children).
1516
1517 int tmpsign=can_swap_ilist_ilist(one, two);
1518 if(tmpsign==0) return 0;
1519
1520 // The second step is to check for product-like and sum-like behaviour. The following
1521 // take into account all commutativity properties of explict with implicit indices,
1522 // as well as hard-specified commutativity of factors.
1523
1524 const CommutingAsProduct *comap1 = properties.get<CommutingAsProduct>(one);
1525 const CommutingAsProduct *comap2 = properties.get<CommutingAsProduct>(two);
1526 const CommutingAsSum *comas1 = properties.get<CommutingAsSum>(one);
1527 const CommutingAsSum *comas2 = properties.get<CommutingAsSum>(two);
1528
1529 if(comap1 && comap2) return tmpsign*can_swap_prod_prod(one,two,ignore_implicit_indices);
1530 if(comap1 && comas2) return tmpsign*can_swap_prod_sum(one,two,ignore_implicit_indices);
1531 if(comap2 && comas1) return tmpsign*can_swap_prod_sum(two,one,ignore_implicit_indices);
1532 if(comas1 && comas2) return tmpsign*can_swap_sum_sum(one,two,ignore_implicit_indices);
1533 if(comap1) return tmpsign*can_swap_prod_obj(one,two,ignore_implicit_indices);
1534 if(comap2) return tmpsign*can_swap_prod_obj(two,one,ignore_implicit_indices);
1535 if(comas1) return tmpsign*can_swap_sum_obj(one,two,ignore_implicit_indices);
1536 if(comas2) return tmpsign*can_swap_sum_obj(two,one,ignore_implicit_indices);
1537
1538 return 1; // default: commuting.
1539 }
1540
satisfies_conditions(Ex::iterator conditions,std::string & error)1541 bool Ex_comparator::satisfies_conditions(Ex::iterator conditions, std::string& error)
1542 {
1543 // std::cerr << "satisfies_conditions" << std::endl;
1544 for(unsigned int i=0; i<Ex::arg_size(conditions); ++i) {
1545 Ex::iterator cond=Ex::arg(conditions, i);
1546 // std::cerr << "condition " << cond << std::endl;
1547 if(*cond->name=="\\unequals") {
1548 Ex::sibling_iterator lhs=cond.begin();
1549 Ex::sibling_iterator rhs=lhs;
1550 ++rhs;
1551 // Lookup the replacement rules for the two given objects, and return true if
1552 // those rules give a different result. But first check that there are rules
1553 // to start with.
1554 // std::cerr << *lhs->name << " !=? " << *rhs->name << std::endl;
1555 if(replacement_map.find(Ex(lhs))==replacement_map.end() ||
1556 replacement_map.find(Ex(rhs))==replacement_map.end()) return true;
1557 // std::cerr << *lhs->name << " !=?? " << *rhs->name << std::endl;
1558 if(tree_exact_equal(&properties, replacement_map[Ex(lhs)], replacement_map[Ex(rhs)])) {
1559 return false;
1560 }
1561 }
1562 else if(*cond->name=="\\greater" || *cond->name=="\\less") {
1563 Ex::sibling_iterator lhs=cond.begin();
1564 Ex::sibling_iterator rhs=lhs;
1565 ++rhs;
1566
1567 multiplier_t mlhs;
1568 if(lhs->is_rational()==false) {
1569 auto fnd=replacement_map.find(Ex(lhs));
1570 // std::cerr << "finding " << lhs << std::endl;
1571 if(fnd!=replacement_map.end()) {
1572 auto tn=fnd->second.begin();
1573 // std::cerr << tn << std::endl;
1574 if(tn->is_rational())
1575 mlhs=*tn->multiplier;
1576 else {
1577 error="Replacement not numerical.";
1578 return false;
1579 }
1580 }
1581 else {
1582 error="Can only compare objects which evaluate to numbers.";
1583 return false;
1584 }
1585 }
1586 else mlhs=*lhs->multiplier;
1587
1588 // FIXME: abstract into Storage
1589 multiplier_t mrhs;
1590 if(rhs->is_rational()==false) {
1591 auto fnd=replacement_map.find(Ex(rhs));
1592 if(fnd!=replacement_map.end()) {
1593 auto tn=fnd->second.begin();
1594 if(tn->is_rational())
1595 mrhs=*tn->multiplier;
1596 else {
1597 error="Replacement not numerical.";
1598 return false;
1599 }
1600 }
1601 else {
1602 error="Can only compare objects which evaluate to numbers.";
1603 return false;
1604 }
1605 }
1606 else mrhs=*rhs->multiplier;
1607
1608 if(*cond->name=="\\greater" && mlhs <= mrhs) return false;
1609 if(*cond->name=="\\less" && mlhs >= mrhs) return false;
1610 }
1611 else if(*cond->name=="\\indexpairs") {
1612 int countpairs=0;
1613 replacement_map_t::const_iterator it=replacement_map.begin(),it2;
1614 while(it!=replacement_map.end()) {
1615 it2=it;
1616 ++it2;
1617 while(it2!=replacement_map.end()) {
1618 if(tree_exact_equal(&properties, it->second, it2->second)) {
1619 ++countpairs;
1620 break;
1621 }
1622 ++it2;
1623 }
1624 ++it;
1625 }
1626 // txtout << countpairs << " pairs" << std::endl;
1627 if(countpairs!=*(cond.begin()->multiplier))
1628 return false;
1629 }
1630 else if(*cond->name=="\\regex") {
1631 // txtout << "regex matching..." << std::endl;
1632 Ex::sibling_iterator lhs=cond.begin();
1633 Ex::sibling_iterator rhs=lhs;
1634 ++rhs;
1635 // If we have a match, all indices have replacement rules.
1636 std::string pat=(*rhs->name).substr(1,(*rhs->name).size()-2);
1637 // txtout << "matching " << *comp.replacement_map[lhs->name]
1638 // << " with pattern " << pat << std::endl;
1639 std::regex reg(pat);
1640 if (!std::regex_match(*(replacement_map[Ex(lhs)].begin()->name), reg))
1641 return false;
1642 }
1643 // V2: FIXME: re-enable searching for properties
1644 // else if(*cond->name=="\\hasprop") {
1645 // Ex::sibling_iterator lhs=cond.begin();
1646 // Ex::sibling_iterator rhs=lhs;
1647 // ++rhs;
1648 // Properties::registered_property_map_t::iterator pit=properties->store.find(*rhs->name);
1649 // if(pit==properties->store.end()) {
1650 // std::ostringstream str;
1651 // str << "Property \"" << *rhs->name << "\" not registered." << std::endl;
1652 // error=str.str();
1653 // return false;
1654 // }
1655 // const property_base *aprop=pit->second();
1656 //
1657 // subtree_replacement_map_t::iterator subfind=subtree_replacement_map.find(lhs->name);
1658 // replacement_map_t::iterator patfind=replacement_map.find(Ex(lhs));
1659 //
1660 // if(subfind==subtree_replacement_map.end() && patfind==replacement_map.end()) {
1661 // std::ostringstream str;
1662 // str << "Pattern " << *lhs->name << " in \\hasprop did not occur in match." << std::endl;
1663 // delete aprop;
1664 // error=str.str();
1665 // return false;
1666 // }
1667 //
1668 // bool ret=false;
1669 // if(subfind==subtree_replacement_map.end())
1670 // ret=properties->has(aprop, (*patfind).second.begin());
1671 // else
1672 // ret=properties->has(aprop, (*subfind).second);
1673 // delete aprop;
1674 // return ret;
1675 // }
1676 else {
1677 std::ostringstream str;
1678 str << "substitute: condition involving " << *cond->name << " not understood." << std::endl;
1679 error=str.str();
1680 return false;
1681 }
1682 }
1683 return true;
1684 }
1685
Ex_is_equivalent(const Properties & k)1686 Ex_is_equivalent::Ex_is_equivalent(const Properties& k)
1687 : properties(k)
1688 {
1689 }
1690
operator ()(const Ex & one,const Ex & two)1691 bool Ex_is_equivalent::operator()(const Ex& one, const Ex& two)
1692 {
1693 int ret=subtree_compare(&properties, one.begin(), two.begin());
1694 if(ret==0) return true;
1695 else return false;
1696 }
1697
Ex_is_less(const Properties & k,int mp)1698 Ex_is_less::Ex_is_less(const Properties& k, int mp)
1699 : properties(k), mod_prel(mp)
1700 {
1701 }
1702
operator ()(const Ex & one,const Ex & two)1703 bool Ex_is_less::operator()(const Ex& one, const Ex& two)
1704 {
1705 int ret=subtree_compare(&properties, one.begin(), two.begin(), mod_prel);
1706 if(ret < 0) return true;
1707 else return false;
1708 }
1709
1710 }
1711
operator <(const cadabra::Ex::iterator & i1,const cadabra::Ex::iterator & i2)1712 bool operator<(const cadabra::Ex::iterator& i1, const cadabra::Ex::iterator& i2)
1713 {
1714 return i1.node < i2.node;
1715 }
1716
operator <(const cadabra::Ex & e1,const cadabra::Ex & e2)1717 bool operator<(const cadabra::Ex& e1, const cadabra::Ex& e2)
1718 {
1719 return e1.begin().node < e2.begin().node;
1720 }
1721
operator <<(std::ostream & s,cadabra::Ex_comparator::useprops_t up)1722 std::ostream& operator<<(std::ostream& s, cadabra::Ex_comparator::useprops_t up)
1723 {
1724 switch(up) {
1725 case cadabra::Ex_comparator::useprops_t::always:
1726 s << "always";
1727 break;
1728 case cadabra::Ex_comparator::useprops_t::not_at_top:
1729 s << "not_at_top";
1730 break;
1731 case cadabra::Ex_comparator::useprops_t::never:
1732 s << "never";
1733 break;
1734 }
1735 return s;
1736 }
1737
1738
1739