/* Cadabra: a field-theory motivated computer algebra system. Copyright (C) 2001-2014 Kasper Peeters This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "Props.hh" #include "Exceptions.hh" #include "Compare.hh" #include #include #include #include #include "properties/Indices.hh" // #define DEBUG 1 using namespace cadabra; pattern::pattern() { } pattern::pattern(const Ex& o) : obj(o) { } bool pattern::match(const Properties& properties, const Ex::iterator& it, bool ignore_parent_rel, bool ignore_properties) const { Ex_comparator comp(properties); return match_ext(properties, it, comp, ignore_parent_rel, ignore_properties); } bool pattern::match_ext(const Properties& properties, const Ex::iterator& it, Ex_comparator& comp, bool ignore_parent_rel, bool ignore_properties) const { // Special case for range wildcards. // FIXME: move this to Compare.cc (see the FIXME there) // std::cerr << "Attempting to match " << Ex(it) << " to " << Ex(obj) << std::endl; if(it->name==obj.begin()->name && children_wildcard()) { Ex::iterator hm=obj.begin(obj.begin()); if(Ex::number_of_children(hm)==0) { return true; // # without arguments } Ex::iterator hmarg=hm.begin(); Ex::iterator seqarg=hm; const Indices *ind=0; if(*hmarg->name=="\\comma" || *hmarg->name!="\\sequence") { Ex::iterator stt=hmarg; if(*hmarg->name=="\\comma") { stt=hmarg.begin(); seqarg=hmarg.begin(); seqarg.skip_children(); ++seqarg; } ind=properties.get(stt, true); } else seqarg=hmarg; if(seqarg!=hm) { Ex::sibling_iterator seqit=seqarg.begin(); unsigned int from=to_long(*seqit->multiplier); ++seqit; unsigned int to =to_long(*seqit->multiplier); if(Ex::number_of_children(it)to ) return false; } if(ind!=0) { Ex::sibling_iterator indit=it.begin(); while(indit!=it.end()) { const Indices *gi=properties.get(indit, true); if(gi!=ind) { return false; } ++indit; } } return true; } // Cases without range wildcard. Compare making full use of // property information. Note the order of the arguments to // 'equal_subtree': the first argument is supposed to be a // pattern, the second an expression which is to be matched. #ifdef DEBUG std::cerr << "vvvvvvv " << ignore_properties << std::endl; #endif comp.clear(); Ex_comparator::match_t res= comp.equal_subtree(obj.begin(), it, ignore_properties?Ex_comparator::useprops_t::never:Ex_comparator::useprops_t::not_at_top, ignore_parent_rel); #ifdef DEBUG std::cerr << "pattern::match: comparing" << Ex(it) << " with " << Ex(obj) << " = " << static_cast(res) << std::endl; std::cerr << "^^^^^^" << std::endl; #endif if(is_in(res, { Ex_comparator::match_t::subtree_match, Ex_comparator::match_t::match_index_less, Ex_comparator::match_t::match_index_greater, Ex_comparator::match_t::node_match } )) { return true; } return false; } bool pattern::children_wildcard() const { if(Ex::number_of_children(obj.begin())==1) if(obj.begin(obj.begin())->is_range_wildcard()) return true; return false; } bool Properties::has(const property *pb, Ex::iterator it) { std::pair pit=props.equal_range(it->name_only()); while(pit.first!=pit.second) { // txtout << *it->name << std::endl; // txtout << typeid(pit.first->second.second).name() << " versus " // << typeid(pb).name() << std::endl; const property *tmp = (pit.first->second.second); if(typeid(*tmp)==typeid(*pb) && pit.first->second.first->match(*this, it)) // match found return true; ++pit.first; } return false; } void Properties::clear() { // Clear and free the property lists. Since pointers to properties can // be shared, we use the pats map and make sure that we only free each // property* pointer once. pattern_map_t::const_iterator it=pats.begin(); const property *previous=0; while(it!=pats.end()) { if(previous!=it->first) { previous=it->first; delete it->first; } delete it->second; ++it; } props.clear(); pats.clear(); } Properties::registered_property_map_t::~registered_property_map_t() { // FIXME: V2 } void Properties::register_property(property* (*fun)(), const std::string& name) { registered_properties.store[name]=fun; } keyval_t::const_iterator keyval_t::find(const std::string& key) const { keyval_t::const_iterator it=keyvals.begin(); while(it!=keyvals.end()) { if(it->first==key) break; ++it; } return it; } keyval_t::iterator keyval_t::find(const std::string& key) { keyval_t::iterator it=keyvals.begin(); while(it!=keyvals.end()) { if(it->first==key) break; ++it; } return it; } keyval_t::const_iterator keyval_t::begin() const { return keyvals.begin(); } keyval_t::const_iterator keyval_t::end() const { return keyvals.end(); } void keyval_t::push_back(const kvpair_t& kv) { keyvals.push_back(kv); } void keyval_t::erase(iterator it) { keyvals.erase(it); } property::property(bool h) : hidden_(h) { } void property::hidden(bool h) { hidden_=h; } bool property::hidden() const { return hidden_; } bool property::parse(Kernel&, keyval_t&) { return true; } bool property::parse(Kernel& kernel, std::shared_ptr, keyval_t& keyvals) { // The default is to run the 'parse' without 'ex', as most properties // do not implement the new interface. return parse(kernel, keyvals); } void property::validate(const Kernel&, const Ex&) const { } bool property::parse_one_argument(Ex::iterator arg, keyval_t& keyvals) { if(*arg->name=="\\equals") { Ex::sibling_iterator key=arg.begin(); if(key==arg.end()) return false; Ex::sibling_iterator val=key; ++val; if(val==arg.end()) return false; keyvals.push_back(keyval_t::value_type(*arg.begin()->name, val)); } else { if(unnamed_argument()!="") { // std::cerr << unnamed_argument() << " unnamed " << *arg->name << std::endl; keyvals.push_back(keyval_t::value_type(unnamed_argument(), arg)); } else return false; } return true; } bool property::parse_to_keyvals(const Ex& tr, keyval_t& keyvals) { if(tr.begin()==tr.end()) return true; auto it=tr.begin(); // std::cout << "parsing to keyvals" << std::endl; if(*(it)->name!="\\comma") { // one argument if(parse_one_argument(it, keyvals)==false) return false; } else { Ex::sibling_iterator sib=tr.begin(it); while(sib!=tr.end(it)) { if(parse_one_argument(sib, keyvals)==false) return false; ++sib; } } // for(auto it=keyvals.begin(); it!=keyvals.end(); ++it) // std::cerr << (*it).first << " = " << *(*it).second->name << std::endl; return true; } void property::latex(std::ostream& str) const { str << name(); } std::string property::unnamed_argument() const { return ""; } property::match_t property::equals(const property *) const { return exact_match; } bool labelled_property::parse(Kernel&, std::shared_ptr, keyval_t& keyvals) { keyval_t::const_iterator lit=keyvals.find("label"); if(lit!=keyvals.end()) { label=*lit->second->name; return true; } else { // FIXME: not all labelled properties have an actual label, e.g. // Derivative derives from WeightBase but not all derivatives need // a label. If we throw an exception here, those properties fail // to run. // throw ArgumentException("Need a 'label' parameter for property."); return false; } } //bool operator<(const pattern& one, const pattern& two) // { // return tree_less(one.obj, two.obj); //// if(*(one.obj.begin()->name)<*(two.obj.begin()->name)) return true; //// return false; // } //bool operator==(const pattern& one, const pattern& two) // { // return one.obj==two.obj; // FIXME: handle dummy indices // } void Properties::insert_prop(const Ex& et, const property *pr) { // assert(pats.find(pr)==pats.end()); // identical properties have to be assigned through insert_list_prop // FIXME: is it really necessary to store this by pointer? We are in any case // not cleaning this up correctly yet. pattern *pat=new pattern(et); std::pair pit= props.equal_range(pat->obj.begin()->name_only()); property_map_t::iterator first_nonpattern=pit.first; while(pit.first!=pit.second) { // keep track of the first non-pattern element if(Ex::number_of_children((*pit.first).second.first->obj.begin())==1) if((*pit.first).second.first->obj.begin().begin()->is_range_wildcard()) ++first_nonpattern; // A given pattern can only have one property of any given type. The following // triggers on entries in the props map which match the pattern to be inserted. if((*pit.first).second.first->match(*this, et.begin())) { // Does this entry in props give a property of the same type as the one we // are trying to insert? const property *tmp = (*pit.first).second.second; if(typeid(*pr)==typeid(*tmp)) { // If this is a labelled property, is the label different from the one on the // property we are trying to insert? const labelled_property *lp =dynamic_cast(pr); const labelled_property *lpold=dynamic_cast(pit.first->second.second); if(!lp || !lpold || lp->label==lpold->label) { // The to-be-inserted property cannot co-exist on this pattern with the // one that is currently associated to the pattern. Remove it. pattern *oldpat =pit.first->second.first; const property *oldprop=pit.first->second.second; // If the new property instance is the same as the old one, we can stop // (this happens if a pattern is accidentally repeated in a property assignment). if(oldprop==pr) { delete pat; return; } // Erase the pattern->property entry, and delete the pattern. // FIXME: store pattern by value. props.erase(pit.first); delete oldpat; // Remove the property->pattern entry. Only delete the property // if it is no longer associated to any other pattern. // FIXME: // {A, B}::SelfAntiCommuting. // {A}::SelfAntiCommuting. // {B}::SelfAntiCommuting. // leads to two properties SelfAntiCommuting, which are identical. // We need a way to compare properties and decide when they are // identical, or when they can coexist, or something like that. for(auto pi=pats.begin(); pi!=pats.end(); ++pi) { if((*pi).first==oldprop && (*pi).second==oldpat) { // std::cerr << "found old entry, deleting" << std::endl; pats.erase(pi); break; } } if(pats.find(oldprop)==pats.end()) { // std::cerr << "no other references" << std::endl; delete oldprop; } break; } } } ++pit.first; } pats.insert(pattern_map_t::value_type(pr, pat)); // std::cerr << "inserting for " << *(pat->obj.begin()->name) << std::endl; props.insert(property_map_t::value_type(pat->obj.begin()->name_only(), pat_prop_pair_t(pat,pr))); } void Properties::insert_list_prop(const std::vector& its, const list_property *pr) { assert(pats.find(pr)==pats.end()); // identical properties have to be assigned through insert_list_prop assert(its.size()>0); // If 'pr' is exactly equal to an existing property, we should use that one instead of // introducing a duplicate. pattern_map_t::iterator fit=pats.begin(); while(fit!=pats.end()) { const property *tmp = (*fit).first; if(typeid(*tmp)==typeid(*pr)) if(pr->equals((*fit).first)==property::exact_match) { pr=static_cast( (*fit).first ); break; } ++fit; } // If 'pr' has id_match with an existing property, we need to remove all property assignments // for the existing one, except when there is an exact_match. const property *to_delete_property=0; pattern_map_t::iterator pit=pats.begin(); while(pit!=pats.end()) { const property *tmp = (*pit).first; if(typeid(*tmp)==typeid(*pr)) if(pr->equals((*pit).first)==property::id_match) { to_delete_property = (*pit).first; break; } ++pit; } if(to_delete_property) { pats.erase(to_delete_property); property_map_t::iterator it=props.begin(); while(it!=props.end()) { property_map_t::iterator nxt=it; ++nxt; if((*it).second.second==to_delete_property) props.erase(it); it=nxt; } } // Now register the list property. for(unsigned int i=0; i pit= // props.equal_range(its[i]->name); // // while(pit.first!=pit.second) { // if((*pit.first).second.first->match(its[i])) { // found the pattern 'its[i]' in the property list // if(typeid(*pr)==typeid(*(*pit.first).second.second)) { //// txtout << "found a property for " << *(its[i]->name) << std::endl; //// Ex::print_recursive_treeform(txtout, its[i]); // // pattern *oldpat=pit.first->second.first; // const property *oldprop=pit.first->second.second; // //// props.erase(pit.first); THIS // // // Delete only those entries in the pattern map which are related to // // this particular pattern _and_ this particular property // std::pair patrange= // pats.equal_range(oldprop); // while(patrange.first!=patrange.second) { // if(patrange.first->first==oldprop && patrange.first->second==oldpat) { //// txtout << "erasing property for " << *(oldpat->headnode) << std::endl; //// pats.erase(patrange.first); // THIS // break; // } // ++patrange.first; // } //// delete oldpat; THIS // break; // } // } // ++pit.first; // } // Now register the property. // txtout << "registering " << *(pat->headnode) << std::endl; pats.insert(pattern_map_t::value_type(pr, pat)); props.insert(property_map_t::value_type(pat->obj.begin()->name_only(), pat_prop_pair_t(pat,pr))); } } int Properties::serial_number(const property *listprop, const pattern *pat) const { int serialnum=0; std::pair pm=pats.equal_range(listprop); serialnum=0; while(pm.first!=pm.second) { if((*pm.first).second==pat) break; ++serialnum; ++pm.first; } return serialnum; } /* {a,b,c,d,e}::Indices(vector). {a,b,c}::Indices(spinor). This should make a,b,c spinor indices, and keep d,e as vector indices. {a,b,c}::Indices(vector). {d,e}::Indices(vector). This should make all of a,b,c,d,e vector indices. {a,b,c}::Indices(vector). {a,b,c,d,e,f}::Indices(spinor). This should make all indices spinor indices. {a,b,c,d,e}::Indices(vector, position=free). {a,b,c}::Indices(vector, position=fixed). You can only have one type of index for each name, so this declaration implies that d,e should have their property removed. */ // Insert a property for the given pattern Ex. Determines whether the property // is a list property or a normal one, and dispatches accordingly. std::string Properties::master_insert(Ex proptree, const property *thepropbase) { std::ostringstream str; Ex::sibling_iterator st=proptree.begin(); const list_property *thelistprop=dynamic_cast(thepropbase); if(thelistprop) { // a list property std::vector objs; if(*st->name=="\\comma") { Ex::sibling_iterator sib=proptree.begin(st); while(sib!=proptree.end(st)) { if(sib->fl.parent_rel!=str_node::p_property) { objs.push_back(Ex(sib)); } ++sib; } } if(objs.size()<2) throw ConsistencyException("A list property cannot be assigned to a single object."); // FIXME: we special-case Indices, as those pass a list of objects with parent_rel==p_none, // but we need the patterns to have parent_rel set to p_sub and p_super in order to avoid // special cases in the pattern matcher later. // DOCME: the above if(dynamic_cast(thelistprop)) { std::vector objs2; for(auto& obj: objs) { Ex obj2(obj); obj2.begin()->fl.parent_rel=str_node::p_super; objs2.push_back(obj2); } for(auto& obj: objs) { Ex obj2(obj); obj2.begin()->fl.parent_rel=str_node::p_sub; objs2.push_back(obj2); } insert_list_prop(objs2, thelistprop); } else { insert_list_prop(objs, thelistprop); } } else { // a normal property const property *theprop=thepropbase; assert(theprop); if(*st->name=="\\comma") { Ex::sibling_iterator sib=proptree.begin(st); while(sib!=proptree.end(st)) { if(sib->fl.parent_rel!=str_node::p_property) { // std::cerr << "inserting property for " << Ex(sib) << std::endl; insert_prop(Ex(sib), theprop); } ++sib; } } else { insert_prop(Ex(st), theprop); } } return str.str(); } bool Properties::check_label(const property* p, const std::string& label) const { return true; } bool Properties::check_label(const labelled_property* p, const std::string& label) const { return (p->label==label || p->label=="all"); } Ex_comparator *Properties::create_comparator() const { return new Ex_comparator(*this); } void Properties::destroy_comparator(Ex_comparator *c) const { delete c; }