From bd8c967eda68474961dbac1dfd8ffbe8c9222d0f Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:19:09 -0400 Subject: [PATCH 01/18] First attempt at property enhancements. Added dict_maps to Properties. (In progress.) --- core/Props.cc | 120 +++++++++++++++++++++++++++++++++++++- core/Props.hh | 158 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 270 insertions(+), 8 deletions(-) diff --git a/core/Props.cc b/core/Props.cc index 3b9683c074..4e514189ff 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -22,7 +22,7 @@ along with this program. If not, see . #include "Exceptions.hh" #include "Compare.hh" #include -#include +// #include #include #include #include "properties/Indices.hh" @@ -177,11 +177,45 @@ Properties::registered_property_map_t::~registered_property_map_t() // FIXME: V2 } +template +void Properties::registered_property_map_t::register_type() { + T obj; + std::type_index typeidx = typeid(obj); + auto it = types_to_names_.find(typeidx); + if (it == types_to_names_.end()) { + types_to_names_[typeidx] = obj.name(); + names_to_types_.insert({obj.name(),typeidx}); + } +} + +template +void Properties::registered_property_map_t::register_type(const T& obj) { + std::type_index typeidx(typeid(obj)); + auto it = types_to_names_.find(typeidx); + if (it == types_to_names_.end()) { + types_to_names_[typeidx] = obj.name(); + names_to_types_.insert({obj.name(),typeidx}); + } +} + + void Properties::register_property(property* (*fun)(), const std::string& name) { registered_properties.store[name]=fun; } +template +void Properties::register_property_type() + { + registered_properties.register_type(); + } + +template +void Properties::register_property_type(const T& obj) + { + registered_properties.register_type(obj); + } + keyval_t::const_iterator keyval_t::find(const std::string& key) const { keyval_t::const_iterator it=keyvals.begin(); @@ -396,6 +430,10 @@ void Properties::insert_prop(const Ex& et, const property *pr) // Erase the pattern->property entry, and delete the pattern. // FIXME: store pattern by value. + + // erase from dicts + dict_erase_(oldprop, oldpat); + // erase pattern from props props.erase(pit.first); delete oldpat; @@ -408,6 +446,8 @@ void Properties::insert_prop(const Ex& et, const property *pr) // 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. + + // erase pattern from props for(auto pi=pats.begin(); pi!=pats.end(); ++pi) { if((*pi).first==oldprop && (*pi).second==oldpat) { // std::cerr << "found old entry, deleting" << std::endl; @@ -430,6 +470,7 @@ void Properties::insert_prop(const Ex& et, const property *pr) 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))); + dict_insert_(pr, pat); } void Properties::insert_list_prop(const std::vector& its, const list_property *pr) @@ -464,6 +505,7 @@ void Properties::insert_list_prop(const std::vector& its, const list_propert ++pit; } if(to_delete_property) { + dict_erase_(to_delete_property); pats.erase(to_delete_property); property_map_t::iterator it=props.begin(); while(it!=props.end()) { @@ -523,6 +565,7 @@ void Properties::insert_list_prop(const std::vector& its, const list_propert // 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))); + dict_insert_(pr, pat); } } @@ -659,3 +702,78 @@ void Properties::destroy_comparator(Ex_comparator *c) const { delete c; } + + +// Completely erase a property. +void Properties::dict_erase_(const property* p) { + std::type_index type_idx = typeid(*p); + auto pats_dict_it = pats_dict.find(type_idx); + // If we don't find it, end here. + if (pats_dict_it == pats_dict.end()) { + return; + } + // The multimap lies at pats_dict_it->second. + // As with the pats/props maps, we need to look for the pattern in the props_dict. + auto range = pats_dict_it->second.equal_range(p); + + for (auto pi = range.first; pi != range.second;) { + // pi iterates over the in the multimap + // Before erasing the entry in pats_dict, we need to find the elements in props_dict to erase. + // Get the pattern. + auto pat = pi->second; + auto it = props_dict.find(pat->obj.begin()->name_only()); + if (it == props_dict.end()) { + ++pi; + continue; + } + // `it` points to the multimap within props_dict + auto props_dict_mmap = it->second; + auto props_dict_range = props_dict_mmap.equal_range(type_idx); + for (auto it2 = props_dict_range.first; it2 != props_dict_range.second; ++it2) { + if (it2->second.second == p) { + props_dict_mmap.erase(it2); + break; + } + } + // FIXME: Error if no element found to be erased? + pi = pats_dict_it->second.erase(pi); + } +} + +// Erase a property and pattern from the dict_maps +void Properties::dict_erase_(const property* prop, pattern* pat) { + std::type_index type_idx = typeid(*prop); + auto name = pat->obj.begin()->name_only(); + + // FIXME: Avoid operator[] if possible? + + // erase from props_dict + mmap_erase_key_value_(props_dict[name], type_idx, pat_prop_pair_t(pat, prop)); + + // erase from pats_dict + mmap_erase_key_value_(pats_dict[type_idx], prop, pat); +} + + +// Insert a property and pattern into the dict_maps +void Properties::dict_insert_(const property* prop, pattern* pat) { + register_property_type(*prop); + std::type_index type_idx = typeid(*prop); + auto name = pat->obj.begin()->name_only(); + props_dict[name].emplace( type_idx, std::make_pair(pat,prop)); + pats_dict[type_idx].emplace(prop, pat); +} + + +// Erases the first key-value pair from a multimap, returning 1 if found, 0 if not. +template +int Properties::mmap_erase_key_value_(std::multimap& mmap, const K& key, const V& value) { + auto range = mmap.equal_range(key); + for (auto it = range.first; it != range.second; ++it) { + if (it->second == value) { + mmap.erase(it); + return 1; + } + } + return 0; +} diff --git a/core/Props.hh b/core/Props.hh index b2db4fa953..0e42fe91d9 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -27,7 +27,8 @@ along with this program. If not, see . #include #include #include "Storage.hh" - +#include + namespace cadabra { class Properties; @@ -241,7 +242,8 @@ namespace cadabra { class Properties { public: - // Registering property types. + + // Class to store names and std::type_index for property objects class registered_property_map_t { public: ~registered_property_map_t(); @@ -250,16 +252,47 @@ namespace cadabra { typedef internal_property_map_t::iterator iterator; internal_property_map_t store; - }; + // Modifications to registered_property_map_t. + // FIXME: Pieces above here are old and do not seem to be used anywhere. + // (Pieces below here are new.) + + // Register a type by template. Usage: `register_type();` + template + void register_type(); + + // Register the type of an object. Usage: `register_type(obj);` + template + void register_type(const T&); + + private: + // Dictionary from type_index to human readable name. + std::map types_to_names_; + // Dictionary from human readable name to type_index. + std::multimap names_to_types_; + + + }; /// Registering properties. When inserting a property or /// list_property, ownership of the property gets transferred to /// this class. - + + // FIXME: register_property does not seem to be used. void register_property(property* (*)(), const std::string& name); + registered_property_map_t registered_properties; typedef std::pair pat_prop_pair_t; + // Register a type by template. Usage: `register_property_type();` + // Just calls `registered_properties.register_type()` + template void register_property_type(); + + // Register the type of an object. Usage: `register_property_type(obj);` + // Just calls `registered_properties.register_type(obj)` + template void register_property_type(const T&); + + + /// We keep two multi-maps: one from the pattern to the property (roughly) and /// one from the property to the pattern. These are both multi-maps because /// one pattern can have multiple properties assigned to it, and one property can @@ -284,6 +317,30 @@ namespace cadabra { property_map_t props; // pattern -> property pattern_map_t pats; // property -> pattern; for list properties, patterns are stored here in order + + /************************************************************************************** + * BELOW REVISES EXISTING PROPS/PATS STRUCTURE. + * + * The original props/pats maps can be inefficient because accessing requires dynamic casts, + * and the number of such casts can quickly become huge. The below is intended as + * a replacement for props/pats by including information about the property_type directly. + * Currently it works side-by-side with props/parts, duplicating the work + * if/until we replace props/pats entirely with these. + * + * props_dict groups the pat_prop_pairs in a multimap by their property_type + * pats_dict groups the properties in pats by their property_type. This allows us to + * iterate over e.g. all AntiCommuting patterns directly. + **************************************************************************************/ + + typedef std::map>, + nset_it_less> property_dictmap_t; + typedef std::map> pattern_dictmap_t; + + property_dictmap_t props_dict; // pattern -> map(property_type, pat_prop_pairs) + pattern_dictmap_t pats_dict; // property_type -> multimap(property, pattern) + /// Normal search: given a pattern, get its property if any. template const T* get(Ex::iterator, bool ignore_parent_rel=false) const; template const T* get(Ex::iterator, int& serialnum, bool doserial=true, bool ignore_parent_rel=false) const; @@ -316,6 +373,10 @@ namespace cadabra { // map from this particular point. Note: this searches on property type, not exact property. // template // property_map_t::iterator get_pattern(property_map_t::iterator=props.begin()); + template + property_map_t::iterator get_pattern(); + template + property_map_t::iterator get_pattern(property_map_t::iterator); // Equivalent search: given a node, get a pattern of equivalents. // property_map_t::iterator get_equivalent(Ex::iterator, @@ -334,6 +395,18 @@ namespace cadabra { int serial_number(const property *, const pattern *) const; Ex_comparator *create_comparator() const; void destroy_comparator(Ex_comparator *) const; + + // Erases property completely. + void dict_erase_(const property*); + // Erases pattern from a given property, leaving other patterns alone. + void dict_erase_(const property*, pattern*); + // Insert property/pattern into the dictmaps. + void dict_insert_(const property*, pattern*); + + // Erases the first key-value pair from a multimap, returning 1 if found, 0 if not. + template + int mmap_erase_key_value_(std::multimap&, const K&, const V&); + }; template @@ -366,9 +439,9 @@ namespace cadabra { int& serialnum, const std::string& label, bool doserial, bool ignore_parent_rel) const { - std::pair ret; - ret.first=0; - ret.second=0; + + std::pair ret = {nullptr, nullptr}; + bool inherits=false; //std::cerr << *it->name_only() << std::endl; @@ -386,7 +459,56 @@ namespace cadabra { bool ignore_properties=false; if(std::is_same::value) ignore_properties=true; + + auto props_dict_it = props_dict.find(it->name_only()); + if (props_dict_it == props_dict.end()) { + return ret; + } + const auto& bucket = props_dict_it->second; + // bucket is a std::multimap taking std::type_index to pat_prop_pair_t + // We only care about std::type_index entries that are either castable to T + // or inherit from a child + + bool found_property = false; + for (const auto& entry : bucket) { + // entry.second contains all pat_prop_pairs of the same property type + // So we can quickly shortcut some things right now. + + // Is entry castable to T? + bool T_castable = is_castable(typeid(T), entry.first); + inherits = inherits || is_castable(typeid(PropertyInherit), entry.first) || is_castable(typeid(Inherit, entry.first)); + + if (T_castable) { + for (const auto& pat_prop_pair : entry.second) { + if(wildcards==pat_prop_pair.first->children_wildcard()) { + ret.first=dynamic_cast(pat_prop_pair.second); + if(ret.first) { + if(pat_prop_pair.first->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { + ret.second=pat_prop_pair.first; + if(!check_label(ret.first, label)) + ret.first=0; + else { + if(doserial) + serialnum=serial_number( pat_prop_pair.second, pat_prop_pair.first); + found_property = true; + break; + } + } + } + ret.first=0; + } + } + } + if (found_property) + break; + } + + // First deal with ones that are directly castable to T + // FIXME: Add a cache for this + + + for(;;) { property_map_t::const_iterator walk=pit.first; while(walk!=pit.second) { @@ -552,4 +674,26 @@ done: return dn; } + + template + Properties::property_map_t::iterator Properties::get_pattern() + { + return get_pattern(props.begin()); + } + + template + Properties::property_map_t::iterator Properties::get_pattern(Properties::property_map_t::iterator prop_it) + { + // Return the next property of type T (or that inherits from T) + while (prop_it != props.end()) { + if (dynamic_cast(prop_it->second.second)) { + return prop_it; + } + ++prop_it; + } + return props.end(); + } + + + } From 4bc36e2682e50ad3b7656b61dd5528a76342a5ca Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:19:09 -0400 Subject: [PATCH 02/18] Introduced PropertyIterartor class and PropertyFilter for ranges. --- core/Props.hh | 222 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 167 insertions(+), 55 deletions(-) diff --git a/core/Props.hh b/core/Props.hh index 0e42fe91d9..40eef4bc04 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -28,7 +28,9 @@ along with this program. If not, see . #include #include "Storage.hh" #include - +#include +#include + namespace cadabra { class Properties; @@ -331,15 +333,149 @@ namespace cadabra { * pats_dict groups the properties in pats by their property_type. This allows us to * iterate over e.g. all AntiCommuting patterns directly. **************************************************************************************/ - - typedef std::map>, - nset_it_less> property_dictmap_t; - typedef std::map> pattern_dictmap_t; + typedef std::map> pat_prop_typemap_t; + typedef std::map property_dictmap_t; + typedef std::map> pattern_dictmap_t; property_dictmap_t props_dict; // pattern -> map(property_type, pat_prop_pairs) pattern_dictmap_t pats_dict; // property_type -> multimap(property, pattern) + + /// Specialized property iterator for iterating over properties in a pat_prop_typemap_t that match a condition + class PropertyIterator { + + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = pat_prop_pair_t; + using pointer = value_type*; + using reference = value_type&; + + using InnerIter = typename std::vector::iterator; + using OuterIter = typename pat_prop_typemap_t::iterator; + + public: + PropertyIterator(pat_prop_typemap_t& m, std::function condition, bool is_end = false) : typemap_(m), condition_(condition) { + if (is_end) { + outer_it_ = typemap_.end(); + inner_it_ = InnerIter(); + } else { + outer_it_ = typemap_.begin(); + skip_ahead(); + } + } + + PropertyIterator& operator++() { + ++inner_it_; + if (inner_it_ == outer_it_->second.end()) { + ++outer_it_; + skip_ahead(); + } + return *this; + } + + PropertyIterator& operator--() { + if (outer_it_ == typemap_.end()) { // End() to last element + --outer_it_; + skip_back(); + } + if (inner_it_ == outer_it_->second.begin()) { + --outer_it_; + skip_back(); + } + --inner_it_; + return *this; + } + + reference operator*() const { return *inner_it_; } + pointer operator->() const { return &(*inner_it_); } + + private: + pat_prop_typemap_t& typemap_; + std::function condition_; + OuterIter outer_it_; + InnerIter inner_it_; + + // Skip ahead to the next property class if needed + void skip_ahead() { + while (outer_it_ != typemap_.end()) { + if (condition_(outer_it_->first)) { + inner_it_ = outer_it_->second.begin(); + if (inner_it_ != outer_it_->second.end()) return; + } + ++outer_it_; + } + // At end, so inner_it_ is blank. + inner_it_ = InnerIter(); + } + + // Skip back to the previous class if needed + void skip_back() { + while (outer_it_ != typemap_.begin()) { + if (condition_(outer_it_->first)) { + inner_it_ = outer_it_->second.end(); + if (inner_it_ != outer_it_->second.begin()) { + --inner_it_; + return; + } + } + --outer_it_; + } + + // assert(outer_it_ == typemap_.begin()) + if (condition_(outer_it_->first)) { + inner_it_ = outer_it_->second.end(); + if (inner_it_ != outer_it_->second.begin()) { + --inner_it_; + return; + } + } else { + inner_it_ = outer_it_->second.begin(); + } + // assert(inner_it_ == outer_it_->second.begin()) + } + + + bool operator==(const PropertyIterator& other) const { + return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; + } + + bool operator!=(const PropertyIterator& other) const { + return !(*this == other); + } + + }; + + class PropertyFilter { + public: + PropertyFilter(pat_prop_typemap_t& m) + : typemap_(m), + condition_([](const std::type_index&) { return true; }), + begin_(typemap_, condition_, /*is_end=*/false), + end_(typemap_, condition_, /*is_end=*/true) + {} + + PropertyFilter(pat_prop_typemap_t& m, std::function condition) + : typemap_(m), + condition_(condition), + begin_(typemap_, condition_, /*is_end=*/false), + end_(typemap_, condition_, /*is_end=*/true) + {} + + PropertyIterator begin() const { + return begin_; + } + + PropertyIterator end() const { + return end_; + } + + private: + pat_prop_typemap_t& typemap_; + std::function condition_; + PropertyIterator begin_; + PropertyIterator end_; + + }; + /// Normal search: given a pattern, get its property if any. template const T* get(Ex::iterator, bool ignore_parent_rel=false) const; @@ -468,18 +604,22 @@ namespace cadabra { // bucket is a std::multimap taking std::type_index to pat_prop_pair_t // We only care about std::type_index entries that are either castable to T // or inherit from a child - - bool found_property = false; - - for (const auto& entry : bucket) { - // entry.second contains all pat_prop_pairs of the same property type - // So we can quickly shortcut some things right now. - - // Is entry castable to T? - bool T_castable = is_castable(typeid(T), entry.first); - inherits = inherits || is_castable(typeid(PropertyInherit), entry.first) || is_castable(typeid(Inherit, entry.first)); - - if (T_castable) { + + // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true + for(;;;) { + // Need an extra boolean to break two `for` loops below when needed + bool found_property = false; + for (const auto& entry : bucket) { + // entry.second contains all pat_prop_pairs of the same property type + // so we can quickly shortcut some things right now. + + // Is entry castable to T? + bool T_castable = is_castable(typeid(T), entry.first); + // Is an interit possible? + inherits = inherits || is_castable(typeid(PropertyInherit), entry.first) || is_castable(typeid(Inherit, entry.first)); + + if (!T_castable) continue; + // Only enter this loop if castable to T, so we don't waste time for (const auto& pat_prop_pair : entry.second) { if(wildcards==pat_prop_pair.first->children_wildcard()) { ret.first=dynamic_cast(pat_prop_pair.second); @@ -499,49 +639,19 @@ namespace cadabra { ret.first=0; } } + // break out to wildcard loop + if (found_property) + break; } - if (found_property) - break; - } - - // First deal with ones that are directly castable to T - // FIXME: Add a cache for this - - - - for(;;) { - property_map_t::const_iterator walk=pit.first; - while(walk!=pit.second) { - if(wildcards==(*walk).second.first->children_wildcard()) { - // First check property type; a dynamic cast is much faster than a pattern match. - ret.first=dynamic_cast((*walk).second.second); - if(ret.first) { - if((*walk).second.first->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { - ret.second=(*walk).second.first; - if(!check_label(ret.first, label)) - ret.first=0; - else { - if(doserial) - serialnum=serial_number( (*walk).second.second, (*walk).second.first ); - break; - } - } - } - ret.first=0; - if(dynamic_cast((*walk).second.second)) - inherits=true; - else if(dynamic_cast *>((*walk).second.second)) - inherits=true; - } - ++walk; - } + + // Possible repeat loop for wildcard if(!wildcards && !ret.first) { // std::cerr << "not yet found, switching to wildcards" << std::endl; wildcards=true; } else break; } - + // Do not walk down the tree if the property cannot be passed up the tree. // FIXME: see issue/259. if(std::is_same::value) @@ -594,9 +704,11 @@ namespace cadabra { bool found=false; bool inherits1=false, inherits2=false; + std::pair pit1=props.equal_range(it1->name_only()); std::pair pit2=props.equal_range(it2->name_only()); + // walk1 walks the properties matching it1's name property_map_t::const_iterator walk1=pit1.first; while(walk1!=pit1.second) { if((*walk1).second.first->match(*this, it1, ignore_parent_rel)) { // match for object 1 found From 016409893cc6704039260cae2616901402aefb6e Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:19:09 -0400 Subject: [PATCH 03/18] Additional changes to Props. --- core/Props.cc | 17 ++- core/Props.hh | 344 +++++++++++++++++++++++++++++--------------------- 2 files changed, 207 insertions(+), 154 deletions(-) diff --git a/core/Props.cc b/core/Props.cc index 4e514189ff..050240235c 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -180,6 +180,7 @@ Properties::registered_property_map_t::~registered_property_map_t() template void Properties::registered_property_map_t::register_type() { T obj; + // obj is an actual property object (e.g. AntiCommuting) and not a pointer std::type_index typeidx = typeid(obj); auto it = types_to_names_.find(typeidx); if (it == types_to_names_.end()) { @@ -188,13 +189,12 @@ void Properties::registered_property_map_t::register_type() { } } -template -void Properties::registered_property_map_t::register_type(const T& obj) { - std::type_index typeidx(typeid(obj)); +void Properties::registered_property_map_t::register_type(const property* prop) { + std::type_index typeidx(typeid(*prop)); auto it = types_to_names_.find(typeidx); if (it == types_to_names_.end()) { - types_to_names_[typeidx] = obj.name(); - names_to_types_.insert({obj.name(),typeidx}); + types_to_names_[typeidx] = prop->name(); + names_to_types_.insert({prop->name(),typeidx}); } } @@ -210,10 +210,9 @@ void Properties::register_property_type() registered_properties.register_type(); } -template -void Properties::register_property_type(const T& obj) +void Properties::register_property_type(const property* prop) { - registered_properties.register_type(obj); + registered_properties.register_type(prop); } keyval_t::const_iterator keyval_t::find(const std::string& key) const @@ -757,7 +756,7 @@ void Properties::dict_erase_(const property* prop, pattern* pat) { // Insert a property and pattern into the dict_maps void Properties::dict_insert_(const property* prop, pattern* pat) { - register_property_type(*prop); + register_property_type(prop); std::type_index type_idx = typeid(*prop); auto name = pat->obj.begin()->name_only(); props_dict[name].emplace( type_idx, std::make_pair(pat,prop)); diff --git a/core/Props.hh b/core/Props.hh index 40eef4bc04..4b92dacbe3 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -263,9 +263,8 @@ namespace cadabra { template void register_type(); - // Register the type of an object. Usage: `register_type(obj);` - template - void register_type(const T&); + // Register the type of an object. Usage: `register_type(prop);` + void register_type(const property* prop); private: // Dictionary from type_index to human readable name. @@ -289,9 +288,9 @@ namespace cadabra { // Just calls `registered_properties.register_type()` template void register_property_type(); - // Register the type of an object. Usage: `register_property_type(obj);` + // Register the type of an object. Usage: `register_property_type(prop);` // Just calls `registered_properties.register_type(obj)` - template void register_property_type(const T&); + void register_property_type(const property* prop); @@ -340,7 +339,78 @@ namespace cadabra { property_dictmap_t props_dict; // pattern -> map(property_type, pat_prop_pairs) pattern_dictmap_t pats_dict; // property_type -> multimap(property, pattern) - /// Specialized property iterator for iterating over properties in a pat_prop_typemap_t that match a condition + template bool is_castable(std::type_index obj_type, U obj); + + /// Normal search: given a pattern, get its property if any. + template const T* get(Ex::iterator, bool ignore_parent_rel=false) const; + template const T* get(Ex::iterator, int& serialnum, bool doserial=true, bool ignore_parent_rel=false) const; + /// Ditto for labelled properties + template const T* get(Ex::iterator, const std::string& label) const; + template const T* get(Ex::iterator, int& serialnum, const std::string& label, bool doserial=true) const; + /// For list properties: given two patterns, get a common property. + template const T* get(Ex::iterator, Ex::iterator, bool ignore_parent_rel=false) const; + template const T* get(Ex::iterator, Ex::iterator, int&, int&, bool ignore_parent_rel=false) const; + + /// General property finder, which will return not only the property but also + /// the pattern which matched the given node. All 'get' functions above call + /// this function; all functionality is contained in here. + template + std::pair get_with_pattern(Ex::iterator, int& serialnum, + const std::string& label, + bool doserial=true, bool ignore_parent_rel=false) const; + + template + std::pair get_with_pattern_ext(Ex::iterator, Ex_comparator&, int& serialnum, + const std::string& label, + bool doserial=true, bool ignore_parent_rel=false) const; + + // Get the outermost node which has the given property attached, i.e. go down through + // all (if any) nodes which have just inherited the property. + template Ex::iterator head(Ex::iterator, bool ignore_parent_rel=false) const; + + // Inverse search: given a property type, get a pattern which has this property. + // When given an iterator, it starts to search in the property + // map from this particular point. Note: this searches on property type, not exact property. + // template + // property_map_t::iterator get_pattern(property_map_t::iterator=props.begin()); + // template + // property_map_t::iterator get_pattern(); + // template + // property_map_t::iterator get_pattern(property_map_t::iterator); + + // Equivalent search: given a node, get a pattern of equivalents. + // property_map_t::iterator get_equivalent(Ex::iterator, + // property_map_t::iterator=props.begin()); + + private: + // Insert a property. Do not use this directly, use the public + // interface `master_insert` instead. + void insert_prop(const Ex&, const property *); + void insert_list_prop(const std::vector&, const list_property *); + bool check_label(const property *, const std::string&) const; + bool check_label(const labelled_property *, const std::string&) const; + // Search through pointers + bool has(const property *, Ex::iterator); + // Find serial number of a pattern in a given list property + int serial_number(const property *, const pattern *) const; + Ex_comparator *create_comparator() const; + void destroy_comparator(Ex_comparator *) const; + + // Erases property completely. + void dict_erase_(const property*); + // Erases pattern from a given property, leaving other patterns alone. + void dict_erase_(const property*, pattern*); + // Insert property/pattern into the dictmaps. + void dict_insert_(const property*, pattern*); + + // Erases the first key-value pair from a multimap, returning 1 if found, 0 if not. + template + int mmap_erase_key_value_(std::multimap&, const K&, const V&); + + std::map, bool> castable_table; + + public: + /// Specialized property iterator for iterating over properties in a pat_prop_typemap_t that match a condition. class PropertyIterator { using iterator_category = std::bidirectional_iterator_tag; @@ -353,7 +423,7 @@ namespace cadabra { using OuterIter = typename pat_prop_typemap_t::iterator; public: - PropertyIterator(pat_prop_typemap_t& m, std::function condition, bool is_end = false) : typemap_(m), condition_(condition) { + PropertyIterator(pat_prop_typemap_t& m, std::function& condition, bool is_end = false) : typemap_(m), condition_(condition) { if (is_end) { outer_it_ = typemap_.end(); inner_it_ = InnerIter(); @@ -385,19 +455,31 @@ namespace cadabra { return *this; } + /// Advance the iterator to the next property type + PropertyIterator& next_proptype() { + ++outer_it_; + while (outer_it_ != typemap_.end()) { + if (condition_(*this)) { + inner_it_ = outer_it_->second.begin(); + if (inner_it_ != outer_it_->second.end()) return; + } + ++outer_it_; + } + } + reference operator*() const { return *inner_it_; } pointer operator->() const { return &(*inner_it_); } private: pat_prop_typemap_t& typemap_; - std::function condition_; + std::function& condition_; OuterIter outer_it_; InnerIter inner_it_; // Skip ahead to the next property class if needed void skip_ahead() { while (outer_it_ != typemap_.end()) { - if (condition_(outer_it_->first)) { + if (condition_(*this)) { inner_it_ = outer_it_->second.begin(); if (inner_it_ != outer_it_->second.end()) return; } @@ -410,7 +492,7 @@ namespace cadabra { // Skip back to the previous class if needed void skip_back() { while (outer_it_ != typemap_.begin()) { - if (condition_(outer_it_->first)) { + if (condition_(*this)) { inner_it_ = outer_it_->second.end(); if (inner_it_ != outer_it_->second.begin()) { --inner_it_; @@ -421,7 +503,7 @@ namespace cadabra { } // assert(outer_it_ == typemap_.begin()) - if (condition_(outer_it_->first)) { + if (condition_(*this)) { inner_it_ = outer_it_->second.end(); if (inner_it_ != outer_it_->second.begin()) { --inner_it_; @@ -444,16 +526,17 @@ namespace cadabra { }; + /// Specialized property filter. Use to build PropertyIterators that satisfy a condition. class PropertyFilter { public: PropertyFilter(pat_prop_typemap_t& m) : typemap_(m), - condition_([](const std::type_index&) { return true; }), + condition_([](const PropertyIterator&) { return true; }), begin_(typemap_, condition_, /*is_end=*/false), end_(typemap_, condition_, /*is_end=*/true) {} - PropertyFilter(pat_prop_typemap_t& m, std::function condition) + PropertyFilter(pat_prop_typemap_t& m, std::function condition) : typemap_(m), condition_(condition), begin_(typemap_, condition_, /*is_end=*/false), @@ -470,79 +553,13 @@ namespace cadabra { private: pat_prop_typemap_t& typemap_; - std::function condition_; + std::function condition_; PropertyIterator begin_; PropertyIterator end_; }; - /// Normal search: given a pattern, get its property if any. - template const T* get(Ex::iterator, bool ignore_parent_rel=false) const; - template const T* get(Ex::iterator, int& serialnum, bool doserial=true, bool ignore_parent_rel=false) const; - /// Ditto for labelled properties - template const T* get(Ex::iterator, const std::string& label) const; - template const T* get(Ex::iterator, int& serialnum, const std::string& label, bool doserial=true) const; - /// For list properties: given two patterns, get a common property. - template const T* get(Ex::iterator, Ex::iterator, bool ignore_parent_rel=false) const; - template const T* get(Ex::iterator, Ex::iterator, int&, int&, bool ignore_parent_rel=false) const; - - /// General property finder, which will return not only the property but also - /// the pattern which matched the given node. All 'get' functions above call - /// this function; all functionality is contained in here. - template - std::pair get_with_pattern(Ex::iterator, int& serialnum, - const std::string& label, - bool doserial=true, bool ignore_parent_rel=false) const; - - template - std::pair get_with_pattern_ext(Ex::iterator, Ex_comparator&, int& serialnum, - const std::string& label, - bool doserial=true, bool ignore_parent_rel=false) const; - - // Get the outermost node which has the given property attached, i.e. go down through - // all (if any) nodes which have just inherited the property. - template Ex::iterator head(Ex::iterator, bool ignore_parent_rel=false) const; - - // Inverse search: given a property type, get a pattern which has this property. - // When given an iterator, it starts to search in the property - // map from this particular point. Note: this searches on property type, not exact property. - // template - // property_map_t::iterator get_pattern(property_map_t::iterator=props.begin()); - template - property_map_t::iterator get_pattern(); - template - property_map_t::iterator get_pattern(property_map_t::iterator); - - // Equivalent search: given a node, get a pattern of equivalents. - // property_map_t::iterator get_equivalent(Ex::iterator, - // property_map_t::iterator=props.begin()); - - private: - // Insert a property. Do not use this directly, use the public - // interface `master_insert` instead. - void insert_prop(const Ex&, const property *); - void insert_list_prop(const std::vector&, const list_property *); - bool check_label(const property *, const std::string&) const; - bool check_label(const labelled_property *, const std::string&) const; - // Search through pointers - bool has(const property *, Ex::iterator); - // Find serial number of a pattern in a given list property - int serial_number(const property *, const pattern *) const; - Ex_comparator *create_comparator() const; - void destroy_comparator(Ex_comparator *) const; - - // Erases property completely. - void dict_erase_(const property*); - // Erases pattern from a given property, leaving other patterns alone. - void dict_erase_(const property*, pattern*); - // Insert property/pattern into the dictmaps. - void dict_insert_(const property*, pattern*); - - // Erases the first key-value pair from a multimap, returning 1 if found, 0 if not. - template - int mmap_erase_key_value_(std::multimap&, const K&, const V&); - }; template @@ -578,11 +595,14 @@ namespace cadabra { std::pair ret = {nullptr, nullptr}; - bool inherits=false; - + bool inherits = false; //std::cerr << *it->name_only() << std::endl; // std::cerr << props.size() << std::endl; - std::pair pit=props.equal_range(it->name_only()); + + // The original version of this used walked the iterator pit and checked dynamic casts to T + // and to Inherit and PropertyInherit. The new PropertyFilter lets us walk the dictmap + // just for the various castable cases. But we have to split the logic up and check + // the T castable cases first. // First look for properties of the node itself. Go through the loop twice: // once looking for patterns which do not have wildcards, and then looking @@ -597,61 +617,45 @@ namespace cadabra { ignore_properties=true; auto props_dict_it = props_dict.find(it->name_only()); - if (props_dict_it == props_dict.end()) { - return ret; - } - const auto& bucket = props_dict_it->second; - // bucket is a std::multimap taking std::type_index to pat_prop_pair_t - // We only care about std::type_index entries that are either castable to T - // or inherit from a child - - // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true - for(;;;) { - // Need an extra boolean to break two `for` loops below when needed - bool found_property = false; - for (const auto& entry : bucket) { - // entry.second contains all pat_prop_pairs of the same property type - // so we can quickly shortcut some things right now. - - // Is entry castable to T? - bool T_castable = is_castable(typeid(T), entry.first); - // Is an interit possible? - inherits = inherits || is_castable(typeid(PropertyInherit), entry.first) || is_castable(typeid(Inherit, entry.first)); - - if (!T_castable) continue; - // Only enter this loop if castable to T, so we don't waste time - for (const auto& pat_prop_pair : entry.second) { - if(wildcards==pat_prop_pair.first->children_wildcard()) { - ret.first=dynamic_cast(pat_prop_pair.second); + if (props_dict_it != props_dict.end()) { + auto heritable_props = PropertyFilter(props_dict_it->second, + [](std::type_index idx, const property* prop) { + return is_castable>(idx, prop) || is_castable(idx, prop); + }); + inherits = (heritable_props.begin() != heritable_props.end()); + + // Filter all properties that are castable to T. + auto castable_props = PropertyFilter(props_dict_it->second, + [](std::type_index idx, const property* prop) { + return is_castable(idx, prop); + }); + // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true + for(;;) { + for (const pat_prop_pair_t& pat_prop : castable_props) { + if(wildcards==pat_prop.first->children_wildcard()) { + ret.first=dynamic_cast(pat_prop.second); if(ret.first) { - if(pat_prop_pair.first->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { - ret.second=pat_prop_pair.first; + if(pat_prop.first->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { + ret.second=pat_prop.first; if(!check_label(ret.first, label)) ret.first=0; else { if(doserial) - serialnum=serial_number( pat_prop_pair.second, pat_prop_pair.first); - found_property = true; + serialnum=serial_number( pat_prop.second, pat_prop.first); break; } } } ret.first=0; - } + } } - // break out to wildcard loop - if (found_property) - break; - } - - // Possible repeat loop for wildcard - if(!wildcards && !ret.first) { - // std::cerr << "not yet found, switching to wildcards" << std::endl; - wildcards=true; + if(!wildcards && !ret.first) { + // std::cerr << "not yet found, switching to wildcards" << std::endl; + wildcards=true; + } + else break; } - else break; } - // Do not walk down the tree if the property cannot be passed up the tree. // FIXME: see issue/259. if(std::is_same::value) @@ -704,17 +708,75 @@ namespace cadabra { bool found=false; bool inherits1=false, inherits2=false; + + // Find all common properties of it1 and it2 + auto props_dict_it1 = props_dict.find(it1->name_only()); + auto props_dict_it2 = props_dict.find(it2->name_only()); + if (props_dict_it1 != props_dict.end() && props_dict_it2 != props_dict.end()) { + // Are any of these properties heritable? + // FIXME: Below just uses PropertyInherit and not Inherit? + auto heritable_props = PropertyFilter(props_dict_it1->second, + [](std::type_index idx, const property* prop) { + return is_castable(idx, prop); + }); + inherits1 = (heritable_props.begin() != heritable_props.end()); + + heritable_props = PropertyFilter(props_dict_it2->second, + [](std::type_index idx, const property* prop) { + return is_castable(idx, prop); + }); + inherits2 = (heritable_props.begin() != heritable_props.end()); + + // Find all castable properties that appear in both props_dict_t1->second and props_dict_t2->second + auto castable_props1 = PropertyFilter(props_dict_it1->second, + [](std::type_index idx, const property* prop) { + return is_castable(idx, prop); + }); + auto castable_props2 = PropertyFilter(props_dict_it2->second, + [](std::type_index idx, const property* prop) { + return is_castable(idx, prop); + }); + + // Search for properties that appear in both pit1 and pit2 + for (auto it1 = castable_props1.begin(), it2 = castable_props2.begin(); + it1 != castable_props1.end() && it2 != castable_props2.end(); ) + { + // it->first is std::type_index + auto foo = it1; + if (it1->first < it2->first) { + ++it1; + } + else if (it2->first < it1->first) { + ++it2; + } + else { + // + const K& key = it1->first; + const std::vector& vec1 = it1->second; + const std::vector& vec2 = it2->second; + // Do something with vec1 and vec2 + process_vectors(key, vec1, vec2); + + ++it1; + ++it2; + } + } + + + + } std::pair pit1=props.equal_range(it1->name_only()); std::pair pit2=props.equal_range(it2->name_only()); - // walk1 walks the properties matching it1's name + // walk the properties matching it1's name property_map_t::const_iterator walk1=pit1.first; while(walk1!=pit1.second) { if((*walk1).second.first->match(*this, it1, ignore_parent_rel)) { // match for object 1 found ret1=dynamic_cast((*walk1).second.second); if(ret1) { // property of the right type found for object 1 property_map_t::const_iterator walk2=pit2.first; + // walk the properties matching it2's name while(walk2!=pit2.second) { if((*walk2).second.first->match(*this, it2, ignore_parent_rel)) { // match for object 2 found ret2=dynamic_cast((*walk2).second.second); @@ -787,25 +849,17 @@ done: } - template - Properties::property_map_t::iterator Properties::get_pattern() - { - return get_pattern(props.begin()); - } - - template - Properties::property_map_t::iterator Properties::get_pattern(Properties::property_map_t::iterator prop_it) - { - // Return the next property of type T (or that inherits from T) - while (prop_it != props.end()) { - if (dynamic_cast(prop_it->second.second)) { - return prop_it; - } - ++prop_it; - } - return props.end(); + template + bool Properties::is_castable(std::type_index obj_type, U obj) { + auto it = castable_table.find(std::make_pair(std::type_index(typeid(T)), obj_type)); + if (it != castable_table.end()) { + return it->second; } - - + if (dynamic_cast(obj)) { + castable_table.insert(std::make_pair(std::type_index(typeid(T)), obj_type), true); + } else { + castable_table.insert(std::make_pair(std::type_index(typeid(T)), obj_type), false); + } + } } From 14c60260f668134fb4f893c7ec4e7e5643022c7f Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:19:09 -0400 Subject: [PATCH 04/18] Backup prior to some refactoring. --- core/Props.cc | 232 +++++++++++++++++++++++++++++------------------- core/Props.hh | 238 ++++++++++++++++++++++++++------------------------ 2 files changed, 270 insertions(+), 200 deletions(-) diff --git a/core/Props.cc b/core/Props.cc index 050240235c..5e7088d7ba 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -139,37 +139,50 @@ bool pattern::children_wildcard() const 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 + // Does Ex::iterator `it` possess property *pb? + + auto pdit = props_dict.find(it->name_only()); + if (pdit == props_dict.end()) { + return false; + } + + // Look for property *pb + pat_prop_typemap_t::iterator pptit = pdit->second.find(typeid(*pb)); + if (pptit == pdit->second.end()) { + return false; + } + + // Check if any pat_prop pair matches + for (const pat_prop_pair_t& pat_prop : pptit->second) { + if (pat_prop.first->match(*this, it)) { 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 + // be shared, we use the pats_dict 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; + + for (const auto& [_, this_pats] : pats_dict) { + pattern_map_t::const_iterator it=this_pats.begin(); + const property *previous=0; + while(it!=this_pats.end()) { + if(previous!=it->first) { + previous=it->first; + delete it->first; + } + delete it->second; + ++it; } - delete it->second; - ++it; } - props.clear(); - pats.clear(); + props_dict.clear(); + pats_dict.clear(); } Properties::registered_property_map_t::~registered_property_map_t() @@ -185,7 +198,7 @@ void Properties::registered_property_map_t::register_type() { auto it = types_to_names_.find(typeidx); if (it == types_to_names_.end()) { types_to_names_[typeidx] = obj.name(); - names_to_types_.insert({obj.name(),typeidx}); + names_to_types_.emplace(obj.name(),typeidx); } } @@ -194,7 +207,7 @@ void Properties::registered_property_map_t::register_type(const property* prop) auto it = types_to_names_.find(typeidx); if (it == types_to_names_.end()) { types_to_names_[typeidx] = prop->name(); - names_to_types_.insert({prop->name(),typeidx}); + names_to_types_.emplace(prop->name(),typeidx); } } @@ -379,7 +392,84 @@ bool labelled_property::parse(Kernel&, std::shared_ptr, keyval_t& keyvals) // return one.obj==two.obj; // FIXME: handle dummy indices // } -void Properties::insert_prop(const Ex& et, const property *pr) +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 + + // Create the pattern from et + pattern *pat = new pattern(et); + + // Make sure there is no existing property of the same type matching pat + auto pdit = props_dict.find(pat->obj.begin()->name_only()); + if (pdit != props_dict.end() ) { + auto possible_dups = PropertyFilter(pdit->second, + [pr](const PropertyIterator& pit) { + return typeid(*pr) == typeid(*(pit->second)); + }); + + for (PropertyIterator pdit = possible_dups.begin(); pdit != possible_dups.end();) { + // 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 + // and are of the same type as pr. + if( pdit->first->match(*this, et.begin())) { + // 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(pdit->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 = pdit->first; + const property *oldprop = pdit->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. + PropertyIterator old_pdit = pdit; + ++pdit; + erase(old_pdit->second, old_pdit->first); + + // 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. + + } + } else { + ++pdit; + } + } + } + + // Add to the props_dict + auto pat_prop_pairs = props_dict[pat->obj.begin()->name_only()][typeid(*pr)]; + pat_prop_pairs.emplace_back(pat,pr); + // Keep the pats_vector sorted + std::sort(pat_prop_pairs.begin(), pat_prop_pairs.end(), + [](const pat_prop_pair_t& a, const pat_prop_pair_t& b) { + // Order just on the property pointers + return a.second < b.second; + }); + + // Add to the pats_dict + pats_dict[typeid(*pr)].emplace(pr, pat); + +} + + + +void Properties::insert_prop_old(const Ex& et, const property *pr) { // assert(pats.find(pr)==pats.end()); // identical properties have to be assigned through insert_list_prop @@ -387,6 +477,7 @@ void Properties::insert_prop(const Ex& et, const property *pr) // not cleaning this up correctly yet. pattern *pat=new pattern(et); + // pit iterates over pat_prop pairs matching name_only() std::pair pit= props.equal_range(pat->obj.begin()->name_only()); @@ -397,7 +488,7 @@ void Properties::insert_prop(const Ex& et, const property *pr) 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())) { @@ -430,8 +521,6 @@ void Properties::insert_prop(const Ex& et, const property *pr) // Erase the pattern->property entry, and delete the pattern. // FIXME: store pattern by value. - // erase from dicts - dict_erase_(oldprop, oldpat); // erase pattern from props props.erase(pit.first); delete oldpat; @@ -469,7 +558,6 @@ void Properties::insert_prop(const Ex& et, const property *pr) 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))); - dict_insert_(pr, pat); } void Properties::insert_list_prop(const std::vector& its, const list_property *pr) @@ -704,75 +792,43 @@ void Properties::destroy_comparator(Ex_comparator *c) const // Completely erase a property. -void Properties::dict_erase_(const property* p) { - std::type_index type_idx = typeid(*p); - auto pats_dict_it = pats_dict.find(type_idx); - // If we don't find it, end here. - if (pats_dict_it == pats_dict.end()) { - return; - } - // The multimap lies at pats_dict_it->second. - // As with the pats/props maps, we need to look for the pattern in the props_dict. - auto range = pats_dict_it->second.equal_range(p); - - for (auto pi = range.first; pi != range.second;) { - // pi iterates over the in the multimap - // Before erasing the entry in pats_dict, we need to find the elements in props_dict to erase. - // Get the pattern. - auto pat = pi->second; - auto it = props_dict.find(pat->obj.begin()->name_only()); - if (it == props_dict.end()) { - ++pi; - continue; - } - // `it` points to the multimap within props_dict - auto props_dict_mmap = it->second; - auto props_dict_range = props_dict_mmap.equal_range(type_idx); - for (auto it2 = props_dict_range.first; it2 != props_dict_range.second; ++it2) { - if (it2->second.second == p) { - props_dict_mmap.erase(it2); - break; - } - } - // FIXME: Error if no element found to be erased? - pi = pats_dict_it->second.erase(pi); - } -} - -// Erase a property and pattern from the dict_maps -void Properties::dict_erase_(const property* prop, pattern* pat) { - std::type_index type_idx = typeid(*prop); - auto name = pat->obj.begin()->name_only(); - - // FIXME: Avoid operator[] if possible? +void Properties::erase(const property* p) { - // erase from props_dict - mmap_erase_key_value_(props_dict[name], type_idx, pat_prop_pair_t(pat, prop)); - // erase from pats_dict - mmap_erase_key_value_(pats_dict[type_idx], prop, pat); } +// Erase a property and related pattern. +void Properties::erase(const property* prop, pattern* pat) { + // 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. -// Insert a property and pattern into the dict_maps -void Properties::dict_insert_(const property* prop, pattern* pat) { - register_property_type(prop); std::type_index type_idx = typeid(*prop); auto name = pat->obj.begin()->name_only(); - props_dict[name].emplace( type_idx, std::make_pair(pat,prop)); - pats_dict[type_idx].emplace(prop, pat); -} - -// Erases the first key-value pair from a multimap, returning 1 if found, 0 if not. -template -int Properties::mmap_erase_key_value_(std::multimap& mmap, const K& key, const V& value) { - auto range = mmap.equal_range(key); - for (auto it = range.first; it != range.second; ++it) { - if (it->second == value) { - mmap.erase(it); - return 1; + // Eliminate from props_dict + auto pdit = props_dict.find(name); + if (pdit != props_dict.end()) { + auto ppit = pdit->second.find(typeid(*prop)); + if (ppit != pdit->second.end()) { + auto pat_prop_pairs = ppit->second; + for (auto pairs_it = pat_prop_pairs.begin(); pairs_it != pat_prop_pairs.end(); ) { + if (pairs_it->first == pat && pairs_it->second == prop) { + pairs_it = pat_prop_pairs.erase(pairs_it); + } else { + ++pairs_it; + } + } } } - return 0; + + // + } + diff --git a/core/Props.hh b/core/Props.hh index 4b92dacbe3..c0027b2367 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -30,6 +30,7 @@ along with this program. If not, see . #include #include #include +#include namespace cadabra { @@ -244,7 +245,7 @@ namespace cadabra { class Properties { public: - + // Class to store names and std::type_index for property objects class registered_property_map_t { public: @@ -332,13 +333,20 @@ namespace cadabra { * pats_dict groups the properties in pats by their property_type. This allows us to * iterate over e.g. all AntiCommuting patterns directly. **************************************************************************************/ - typedef std::map> pat_prop_typemap_t; + class pat_prop_less { + public: + bool operator()(const pat_prop_pair_t&, const pat_prop_pair_t&); + }; + + typedef boost::container::flat_set pat_props_t; + typedef std::map pat_prop_typemap_t; typedef std::map property_dictmap_t; typedef std::map> pattern_dictmap_t; property_dictmap_t props_dict; // pattern -> map(property_type, pat_prop_pairs) pattern_dictmap_t pats_dict; // property_type -> multimap(property, pattern) + /// Determine if obj of type_index obj_type is castable to type T template bool is_castable(std::type_index obj_type, U obj); /// Normal search: given a pattern, get its property if any. @@ -364,6 +372,8 @@ namespace cadabra { const std::string& label, bool doserial=true, bool ignore_parent_rel=false) const; + + // Get the outermost node which has the given property attached, i.e. go down through // all (if any) nodes which have just inherited the property. template Ex::iterator head(Ex::iterator, bool ignore_parent_rel=false) const; @@ -381,11 +391,20 @@ namespace cadabra { // Equivalent search: given a node, get a pattern of equivalents. // property_map_t::iterator get_equivalent(Ex::iterator, // property_map_t::iterator=props.begin()); + class PropertyIterator; + class PropertyFilter; + + /// Erases property completely. + void erase(const property*); + /// Erases pattern from a given property, leaving other patterns alone. + void erase(const property*, pattern*); + private: // Insert a property. Do not use this directly, use the public // interface `master_insert` instead. void insert_prop(const Ex&, const property *); + void insert_prop_old(const Ex&, const property *); void insert_list_prop(const std::vector&, const list_property *); bool check_label(const property *, const std::string&) const; bool check_label(const labelled_property *, const std::string&) const; @@ -396,18 +415,21 @@ namespace cadabra { Ex_comparator *create_comparator() const; void destroy_comparator(Ex_comparator *) const; - // Erases property completely. - void dict_erase_(const property*); - // Erases pattern from a given property, leaving other patterns alone. - void dict_erase_(const property*, pattern*); - // Insert property/pattern into the dictmaps. - void dict_insert_(const property*, pattern*); + std::map, bool> castable_table; + + // Helper functions for use with Propery Filter + + template + bool helper_castable(const PropertyIterator& pit) { + return is_castable(pit.proptype(), pit->second); + } + + template + bool helper_heritable(const PropertyIterator& pit) { + return is_castable>(pit.proptype(), pit->second) || is_castable(pit.proptype(), pit->second); + } - // Erases the first key-value pair from a multimap, returning 1 if found, 0 if not. - template - int mmap_erase_key_value_(std::multimap&, const K&, const V&); - std::map, bool> castable_table; public: /// Specialized property iterator for iterating over properties in a pat_prop_typemap_t that match a condition. @@ -423,7 +445,7 @@ namespace cadabra { using OuterIter = typename pat_prop_typemap_t::iterator; public: - PropertyIterator(pat_prop_typemap_t& m, std::function& condition, bool is_end = false) : typemap_(m), condition_(condition) { + PropertyIterator(pat_prop_typemap_t& m, std::function condition, bool is_end = false) : typemap_(m), condition_(condition) { if (is_end) { outer_it_ = typemap_.end(); inner_it_ = InnerIter(); @@ -465,14 +487,31 @@ namespace cadabra { } ++outer_it_; } + return *this; + } + /// Return the current property type + std::type_index proptype() const { + return outer_it_->first; + } + /// Return the current vector (no copy) of pat_prop_pairs + std::vector& pat_prop_pairs() const { + return outer_it_->second; } reference operator*() const { return *inner_it_; } pointer operator->() const { return &(*inner_it_); } + bool operator==(const PropertyIterator& other) const { + return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; + } + + bool operator!=(const PropertyIterator& other) const { + return !(*this == other); + } + private: pat_prop_typemap_t& typemap_; - std::function& condition_; + std::function condition_; OuterIter outer_it_; InnerIter inner_it_; @@ -516,26 +555,19 @@ namespace cadabra { } - bool operator==(const PropertyIterator& other) const { - return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; - } - - bool operator!=(const PropertyIterator& other) const { - return !(*this == other); - } - }; + private: + /// Specialized property filter. Use to build PropertyIterators that satisfy a condition. class PropertyFilter { public: PropertyFilter(pat_prop_typemap_t& m) : typemap_(m), - condition_([](const PropertyIterator&) { return true; }), + condition_([](const PropertyIterator&) {return true;}), begin_(typemap_, condition_, /*is_end=*/false), end_(typemap_, condition_, /*is_end=*/true) {} - PropertyFilter(pat_prop_typemap_t& m, std::function condition) : typemap_(m), condition_(condition), @@ -555,8 +587,7 @@ namespace cadabra { pat_prop_typemap_t& typemap_; std::function condition_; PropertyIterator begin_; - PropertyIterator end_; - + PropertyIterator end_; }; @@ -618,17 +649,12 @@ namespace cadabra { auto props_dict_it = props_dict.find(it->name_only()); if (props_dict_it != props_dict.end()) { - auto heritable_props = PropertyFilter(props_dict_it->second, - [](std::type_index idx, const property* prop) { - return is_castable>(idx, prop) || is_castable(idx, prop); - }); + auto heritable_props = PropertyFilter(props_dict_it->second, &helper_heritable); inherits = (heritable_props.begin() != heritable_props.end()); // Filter all properties that are castable to T. - auto castable_props = PropertyFilter(props_dict_it->second, - [](std::type_index idx, const property* prop) { - return is_castable(idx, prop); - }); + auto castable_props = PropertyFilter(props_dict_it->second, &helper_castable); + // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true for(;;) { for (const pat_prop_pair_t& pat_prop : castable_props) { @@ -703,8 +729,6 @@ namespace cadabra { template const T* Properties::get(Ex::iterator it1, Ex::iterator it2, int& serialnum1, int& serialnum2, bool ignore_parent_rel) const { - const T* ret1=0; - const T* ret2=0; bool found=false; bool inherits1=false, inherits2=false; @@ -715,93 +739,88 @@ namespace cadabra { if (props_dict_it1 != props_dict.end() && props_dict_it2 != props_dict.end()) { // Are any of these properties heritable? // FIXME: Below just uses PropertyInherit and not Inherit? - auto heritable_props = PropertyFilter(props_dict_it1->second, - [](std::type_index idx, const property* prop) { - return is_castable(idx, prop); - }); + auto heritable_props = PropertyFilter(props_dict_it1->second, &helper_castable); inherits1 = (heritable_props.begin() != heritable_props.end()); - heritable_props = PropertyFilter(props_dict_it2->second, - [](std::type_index idx, const property* prop) { - return is_castable(idx, prop); - }); + heritable_props = PropertyFilter(props_dict_it2->second, &helper_castable); inherits2 = (heritable_props.begin() != heritable_props.end()); // Find all castable properties that appear in both props_dict_t1->second and props_dict_t2->second - auto castable_props1 = PropertyFilter(props_dict_it1->second, - [](std::type_index idx, const property* prop) { - return is_castable(idx, prop); - }); - auto castable_props2 = PropertyFilter(props_dict_it2->second, - [](std::type_index idx, const property* prop) { - return is_castable(idx, prop); - }); + auto castable_props1 = PropertyFilter(props_dict_it1->second, &helper_castable); + auto castable_props2 = PropertyFilter(props_dict_it2->second, &helper_castable); // Search for properties that appear in both pit1 and pit2 - for (auto it1 = castable_props1.begin(), it2 = castable_props2.begin(); - it1 != castable_props1.end() && it2 != castable_props2.end(); ) + for (auto pit1 = castable_props1.begin(), pit2 = castable_props2.begin(); + pit1 != castable_props1.end() && pit2 != castable_props2.end(); ) { - // it->first is std::type_index - auto foo = it1; - if (it1->first < it2->first) { - ++it1; + // Because we use a std::map the std::type_index prop types are sorted + if (pit1.proptype() < pit2.proptype()) { + pit1.next_proptype(); } - else if (it2->first < it1->first) { - ++it2; + else if (pit2.proptype() < pit1.proptype()) { + pit2.next_proptype(); } else { - // - const K& key = it1->first; - const std::vector& vec1 = it1->second; - const std::vector& vec2 = it2->second; - - // Do something with vec1 and vec2 - process_vectors(key, vec1, vec2); - - ++it1; - ++it2; - } - } - - - - } - std::pair pit1=props.equal_range(it1->name_only()); - std::pair pit2=props.equal_range(it2->name_only()); - - // walk the properties matching it1's name - property_map_t::const_iterator walk1=pit1.first; - while(walk1!=pit1.second) { - if((*walk1).second.first->match(*this, it1, ignore_parent_rel)) { // match for object 1 found - ret1=dynamic_cast((*walk1).second.second); - if(ret1) { // property of the right type found for object 1 - property_map_t::const_iterator walk2=pit2.first; - // walk the properties matching it2's name - while(walk2!=pit2.second) { - if((*walk2).second.first->match(*this, it2, ignore_parent_rel)) { // match for object 2 found - ret2=dynamic_cast((*walk2).second.second); - if(ret2) { // property of the right type found for object 2 - if(ret1==ret2 && walk1!=walk2) { // accept if properties are the same and patterns are not - serialnum1=serial_number( (*walk1).second.second, (*walk1).second.first ); - serialnum2=serial_number( (*walk2).second.second, (*walk2).second.first ); - found=true; - goto done; - } + // it1 and it2 are of the same property type + + // Get the pat_prop_pairs for each + auto& pat_props1 = pit1.pat_prop_pairs(); + auto& pat_props2 = pit2.pat_prop_pairs(); + + match_t match1 = match_t::unknown; + match_t match2 = match_t::unknown; + bool match1_found = false; + bool match2_found = false; + + // By construction, the pat_props are always sorted by property pointer. + // This allows the iteration below to be as fast as possible. + for (auto ppit1 = pat_props1.begin(), ppit2 = pat_props2.begin(); + ppit1 != pat_props1.end() && ppit2 != pat_props2.end(); ) + { + // Advance both iterators until their properties match + if (ppit1->second < ppit2->second) { + ++ppit1; + match1_found = false; + } else if (ppit2->second < ppit1->second) { + ++ppit2; + match2_found = false; + } else { + // The properties match, so see if there is a pattern match + if (!match1_found) { + if (ppit1->first->match(*this, it1, ignore_parent_rel)) { + match1_found = true; + } else { + ++ppit1; + // assert(match1_found == false); + continue; } } - if(dynamic_cast((*walk2).second.second)) - inherits2=true; - ++walk2; + if (!match2_found) { + if (ppit2->first->match(*this, it2, ignore_parent_rel)) { + match2_found = true; + } else { + ++ppit2; + // assert(match2_found == false); + continue; + } + } + // assert(match1_found && match2_found) + + // accept if properties are the same and patterns are not + if ((ppit1->second == ppit2->second) && (ppit1->first != ppit2->first)) { + serialnum1=serial_number( ppit1->second, ppit1->first ); + serialnum2=serial_number( ppit2->second, ppit2->first ); + // Return the recasted property + return dynamic_cast( ppit1->second ); + } } } - if(dynamic_cast((*walk1).second.second)) - inherits1=true; } - ++walk1; } + } // If no property was found, figure out whether a property is inherited from a child node. - if(!found && (inherits1 || inherits2)) { + if(inherits1 || inherits2) { Ex::sibling_iterator sib1, sib2; if(inherits1) sib1=it1.begin(); else sib1=it1; @@ -813,10 +832,8 @@ namespace cadabra { do { // 2 const T* tmp=get((Ex::iterator)(sib1), (Ex::iterator)(sib2), serialnum1, serialnum2, ignore_parent_rel); if(tmp) { - ret1=tmp; - found=true; - goto done; - } + return tmp; + } if(!inherits2 || ++sib2==it2.end()) keepgoing2=false; } @@ -826,10 +843,7 @@ namespace cadabra { } while(keepgoing1); } - -done: - if(!found) ret1=0; - return ret1; + return nullptr; } template From 149633ae3c1a2d3927602e2981066d7d9ef8be86 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:21:04 -0400 Subject: [PATCH 05/18] Finish modifications... --- core/IndexClassifier.cc | 95 ++++--- core/Props.cc | 221 +++++++++------ core/Props.hh | 384 ++++++++++++++++++-------- core/algorithms/rename_dummies.cc | 22 +- core/cadabra2_defaults.py.in | 3 + core/properties/CommutingBehaviour.cc | 2 +- core/properties/Indices.cc | 4 +- core/properties/SortOrder.cc | 2 +- core/pythoncdb/py_properties.cc | 6 +- 9 files changed, 478 insertions(+), 261 deletions(-) diff --git a/core/IndexClassifier.cc b/core/IndexClassifier.cc index 947e75e520..8703ceab85 100644 --- a/core/IndexClassifier.cc +++ b/core/IndexClassifier.cc @@ -490,57 +490,60 @@ Ex IndexClassifier::get_dummy(const list_property *dums, const index_map_t * four, const index_map_t * five) const { - std::pair - pr=kernel.properties.pats.equal_range(dums); - - // std::cerr << "finding index not in: " << std::endl; - // if(one) - // for(auto& i: *one) - // std::cerr << i.first << std::endl; - // if(two) - // for(auto& i: *two) - // std::cerr << i.first << std::endl; - // if(three) - // for(auto& i: *three) - // std::cerr << i.first << std::endl; - // if(four) - // for(auto& i: *four) - // std::cerr << i.first << std::endl; - // if(five) - // for(auto& i: *five) - // std::cerr << i.first << std::endl; - - while(pr.first!=pr.second) { - // std::cerr << "trying: " << std::endl; + auto pats_it = kernel.properties.pats_dict.find(typeid(*dums)); + if (pats_it != kernel.properties.pats_dict.end()) { + Properties::pattern_map_t pats = pats_it->second; + std::pair pr=pats.equal_range(dums); + + // std::cerr << "finding index not in: " << std::endl; + // if(one) + // for(auto& i: *one) + // std::cerr << i.first << std::endl; + // if(two) + // for(auto& i: *two) + // std::cerr << i.first << std::endl; + // if(three) + // for(auto& i: *three) + // std::cerr << i.first << std::endl; + // if(four) + // for(auto& i: *four) + // std::cerr << i.first << std::endl; + // if(five) + // for(auto& i: *five) + // std::cerr << i.first << std::endl; + + while(pr.first!=pr.second) { + // std::cerr << "trying: " << std::endl; // std::cerr << pr.first->second->obj << std::endl; - if(pr.first->second->obj.begin()->is_autodeclare_wildcard()) { + if(pr.first->second->obj.begin()->is_autodeclare_wildcard()) { // std::cerr << "is autodeclare wildcard" << std::endl; - std::string base=*pr.first->second->obj.begin()->name_only(); - int used=max_numbered_name(base, one, two, three, four, five); - std::ostringstream str; - str << base << used+1; - // txtout << "going to use " << str.str() << std::endl; - nset_t::iterator newnm=name_set.insert(str.str()).first; - Ex ret; - ret.set_head(str_node(newnm)); - return ret; - } - else { + std::string base=*pr.first->second->obj.begin()->name_only(); + int used=max_numbered_name(base, one, two, three, four, five); + std::ostringstream str; + str << base << used+1; + // txtout << "going to use " << str.str() << std::endl; + nset_t::iterator newnm=name_set.insert(str.str()).first; + Ex ret; + ret.set_head(str_node(newnm)); + return ret; + } + else { // std::cerr << "is NOT autodeclare" << std::endl; - const Ex& inm=(*pr.first).second->obj; - // BUG: even if only _{a} is in the used map, we should not - // accept ^{a}. But since ... - if(index_in_set(inm, one)==false && - index_in_set(inm, two)==false && - index_in_set(inm, three)==false && - index_in_set(inm, four)==false && - index_in_set(inm, five)==false) { - // std::cerr << "ok to use " << inm << std::endl; - return inm; + const Ex& inm=(*pr.first).second->obj; + // BUG: even if only _{a} is in the used map, we should not + // accept ^{a}. But since ... + if(index_in_set(inm, one)==false && + index_in_set(inm, two)==false && + index_in_set(inm, three)==false && + index_in_set(inm, four)==false && + index_in_set(inm, five)==false) { + // std::cerr << "ok to use " << inm << std::endl; + return inm; + } } + ++pr.first; } - ++pr.first; - } + } const Indices *dd=dynamic_cast(dums); assert(dd); diff --git a/core/Props.cc b/core/Props.cc index 5e7088d7ba..806649bbd4 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -26,6 +26,7 @@ along with this program. If not, see . #include #include #include "properties/Indices.hh" +#include // #define DEBUG 1 @@ -147,14 +148,14 @@ bool Properties::has(const property *pb, Ex::iterator it) } // Look for property *pb - pat_prop_typemap_t::iterator pptit = pdit->second.find(typeid(*pb)); + prop_pat_typemap_t::iterator pptit = pdit->second.find(typeid(*pb)); if (pptit == pdit->second.end()) { return false; } - // Check if any pat_prop pair matches - for (const pat_prop_pair_t& pat_prop : pptit->second) { - if (pat_prop.first->match(*this, it)) { + // Check if any prop_pat pair matches + for (const prop_pat_pair_t& prop_pat : pptit->second) { + if (prop_pat.second->match(*this, it)) { return true; } } @@ -170,7 +171,7 @@ void Properties::clear() // property* pointer once. for (const auto& [_, this_pats] : pats_dict) { - pattern_map_t::const_iterator it=this_pats.begin(); + auto it=this_pats.begin(); const property *previous=0; while(it!=this_pats.end()) { if(previous!=it->first) { @@ -358,7 +359,7 @@ std::string property::unnamed_argument() const return ""; } -property::match_t property::equals(const property *) const +list_property::match_t list_property::equals(const property *) const { return exact_match; } @@ -401,26 +402,26 @@ void Properties::insert_prop(const Ex& et, const property *pr) { // Make sure there is no existing property of the same type matching pat auto pdit = props_dict.find(pat->obj.begin()->name_only()); if (pdit != props_dict.end() ) { - auto possible_dups = PropertyFilter(pdit->second, - [pr](const PropertyIterator& pit) { + auto possible_dups = PropertyFilter(&pdit->second, + [pr](const iterator& pit) { return typeid(*pr) == typeid(*(pit->second)); }); - for (PropertyIterator pdit = possible_dups.begin(); pdit != possible_dups.end();) { + for (iterator pdit = possible_dups.begin(); pdit != possible_dups.end();) { // 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 // and are of the same type as pr. - if( pdit->first->match(*this, et.begin())) { + if( pdit->second->match(*this, et.begin())) { // 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(pdit->second); + const labelled_property *lpold = dynamic_cast(pdit->first); 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 = pdit->first; - const property *oldprop = pdit->second; + pattern *oldpat = pdit->second; + const property *oldprop = pdit->first; // 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). @@ -430,10 +431,10 @@ void Properties::insert_prop(const Ex& et, const property *pr) { } // Erase the pattern->property entry, and delete the pattern. - // FIXME: store pattern by value. - PropertyIterator old_pdit = pdit; + // FIXME: store pattern by value. (why?) + iterator old_pdit = pdit; ++pdit; - erase(old_pdit->second, old_pdit->first); + erase(old_pdit->first, old_pdit->second); // Remove the property->pattern entry. Only delete the property // if it is no longer associated to any other pattern. @@ -444,7 +445,8 @@ void Properties::insert_prop(const Ex& et, const property *pr) { // 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. - + + // FIXME: SelfAntiCommuting is not a list property, so above should be fine. } } else { ++pdit; @@ -452,23 +454,15 @@ void Properties::insert_prop(const Ex& et, const property *pr) { } } + register_property_type(pr); // Add to the props_dict - auto pat_prop_pairs = props_dict[pat->obj.begin()->name_only()][typeid(*pr)]; - pat_prop_pairs.emplace_back(pat,pr); - // Keep the pats_vector sorted - std::sort(pat_prop_pairs.begin(), pat_prop_pairs.end(), - [](const pat_prop_pair_t& a, const pat_prop_pair_t& b) { - // Order just on the property pointers - return a.second < b.second; - }); - + props_dict[pat->obj.begin()->name_only()][typeid(*pr)].emplace(pr,pat); // Add to the pats_dict pats_dict[typeid(*pr)].emplace(pr, pat); - } - +/* void Properties::insert_prop_old(const Ex& et, const property *pr) { // assert(pats.find(pr)==pats.end()); // identical properties have to be assigned through insert_list_prop @@ -559,54 +553,75 @@ void Properties::insert_prop_old(const Ex& et, const property *pr) // 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); + + auto pats = pats_dict[typeid(*pr)]; + assert(pats.find(pr)==pats.end()); // identical properties have to be assigned through insert_list_prop + + /* Description of below code + + List properties in Cadabra include (currently) Indices, SortOrder, CommutingBehaviour (and derived classes). + Calling `equals` on another list property will yield `no_match`, `id_match`, or `exact_match`. + Upon insertion of a list property, we look for existing properties of the same type. + + If we find one of the same type and `equals` returns `exact_match`, we discard (and delete) + the list_property pointer. This ensures e.g. that there is only one + Indices property with the label "vector". The code the continues, as if this property was the + one passed to `insert_list_prop`. + + If we find a list property that returns `id_match`, we delete that older list property. + This happens e.g. if we have Indices(vector, position=free) and then create + Indices(vector, position=fixed). This invalidates all the prior free vector indices. + + If `no_match`, we continue as normal. + + The is the current state of affairs (except `equals` was declared in `property` rather than `list_property`.) + + However, a problem arises (as discussed below). In the older code, the following failed: + {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. + Instead, both sets were kept. + Similarly, + {a,b,c}::Indices(vector). + {a,b,c,d,e,f}::Indices(spinor). + This should make all indices spinor indices. + Instead, both sets were kept. + + Eventually we should fix this. It is somewhat subtle as it requires we do something else. + A fourth match type is needed that would indicate we need to look at the properties and + eliminate incompatible types. + + */ // 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(); + // Alternatively, if 'pr' has id_match with an existing property, we need to remove all property assignments + // for the existing one. + auto 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) { - dict_erase_(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; - } + // if(typeid(*tmp)==typeid(*pr)) // unnecessary now + list_property::match_t match_type = pr->equals((*pit).first); + if (match_type == list_property::exact_match) { + delete pr; + pr=static_cast( (*pit).first ); + break; + } else if (match_type == list_property::id_match) { + erase((*pit).first); + break; } - - + ++pit; + } + // Now register the list property. + register_property_type(pr); - for(unsigned int i=0; i& its, const list_propert // 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))); - dict_insert_(pr, pat); + + props_dict[pat->obj.begin()->name_only()][typeid(*pr)].emplace(pr, pat); + pats.emplace(pr, pat); } } @@ -660,10 +675,11 @@ void Properties::insert_list_prop(const std::vector& its, const list_propert int Properties::serial_number(const property *listprop, const pattern *pat) const { int serialnum=0; - - std::pair - pm=pats.equal_range(listprop); - serialnum=0; + auto it = pats_dict.find(typeid(*listprop)); + if (it == pats_dict.end()) { + return serialnum; + } + auto pm=it->second.equal_range(listprop); while(pm.first!=pm.second) { if((*pm.first).second==pat) break; @@ -793,42 +809,71 @@ void Properties::destroy_comparator(Ex_comparator *c) const // Completely erase a property. void Properties::erase(const property* p) { + // Make a list of matching patterns + auto pad_it = pats_dict.find(typeid(*p)); + if (pad_it == pats_dict.end()) + return; + + // FIXME: Above should perhaps throw an exception? Property not found or some such. + + std::vector pattern_names; + auto patterns = pad_it->second.equal_range(p); + for (auto it = patterns.first; it != patterns.second; ++it) { + // Store all the pattern names + pattern_names.push_back(it->second->obj.begin()->name_only()); + // Free all the patterns + delete it->second; + } + // Delete all the entries in pats_dict + pad_it->second.erase(p); + // Delete all the entries in props_dict + for (auto pattern_name : pattern_names) { + props_dict[pattern_name][typeid(*p)].erase(p); + } + + delete p; } // Erase a property and related pattern. void Properties::erase(const property* prop, pattern* pat) { - // 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. - - std::type_index type_idx = typeid(*prop); auto name = pat->obj.begin()->name_only(); + // Eliminate from pats_dict + auto pad_it = pats_dict.find(typeid(*prop)); + if (pad_it != pats_dict.end()) { + auto prop_pat_pairs = pad_it->second; + for (auto pairs_it = prop_pat_pairs.begin(); pairs_it != prop_pat_pairs.end(); ) { + if (pairs_it->first == prop && pairs_it->second == pat) { + pairs_it = prop_pat_pairs.erase(pairs_it); + } else { + ++pairs_it; + } + } + } + // Eliminate from props_dict auto pdit = props_dict.find(name); if (pdit != props_dict.end()) { auto ppit = pdit->second.find(typeid(*prop)); if (ppit != pdit->second.end()) { - auto pat_prop_pairs = ppit->second; - for (auto pairs_it = pat_prop_pairs.begin(); pairs_it != pat_prop_pairs.end(); ) { - if (pairs_it->first == pat && pairs_it->second == prop) { - pairs_it = pat_prop_pairs.erase(pairs_it); + auto prop_pat_pairs = ppit->second; + for (auto pairs_it = prop_pat_pairs.begin(); pairs_it != prop_pat_pairs.end(); ) { + if (pairs_it->first == prop && pairs_it->second == pat) { + pairs_it = prop_pat_pairs.erase(pairs_it); } else { ++pairs_it; } } + // Does prop have any more patterns associated with it? + if (prop_pat_pairs.find(prop) == prop_pat_pairs.end()) { + delete prop; + } + } } - // - + delete pat; } diff --git a/core/Props.hh b/core/Props.hh index c0027b2367..33c647e1af 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -30,7 +30,7 @@ along with this program. If not, see . #include #include #include -#include +#include namespace cadabra { @@ -170,15 +170,6 @@ namespace cadabra { virtual std::string name() const=0; virtual std::string unnamed_argument() const; - // To compare properties we sometimes need to compare their variables, not only - // their type. The following function needs to be overridden in all properties - // for which comparison by type is not sufficient to establish equality. - // - // id_match: only one of these properties can be registered, but their data is not the same - // exact_match: these properties are exactly identical - enum match_t { no_match, id_match, exact_match }; - virtual match_t equals(const property *) const; - /// Properties can be hidden because they only make sense to the /// system; they will not be printed when the user asks for a list /// of properties. @@ -201,6 +192,14 @@ namespace cadabra { class list_property : public property { public: + // To compare list properties we sometimes need to compare their variables, not only + // their type. The following function needs to be overridden in all properties + // for which comparison by type is not sufficient to establish equality. + // + // id_match: only one of these properties can be registered, but their data is not the same + // exact_match: these properties are exactly identical + enum match_t { no_match, id_match, exact_match }; + virtual match_t equals(const property *) const; }; /// If a property X derives from Inherit, and get is called on @@ -283,7 +282,8 @@ namespace cadabra { void register_property(property* (*)(), const std::string& name); registered_property_map_t registered_properties; - typedef std::pair pat_prop_pair_t; + // typedef std::pair pat_prop_pair_t; + typedef std::pair prop_pat_pair_t; // Register a type by template. Usage: `register_property_type();` // Just calls `registered_properties.register_type()` @@ -302,8 +302,9 @@ namespace cadabra { /// /// When we delete properties, we check the pats map to see if the reference count /// for that property has dropped to zero. - typedef std::multimap property_map_t; - typedef std::multimap pattern_map_t; + + // typedef std::multimap property_map_t; + // typedef std::multimap pattern_map_t; /// Register a property for the indicated Ex. Takes both normal and list /// properties and works out which insert calls to make. The property ownership @@ -316,38 +317,33 @@ namespace cadabra { /// in them; use clear() to clean up. Note that pointers can sit in in more than one /// entry in this map (when they are pointing to list_property objects, which are /// shared between patterns). - property_map_t props; // pattern -> property - pattern_map_t pats; // property -> pattern; for list properties, patterns are stored here in order - + // property_map_t props; // pattern -> property + // pattern_map_t pats; // property -> pattern; for list properties, patterns are stored here in order /************************************************************************************** - * BELOW REVISES EXISTING PROPS/PATS STRUCTURE. + * BELOW REVISES PROPS/PATS STRUCTURE. * * The original props/pats maps can be inefficient because accessing requires dynamic casts, - * and the number of such casts can quickly become huge. The below is intended as - * a replacement for props/pats by including information about the property_type directly. - * Currently it works side-by-side with props/parts, duplicating the work - * if/until we replace props/pats entirely with these. + * and the number of such casts can quickly become huge. Moreover, searches (in get) involve + * comparing all properties against all properties, which can be slow when there are many + * properties (and get is called many times). + * + * The below is intended as a replacement for props/pats by including information about the + * property_type directly. * - * props_dict groups the pat_prop_pairs in a multimap by their property_type - * pats_dict groups the properties in pats by their property_type. This allows us to - * iterate over e.g. all AntiCommuting patterns directly. **************************************************************************************/ - class pat_prop_less { - public: - bool operator()(const pat_prop_pair_t&, const pat_prop_pair_t&); - }; + typedef std::multimap pattern_map_t; + typedef boost::container::flat_multimap prop_pat_map_t; - typedef boost::container::flat_set pat_props_t; - typedef std::map pat_prop_typemap_t; - typedef std::map property_dictmap_t; + typedef std::map prop_pat_typemap_t; + typedef std::map property_dictmap_t; typedef std::map> pattern_dictmap_t; - property_dictmap_t props_dict; // pattern -> map(property_type, pat_prop_pairs) - pattern_dictmap_t pats_dict; // property_type -> multimap(property, pattern) + property_dictmap_t props_dict; + pattern_dictmap_t pats_dict; /// Determine if obj of type_index obj_type is castable to type T - template bool is_castable(std::type_index obj_type, U obj); + template bool is_castable(std::type_index obj_type, const U* obj) const; /// Normal search: given a pattern, get its property if any. template const T* get(Ex::iterator, bool ignore_parent_rel=false) const; @@ -391,8 +387,9 @@ namespace cadabra { // Equivalent search: given a node, get a pattern of equivalents. // property_map_t::iterator get_equivalent(Ex::iterator, // property_map_t::iterator=props.begin()); - class PropertyIterator; - class PropertyFilter; + + class iterator; + class const_iterator; /// Erases property completely. void erase(const property*); @@ -415,47 +412,58 @@ namespace cadabra { Ex_comparator *create_comparator() const; void destroy_comparator(Ex_comparator *) const; - std::map, bool> castable_table; + // mutable because castable_table may be modified by get calls + mutable std::map, bool> castable_table; // Helper functions for use with Propery Filter - + /* template bool helper_castable(const PropertyIterator& pit) { - return is_castable(pit.proptype(), pit->second); + return is_castable(pit.proptype(), pit->first); } template bool helper_heritable(const PropertyIterator& pit) { - return is_castable>(pit.proptype(), pit->second) || is_castable(pit.proptype(), pit->second); + return is_castable>(pit.proptype(), pit->first) || is_castable(pit.proptype(), pit->first); } - + */ public: - /// Specialized property iterator for iterating over properties in a pat_prop_typemap_t that match a condition. - class PropertyIterator { + /// Specialized property iterator for iterating over properties in a prop_pat_typemap_t that match a condition. + + class iterator; // forward declaration + class const_iterator { using iterator_category = std::bidirectional_iterator_tag; using difference_type = std::ptrdiff_t; - using value_type = pat_prop_pair_t; + using value_type = const prop_pat_pair_t; using pointer = value_type*; using reference = value_type&; - using InnerIter = typename std::vector::iterator; - using OuterIter = typename pat_prop_typemap_t::iterator; + using InnerIter = prop_pat_map_t::const_iterator; + using OuterIter = prop_pat_typemap_t::const_iterator; public: - PropertyIterator(pat_prop_typemap_t& m, std::function condition, bool is_end = false) : typemap_(m), condition_(condition) { + const_iterator(const prop_pat_typemap_t* m, std::function condition, bool is_end = false) : typemap_(m), condition_(condition) { if (is_end) { - outer_it_ = typemap_.end(); + outer_it_ = typemap_->end(); inner_it_ = InnerIter(); } else { - outer_it_ = typemap_.begin(); + outer_it_ = typemap_->begin(); skip_ahead(); } } - - PropertyIterator& operator++() { + /* + // Convert Properties::iterator to Properties::const_iterator + const_iterator(iterator it) { + typemap_ = it.typemap_; + condition_ = it.condition_; + inner_it_ = it.inner_it_; + outer_it_ = it.outer_it_; + } + */ + const_iterator& operator++() { ++inner_it_; if (inner_it_ == outer_it_->second.end()) { ++outer_it_; @@ -464,8 +472,8 @@ namespace cadabra { return *this; } - PropertyIterator& operator--() { - if (outer_it_ == typemap_.end()) { // End() to last element + const_iterator& operator--() { + if (outer_it_ == typemap_->end()) { // End() to last element --outer_it_; skip_back(); } @@ -478,12 +486,12 @@ namespace cadabra { } /// Advance the iterator to the next property type - PropertyIterator& next_proptype() { + const_iterator& next_proptype() { ++outer_it_; - while (outer_it_ != typemap_.end()) { + while (outer_it_ != typemap_->end()) { if (condition_(*this)) { inner_it_ = outer_it_->second.begin(); - if (inner_it_ != outer_it_->second.end()) return; + if (inner_it_ != outer_it_->second.end()) return *this; } ++outer_it_; } @@ -493,33 +501,34 @@ namespace cadabra { std::type_index proptype() const { return outer_it_->first; } - /// Return the current vector (no copy) of pat_prop_pairs - std::vector& pat_prop_pairs() const { + /// Return the current prop_pat_map + const prop_pat_map_t& prop_pat_pairs() const { return outer_it_->second; } reference operator*() const { return *inner_it_; } pointer operator->() const { return &(*inner_it_); } - bool operator==(const PropertyIterator& other) const { + bool operator==(const const_iterator& other) const { return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; } - bool operator!=(const PropertyIterator& other) const { + bool operator!=(const const_iterator& other) const { return !(*this == other); } private: - pat_prop_typemap_t& typemap_; - std::function condition_; + const prop_pat_typemap_t* typemap_; + std::function condition_; OuterIter outer_it_; InnerIter inner_it_; // Skip ahead to the next property class if needed void skip_ahead() { - while (outer_it_ != typemap_.end()) { + while (outer_it_ != typemap_->end()) { + inner_it_ = outer_it_->second.begin(); if (condition_(*this)) { - inner_it_ = outer_it_->second.begin(); + // inner_it_ = outer_it_->second.begin(); if (inner_it_ != outer_it_->second.end()) return; } ++outer_it_; @@ -530,9 +539,10 @@ namespace cadabra { // Skip back to the previous class if needed void skip_back() { - while (outer_it_ != typemap_.begin()) { + while (outer_it_ != typemap_->begin()) { + inner_it_ = outer_it_->second.end(); if (condition_(*this)) { - inner_it_ = outer_it_->second.end(); + // inner_it_ = outer_it_->second.end(); if (inner_it_ != outer_it_->second.begin()) { --inner_it_; return; @@ -541,7 +551,7 @@ namespace cadabra { --outer_it_; } - // assert(outer_it_ == typemap_.begin()) + // assert(outer_it_ == typemap_->begin()) if (condition_(*this)) { inner_it_ = outer_it_->second.end(); if (inner_it_ != outer_it_->second.begin()) { @@ -553,44 +563,162 @@ namespace cadabra { } // assert(inner_it_ == outer_it_->second.begin()) } + }; + class iterator { - }; + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = prop_pat_pair_t; + using pointer = value_type*; + using reference = value_type&; - private: + using InnerIter = prop_pat_map_t::iterator; + using OuterIter = prop_pat_typemap_t::iterator; + // friend class const_iterator; + + public: + iterator(prop_pat_typemap_t* m, std::function condition, bool is_end = false) : typemap_(m), condition_(condition) { + if (is_end) { + outer_it_ = typemap_->end(); + inner_it_ = InnerIter(); + } else { + outer_it_ = typemap_->begin(); + skip_ahead(); + } + } + + iterator& operator++() { + ++inner_it_; + if (inner_it_ == outer_it_->second.end()) { + ++outer_it_; + skip_ahead(); + } + return *this; + } + iterator& operator--() { + if (outer_it_ == typemap_->end()) { // End() to last element + --outer_it_; + skip_back(); + } + if (inner_it_ == outer_it_->second.begin()) { + --outer_it_; + skip_back(); + } + --inner_it_; + return *this; + } + + /// Advance the iterator to the next property type + iterator& next_proptype() { + ++outer_it_; + while (outer_it_ != typemap_->end()) { + if (condition_(*this)) { + inner_it_ = outer_it_->second.begin(); + if (inner_it_ != outer_it_->second.end()) return *this; + } + ++outer_it_; + } + return *this; + } + /// Return the current property type + std::type_index proptype() const { + return outer_it_->first; + } + /// Return the current prop_pat_map + const prop_pat_map_t& prop_pat_pairs() const { + return outer_it_->second; + } + + reference operator*() const { return *inner_it_; } + pointer operator->() const { return &(*inner_it_); } + + bool operator==(const iterator& other) const { + return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; + } + + bool operator!=(const iterator& other) const { + return !(*this == other); + } + + private: + prop_pat_typemap_t* typemap_; + std::function condition_; + OuterIter outer_it_; + InnerIter inner_it_; + + // Skip ahead to the next property class if needed + void skip_ahead() { + while (outer_it_ != typemap_->end()) { + if (condition_(*this)) { + inner_it_ = outer_it_->second.begin(); + if (inner_it_ != outer_it_->second.end()) return; + } + ++outer_it_; + } + // At end, so inner_it_ is blank. + inner_it_ = InnerIter(); + } + + // Skip back to the previous class if needed + void skip_back() { + while (outer_it_ != typemap_->begin()) { + if (condition_(*this)) { + inner_it_ = outer_it_->second.end(); + if (inner_it_ != outer_it_->second.begin()) { + --inner_it_; + return; + } + } + --outer_it_; + } + + // assert(outer_it_ == typemap_->begin()) + if (condition_(*this)) { + inner_it_ = outer_it_->second.end(); + if (inner_it_ != outer_it_->second.begin()) { + --inner_it_; + return; + } + } else { + inner_it_ = outer_it_->second.begin(); + } + // assert(inner_it_ == outer_it_->second.begin()) + } + }; + /// Specialized property filter. Use to build PropertyIterators that satisfy a condition. + template class PropertyFilter { public: - PropertyFilter(pat_prop_typemap_t& m) + PropertyFilter(Map* m) : typemap_(m), - condition_([](const PropertyIterator&) {return true;}), - begin_(typemap_, condition_, /*is_end=*/false), - end_(typemap_, condition_, /*is_end=*/true) - {} - PropertyFilter(pat_prop_typemap_t& m, std::function condition) + condition_([](const It&) {return true;}) + { + } + PropertyFilter(Map* m, std::function condition) : typemap_(m), condition_(condition), begin_(typemap_, condition_, /*is_end=*/false), end_(typemap_, condition_, /*is_end=*/true) {} - PropertyIterator begin() const { + It begin() const { return begin_; } - PropertyIterator end() const { + It end() const { return end_; } private: - pat_prop_typemap_t& typemap_; - std::function condition_; - PropertyIterator begin_; - PropertyIterator end_; + Map* typemap_; + std::function condition_; + It begin_; + It end_; }; - }; template @@ -623,7 +751,13 @@ namespace cadabra { int& serialnum, const std::string& label, bool doserial, bool ignore_parent_rel) const { - + auto helper_heritable = [this](const const_iterator& pit) { + return is_castable>(pit.proptype(), pit->first) || is_castable(pit.proptype(), pit->first); + }; + auto helper_castable = [this](const const_iterator& pit) { + return is_castable(pit.proptype(), pit->first); + }; + std::pair ret = {nullptr, nullptr}; bool inherits = false; @@ -647,27 +781,44 @@ namespace cadabra { if(std::is_same::value) ignore_properties=true; + auto props_dict_it = props_dict.find(it->name_only()); if (props_dict_it != props_dict.end()) { - auto heritable_props = PropertyFilter(props_dict_it->second, &helper_heritable); + auto heritable_props = PropertyFilter(&props_dict_it->second, + helper_heritable + // static_cast(&helper_heritable) + /* + ([this](const const_iterator& pit) { + return is_castable>(pit.proptype(), pit->first) || is_castable(pit.proptype(), pit->first); + }) + */ + ); inherits = (heritable_props.begin() != heritable_props.end()); // Filter all properties that are castable to T. - auto castable_props = PropertyFilter(props_dict_it->second, &helper_castable); + auto castable_props = PropertyFilter(&props_dict_it->second, + helper_castable + // static_cast(&helper_castable) + /* + ([this](const const_iterator& pit) { + return is_castable(pit.proptype(), pit->first); + }) + */ + ); // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true for(;;) { - for (const pat_prop_pair_t& pat_prop : castable_props) { - if(wildcards==pat_prop.first->children_wildcard()) { - ret.first=dynamic_cast(pat_prop.second); + for (const prop_pat_pair_t& prop_pat : castable_props) { + if(wildcards==prop_pat.second->children_wildcard()) { + ret.first=dynamic_cast(prop_pat.first); if(ret.first) { - if(pat_prop.first->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { - ret.second=pat_prop.first; + if(prop_pat.second->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { + ret.second=prop_pat.second; if(!check_label(ret.first, label)) ret.first=0; else { if(doserial) - serialnum=serial_number( pat_prop.second, pat_prop.first); + serialnum=serial_number( prop_pat.first, prop_pat.second); break; } } @@ -739,15 +890,24 @@ namespace cadabra { if (props_dict_it1 != props_dict.end() && props_dict_it2 != props_dict.end()) { // Are any of these properties heritable? // FIXME: Below just uses PropertyInherit and not Inherit? - auto heritable_props = PropertyFilter(props_dict_it1->second, &helper_castable); + auto heritable_props = PropertyFilter(&props_dict_it1->second, + // static_cast(&helper_castable) ); + ([this](const const_iterator& pit) {return is_castable(pit.proptype(), pit->first);})); inherits1 = (heritable_props.begin() != heritable_props.end()); - heritable_props = PropertyFilter(props_dict_it2->second, &helper_castable); + heritable_props = PropertyFilter(&props_dict_it2->second, + // static_cast(&helper_castable) ); + ([this](const const_iterator& pit) {return is_castable(pit.proptype(), pit->first);})); inherits2 = (heritable_props.begin() != heritable_props.end()); // Find all castable properties that appear in both props_dict_t1->second and props_dict_t2->second - auto castable_props1 = PropertyFilter(props_dict_it1->second, &helper_castable); - auto castable_props2 = PropertyFilter(props_dict_it2->second, &helper_castable); + auto castable_props1 = PropertyFilter(&props_dict_it1->second, + // static_cast(&helper_castable) ); + ([this](const const_iterator& pit) {return is_castable(pit.proptype(), pit->first);}) ); + auto castable_props2 = PropertyFilter(&props_dict_it2->second, + // static_cast(&helper_castable) ); + ([this](const const_iterator& pit) {return is_castable(pit.proptype(), pit->first);})); + // Search for properties that appear in both pit1 and pit2 for (auto pit1 = castable_props1.begin(), pit2 = castable_props2.begin(); @@ -764,30 +924,28 @@ namespace cadabra { // it1 and it2 are of the same property type // Get the pat_prop_pairs for each - auto& pat_props1 = pit1.pat_prop_pairs(); - auto& pat_props2 = pit2.pat_prop_pairs(); + auto& prop_pats1 = pit1.prop_pat_pairs(); + auto& prop_pats2 = pit2.prop_pat_pairs(); - match_t match1 = match_t::unknown; - match_t match2 = match_t::unknown; bool match1_found = false; bool match2_found = false; // By construction, the pat_props are always sorted by property pointer. // This allows the iteration below to be as fast as possible. - for (auto ppit1 = pat_props1.begin(), ppit2 = pat_props2.begin(); - ppit1 != pat_props1.end() && ppit2 != pat_props2.end(); ) + for (auto ppit1 = prop_pats1.begin(), ppit2 = prop_pats2.begin(); + ppit1 != prop_pats1.end() && ppit2 != prop_pats2.end(); ) { // Advance both iterators until their properties match - if (ppit1->second < ppit2->second) { + if (ppit1->first < ppit2->first) { ++ppit1; match1_found = false; - } else if (ppit2->second < ppit1->second) { + } else if (ppit2->first < ppit1->first) { ++ppit2; match2_found = false; } else { // The properties match, so see if there is a pattern match if (!match1_found) { - if (ppit1->first->match(*this, it1, ignore_parent_rel)) { + if (ppit1->second->match(*this, it1, ignore_parent_rel)) { match1_found = true; } else { ++ppit1; @@ -796,7 +954,7 @@ namespace cadabra { } } if (!match2_found) { - if (ppit2->first->match(*this, it2, ignore_parent_rel)) { + if (ppit2->second->match(*this, it2, ignore_parent_rel)) { match2_found = true; } else { ++ppit2; @@ -807,11 +965,11 @@ namespace cadabra { // assert(match1_found && match2_found) // accept if properties are the same and patterns are not - if ((ppit1->second == ppit2->second) && (ppit1->first != ppit2->first)) { - serialnum1=serial_number( ppit1->second, ppit1->first ); - serialnum2=serial_number( ppit2->second, ppit2->first ); + if ((ppit1->first == ppit2->first) && (ppit1->second != ppit2->second)) { + serialnum1=serial_number( ppit1->first, ppit1->second ); + serialnum2=serial_number( ppit2->first, ppit2->second ); // Return the recasted property - return dynamic_cast( ppit1->second ); + return dynamic_cast( ppit1->first ); } } } @@ -864,15 +1022,17 @@ namespace cadabra { template - bool Properties::is_castable(std::type_index obj_type, U obj) { + bool Properties::is_castable(std::type_index obj_type, const U* obj) const { auto it = castable_table.find(std::make_pair(std::type_index(typeid(T)), obj_type)); if (it != castable_table.end()) { return it->second; } if (dynamic_cast(obj)) { - castable_table.insert(std::make_pair(std::type_index(typeid(T)), obj_type), true); + castable_table.emplace(std::make_pair(std::type_index(typeid(T)), obj_type), true); + return true; } else { - castable_table.insert(std::make_pair(std::type_index(typeid(T)), obj_type), false); + castable_table.emplace(std::make_pair(std::type_index(typeid(T)), obj_type), false); + return false; } } diff --git a/core/algorithms/rename_dummies.cc b/core/algorithms/rename_dummies.cc index 16d79fd9f6..df50c57bb0 100644 --- a/core/algorithms/rename_dummies.cc +++ b/core/algorithms/rename_dummies.cc @@ -84,16 +84,20 @@ Algorithm::result_t rename_dummies::apply(iterator& st) // with this name. const Indices *ind2=0; if(dset2!="") { - auto f2=kernel.properties.pats.begin(); - while(f2!=kernel.properties.pats.end()) { - ind2 = dynamic_cast(f2->first); - if(ind2) { - if(ind2->set_name==dset2) - break; - else ind2=0; + auto pats_it = kernel.properties.pats_dict.find(typeid(Indices)); + if (pats_it != kernel.properties.pats_dict.end()) { + auto pats = pats_it->second; + auto f2=pats.begin(); + while(f2!=pats.end()) { + ind2 = dynamic_cast(f2->first); + if(ind2) { + if(ind2->set_name==dset2) + break; + else ind2=0; + } + ++f2; } - ++f2; - } + } if(ind2==0) throw ConsistencyException("No index set with name `"+dset2+"' known."); } diff --git a/core/cadabra2_defaults.py.in b/core/cadabra2_defaults.py.in index eccb76d03f..ef654a2041 100644 --- a/core/cadabra2_defaults.py.in +++ b/core/cadabra2_defaults.py.in @@ -550,6 +550,7 @@ class Console(object): """ def log(self, *objs): """Sends a string representation of objs to the console""" + cell_id = 0 if server.architecture() == "terminal": print(*objs) elif server.architecture() == "client-server": @@ -557,10 +558,12 @@ class Console(object): def clear(self): """Clears the output of the console window""" + cell_id = 0 if server.architecture() == "client-server": server.send("", "csl_clear", 0, cell_id, False) def warn(self, msg): + cell_id = 0 if server.architecture() == "terminal": print("Warning: " + msg) elif server.architecture() == "client-server": diff --git a/core/properties/CommutingBehaviour.cc b/core/properties/CommutingBehaviour.cc index 28acdb30af..bbb7d76ce4 100644 --- a/core/properties/CommutingBehaviour.cc +++ b/core/properties/CommutingBehaviour.cc @@ -3,7 +3,7 @@ using namespace cadabra; -property::match_t CommutingBehaviour::equals(const property *) const +list_property::match_t CommutingBehaviour::equals(const property *) const { return no_match; // you can have as many of these as you like } diff --git a/core/properties/Indices.cc b/core/properties/Indices.cc index 5b15b64497..411387bc08 100644 --- a/core/properties/Indices.cc +++ b/core/properties/Indices.cc @@ -20,7 +20,7 @@ std::string Indices::name() const return "Indices"; } -property::match_t Indices::equals(const property *other) const +list_property::match_t Indices::equals(const property *other) const { const Indices *cast_other = dynamic_cast(other); if(cast_other) { @@ -32,7 +32,7 @@ property::match_t Indices::equals(const property *other) const } return no_match; } - return property::equals(other); + return list_property::equals(other); } bool Indices::parse(Kernel& kernel, std::shared_ptr ex, keyval_t& keyvals) diff --git a/core/properties/SortOrder.cc b/core/properties/SortOrder.cc index 5b12ecf584..b66b7e2e57 100644 --- a/core/properties/SortOrder.cc +++ b/core/properties/SortOrder.cc @@ -3,7 +3,7 @@ using namespace cadabra; -property::match_t SortOrder::equals(const property *) const +list_property::match_t SortOrder::equals(const property *) const { return no_match; // you can have as many of these as you like } diff --git a/core/pythoncdb/py_properties.cc b/core/pythoncdb/py_properties.cc index e725942284..44c12f28c0 100644 --- a/core/pythoncdb/py_properties.cc +++ b/core/pythoncdb/py_properties.cc @@ -284,6 +284,7 @@ namespace cadabra { pybind11::list ret; std::string res; bool multi = false; + /* for (auto it = props.pats.begin(); it != props.pats.end(); ++it) { if (it->first->hidden()) continue; @@ -324,14 +325,15 @@ namespace cadabra { res += ", "; } } - + */ return ret; } std::vector indices_get_all(const Indices* indices, bool include_wildcards) { auto kernel = get_kernel_from_scope(); - auto its = kernel->properties.pats.equal_range(indices); + // auto its = kernel->properties.pats.equal_range(indices); + auto its = kernel->properties.pats_dict[typeid(*indices)].equal_range(indices); std::vector res; for (auto it = its.first; it != its.second; ++it) { From f1cffd001e3e6325ca49d6ffbc2dfada87a8191c Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:21:04 -0400 Subject: [PATCH 06/18] Further enhancements. --- core/Compare.cc | 2 +- core/IndexClassifier.cc | 3 +- core/Props.cc | 2 +- core/Props.hh | 344 ++++++++++---------------------- core/pythoncdb/py_ex.cc | 1 + core/pythoncdb/py_properties.cc | 152 ++++++++++---- core/pythoncdb/py_properties.hh | 46 ++++- 7 files changed, 269 insertions(+), 281 deletions(-) diff --git a/core/Compare.cc b/core/Compare.cc index 41fddff3cd..8d08976a68 100644 --- a/core/Compare.cc +++ b/core/Compare.cc @@ -320,7 +320,7 @@ namespace cadabra { // // if(!i1.is_valid() && !i2.is_valid()) // return match_t::subtree_match; - + DEBUGLN( std::cerr << tab() << "equal_subtree with use_props = " << use_props << std::endl; ); ++offset; diff --git a/core/IndexClassifier.cc b/core/IndexClassifier.cc index 8703ceab85..3f67906cb6 100644 --- a/core/IndexClassifier.cc +++ b/core/IndexClassifier.cc @@ -490,7 +490,8 @@ Ex IndexClassifier::get_dummy(const list_property *dums, const index_map_t * four, const index_map_t * five) const { - auto pats_it = kernel.properties.pats_dict.find(typeid(*dums)); + std::type_index type_idx = std::type_index(typeid(*dums)); + auto pats_it = kernel.properties.pats_dict.find(type_idx); if (pats_it != kernel.properties.pats_dict.end()) { Properties::pattern_map_t pats = pats_it->second; std::pair pr=pats.equal_range(dums); diff --git a/core/Props.cc b/core/Props.cc index 806649bbd4..ee20c8416b 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -559,7 +559,7 @@ void Properties::insert_list_prop(const std::vector& its, const list_propert { assert(its.size()>0); - auto pats = pats_dict[typeid(*pr)]; + auto& pats = pats_dict[typeid(*pr)]; assert(pats.find(pr)==pats.end()); // identical properties have to be assigned through insert_list_prop /* Description of below code diff --git a/core/Props.hh b/core/Props.hh index 33c647e1af..b25836acb6 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -283,7 +283,7 @@ namespace cadabra { registered_property_map_t registered_properties; // typedef std::pair pat_prop_pair_t; - typedef std::pair prop_pat_pair_t; + typedef std::pair prop_pat_pair_t; // Register a type by template. Usage: `register_property_type();` // Just calls `registered_properties.register_type()` @@ -333,7 +333,8 @@ namespace cadabra { * **************************************************************************************/ typedef std::multimap pattern_map_t; - typedef boost::container::flat_multimap prop_pat_map_t; + // typedef boost::container::flat_multimap prop_pat_map_t; + typedef std::multimap prop_pat_map_t; typedef std::map prop_pat_typemap_t; typedef std::map property_dictmap_t; @@ -388,9 +389,6 @@ namespace cadabra { // property_map_t::iterator get_equivalent(Ex::iterator, // property_map_t::iterator=props.begin()); - class iterator; - class const_iterator; - /// Erases property completely. void erase(const property*); /// Erases pattern from a given property, leaving other patterns alone. @@ -430,263 +428,122 @@ namespace cadabra { public: - /// Specialized property iterator for iterating over properties in a prop_pat_typemap_t that match a condition. - - class iterator; // forward declaration - class const_iterator { + // Need specialized iterator and const_iterator classes. These are built from iterator_base below. + template + class iterator_base { + public: + using map_type = std::conditional_t; + using outer_iterator = std::conditional_t; + using inner_iterator = std::conditional_t; using iterator_category = std::bidirectional_iterator_tag; using difference_type = std::ptrdiff_t; - using value_type = const prop_pat_pair_t; - using pointer = value_type*; - using reference = value_type&; - - using InnerIter = prop_pat_map_t::const_iterator; - using OuterIter = prop_pat_typemap_t::const_iterator; - - public: - const_iterator(const prop_pat_typemap_t* m, std::function condition, bool is_end = false) : typemap_(m), condition_(condition) { - if (is_end) { - outer_it_ = typemap_->end(); - inner_it_ = InnerIter(); - } else { - outer_it_ = typemap_->begin(); - skip_ahead(); - } - } - /* - // Convert Properties::iterator to Properties::const_iterator - const_iterator(iterator it) { - typemap_ = it.typemap_; - condition_ = it.condition_; - inner_it_ = it.inner_it_; - outer_it_ = it.outer_it_; - } - */ - const_iterator& operator++() { - ++inner_it_; - if (inner_it_ == outer_it_->second.end()) { - ++outer_it_; - skip_ahead(); - } - return *this; - } - - const_iterator& operator--() { - if (outer_it_ == typemap_->end()) { // End() to last element - --outer_it_; - skip_back(); - } - if (inner_it_ == outer_it_->second.begin()) { - --outer_it_; - skip_back(); - } - --inner_it_; - return *this; + using value_type = std::conditional_t; + using pointer = value_type*; + using reference = value_type&; + + using condition_t = std::function&)>; + + iterator_base(map_type* m, condition_t cond, bool is_end = false) + : typemap_(m), condition_(std::move(cond)) { + if (is_end) { + outer_it_ = typemap_->end(); + inner_it_ = inner_iterator(); + } else { + outer_it_ = typemap_->begin(); + skip_ahead(); } + } - /// Advance the iterator to the next property type - const_iterator& next_proptype() { + iterator_base& operator++() { + ++inner_it_; + if (inner_it_ == outer_it_->second.end()) { ++outer_it_; - while (outer_it_ != typemap_->end()) { - if (condition_(*this)) { - inner_it_ = outer_it_->second.begin(); - if (inner_it_ != outer_it_->second.end()) return *this; - } - ++outer_it_; - } - return *this; + skip_ahead(); } - /// Return the current property type - std::type_index proptype() const { - return outer_it_->first; - } - /// Return the current prop_pat_map - const prop_pat_map_t& prop_pat_pairs() const { - return outer_it_->second; - } - - reference operator*() const { return *inner_it_; } - pointer operator->() const { return &(*inner_it_); } + return *this; + } - bool operator==(const const_iterator& other) const { - return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; + iterator_base& operator--() { + if (outer_it_ == typemap_->end()) { + --outer_it_; + skip_back(); } - - bool operator!=(const const_iterator& other) const { - return !(*this == other); + if (inner_it_ == outer_it_->second.begin()) { + --outer_it_; + skip_back(); } + --inner_it_; + return *this; + } - private: - const prop_pat_typemap_t* typemap_; - std::function condition_; - OuterIter outer_it_; - InnerIter inner_it_; - - // Skip ahead to the next property class if needed - void skip_ahead() { - while (outer_it_ != typemap_->end()) { - inner_it_ = outer_it_->second.begin(); - if (condition_(*this)) { - // inner_it_ = outer_it_->second.begin(); - if (inner_it_ != outer_it_->second.end()) return; - } - ++outer_it_; - } - // At end, so inner_it_ is blank. - inner_it_ = InnerIter(); - } - - // Skip back to the previous class if needed - void skip_back() { - while (outer_it_ != typemap_->begin()) { - inner_it_ = outer_it_->second.end(); - if (condition_(*this)) { - // inner_it_ = outer_it_->second.end(); - if (inner_it_ != outer_it_->second.begin()) { - --inner_it_; - return; - } - } - --outer_it_; - } + iterator_base& next_proptype() { + ++outer_it_; + skip_ahead(); + return *this; + } - // assert(outer_it_ == typemap_->begin()) - if (condition_(*this)) { - inner_it_ = outer_it_->second.end(); - if (inner_it_ != outer_it_->second.begin()) { - --inner_it_; - return; - } - } else { - inner_it_ = outer_it_->second.begin(); - } - // assert(inner_it_ == outer_it_->second.begin()) - } - }; + std::type_index proptype() const { + return outer_it_->first; + } - class iterator { + const prop_pat_map_t& prop_pat_pairs() const { + return outer_it_->second; + } - using iterator_category = std::bidirectional_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = prop_pat_pair_t; - using pointer = value_type*; - using reference = value_type&; + reference operator*() const { return *inner_it_; } + pointer operator->() const { return &(*inner_it_); } - using InnerIter = prop_pat_map_t::iterator; - using OuterIter = prop_pat_typemap_t::iterator; - // friend class const_iterator; + bool operator==(const iterator_base& other) const { + return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; + } - public: - iterator(prop_pat_typemap_t* m, std::function condition, bool is_end = false) : typemap_(m), condition_(condition) { - if (is_end) { - outer_it_ = typemap_->end(); - inner_it_ = InnerIter(); - } else { - outer_it_ = typemap_->begin(); - skip_ahead(); - } - } + bool operator!=(const iterator_base& other) const { + return !(*this == other); + } - iterator& operator++() { - ++inner_it_; - if (inner_it_ == outer_it_->second.end()) { - ++outer_it_; - skip_ahead(); - } - return *this; - } + protected: + map_type* typemap_; + condition_t condition_; + outer_iterator outer_it_; + inner_iterator inner_it_; - iterator& operator--() { - if (outer_it_ == typemap_->end()) { // End() to last element - --outer_it_; - skip_back(); - } - if (inner_it_ == outer_it_->second.begin()) { - --outer_it_; - skip_back(); + void skip_ahead() { + while (outer_it_ != typemap_->end()) { + inner_it_ = outer_it_->second.begin(); + if (condition_(*this)) { + if (inner_it_ != outer_it_->second.end()) return; } - --inner_it_; - return *this; - } - - /// Advance the iterator to the next property type - iterator& next_proptype() { ++outer_it_; - while (outer_it_ != typemap_->end()) { - if (condition_(*this)) { - inner_it_ = outer_it_->second.begin(); - if (inner_it_ != outer_it_->second.end()) return *this; - } - ++outer_it_; - } - return *this; - } - /// Return the current property type - std::type_index proptype() const { - return outer_it_->first; - } - /// Return the current prop_pat_map - const prop_pat_map_t& prop_pat_pairs() const { - return outer_it_->second; - } - - reference operator*() const { return *inner_it_; } - pointer operator->() const { return &(*inner_it_); } - - bool operator==(const iterator& other) const { - return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; - } - - bool operator!=(const iterator& other) const { - return !(*this == other); - } - - private: - prop_pat_typemap_t* typemap_; - std::function condition_; - OuterIter outer_it_; - InnerIter inner_it_; - - // Skip ahead to the next property class if needed - void skip_ahead() { - while (outer_it_ != typemap_->end()) { - if (condition_(*this)) { - inner_it_ = outer_it_->second.begin(); - if (inner_it_ != outer_it_->second.end()) return; - } - ++outer_it_; - } - // At end, so inner_it_ is blank. - inner_it_ = InnerIter(); } - - // Skip back to the previous class if needed - void skip_back() { - while (outer_it_ != typemap_->begin()) { - if (condition_(*this)) { - inner_it_ = outer_it_->second.end(); - if (inner_it_ != outer_it_->second.begin()) { - --inner_it_; - return; - } - } - --outer_it_; - } + inner_it_ = inner_iterator(); + } - // assert(outer_it_ == typemap_->begin()) + void skip_back() { + while (outer_it_ != typemap_->begin()) { + inner_it_ = outer_it_->second.end(); if (condition_(*this)) { - inner_it_ = outer_it_->second.end(); if (inner_it_ != outer_it_->second.begin()) { --inner_it_; return; } - } else { - inner_it_ = outer_it_->second.begin(); } - // assert(inner_it_ == outer_it_->second.begin()) + --outer_it_; } + if (condition_(*this)) { + inner_it_ = outer_it_->second.end(); + if (inner_it_ != outer_it_->second.begin()) { + --inner_it_; + return; + } + } else { + inner_it_ = outer_it_->second.begin(); + } + } }; + + typedef iterator_base iterator; + typedef iterator_base const_iterator; /// Specialized property filter. Use to build PropertyIterators that satisfy a condition. template @@ -721,6 +578,9 @@ namespace cadabra { }; + + + template const T* Properties::get(Ex::iterator it, bool ignore_parent_rel) const { @@ -748,8 +608,8 @@ namespace cadabra { template std::pair Properties::get_with_pattern_ext(Ex::iterator it, Ex_comparator& comp, - int& serialnum, const std::string& label, - bool doserial, bool ignore_parent_rel) const + int& serialnum, const std::string& label, + bool doserial, bool ignore_parent_rel) const { auto helper_heritable = [this](const const_iterator& pit) { return is_castable>(pit.proptype(), pit->first) || is_castable(pit.proptype(), pit->first); @@ -891,7 +751,7 @@ namespace cadabra { // Are any of these properties heritable? // FIXME: Below just uses PropertyInherit and not Inherit? auto heritable_props = PropertyFilter(&props_dict_it1->second, - // static_cast(&helper_castable) ); + // static_cast(&helper_castable) ); ([this](const const_iterator& pit) {return is_castable(pit.proptype(), pit->first);})); inherits1 = (heritable_props.begin() != heritable_props.end()); @@ -913,7 +773,7 @@ namespace cadabra { for (auto pit1 = castable_props1.begin(), pit2 = castable_props2.begin(); pit1 != castable_props1.end() && pit2 != castable_props2.end(); ) { - // Because we use a std::map the std::type_index prop types are sorted + // Because we use a std::map, the std::type_index prop types are sorted if (pit1.proptype() < pit2.proptype()) { pit1.next_proptype(); } @@ -921,7 +781,7 @@ namespace cadabra { pit2.next_proptype(); } else { - // it1 and it2 are of the same property type + // pit1 and pit2 are of the same property type // Get the pat_prop_pairs for each auto& prop_pats1 = pit1.prop_pat_pairs(); @@ -971,8 +831,14 @@ namespace cadabra { // Return the recasted property return dynamic_cast( ppit1->first ); } + // Otherwise, continue. + ++ppit1; + ++ppit2; } } + + pit1.next_proptype(); + pit2.next_proptype(); } } } @@ -1036,4 +902,8 @@ namespace cadabra { } } + + + + } diff --git a/core/pythoncdb/py_ex.cc b/core/pythoncdb/py_ex.cc index 7376b302f4..7da0196b17 100644 --- a/core/pythoncdb/py_ex.cc +++ b/core/pythoncdb/py_ex.cc @@ -715,6 +715,7 @@ namespace cadabra { .def("state", &Ex::state) .def("reset", &Ex::reset_state) .def("copy", [](const Ex& ex) { return std::make_shared(ex); }) + .def("size", [](const Ex& ex) { return ex.size();}) .def("changed", &Ex::changed_state) .def("cleanup", &Ex_cleanup) .def("__array__", [](Ex& ex) { diff --git a/core/pythoncdb/py_properties.cc b/core/pythoncdb/py_properties.cc index 44c12f28c0..b66de21849 100644 --- a/core/pythoncdb/py_properties.cc +++ b/core/pythoncdb/py_properties.cc @@ -251,6 +251,9 @@ namespace cadabra { using cpp_type = typename base_type::cpp_type; using py_type = typename base_type::py_type; + // Register the property type for dynamic lookup. + py_property_registry.register_type(); + return py_type(m, std::make_shared()->name().c_str(), py::multiple_inheritance(), read_manual(m, "properties", std::make_shared()->name().c_str()).c_str()) .def(py::init(), py::arg("ex"), py::arg("param")=Ex{}) @@ -284,51 +287,118 @@ namespace cadabra { pybind11::list ret; std::string res; bool multi = false; - /* - for (auto it = props.pats.begin(); it != props.pats.end(); ++it) { - if (it->first->hidden()) continue; - - // print the property name if we are at the end or if the next entry is for - // a different property. - decltype(it) nxt = it; - ++nxt; - if (res == "" && (nxt != props.pats.end() && it->first == nxt->first)) { - if(handles_latex_view) res += "\\{"; - else res += "{"; - multi = true; - } - - std::ostringstream str; - if(handles_latex_view) { - DisplayTeX dt(*get_kernel_from_scope(), it->second->obj); - dt.output(str); - } - else { - DisplayTerminal dt(*get_kernel_from_scope(), it->second->obj); - dt.output(str); - } - - res += str.str(); - - if (nxt == props.pats.end() || it->first != nxt->first) { - if (multi) { - if(handles_latex_view) res += "\\}"; - else res += "}"; + for (const auto& [_, pats] : props.pats_dict) { + for (auto it = pats.begin(); it != pats.end(); ++it) { + if (it->first->hidden()) continue; + + // print the property name if we are at the end or if the next entry is for + // a different property. + decltype(it) nxt = it; + ++nxt; + if (res == "" && (nxt != pats.end() && it->first == nxt->first)) { + if(handles_latex_view) res += "\\{"; + else res += "{"; + multi = true; + } + + std::ostringstream str; + if(handles_latex_view) { + DisplayTeX dt(*get_kernel_from_scope(), it->second->obj); + dt.output(str); + } + else { + DisplayTerminal dt(*get_kernel_from_scope(), it->second->obj); + dt.output(str); + } + + res += str.str(); + + if (nxt == pats.end() || it->first != nxt->first) { + if (multi) { + if(handles_latex_view) res += "\\}"; + else res += "}"; + } + multi = false; + res += "::\\texttt{"; + res += (*it).first->name() + "}"; + ret.append(LaTeXString(res)); + res = ""; + } + else { + res += ", "; } - multi = false; - res += "::\\texttt{"; - res += (*it).first->name() + "}"; - ret.append(LaTeXString(res)); - res = ""; - } - else { - res += ", "; } } - */ return ret; } + pybind11::list list_properties2() { + Kernel* kernel = get_kernel_from_scope(); + Properties& props = kernel->properties; + + /* + pybind11::dict globals = get_globals(); + bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); + */ + + pybind11::list ret; + for (const auto& [_, pats] : props.pats_dict) { + for (auto it = pats.begin(); it != pats.end(); ++it) { + if (it->first->hidden()) continue; + const property* prop = it->first; + Ex_ptr ex_ptr = std::make_shared(it->second->obj); + auto bound_property = py_property_registry.create_bound_property(prop, ex_ptr); + if (bound_property) { + ret.append(bound_property); + } else { + // Nothing to do yet. + } + } + } + return ret; + } + + + pybind11::list list_properties3() { + Kernel* kernel = get_kernel_from_scope(); + Properties& props = kernel->properties; + + /* + pybind11::dict globals = get_globals(); + bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); + */ + + pybind11::list ret; + + for (const auto& [_, pats] : props.pats_dict) { + for (auto it = pats.begin(); it != pats.end(); ++it) { + if (it->first->hidden()) continue; + + auto nxt = it; + ++nxt; + if (nxt != pats.end() && it->first == nxt->first) { + // If nxt property is the same, bind them together in a list + if (it->first->hidden()) continue; + pybind11::list shared_property; + while (it != pats.end() && it->first == nxt->first) { + const property* prop = it->first; + Ex_ptr ex_ptr = std::make_shared(it->second->obj); + auto bound_property = py_property_registry.create_bound_property(prop, ex_ptr); + shared_property.append(bound_property); + it++; + } + it--; + ret.append(shared_property); + } else { + const property* prop = it->first; + Ex_ptr ex_ptr = std::make_shared(it->second->obj); + auto bound_property = py_property_registry.create_bound_property(prop, ex_ptr); + ret.append(bound_property); + } + } + } + return ret; + } std::vector indices_get_all(const Indices* indices, bool include_wildcards) { auto kernel = get_kernel_from_scope(); @@ -355,6 +425,8 @@ namespace cadabra { { m.def("properties", &list_properties); + m.def("properties2", &list_properties2); + m.def("properties3", &list_properties3); py::class_>(m, "Property") .def_property_readonly("for_obj", &BoundPropertyBase::get_ex); @@ -560,4 +632,6 @@ namespace cadabra { } + + BoundPropertyRegistry py_property_registry; } diff --git a/core/pythoncdb/py_properties.hh b/core/pythoncdb/py_properties.hh index 0a72b74160..df82dbed43 100644 --- a/core/pythoncdb/py_properties.hh +++ b/core/pythoncdb/py_properties.hh @@ -5,8 +5,10 @@ #include #include #include -#include -#include +// #include +// #include +#include "pybind11/pybind11.h" +#include "pybind11/stl.h" #include "py_ex.hh" #include "py_kernel.hh" #include "py_tableau.hh" @@ -76,8 +78,47 @@ namespace cadabra { // Return type is not the same as BoundPropertyBase, but this is ok // by the standard as cpp_type* is convertible to property* const cpp_type* get_prop() const; + }; + + class BoundPropertyRegistry { + public: + using FactoryFunction = std::function; + + template + void register_type() { + const std::type_index type_id = typeid(typename BoundPropT::cpp_type); + if (registry_.count(type_id) > 0) { + throw std::runtime_error("Property already registered"); + } + + registry_[type_id] = [](const property* prop, Ex_ptr ex) { + auto casted_prop = dynamic_cast(prop); + if (!casted_prop) { + throw std::runtime_error("Failed to cast property"); + } + return pybind11::cast(std::make_shared(casted_prop, ex)); + }; + + } + + pybind11::object create_bound_property(const property* prop, Ex_ptr ex) const { + std::type_index type_id = typeid(*prop); + auto it = registry_.find(type_id); + if (it == registry_.end()) { + // throw std::runtime_error("No BoundProperty registered for this type"); + return pybind11::none(); + } + return it->second(prop, ex); + } + + private: + // the registry_ maps a type_index for the cpp type to a FactoryFunction, + // which takes property and pattern pointers and returns a Python BoundProperty + std::map registry_; + }; + template std::string get_name(); template typename BoundPropT::py_type def_abstract_prop(pybind11::module& m); @@ -87,4 +128,5 @@ namespace cadabra { void init_properties(pybind11::module& m); + extern BoundPropertyRegistry py_property_registry; } From a67714e9e30341ddd63e7d142cd2d726640c0d88 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:21:04 -0400 Subject: [PATCH 07/18] Working version (no segfaults!). --- core/Props.cc | 84 +++++++++++++++------------ core/Props.hh | 155 +++++++++++++++++++++----------------------------- 2 files changed, 115 insertions(+), 124 deletions(-) diff --git a/core/Props.cc b/core/Props.cc index ee20c8416b..c1b66bd564 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -400,28 +400,29 @@ void Properties::insert_prop(const Ex& et, const property *pr) { pattern *pat = new pattern(et); // Make sure there is no existing property of the same type matching pat - auto pdit = props_dict.find(pat->obj.begin()->name_only()); - if (pdit != props_dict.end() ) { - auto possible_dups = PropertyFilter(&pdit->second, - [pr](const iterator& pit) { - return typeid(*pr) == typeid(*(pit->second)); - }); - - for (iterator pdit = possible_dups.begin(); pdit != possible_dups.end();) { + auto tmp = props_dict.find(pat->obj.begin()->name_only()); + if (tmp != props_dict.end() ) { + auto possible_dups = PropertyFilter(&tmp->second); + for (iterator dup_it = possible_dups.begin(); dup_it != possible_dups.end();) { + // std::cerr << "Accessing: " << dup_it->first << '\n'; + if (typeid(*dup_it->first) != typeid(*pr)) { + dup_it.next_proptype(); + continue; + } // 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 // and are of the same type as pr. - if( pdit->second->match(*this, et.begin())) { + if( dup_it->second->match(*this, et.begin())) { // 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(pdit->first); + const labelled_property *lpold = dynamic_cast(dup_it->first); 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 = pdit->second; - const property *oldprop = pdit->first; + pattern *oldpat = dup_it->second; + const property *oldprop = dup_it->first; // 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). @@ -432,9 +433,10 @@ void Properties::insert_prop(const Ex& et, const property *pr) { // Erase the pattern->property entry, and delete the pattern. // FIXME: store pattern by value. (why?) - iterator old_pdit = pdit; - ++pdit; - erase(old_pdit->first, old_pdit->second); + iterator old_dup_it = dup_it; + ++dup_it; + // std::cerr << "Freeing: " << old_dup_it->first << '\n'; + erase(old_dup_it->first, old_dup_it->second); // Remove the property->pattern entry. Only delete the property // if it is no longer associated to any other pattern. @@ -447,9 +449,11 @@ void Properties::insert_prop(const Ex& et, const property *pr) { // identical, or when they can coexist, or something like that. // FIXME: SelfAntiCommuting is not a list property, so above should be fine. + } else { + ++dup_it; } } else { - ++pdit; + ++dup_it; } } } @@ -555,7 +559,7 @@ void Properties::insert_prop_old(const Ex& et, const property *pr) } */ -void Properties::insert_list_prop(const std::vector& its, const list_property *pr) +void Properties::insert_list_prop(const std::vector& its, const list_property*& pr) { assert(its.size()>0); @@ -610,6 +614,7 @@ void Properties::insert_list_prop(const std::vector& its, const list_propert if (match_type == list_property::exact_match) { delete pr; pr=static_cast( (*pit).first ); + // Because pr is passed by reference, the caller maintains a valid pointer. break; } else if (match_type == list_property::id_match) { erase((*pit).first); @@ -839,41 +844,50 @@ void Properties::erase(const property* p) { // Erase a property and related pattern. void Properties::erase(const property* prop, pattern* pat) { auto name = pat->obj.begin()->name_only(); + int num_found = 0; - // Eliminate from pats_dict auto pad_it = pats_dict.find(typeid(*prop)); - if (pad_it != pats_dict.end()) { - auto prop_pat_pairs = pad_it->second; - for (auto pairs_it = prop_pat_pairs.begin(); pairs_it != prop_pat_pairs.end(); ) { - if (pairs_it->first == prop && pairs_it->second == pat) { - pairs_it = prop_pat_pairs.erase(pairs_it); - } else { - ++pairs_it; - } - } + if (pad_it == pats_dict.end()) { + throw ConsistencyException("Properties erase failure: Cannot find property of matching type to property/pattern pair to erase."); } + - // Eliminate from props_dict + // Eliminate from props_dict (where they are first keyed by name of pattern) auto pdit = props_dict.find(name); if (pdit != props_dict.end()) { auto ppit = pdit->second.find(typeid(*prop)); if (ppit != pdit->second.end()) { - auto prop_pat_pairs = ppit->second; - for (auto pairs_it = prop_pat_pairs.begin(); pairs_it != prop_pat_pairs.end(); ) { + auto& named_typed_pairs = ppit->second; + for (auto pairs_it = named_typed_pairs.begin(); pairs_it != named_typed_pairs.end(); ) { if (pairs_it->first == prop && pairs_it->second == pat) { - pairs_it = prop_pat_pairs.erase(pairs_it); + pairs_it = named_typed_pairs.erase(pairs_it); + ++num_found; } else { ++pairs_it; } } - // Does prop have any more patterns associated with it? - if (prop_pat_pairs.find(prop) == prop_pat_pairs.end()) { - delete prop; - } + } + } + // Look over all prop/pat pairs of matching types and erase them + auto& all_typed_pairs = pad_it->second; + for (auto pairs_it = all_typed_pairs.begin(); pairs_it != all_typed_pairs.end(); ) { + if (pairs_it->first == prop && pairs_it->second == pat) { + pairs_it = all_typed_pairs.erase(pairs_it); + ++num_found; + } else { + ++pairs_it; } } + // Does prop have any more patterns associated with it? + if (all_typed_pairs.find(prop) == all_typed_pairs.end()) { + delete prop; + } + if (num_found != 2) { + throw ConsistencyException("Properties erase failure: Inconsistent numbers of property/patterns erased."); + } + delete pat; } diff --git a/core/Props.hh b/core/Props.hh index b25836acb6..6ee31bee20 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -400,7 +400,7 @@ namespace cadabra { // interface `master_insert` instead. void insert_prop(const Ex&, const property *); void insert_prop_old(const Ex&, const property *); - void insert_list_prop(const std::vector&, const list_property *); + void insert_list_prop(const std::vector&, const list_property *&); bool check_label(const property *, const std::string&) const; bool check_label(const labelled_property *, const std::string&) const; // Search through pointers @@ -429,10 +429,13 @@ namespace cadabra { public: // Need specialized iterator and const_iterator classes. These are built from iterator_base below. - template + template class iterator_base { + // Compile-time assurance that we cannot have T1=void but T2 not void. + static_assert(!(std::is_void::value && !std::is_void::value), "If T1 is void, T2 must also be void."); + public: - using map_type = std::conditional_t; + using map_t = std::conditional_t; using outer_iterator = std::conditional_t; using inner_iterator = std::conditional_t; @@ -441,11 +444,10 @@ namespace cadabra { using value_type = std::conditional_t; using pointer = value_type*; using reference = value_type&; + using self_type = iterator_base; - using condition_t = std::function&)>; - - iterator_base(map_type* m, condition_t cond, bool is_end = false) - : typemap_(m), condition_(std::move(cond)) { + iterator_base(map_t* m, bool is_end = false) + : typemap_(m) { if (is_end) { outer_it_ = typemap_->end(); inner_it_ = inner_iterator(); @@ -455,7 +457,7 @@ namespace cadabra { } } - iterator_base& operator++() { + self_type& operator++() { ++inner_it_; if (inner_it_ == outer_it_->second.end()) { ++outer_it_; @@ -464,7 +466,7 @@ namespace cadabra { return *this; } - iterator_base& operator--() { + self_type& operator--() { if (outer_it_ == typemap_->end()) { --outer_it_; skip_back(); @@ -477,7 +479,7 @@ namespace cadabra { return *this; } - iterator_base& next_proptype() { + self_type& next_proptype() { ++outer_it_; skip_ahead(); return *this; @@ -494,24 +496,35 @@ namespace cadabra { reference operator*() const { return *inner_it_; } pointer operator->() const { return &(*inner_it_); } - bool operator==(const iterator_base& other) const { + bool operator==(const self_type& other) const { return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; } - bool operator!=(const iterator_base& other) const { + bool operator!=(const self_type& other) const { return !(*this == other); } protected: - map_type* typemap_; - condition_t condition_; + map_t* typemap_; outer_iterator outer_it_; inner_iterator inner_it_; + static bool condition_(const property* pr) { + if constexpr (std::is_void::value) return true; + if constexpr (std::is_void::value) + return dynamic_cast(pr) != nullptr; + return dynamic_cast(pr) != nullptr || + dynamic_cast(pr) != nullptr; + } + void skip_ahead() { while (outer_it_ != typemap_->end()) { + if (outer_it_->second.empty()) { + ++outer_it_; + continue; + } inner_it_ = outer_it_->second.begin(); - if (condition_(*this)) { + if (condition_(inner_it_->first)) { if (inner_it_ != outer_it_->second.end()) return; } ++outer_it_; @@ -519,10 +532,11 @@ namespace cadabra { inner_it_ = inner_iterator(); } + // The below still needs to be fixed but we don't use it really. void skip_back() { while (outer_it_ != typemap_->begin()) { inner_it_ = outer_it_->second.end(); - if (condition_(*this)) { + if (condition_(inner_it_->first)) { if (inner_it_ != outer_it_->second.begin()) { --inner_it_; return; @@ -530,7 +544,7 @@ namespace cadabra { } --outer_it_; } - if (condition_(*this)) { + if (condition_(inner_it_->first)) { inner_it_ = outer_it_->second.end(); if (inner_it_ != outer_it_->second.begin()) { --inner_it_; @@ -546,34 +560,29 @@ namespace cadabra { typedef iterator_base const_iterator; /// Specialized property filter. Use to build PropertyIterators that satisfy a condition. - template + template class PropertyFilter { - public: - PropertyFilter(Map* m) - : typemap_(m), - condition_([](const It&) {return true;}) - { + using map_t = std::conditional_t; + using It = iterator_base; + public: + PropertyFilter(map_t* m) + : typemap_(m), + begin_(typemap_, /*is_end=*/false), + end_(typemap_, /*is_end=*/true) + {} + + It begin() const { + return begin_; } - PropertyFilter(Map* m, std::function condition) - : typemap_(m), - condition_(condition), - begin_(typemap_, condition_, /*is_end=*/false), - end_(typemap_, condition_, /*is_end=*/true) - {} - - It begin() const { - return begin_; - } - It end() const { - return end_; - } + It end() const { + return end_; + } - private: - Map* typemap_; - std::function condition_; - It begin_; - It end_; + private: + map_t* typemap_; + It begin_; + It end_; }; }; @@ -611,13 +620,6 @@ namespace cadabra { int& serialnum, const std::string& label, bool doserial, bool ignore_parent_rel) const { - auto helper_heritable = [this](const const_iterator& pit) { - return is_castable>(pit.proptype(), pit->first) || is_castable(pit.proptype(), pit->first); - }; - auto helper_castable = [this](const const_iterator& pit) { - return is_castable(pit.proptype(), pit->first); - }; - std::pair ret = {nullptr, nullptr}; bool inherits = false; @@ -644,43 +646,26 @@ namespace cadabra { auto props_dict_it = props_dict.find(it->name_only()); if (props_dict_it != props_dict.end()) { - auto heritable_props = PropertyFilter(&props_dict_it->second, - helper_heritable - // static_cast(&helper_heritable) - /* - ([this](const const_iterator& pit) { - return is_castable>(pit.proptype(), pit->first) || is_castable(pit.proptype(), pit->first); - }) - */ - ); + auto heritable_props = PropertyFilter, PropertyInherit>(&props_dict_it->second); inherits = (heritable_props.begin() != heritable_props.end()); // Filter all properties that are castable to T. - auto castable_props = PropertyFilter(&props_dict_it->second, - helper_castable - // static_cast(&helper_castable) - /* - ([this](const const_iterator& pit) { - return is_castable(pit.proptype(), pit->first); - }) - */ - ); + auto castable_props = PropertyFilter(&props_dict_it->second); // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true for(;;) { for (const prop_pat_pair_t& prop_pat : castable_props) { if(wildcards==prop_pat.second->children_wildcard()) { + // The cast is guaranteed to work so we can use ret.first=dynamic_cast(prop_pat.first); - if(ret.first) { - if(prop_pat.second->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { - ret.second=prop_pat.second; - if(!check_label(ret.first, label)) - ret.first=0; - else { - if(doserial) - serialnum=serial_number( prop_pat.first, prop_pat.second); - break; - } + if(prop_pat.second->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { + ret.second=prop_pat.second; + if(!check_label(ret.first, label)) + ret.first=0; + else { + if(doserial) + serialnum=serial_number( prop_pat.first, prop_pat.second); + break; } } ret.first=0; @@ -750,24 +735,16 @@ namespace cadabra { if (props_dict_it1 != props_dict.end() && props_dict_it2 != props_dict.end()) { // Are any of these properties heritable? // FIXME: Below just uses PropertyInherit and not Inherit? - auto heritable_props = PropertyFilter(&props_dict_it1->second, - // static_cast(&helper_castable) ); - ([this](const const_iterator& pit) {return is_castable(pit.proptype(), pit->first);})); + auto heritable_props = PropertyFilter(&props_dict_it1->second); inherits1 = (heritable_props.begin() != heritable_props.end()); - heritable_props = PropertyFilter(&props_dict_it2->second, - // static_cast(&helper_castable) ); - ([this](const const_iterator& pit) {return is_castable(pit.proptype(), pit->first);})); + heritable_props = PropertyFilter(&props_dict_it2->second); inherits2 = (heritable_props.begin() != heritable_props.end()); // Find all castable properties that appear in both props_dict_t1->second and props_dict_t2->second - auto castable_props1 = PropertyFilter(&props_dict_it1->second, - // static_cast(&helper_castable) ); - ([this](const const_iterator& pit) {return is_castable(pit.proptype(), pit->first);}) ); - auto castable_props2 = PropertyFilter(&props_dict_it2->second, - // static_cast(&helper_castable) ); - ([this](const const_iterator& pit) {return is_castable(pit.proptype(), pit->first);})); - + auto castable_props1 = PropertyFilter(&props_dict_it1->second); + auto castable_props2 = PropertyFilter(&props_dict_it2->second); + // Search for properties that appear in both pit1 and pit2 for (auto pit1 = castable_props1.begin(), pit2 = castable_props2.begin(); From ffa513279632dd29614075a055834a52d8264a80 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:21:04 -0400 Subject: [PATCH 08/18] Added property dictionary to Python. --- core/pythoncdb/py_properties.cc | 104 +++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/core/pythoncdb/py_properties.cc b/core/pythoncdb/py_properties.cc index b66de21849..249e7b292a 100644 --- a/core/pythoncdb/py_properties.cc +++ b/core/pythoncdb/py_properties.cc @@ -340,7 +340,7 @@ namespace cadabra { pybind11::dict globals = get_globals(); bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); */ - + pybind11::list ret; for (const auto& [_, pats] : props.pats_dict) { for (auto it = pats.begin(); it != pats.end(); ++it) { @@ -399,6 +399,97 @@ namespace cadabra { } return ret; } + + + pybind11::dict properties_dict() { + Kernel* kernel = get_kernel_from_scope(); + Properties& props = kernel->properties; + + // Dictionary of properties, keyed by property type + pybind11::dict ret; + const property* last_list_prop = nullptr; + Ex_ptr ex_pattern_list = nullptr; + + for (const auto& [_, pats] : props.pats_dict) { + for (auto it = pats.begin(); it != pats.end(); ++it) { + if (it->first->hidden()) continue; + + const property* prop = it->first; + const list_property* list_prop = dynamic_cast(prop); + + // Close the current list property if necessary + // (when prop isn't a list or not the same list) + if (last_list_prop && list_prop != last_list_prop) { + pybind11::object bound_property = + py_property_registry.create_bound_property(last_list_prop, ex_pattern_list); + if (bound_property) { + // Key: Python class name of the bound property + pybind11::str key = + pybind11::str(bound_property.get_type().attr("__name__")); + + // Ensure a list exists for this key and append + if (!ret.contains(key)) + ret[key] = pybind11::list(); + + ret[key].cast().append(bound_property); + } + last_list_prop = nullptr; + } + + // Open a new list property if prop is a list and no existing last list + if (!last_list_prop && list_prop) { + // Open a new list property + last_list_prop = list_prop; + ex_pattern_list = std::make_shared("\\comma"); + } + + // Add list property or add regular property + if (list_prop) { + ex_pattern_list->append_child(ex_pattern_list->begin(), it->second->obj.begin()); + } else { + Ex_ptr ex_ptr = std::make_shared(it->second->obj); + + pybind11::object bound_property = + py_property_registry.create_bound_property(prop, ex_ptr); + + if (!bound_property) + continue; + + // Key: Python class name of the bound property + pybind11::str key = + pybind11::str(bound_property.get_type().attr("__name__")); + + // Ensure a list exists for this key and append + if (!ret.contains(key)) + ret[key] = pybind11::list(); + + ret[key].cast().append(bound_property); + } + + } + // If there is an open list property, close it. + if (last_list_prop) { + pybind11::object bound_property = + py_property_registry.create_bound_property(last_list_prop, ex_pattern_list); + if (bound_property) { + // Key: Python class name of the bound property + pybind11::str key = + pybind11::str(bound_property.get_type().attr("__name__")); + + // Ensure a list exists for this key and append + if (!ret.contains(key)) + ret[key] = pybind11::list(); + + ret[key].cast().append(bound_property); + } + last_list_prop = nullptr; + ex_pattern_list = nullptr; + } + + } + return ret; + } + std::vector indices_get_all(const Indices* indices, bool include_wildcards) { auto kernel = get_kernel_from_scope(); @@ -427,6 +518,7 @@ namespace cadabra { m.def("properties", &list_properties); m.def("properties2", &list_properties2); m.def("properties3", &list_properties3); + m.def("properties_dict", &properties_dict); py::class_>(m, "Property") .def_property_readonly("for_obj", &BoundPropertyBase::get_ex); @@ -467,6 +559,16 @@ namespace cadabra { return mult; } else return pybind11::cast(m.get_double()); + }) + .def("value", [](const Py_WeightBase & p) { + auto m = p.get_prop()->value(p.get_kernel(), p.get_it(), p.get_prop()->label); + if(m.is_rational()) { + // This is mpq_class, convert to the Python equivalent. + pybind11::object mpq = pybind11::module::import("gmpy2").attr("mpq"); + pybind11::object mult = mpq(m.get_rational().get_num().get_si(), m.get_rational().get_den().get_si()); + return mult; + } + else return pybind11::cast(m.get_double()); }); def_abstract_prop(m, "DifferentialFormBase") From e6c5f6c2afddaa49a015bbc94a314cc17fc251f2 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:21:04 -0400 Subject: [PATCH 09/18] Minor additions. --- core/pythoncdb/py_properties.cc | 19 ++++++++++++++----- core/pythoncdb/py_properties.hh | 6 ++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/core/pythoncdb/py_properties.cc b/core/pythoncdb/py_properties.cc index 249e7b292a..37fceb6add 100644 --- a/core/pythoncdb/py_properties.cc +++ b/core/pythoncdb/py_properties.cc @@ -84,6 +84,7 @@ namespace cadabra { std::string BoundPropertyBase::str_() const { + if (!prop) return "deleted"; std::ostringstream str; str << "Property "; // std::cerr << "going to print" << std::endl; @@ -94,11 +95,11 @@ namespace cadabra { std::string BoundPropertyBase::latex_() const { + if (!prop) return "deleted"; std::ostringstream str; // HERE: this text should go away, property should just print itself in a python form, // the decorating text should be printed in a separate place. - str << "\\text{Property "; prop->latex(str); std::string bare = Ex_as_latex(for_obj); @@ -169,13 +170,12 @@ namespace cadabra { { auto new_prop = new cpp_type(); get_kernel_from_scope()->inject_property(new_prop, ex, param); - BoundPropertyBase::prop = new_prop; + this->prop = new_prop; } template std::shared_ptr> BoundProperty::get_from_kernel(Ex::iterator it, const std::string& label, bool ignore_parent_rel) - { int tmp; auto res = get_kernel_from_scope()->properties.get_with_pattern( @@ -192,6 +192,14 @@ namespace cadabra { } + template + void BoundProperty::remove_from_kernel() + { + get_kernel_from_scope()->properties.erase(this->prop); + this->prop = nullptr; + } + + template const PropT* BoundProperty::get_prop() const { @@ -263,6 +271,7 @@ namespace cadabra { .def("__str__", &BoundPropT::str_) .def("__repr__", &BoundPropT::repr_) .def("_latex_", &BoundPropT::latex_) + .def("erase", &BoundPropT::remove_from_kernel) ; } @@ -422,7 +431,7 @@ namespace cadabra { if (last_list_prop && list_prop != last_list_prop) { pybind11::object bound_property = py_property_registry.create_bound_property(last_list_prop, ex_pattern_list); - if (bound_property) { + if (!bound_property.is_none()) { // Key: Python class name of the bound property pybind11::str key = pybind11::str(bound_property.get_type().attr("__name__")); @@ -471,7 +480,7 @@ namespace cadabra { if (last_list_prop) { pybind11::object bound_property = py_property_registry.create_bound_property(last_list_prop, ex_pattern_list); - if (bound_property) { + if (!bound_property.is_none()) { // Key: Python class name of the bound property pybind11::str key = pybind11::str(bound_property.get_type().attr("__name__")); diff --git a/core/pythoncdb/py_properties.hh b/core/pythoncdb/py_properties.hh index df82dbed43..384bbe3dd1 100644 --- a/core/pythoncdb/py_properties.hh +++ b/core/pythoncdb/py_properties.hh @@ -41,6 +41,8 @@ namespace cadabra { // when the Python object goes out of scope. const property* prop; + std::vector pats; + // We also keep a shared pointer to the expression for which we // have defined this property, so that we can print sensible // information. @@ -79,6 +81,10 @@ namespace cadabra { // by the standard as cpp_type* is convertible to property* const cpp_type* get_prop() const; + /// Delete the entire property from the kernel + void remove_from_kernel(); + + }; From 5d0701a153979e9d6edcdb5adc50eea0e52b99e8 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:21:50 -0400 Subject: [PATCH 10/18] Modified property insertion routines to return a pointer to the inserted property. This is necessary since list properties can be absorbed by existing list properties of the same type. --- core/Kernel.cc | 4 +- core/Kernel.hh | 2 +- core/Props.cc | 40 ++++- core/Props.hh | 7 +- core/pythoncdb/py_properties.cc | 275 +++++++++++++++----------------- core/pythoncdb/py_properties.hh | 17 +- 6 files changed, 178 insertions(+), 167 deletions(-) diff --git a/core/Kernel.cc b/core/Kernel.cc index c5a71d95eb..e8fa1e5a10 100644 --- a/core/Kernel.cc +++ b/core/Kernel.cc @@ -112,7 +112,7 @@ Kernel::~Kernel() // std::cerr << "~Kernel() " << this << std::endl; } -void Kernel::inject_property(property *prop, std::shared_ptr ex, std::shared_ptr param) +const property* Kernel::inject_property(property *prop, std::shared_ptr ex, std::shared_ptr param) { Ex::iterator it=ex->begin(); @@ -124,7 +124,7 @@ void Kernel::inject_property(property *prop, std::shared_ptr ex, std::shared } // Validate and insert a copy of the property. prop->validate(*this, ex); - properties.master_insert(Ex(it), prop); + return properties.master_insert(Ex(it), prop); } std::shared_ptr Kernel::ex_from_string(const std::string& s) diff --git a/core/Kernel.hh b/core/Kernel.hh index 1c8c178708..57a12892b7 100644 --- a/core/Kernel.hh +++ b/core/Kernel.hh @@ -20,7 +20,7 @@ namespace cadabra { /// Inject a property into the system and attach it to the given pattern. /// Transfers property ownership to the kernel. - void inject_property(property *prop, std::shared_ptr pattern, std::shared_ptr property_arguments); + const property* inject_property(property *prop, std::shared_ptr pattern, std::shared_ptr property_arguments); /// Create an Ex expression object from a string, which will be parsed. std::shared_ptr ex_from_string(const std::string&); diff --git a/core/Props.cc b/core/Props.cc index c1b66bd564..4f78ab2e9d 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -559,7 +559,8 @@ void Properties::insert_prop_old(const Ex& et, const property *pr) } */ -void Properties::insert_list_prop(const std::vector& its, const list_property*& pr) +// Insert a list property into the kernel. +const list_property* Properties::insert_list_prop(const std::vector& its, const list_property* pr) { assert(its.size()>0); @@ -574,7 +575,7 @@ void Properties::insert_list_prop(const std::vector& its, const list_propert If we find one of the same type and `equals` returns `exact_match`, we discard (and delete) the list_property pointer. This ensures e.g. that there is only one - Indices property with the label "vector". The code the continues, as if this property was the + Indices property with the label "vector". The code then continues, as if this property was the one passed to `insert_list_prop`. If we find a list property that returns `id_match`, we delete that older list property. @@ -674,6 +675,7 @@ void Properties::insert_list_prop(const std::vector& its, const list_propert props_dict[pat->obj.begin()->name_only()][typeid(*pr)].emplace(pr, pat); pats.emplace(pr, pat); } + return pr; } @@ -727,10 +729,12 @@ 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. +// Returns a pointer to the new property. (For a list property, this may differ +// from the original pointer.) -std::string Properties::master_insert(Ex proptree, const property *thepropbase) +const property* Properties::master_insert(Ex proptree, const property *thepropbase) { - std::ostringstream str; + // std::ostringstream str; Ex::sibling_iterator st=proptree.begin(); @@ -765,10 +769,10 @@ std::string Properties::master_insert(Ex proptree, const property *thepropbase) obj2.begin()->fl.parent_rel=str_node::p_sub; objs2.push_back(obj2); } - insert_list_prop(objs2, thelistprop); + thelistprop = insert_list_prop(objs2, thelistprop); } else { - insert_list_prop(objs, thelistprop); + thelistprop = insert_list_prop(objs, thelistprop); } } else { // a normal property @@ -788,7 +792,12 @@ std::string Properties::master_insert(Ex proptree, const property *thepropbase) insert_prop(Ex(st), theprop); } } - return str.str(); + // return str.str(); + if (thelistprop) { + return dynamic_cast(thelistprop); + } else { + return thepropbase; + } } bool Properties::check_label(const property* p, const std::string& label) const @@ -891,3 +900,20 @@ void Properties::erase(const property* prop, pattern* pat) { delete pat; } +std::pair > Properties::lookup_property(const property* sus) const { + // Warning: sus may be invalid + std::pair> ret = {nullptr, {}}; + + for (const auto& [_, pats] : pats_dict) { + auto range = pats.equal_range(sus); + if (range.first == range.second) continue; + + // property is found, so sus is valid + ret.first = sus; + for (auto it = range.first; it != range.second; ++it) { + ret.second.push_back(it->second); + } + break; + } + return ret; +} \ No newline at end of file diff --git a/core/Props.hh b/core/Props.hh index 6ee31bee20..1bdc00f0d6 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -309,7 +309,7 @@ namespace cadabra { /// Register a property for the indicated Ex. Takes both normal and list /// properties and works out which insert calls to make. The property ownership /// is transferred to us on using this call. - std::string master_insert(Ex proptree, const property *thepropbase); + const property* master_insert(Ex proptree, const property *thepropbase); void clear(); @@ -394,13 +394,16 @@ namespace cadabra { /// Erases pattern from a given property, leaving other patterns alone. void erase(const property*, pattern*); + /// Helper function to lookup all patterns associated with a property. + /// If the property is invalid, it returns a null pointer in the first slot. + std::pair > lookup_property(const property*) const; private: // Insert a property. Do not use this directly, use the public // interface `master_insert` instead. void insert_prop(const Ex&, const property *); void insert_prop_old(const Ex&, const property *); - void insert_list_prop(const std::vector&, const list_property *&); + const list_property* insert_list_prop(const std::vector&, const list_property *); bool check_label(const property *, const std::string&) const; bool check_label(const labelled_property *, const std::string&) const; // Search through pointers diff --git a/core/pythoncdb/py_properties.cc b/core/pythoncdb/py_properties.cc index 37fceb6add..0157d40194 100644 --- a/core/pythoncdb/py_properties.cc +++ b/core/pythoncdb/py_properties.cc @@ -84,7 +84,8 @@ namespace cadabra { std::string BoundPropertyBase::str_() const { - if (!prop) return "deleted"; + validate(); + if (!prop) return "invalid"; std::ostringstream str; str << "Property "; // std::cerr << "going to print" << std::endl; @@ -95,7 +96,8 @@ namespace cadabra { std::string BoundPropertyBase::latex_() const { - if (!prop) return "deleted"; + validate(); + if (!prop) return "invalid"; std::ostringstream str; // HERE: this text should go away, property should just print itself in a python form, @@ -125,6 +127,7 @@ namespace cadabra { std::string BoundPropertyBase::repr_() const { + validate(); // FIXME: this needs work, it does not output things which can be fed back into python. return "Property::repr: " + prop->name(); } @@ -149,6 +152,24 @@ namespace cadabra { return for_obj->begin(); } + void BoundPropertyBase::validate() const + { + auto pair = get_kernel_from_scope()->properties.lookup_property(prop); + if (pair.first == nullptr) { + prop = nullptr; + } + // Update pattern + if (pair.second.size() == 1) { + for_obj = std::make_shared(pair.second[0]->obj); + } else { + for_obj = std::make_shared("\\comma"); + for (const auto& pat : pair.second) { + for_obj->append_child(for_obj->begin(), pat->obj.begin()); + } + } + } + + template BoundProperty::BoundProperty() @@ -169,8 +190,7 @@ namespace cadabra { : BoundPropertyBase(nullptr, ex) { auto new_prop = new cpp_type(); - get_kernel_from_scope()->inject_property(new_prop, ex, param); - this->prop = new_prop; + this->prop = get_kernel_from_scope()->inject_property(new_prop, ex, param); } @@ -203,7 +223,8 @@ namespace cadabra { template const PropT* BoundProperty::get_prop() const { - return dynamic_cast(BoundPropertyBase::prop); + this->validate(); + return dynamic_cast(this->prop); } template @@ -214,6 +235,7 @@ namespace cadabra { Properties& props = kernel->properties; const auto *thisprop = get_prop(); + if (!thisprop) return; // Maybe raise error message? thisprop->validate(*kernel, obj); props.master_insert(*obj, thisprop); } @@ -277,6 +299,70 @@ namespace cadabra { + pybind11::list list_properties_old() + { + // This function is fundamentally limited. We would *like* to return a list of + // BoundProperties, so that you can do something with the output. But we cannot + // walk the full property list and create a BoundProperty for each of them, as + // we do not know the type (we can only dynamic_cast). + // + // So for now this is just returning a list of LaTeXStrings, obtained by asking + // each property to print itself. + + Kernel *kernel = get_kernel_from_scope(); + Properties& props = kernel->properties; + + pybind11::dict globals = get_globals(); + bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); + + pybind11::list ret; + std::string res; + bool multi = false; + for (const auto& [_, pats] : props.pats_dict) { + for (auto it = pats.begin(); it != pats.end(); ++it) { + if (it->first->hidden()) continue; + // print the property name if we are at the end or if the next entry is for + // a different property. + decltype(it) nxt = it; + ++nxt; + if (res == "" && (nxt != pats.end() && it->first == nxt->first)) { + if(handles_latex_view) res += "\\{"; + else res += "{"; + multi = true; + } + + std::ostringstream str; + if(handles_latex_view) { + DisplayTeX dt(*get_kernel_from_scope(), it->second->obj); + dt.output(str); + } + else { + DisplayTerminal dt(*get_kernel_from_scope(), it->second->obj); + dt.output(str); + } + + res += str.str(); + + if (nxt == pats.end() || it->first != nxt->first) { + if (multi) { + if(handles_latex_view) res += "\\}"; + else res += "}"; + } + multi = false; + res += "::\\texttt{"; + res += (*it).first->name() + "}"; + ret.append(LaTeXString(res)); + res = ""; + } + else { + res += ", "; + } + } + } + return ret; + } + + pybind11::list list_properties() { // This function is fundamentally limited. We would *like* to return a list of @@ -290,16 +376,28 @@ namespace cadabra { Kernel *kernel = get_kernel_from_scope(); Properties& props = kernel->properties; - pybind11::dict globals = get_globals(); - bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); + // pybind11::dict globals = get_globals(); + // bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); pybind11::list ret; - std::string res; - bool multi = false; + // std::string res; + // bool multi = false; for (const auto& [_, pats] : props.pats_dict) { - for (auto it = pats.begin(); it != pats.end(); ++it) { + for (auto it = pats.begin(); it != pats.end(); it=pats.upper_bound(it->first)) { if (it->first->hidden()) continue; - + const property* prop = it->first; + + Ex_ptr ex_ptr = std::make_shared(it->second->obj); + + pybind11::object bound_property = + py_property_registry.create_bound_property(prop, ex_ptr); + + if (bound_property.is_none()) + continue; + + ret.append(bound_property); + + /* // print the property name if we are at the end or if the next entry is for // a different property. decltype(it) nxt = it; @@ -336,165 +434,43 @@ namespace cadabra { else { res += ", "; } + */ } } return ret; } - pybind11::list list_properties2() { - Kernel* kernel = get_kernel_from_scope(); - Properties& props = kernel->properties; - - /* - pybind11::dict globals = get_globals(); - bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); - */ - - pybind11::list ret; - for (const auto& [_, pats] : props.pats_dict) { - for (auto it = pats.begin(); it != pats.end(); ++it) { - if (it->first->hidden()) continue; - const property* prop = it->first; - Ex_ptr ex_ptr = std::make_shared(it->second->obj); - auto bound_property = py_property_registry.create_bound_property(prop, ex_ptr); - if (bound_property) { - ret.append(bound_property); - } else { - // Nothing to do yet. - } - } - } - return ret; - } - - - pybind11::list list_properties3() { - Kernel* kernel = get_kernel_from_scope(); - Properties& props = kernel->properties; - - /* - pybind11::dict globals = get_globals(); - bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); - */ - - pybind11::list ret; - - for (const auto& [_, pats] : props.pats_dict) { - for (auto it = pats.begin(); it != pats.end(); ++it) { - if (it->first->hidden()) continue; - - auto nxt = it; - ++nxt; - if (nxt != pats.end() && it->first == nxt->first) { - // If nxt property is the same, bind them together in a list - if (it->first->hidden()) continue; - pybind11::list shared_property; - while (it != pats.end() && it->first == nxt->first) { - const property* prop = it->first; - Ex_ptr ex_ptr = std::make_shared(it->second->obj); - auto bound_property = py_property_registry.create_bound_property(prop, ex_ptr); - shared_property.append(bound_property); - it++; - } - it--; - ret.append(shared_property); - } else { - const property* prop = it->first; - Ex_ptr ex_ptr = std::make_shared(it->second->obj); - auto bound_property = py_property_registry.create_bound_property(prop, ex_ptr); - ret.append(bound_property); - } - } - } - return ret; - } - - pybind11::dict properties_dict() { Kernel* kernel = get_kernel_from_scope(); Properties& props = kernel->properties; // Dictionary of properties, keyed by property type pybind11::dict ret; - const property* last_list_prop = nullptr; - Ex_ptr ex_pattern_list = nullptr; for (const auto& [_, pats] : props.pats_dict) { - for (auto it = pats.begin(); it != pats.end(); ++it) { + // Iterate over keys to the pats multimap + for (auto it = pats.begin(); it != pats.end(); it=pats.upper_bound(it->first)) { if (it->first->hidden()) continue; - const property* prop = it->first; - const list_property* list_prop = dynamic_cast(prop); - - // Close the current list property if necessary - // (when prop isn't a list or not the same list) - if (last_list_prop && list_prop != last_list_prop) { - pybind11::object bound_property = - py_property_registry.create_bound_property(last_list_prop, ex_pattern_list); - if (!bound_property.is_none()) { - // Key: Python class name of the bound property - pybind11::str key = - pybind11::str(bound_property.get_type().attr("__name__")); - - // Ensure a list exists for this key and append - if (!ret.contains(key)) - ret[key] = pybind11::list(); - - ret[key].cast().append(bound_property); - } - last_list_prop = nullptr; - } - - // Open a new list property if prop is a list and no existing last list - if (!last_list_prop && list_prop) { - // Open a new list property - last_list_prop = list_prop; - ex_pattern_list = std::make_shared("\\comma"); - } - // Add list property or add regular property - if (list_prop) { - ex_pattern_list->append_child(ex_pattern_list->begin(), it->second->obj.begin()); - } else { - Ex_ptr ex_ptr = std::make_shared(it->second->obj); + Ex_ptr ex_ptr = std::make_shared(it->second->obj); - pybind11::object bound_property = - py_property_registry.create_bound_property(prop, ex_ptr); - - if (!bound_property) - continue; + pybind11::object bound_property = + py_property_registry.create_bound_property(prop, ex_ptr); - // Key: Python class name of the bound property - pybind11::str key = - pybind11::str(bound_property.get_type().attr("__name__")); + if (bound_property.is_none()) + continue; - // Ensure a list exists for this key and append - if (!ret.contains(key)) - ret[key] = pybind11::list(); + // Key: Python class name of the bound property + pybind11::str key = + pybind11::str(bound_property.get_type().attr("__name__")); - ret[key].cast().append(bound_property); - } + // Ensure a list exists for this key and append + if (!ret.contains(key)) + ret[key] = pybind11::list(); + ret[key].cast().append(bound_property); } - // If there is an open list property, close it. - if (last_list_prop) { - pybind11::object bound_property = - py_property_registry.create_bound_property(last_list_prop, ex_pattern_list); - if (!bound_property.is_none()) { - // Key: Python class name of the bound property - pybind11::str key = - pybind11::str(bound_property.get_type().attr("__name__")); - - // Ensure a list exists for this key and append - if (!ret.contains(key)) - ret[key] = pybind11::list(); - - ret[key].cast().append(bound_property); - } - last_list_prop = nullptr; - ex_pattern_list = nullptr; - } - } return ret; } @@ -525,8 +501,7 @@ namespace cadabra { { m.def("properties", &list_properties); - m.def("properties2", &list_properties2); - m.def("properties3", &list_properties3); + m.def("properties_old", &list_properties_old); m.def("properties_dict", &properties_dict); py::class_>(m, "Property") diff --git a/core/pythoncdb/py_properties.hh b/core/pythoncdb/py_properties.hh index 384bbe3dd1..453245a458 100644 --- a/core/pythoncdb/py_properties.hh +++ b/core/pythoncdb/py_properties.hh @@ -38,15 +38,22 @@ namespace cadabra { // We keep a pointer to the C++ property, so it is possible to // query properties using the Python interface. However, this C++ // object is owned by the C++ kernel and does not get destroyed - // when the Python object goes out of scope. - const property* prop; + // when the Python object goes out of scope. Member functions + // should always call validate(), which sets prop = nullptr if invalid. + mutable const property* prop; - std::vector pats; - // We also keep a shared pointer to the expression for which we // have defined this property, so that we can print sensible // information. - Ex_ptr for_obj; + mutable Ex_ptr for_obj; + + /// Validate the property and return the associated patterns. + /// If the property is invalid, prop is set to nullptr. + void validate() const; + + // FIXME: The above is a mess because we now call validate() everywhere. + // None of these are actually const, because prop, for_obj above are + // mutable. It would make more sense to just eliminate the const everywhere. }; From 44fe3bbf7098733433e97c607f80f598dc4752c9 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:21:50 -0400 Subject: [PATCH 11/18] . --- core/Props.cc | 6 ++-- core/Props.hh | 79 ++++++++++++++++----------------------------------- 2 files changed, 28 insertions(+), 57 deletions(-) diff --git a/core/Props.cc b/core/Props.cc index 4f78ab2e9d..d3084cddec 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -167,8 +167,8 @@ bool Properties::has(const property *pb, Ex::iterator it) void Properties::clear() { // Clear and free the property lists. Since pointers to properties can - // be shared, we use the pats_dict map and make sure that we only free each - // property* pointer once. + // be shared (but patterns cannot yet), we use the pats_dict map and make + // sure that we only free each property* pointer once. for (const auto& [_, this_pats] : pats_dict) { auto it=this_pats.begin(); @@ -615,7 +615,7 @@ const list_property* Properties::insert_list_prop(const std::vector& its, co if (match_type == list_property::exact_match) { delete pr; pr=static_cast( (*pit).first ); - // Because pr is passed by reference, the caller maintains a valid pointer. + // Later we return the new pr to the caller so they have a valid pointer. break; } else if (match_type == list_property::id_match) { erase((*pit).first); diff --git a/core/Props.hh b/core/Props.hh index 1bdc00f0d6..6f684dc738 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -30,7 +30,6 @@ along with this program. If not, see . #include #include #include -#include namespace cadabra { @@ -323,17 +322,13 @@ namespace cadabra { /************************************************************************************** * BELOW REVISES PROPS/PATS STRUCTURE. * - * The original props/pats maps can be inefficient because accessing requires dynamic casts, - * and the number of such casts can quickly become huge. Moreover, searches (in get) involve - * comparing all properties against all properties, which can be slow when there are many - * properties (and get is called many times). - * * The below is intended as a replacement for props/pats by including information about the - * property_type directly. + * property_type directly. This could be used to circumvent dynamic casts where they are + * known to succeed, but currently only used for quickly finding properties of a specific + * type (e.g. Indices). * **************************************************************************************/ typedef std::multimap pattern_map_t; - // typedef boost::container::flat_multimap prop_pat_map_t; typedef std::multimap prop_pat_map_t; typedef std::map prop_pat_typemap_t; @@ -343,9 +338,6 @@ namespace cadabra { property_dictmap_t props_dict; pattern_dictmap_t pats_dict; - /// Determine if obj of type_index obj_type is castable to type T - template bool is_castable(std::type_index obj_type, const U* obj) const; - /// Normal search: given a pattern, get its property if any. template const T* get(Ex::iterator, bool ignore_parent_rel=false) const; template const T* get(Ex::iterator, int& serialnum, bool doserial=true, bool ignore_parent_rel=false) const; @@ -413,21 +405,7 @@ namespace cadabra { Ex_comparator *create_comparator() const; void destroy_comparator(Ex_comparator *) const; - // mutable because castable_table may be modified by get calls - mutable std::map, bool> castable_table; - // Helper functions for use with Propery Filter - /* - template - bool helper_castable(const PropertyIterator& pit) { - return is_castable(pit.proptype(), pit->first); - } - - template - bool helper_heritable(const PropertyIterator& pit) { - return is_castable>(pit.proptype(), pit->first) || is_castable(pit.proptype(), pit->first); - } - */ public: @@ -442,7 +420,7 @@ namespace cadabra { using outer_iterator = std::conditional_t; using inner_iterator = std::conditional_t; - using iterator_category = std::bidirectional_iterator_tag; + using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = std::conditional_t; using pointer = value_type*; @@ -469,6 +447,7 @@ namespace cadabra { return *this; } + /* skip_back() doesn't work properly yet. self_type& operator--() { if (outer_it_ == typemap_->end()) { --outer_it_; @@ -481,7 +460,7 @@ namespace cadabra { --inner_it_; return *this; } - + */ self_type& next_proptype() { ++outer_it_; skip_ahead(); @@ -535,6 +514,7 @@ namespace cadabra { inner_it_ = inner_iterator(); } + /* // The below still needs to be fixed but we don't use it really. void skip_back() { while (outer_it_ != typemap_->begin()) { @@ -557,12 +537,14 @@ namespace cadabra { inner_it_ = outer_it_->second.begin(); } } + */ + }; typedef iterator_base iterator; typedef iterator_base const_iterator; - /// Specialized property filter. Use to build PropertyIterators that satisfy a condition. + /// Specialized property filter. Use to build PropertyIterators that can be cast to T1 or T2. template class PropertyFilter { using map_t = std::conditional_t; @@ -649,9 +631,6 @@ namespace cadabra { auto props_dict_it = props_dict.find(it->name_only()); if (props_dict_it != props_dict.end()) { - auto heritable_props = PropertyFilter, PropertyInherit>(&props_dict_it->second); - inherits = (heritable_props.begin() != heritable_props.end()); - // Filter all properties that are castable to T. auto castable_props = PropertyFilter(&props_dict_it->second); @@ -680,6 +659,11 @@ namespace cadabra { } else break; } + + // Check heritability + auto heritable_props = PropertyFilter, PropertyInherit>(&props_dict_it->second); + inherits = (heritable_props.begin() != heritable_props.end()); + } // Do not walk down the tree if the property cannot be passed up the tree. // FIXME: see issue/259. @@ -736,24 +720,17 @@ namespace cadabra { auto props_dict_it1 = props_dict.find(it1->name_only()); auto props_dict_it2 = props_dict.find(it2->name_only()); if (props_dict_it1 != props_dict.end() && props_dict_it2 != props_dict.end()) { - // Are any of these properties heritable? - // FIXME: Below just uses PropertyInherit and not Inherit? - auto heritable_props = PropertyFilter(&props_dict_it1->second); - inherits1 = (heritable_props.begin() != heritable_props.end()); - - heritable_props = PropertyFilter(&props_dict_it2->second); - inherits2 = (heritable_props.begin() != heritable_props.end()); // Find all castable properties that appear in both props_dict_t1->second and props_dict_t2->second auto castable_props1 = PropertyFilter(&props_dict_it1->second); auto castable_props2 = PropertyFilter(&props_dict_it2->second); - // Search for properties that appear in both pit1 and pit2 for (auto pit1 = castable_props1.begin(), pit2 = castable_props2.begin(); pit1 != castable_props1.end() && pit2 != castable_props2.end(); ) { // Because we use a std::map, the std::type_index prop types are sorted + // so we can walk them simultaneously. if (pit1.proptype() < pit2.proptype()) { pit1.next_proptype(); } @@ -821,6 +798,15 @@ namespace cadabra { pit2.next_proptype(); } } + + // Are any of these properties heritable? + // FIXME: Below just uses PropertyInherit and not Inherit? + auto heritable_props = PropertyFilter(&props_dict_it1->second); + inherits1 = (heritable_props.begin() != heritable_props.end()); + + heritable_props = PropertyFilter(&props_dict_it2->second); + inherits2 = (heritable_props.begin() != heritable_props.end()); + } // If no property was found, figure out whether a property is inherited from a child node. @@ -867,21 +853,6 @@ namespace cadabra { } - template - bool Properties::is_castable(std::type_index obj_type, const U* obj) const { - auto it = castable_table.find(std::make_pair(std::type_index(typeid(T)), obj_type)); - if (it != castable_table.end()) { - return it->second; - } - if (dynamic_cast(obj)) { - castable_table.emplace(std::make_pair(std::type_index(typeid(T)), obj_type), true); - return true; - } else { - castable_table.emplace(std::make_pair(std::type_index(typeid(T)), obj_type), false); - return false; - } - } - From eb4864218712a2100fb89487ff1b39113454e053 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:21:50 -0400 Subject: [PATCH 12/18] Fixed broken brace. --- core/IndexClassifier.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/core/IndexClassifier.cc b/core/IndexClassifier.cc index 3f67906cb6..531b093702 100644 --- a/core/IndexClassifier.cc +++ b/core/IndexClassifier.cc @@ -550,6 +550,7 @@ Ex IndexClassifier::get_dummy(const list_property *dums, assert(dd); throw ConsistencyException("Ran out of dummy indices for type \""+dd->set_name+"\"."); } + } Ex IndexClassifier::get_dummy(const list_property *dums, Ex::iterator it) const { From 642a6c8476e2dab89ce2d239c5cb602cf26f8ad4 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Mon, 4 Aug 2025 16:24:27 -0400 Subject: [PATCH 13/18] Oops. Double fix after rebasing. --- core/IndexClassifier.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/core/IndexClassifier.cc b/core/IndexClassifier.cc index 531b093702..3f67906cb6 100644 --- a/core/IndexClassifier.cc +++ b/core/IndexClassifier.cc @@ -550,7 +550,6 @@ Ex IndexClassifier::get_dummy(const list_property *dums, assert(dd); throw ConsistencyException("Ran out of dummy indices for type \""+dd->set_name+"\"."); } - } Ex IndexClassifier::get_dummy(const list_property *dums, Ex::iterator it) const { From 8f1d111fc6abc54b3633b5192da3f666d4831f27 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Tue, 5 Aug 2025 19:06:20 -0400 Subject: [PATCH 14/18] Additional simplifications. --- CMakeLists.txt | 2 +- core/IndexClassifier.cc | 4 +- core/Props.cc | 198 +++++-------------- core/Props.hh | 419 +++++++++++++++++----------------------- 4 files changed, 237 insertions(+), 386 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 442146cab2..594cb52d13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,7 +254,7 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized") endif() endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wall -Wextra -Wunused -Wno-psabi -Wno-unknown-pragmas -Wno-misleading-indentation -fvisibility=hidden -Wno-unused-but-set-variable -Wno-unused-parameter") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O1 -Wall -Wextra -Wunused -Wno-psabi -Wno-unknown-pragmas -Wno-misleading-indentation -fvisibility=hidden -Wno-unused-but-set-variable -Wno-unused-parameter -fno-omit-frame-pointer") endif() # Clang diff --git a/core/IndexClassifier.cc b/core/IndexClassifier.cc index 3f67906cb6..4acd97f4b7 100644 --- a/core/IndexClassifier.cc +++ b/core/IndexClassifier.cc @@ -493,8 +493,8 @@ Ex IndexClassifier::get_dummy(const list_property *dums, std::type_index type_idx = std::type_index(typeid(*dums)); auto pats_it = kernel.properties.pats_dict.find(type_idx); if (pats_it != kernel.properties.pats_dict.end()) { - Properties::pattern_map_t pats = pats_it->second; - std::pair pr=pats.equal_range(dums); + Properties::propmap_t pats = pats_it->second; + std::pair pr=pats.equal_range(dums); // std::cerr << "finding index not in: " << std::endl; // if(one) diff --git a/core/Props.cc b/core/Props.cc index d3084cddec..5d5897194c 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -148,7 +148,7 @@ bool Properties::has(const property *pb, Ex::iterator it) } // Look for property *pb - prop_pat_typemap_t::iterator pptit = pdit->second.find(typeid(*pb)); + typemap_t::iterator pptit = pdit->second.find(typeid(*pb)); if (pptit == pdit->second.end()) { return false; } @@ -400,61 +400,59 @@ void Properties::insert_prop(const Ex& et, const property *pr) { pattern *pat = new pattern(et); // Make sure there is no existing property of the same type matching pat - auto tmp = props_dict.find(pat->obj.begin()->name_only()); - if (tmp != props_dict.end() ) { - auto possible_dups = PropertyFilter(&tmp->second); - for (iterator dup_it = possible_dups.begin(); dup_it != possible_dups.end();) { - // std::cerr << "Accessing: " << dup_it->first << '\n'; - if (typeid(*dup_it->first) != typeid(*pr)) { - dup_it.next_proptype(); - continue; - } - // 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 - // and are of the same type as pr. - if( dup_it->second->match(*this, et.begin())) { - // 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(dup_it->first); - - 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 = dup_it->second; - const property *oldprop = dup_it->first; - - // 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. (why?) - iterator old_dup_it = dup_it; - ++dup_it; - // std::cerr << "Freeing: " << old_dup_it->first << '\n'; - erase(old_dup_it->first, old_dup_it->second); - - // 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. - - // FIXME: SelfAntiCommuting is not a list property, so above should be fine. - } else { - ++dup_it; - } + auto walk = begin(pat->obj.begin()->name_only()); + auto end_it = end(pat->obj.begin()->name_only()); + + while (walk != end_it) { + if (typeid(*walk->first) != typeid(*pr)) { + walk.next_proptype(); + continue; + } + // 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 + // and are of the same type as pr. + if( walk->second->match(*this, et.begin())) { + // 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(walk->first); + + 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. + const property *oldprop = walk->first; + pattern *oldpat = walk->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. + iterator old_dup = walk; + ++walk; + // std::cerr << "Freeing: " << old_dup_it->first << '\n'; + erase(old_dup->first, old_dup->second); + + // 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. + + // FIXME: SelfAntiCommuting is not a list property, so above should be fine. } else { - ++dup_it; + ++walk; } + } else { + ++walk; } } @@ -466,98 +464,6 @@ void Properties::insert_prop(const Ex& et, const property *pr) { } -/* -void Properties::insert_prop_old(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); - - // pit iterates over pat_prop pairs matching name_only() - 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. - - // erase pattern from props - 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. - - // erase pattern from props - 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))); - } -*/ // Insert a list property into the kernel. const list_property* Properties::insert_list_prop(const std::vector& its, const list_property* pr) diff --git a/core/Props.hh b/core/Props.hh index 6f684dc738..111e5942c1 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -265,6 +265,10 @@ namespace cadabra { // Register the type of an object. Usage: `register_type(prop);` void register_type(const property* prop); + // Fuzzy bool type + enum fbool {False, True, Unknown}; + fbool castable(std::type_index to, std::type_index from); + private: // Dictionary from type_index to human readable name. std::map types_to_names_; @@ -319,25 +323,13 @@ namespace cadabra { // property_map_t props; // pattern -> property // pattern_map_t pats; // property -> pattern; for list properties, patterns are stored here in order - /************************************************************************************** - * BELOW REVISES PROPS/PATS STRUCTURE. - * - * The below is intended as a replacement for props/pats by including information about the - * property_type directly. This could be used to circumvent dynamic casts where they are - * known to succeed, but currently only used for quickly finding properties of a specific - * type (e.g. Indices). - * - **************************************************************************************/ - typedef std::multimap pattern_map_t; - typedef std::multimap prop_pat_map_t; - - typedef std::map prop_pat_typemap_t; - typedef std::map property_dictmap_t; - typedef std::map> pattern_dictmap_t; - - property_dictmap_t props_dict; - pattern_dictmap_t pats_dict; - + typedef std::multimap propmap_t; + typedef std::map typemap_t; + typedef std::map namemap_t; + + typemap_t pats_dict; + namemap_t props_dict; + /// Normal search: given a pattern, get its property if any. template const T* get(Ex::iterator, bool ignore_parent_rel=false) const; template const T* get(Ex::iterator, int& serialnum, bool doserial=true, bool ignore_parent_rel=false) const; @@ -394,7 +386,6 @@ namespace cadabra { // Insert a property. Do not use this directly, use the public // interface `master_insert` instead. void insert_prop(const Ex&, const property *); - void insert_prop_old(const Ex&, const property *); const list_property* insert_list_prop(const std::vector&, const list_property *); bool check_label(const property *, const std::string&) const; bool check_label(const labelled_property *, const std::string&) const; @@ -409,32 +400,34 @@ namespace cadabra { public: - // Need specialized iterator and const_iterator classes. These are built from iterator_base below. - template - class iterator_base { - // Compile-time assurance that we cannot have T1=void but T2 not void. - static_assert(!(std::is_void::value && !std::is_void::value), "If T1 is void, T2 must also be void."); + // typedef std::multimap propmap_t; + // typedef std::map typemap_t; + template + class iterator_base { public: - using map_t = std::conditional_t; - using outer_iterator = std::conditional_t; - using inner_iterator = std::conditional_t; - + using map_t = std::conditional_t; + using outer_iterator = std::conditional_t; + using inner_iterator = std::conditional_t; using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = std::conditional_t; using pointer = value_type*; using reference = value_type&; - using self_type = iterator_base; + using self_type = iterator_base; + + iterator_base() = default; iterator_base(map_t* m, bool is_end = false) : typemap_(m) { if (is_end) { outer_it_ = typemap_->end(); inner_it_ = inner_iterator(); + // Warning: default-constructed iterator can only be compared with another + // default constructed iterator. Comparison with an actual iterator is undefined. } else { outer_it_ = typemap_->begin(); - skip_ahead(); + inner_it_ = outer_it_->second.begin(); } } @@ -442,28 +435,14 @@ namespace cadabra { ++inner_it_; if (inner_it_ == outer_it_->second.end()) { ++outer_it_; - skip_ahead(); + inner_it_ = outer_it_->second.begin(); } return *this; } - /* skip_back() doesn't work properly yet. - self_type& operator--() { - if (outer_it_ == typemap_->end()) { - --outer_it_; - skip_back(); - } - if (inner_it_ == outer_it_->second.begin()) { - --outer_it_; - skip_back(); - } - --inner_it_; - return *this; - } - */ self_type& next_proptype() { ++outer_it_; - skip_ahead(); + inner_it_ = outer_it_->second.begin(); return *this; } @@ -471,7 +450,7 @@ namespace cadabra { return outer_it_->first; } - const prop_pat_map_t& prop_pat_pairs() const { + const propmap_t& prop_pat_pairs() const { return outer_it_->second; } @@ -479,7 +458,8 @@ namespace cadabra { pointer operator->() const { return &(*inner_it_); } bool operator==(const self_type& other) const { - return inner_it_ == other.inner_it_ && outer_it_ == other.outer_it_; + return outer_it_ == other.outer_it_ && inner_it_ == other.inner_it_; + // should work correctly for end iterator } bool operator!=(const self_type& other) const { @@ -491,84 +471,38 @@ namespace cadabra { outer_iterator outer_it_; inner_iterator inner_it_; - static bool condition_(const property* pr) { - if constexpr (std::is_void::value) return true; - if constexpr (std::is_void::value) - return dynamic_cast(pr) != nullptr; - return dynamic_cast(pr) != nullptr || - dynamic_cast(pr) != nullptr; - } - - void skip_ahead() { - while (outer_it_ != typemap_->end()) { - if (outer_it_->second.empty()) { - ++outer_it_; - continue; - } - inner_it_ = outer_it_->second.begin(); - if (condition_(inner_it_->first)) { - if (inner_it_ != outer_it_->second.end()) return; - } - ++outer_it_; - } - inner_it_ = inner_iterator(); - } - - /* - // The below still needs to be fixed but we don't use it really. - void skip_back() { - while (outer_it_ != typemap_->begin()) { - inner_it_ = outer_it_->second.end(); - if (condition_(inner_it_->first)) { - if (inner_it_ != outer_it_->second.begin()) { - --inner_it_; - return; - } - } - --outer_it_; - } - if (condition_(inner_it_->first)) { - inner_it_ = outer_it_->second.end(); - if (inner_it_ != outer_it_->second.begin()) { - --inner_it_; - return; - } - } else { - inner_it_ = outer_it_->second.begin(); - } - } - */ - }; typedef iterator_base iterator; typedef iterator_base const_iterator; - - /// Specialized property filter. Use to build PropertyIterators that can be cast to T1 or T2. - template - class PropertyFilter { - using map_t = std::conditional_t; - using It = iterator_base; - public: - PropertyFilter(map_t* m) - : typemap_(m), - begin_(typemap_, /*is_end=*/false), - end_(typemap_, /*is_end=*/true) - {} - - It begin() const { - return begin_; - } - It end() const { - return end_; - } - - private: - map_t* typemap_; - It begin_; - It end_; - }; + // Iterator over all properties + iterator begin() {return iterator(&pats_dict);} + iterator end() {return iterator(&pats_dict, /*is_end=*/ true);} + const_iterator begin() const {return const_iterator(&pats_dict);} + const_iterator end() const {return const_iterator(&pats_dict, /*is_end = */ true);} + + // Iterator over all properties with a pattern matching name + iterator begin(nset_t::iterator name) { + auto it = props_dict.find(name); + if (it == props_dict.end()) return iterator{}; + else return iterator(&(it->second)); + } + iterator end(nset_t::iterator name) { + auto it = props_dict.find(name); + if (it == props_dict.end()) return iterator{}; + else return iterator(&(it->second), /*is_end=*/ true); + } + const_iterator begin(nset_t::iterator name) const { + auto it = props_dict.find(name); + if (it == props_dict.end()) return const_iterator{}; + else return const_iterator(&(it->second)); + } + const_iterator end(nset_t::iterator name) const { + auto it = props_dict.find(name); + if (it == props_dict.end()) return const_iterator{}; + else return const_iterator(&(it->second), /*is_end=*/ true); + } }; @@ -611,11 +545,6 @@ namespace cadabra { //std::cerr << *it->name_only() << std::endl; // std::cerr << props.size() << std::endl; - // The original version of this used walked the iterator pit and checked dynamic casts to T - // and to Inherit and PropertyInherit. The new PropertyFilter lets us walk the dictmap - // just for the various castable cases. But we have to split the logic up and check - // the T castable cases first. - // First look for properties of the node itself. Go through the loop twice: // once looking for patterns which do not have wildcards, and then looking // for wildcard patterns. @@ -628,43 +557,64 @@ namespace cadabra { if(std::is_same::value) ignore_properties=true; + // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true + auto walk = begin(it->name_only()); + auto end_it = end(it->name_only()); + // Make sure we found something before bothering to loop - auto props_dict_it = props_dict.find(it->name_only()); - if (props_dict_it != props_dict.end()) { - // Filter all properties that are castable to T. - auto castable_props = PropertyFilter(&props_dict_it->second); - - // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true + if (walk != end_it) { for(;;) { - for (const prop_pat_pair_t& prop_pat : castable_props) { - if(wildcards==prop_pat.second->children_wildcard()) { - // The cast is guaranteed to work so we can use - ret.first=dynamic_cast(prop_pat.first); - if(prop_pat.second->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { - ret.second=prop_pat.second; - if(!check_label(ret.first, label)) - ret.first=0; - else { - if(doserial) - serialnum=serial_number( prop_pat.first, prop_pat.second); - break; + while (walk != end_it) { + if(wildcards==walk->second->children_wildcard()) { + auto this_prop = walk->first; + ret.first=dynamic_cast(this_prop); + if (!ret.first) { + // If we can't dynamically cast this property, then move on + // but check heritability first + if (!inherits) { + inherits = dynamic_cast(this_prop) || + dynamic_cast *>(this_prop); } + walk.next_proptype(); + continue; + } + // If we *can* dynamically cast this property, loop over all associated patterns + // as there may be multiple patterns that are associated with the same property + // and there is no point in repeatedly casting the same property. + while (this_prop == walk->first) { + if(walk->second->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { + ret.second=walk->second; + if(!check_label(ret.first, label)) + ret = {0,0}; + //ret.first=0; + else { + if(doserial) + serialnum=serial_number( walk->first, walk->second); + break; + } + } + ++walk; + } + // None of the patterns associated with this_prop matched + // ret.first=0; + ret = {0,0}; + // Check heritability + if (!inherits) { + inherits = dynamic_cast(this_prop) || + dynamic_cast *>(this_prop); } - ret.first=0; - } + } + ++walk; } if(!wildcards && !ret.first) { // std::cerr << "not yet found, switching to wildcards" << std::endl; wildcards=true; + walk = begin(it->name_only()); // restarting the for loop } else break; } - - // Check heritability - auto heritable_props = PropertyFilter, PropertyInherit>(&props_dict_it->second); - inherits = (heritable_props.begin() != heritable_props.end()); - } + // Do not walk down the tree if the property cannot be passed up the tree. // FIXME: see issue/259. if(std::is_same::value) @@ -712,101 +662,96 @@ namespace cadabra { template const T* Properties::get(Ex::iterator it1, Ex::iterator it2, int& serialnum1, int& serialnum2, bool ignore_parent_rel) const { - bool found=false; - bool inherits1=false, inherits2=false; // Find all common properties of it1 and it2 - auto props_dict_it1 = props_dict.find(it1->name_only()); - auto props_dict_it2 = props_dict.find(it2->name_only()); - if (props_dict_it1 != props_dict.end() && props_dict_it2 != props_dict.end()) { - - // Find all castable properties that appear in both props_dict_t1->second and props_dict_t2->second - auto castable_props1 = PropertyFilter(&props_dict_it1->second); - auto castable_props2 = PropertyFilter(&props_dict_it2->second); - - // Search for properties that appear in both pit1 and pit2 - for (auto pit1 = castable_props1.begin(), pit2 = castable_props2.begin(); - pit1 != castable_props1.end() && pit2 != castable_props2.end(); ) - { - // Because we use a std::map, the std::type_index prop types are sorted - // so we can walk them simultaneously. - if (pit1.proptype() < pit2.proptype()) { - pit1.next_proptype(); - } - else if (pit2.proptype() < pit1.proptype()) { - pit2.next_proptype(); - } - else { - // pit1 and pit2 are of the same property type - - // Get the pat_prop_pairs for each - auto& prop_pats1 = pit1.prop_pat_pairs(); - auto& prop_pats2 = pit2.prop_pat_pairs(); - - bool match1_found = false; - bool match2_found = false; - - // By construction, the pat_props are always sorted by property pointer. - // This allows the iteration below to be as fast as possible. - for (auto ppit1 = prop_pats1.begin(), ppit2 = prop_pats2.begin(); - ppit1 != prop_pats1.end() && ppit2 != prop_pats2.end(); ) - { - // Advance both iterators until their properties match - if (ppit1->first < ppit2->first) { - ++ppit1; - match1_found = false; - } else if (ppit2->first < ppit1->first) { - ++ppit2; - match2_found = false; - } else { - // The properties match, so see if there is a pattern match - if (!match1_found) { - if (ppit1->second->match(*this, it1, ignore_parent_rel)) { - match1_found = true; - } else { - ++ppit1; - // assert(match1_found == false); - continue; - } - } - if (!match2_found) { - if (ppit2->second->match(*this, it2, ignore_parent_rel)) { - match2_found = true; - } else { - ++ppit2; - // assert(match2_found == false); - continue; - } + auto walk1 = begin(it1->name_only()); + auto walk2 = begin(it2->name_only()); + auto end1 = end(it1->name_only()); + auto end2 = end(it2->name_only()); + + if (walk1 != end1 && walk2 != end2) { + if (walk1.proptype() < walk2.proptype()) { + walk1.next_proptype(); + } else if (walk2.proptype() < walk1.proptype()) { + walk2.next_proptype(); + } else if (!dynamic_cast(walk1->first)) { + walk1.next_proptype(); + walk2.next_proptype(); + } else { + // walk1 and walk2 are of the same castable property type + // Take the property maps + auto& propmap1 = walk1.prop_pat_pairs(); + auto& propmap2 = walk2.prop_pat_pairs(); + bool match1_found = false; + bool match2_found = false; + // By construction, the pat_props are always sorted by property pointer. + // This allows the iteration below to be as fast as possible. + for (auto ppit1 = propmap1.begin(), ppit2 = propmap2.begin(); + ppit1 != propmap1.end() && ppit2 != propmap2.end(); ) { + // Advance both iterators until their properties match + if (ppit1->first < ppit2->first) { + ++ppit1; + match1_found = false; + } else if (ppit2->first < ppit1->first) { + ++ppit2; + match2_found = false; + } else { + // The properties match, so see if there is a pattern match + if (!match1_found) { + if (ppit1->second->match(*this, it1, ignore_parent_rel)) { + match1_found = true; + } else { + ++ppit1; + // assert(match1_found == false); + continue; } - // assert(match1_found && match2_found) - - // accept if properties are the same and patterns are not - if ((ppit1->first == ppit2->first) && (ppit1->second != ppit2->second)) { - serialnum1=serial_number( ppit1->first, ppit1->second ); - serialnum2=serial_number( ppit2->first, ppit2->second ); - // Return the recasted property - return dynamic_cast( ppit1->first ); + } + if (!match2_found) { + if (ppit2->second->match(*this, it2, ignore_parent_rel)) { + match2_found = true; + } else { + ++ppit2; + // assert(match2_found == false); + continue; } - // Otherwise, continue. - ++ppit1; - ++ppit2; } + // assert(match1_found && match2_found) + + // accept if properties are the same and patterns are not + if ((ppit1->first == ppit2->first) && (ppit1->second != ppit2->second)) { + serialnum1=serial_number( ppit1->first, ppit1->second ); + serialnum2=serial_number( ppit2->first, ppit2->second ); + // Return the recasted property + return dynamic_cast( ppit1->first ); + } + // Otherwise, continue. + ++ppit1; + ++ppit2; } - - pit1.next_proptype(); - pit2.next_proptype(); } + walk1.next_proptype(); + walk2.next_proptype(); } + } - // Are any of these properties heritable? - // FIXME: Below just uses PropertyInherit and not Inherit? - auto heritable_props = PropertyFilter(&props_dict_it1->second); - inherits1 = (heritable_props.begin() != heritable_props.end()); - - heritable_props = PropertyFilter(&props_dict_it2->second); - inherits2 = (heritable_props.begin() != heritable_props.end()); - + // Are any of these properties heritable? + // FIXME: Below just uses PropertyInherit and not Inherit? + walk1 = begin(it1->name_only()); + while (walk1 != end1) { + if (dynamic_cast(walk1->first)) { + inherits1 = true; + break; + } + walk1.next_proptype(); + } + walk2 = begin(it2->name_only()); + while (walk2 != end2) { + if (dynamic_cast(walk2->first)) { + inherits2 = true; + break; + } + walk2.next_proptype(); } // If no property was found, figure out whether a property is inherited from a child node. From 6413596e6961f1f6e01cfde7a150f62dd6f42507 Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Tue, 5 Aug 2025 22:26:04 -0400 Subject: [PATCH 15/18] Fixed mistakes. --- core/Props.cc | 3 +- core/Props.hh | 93 +++++++++++++++++++++++++++------------------------ 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/core/Props.cc b/core/Props.cc index 5d5897194c..bea677385a 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -401,9 +401,8 @@ void Properties::insert_prop(const Ex& et, const property *pr) { // Make sure there is no existing property of the same type matching pat auto walk = begin(pat->obj.begin()->name_only()); - auto end_it = end(pat->obj.begin()->name_only()); - while (walk != end_it) { + while (walk != end(pat->obj.begin()->name_only())) { if (typeid(*walk->first) != typeid(*pr)) { walk.next_proptype(); continue; diff --git a/core/Props.hh b/core/Props.hh index 111e5942c1..0693511429 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -427,7 +427,7 @@ namespace cadabra { // default constructed iterator. Comparison with an actual iterator is undefined. } else { outer_it_ = typemap_->begin(); - inner_it_ = outer_it_->second.begin(); + skip_ahead(); } } @@ -435,14 +435,14 @@ namespace cadabra { ++inner_it_; if (inner_it_ == outer_it_->second.end()) { ++outer_it_; - inner_it_ = outer_it_->second.begin(); + skip_ahead(); } return *this; } self_type& next_proptype() { ++outer_it_; - inner_it_ = outer_it_->second.begin(); + skip_ahead(); return *this; } @@ -471,6 +471,20 @@ namespace cadabra { outer_iterator outer_it_; inner_iterator inner_it_; + void skip_ahead() { + while (outer_it_ != typemap_->end()) { + if (outer_it_->second.empty()) { + ++outer_it_; + continue; + } + inner_it_ = outer_it_->second.begin(); + return; + } + inner_it_ = inner_iterator(); + } + + + }; typedef iterator_base iterator; @@ -506,9 +520,6 @@ namespace cadabra { }; - - - template const T* Properties::get(Ex::iterator it, bool ignore_parent_rel) const { @@ -560,61 +571,58 @@ namespace cadabra { // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true auto walk = begin(it->name_only()); auto end_it = end(it->name_only()); - // Make sure we found something before bothering to loop + + std::type_index last_type = typeid(void); + // Make sure we found something before bothering to loop if (walk != end_it) { for(;;) { + // first pass: wildcards == false + // second pass (optional): wildcards == true + + // walk takes us through all properties that have patterns matching name while (walk != end_it) { - if(wildcards==walk->second->children_wildcard()) { - auto this_prop = walk->first; - ret.first=dynamic_cast(this_prop); + // Upon (re)starting the loop, check whether the property type has changed. + // If it has, check castability and skip if not castable. + if (last_type != walk.proptype()) { + // This is the first time we are encountering this property type in the loop. + // Check heritability once per type + inherits = inherits || dynamic_cast(walk->first) || dynamic_cast *>(walk->first); + last_type = walk.proptype(); + ret.first = dynamic_cast(walk->first); if (!ret.first) { - // If we can't dynamically cast this property, then move on + // If we can't dynamically cast this property type, then move on... // but check heritability first - if (!inherits) { - inherits = dynamic_cast(this_prop) || - dynamic_cast *>(this_prop); - } walk.next_proptype(); continue; } - // If we *can* dynamically cast this property, loop over all associated patterns - // as there may be multiple patterns that are associated with the same property - // and there is no point in repeatedly casting the same property. - while (this_prop == walk->first) { - if(walk->second->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { - ret.second=walk->second; - if(!check_label(ret.first, label)) - ret = {0,0}; - //ret.first=0; - else { - if(doserial) - serialnum=serial_number( walk->first, walk->second); - break; - } - } - ++walk; + } + // We are only dealing with castable properties now. + if(wildcards==walk->second->children_wildcard()) { + if(walk->second->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { + ret.first = dynamic_cast(walk->first); + ret.second = walk->second; + if(!check_label(ret.first, label)) { + ret.first=0; } - // None of the patterns associated with this_prop matched - // ret.first=0; - ret = {0,0}; - // Check heritability - if (!inherits) { - inherits = dynamic_cast(this_prop) || - dynamic_cast *>(this_prop); + else { + if(doserial) + serialnum=serial_number( walk->first, walk->second); + break; } - } - ++walk; + } + ret.first=0; } + ++walk; + } if(!wildcards && !ret.first) { // std::cerr << "not yet found, switching to wildcards" << std::endl; wildcards=true; walk = begin(it->name_only()); // restarting the for loop } else break; - } } - + } // Do not walk down the tree if the property cannot be passed up the tree. // FIXME: see issue/259. if(std::is_same::value) @@ -637,7 +645,6 @@ namespace cadabra { // std::cout << ret << std::endl; return ret; } - template const T* Properties::get(Ex::iterator it, const std::string& label) const { From a1bbb8cd25985a6cc58d1883f5f3805ea69a9b3d Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Wed, 6 Aug 2025 10:56:19 -0400 Subject: [PATCH 16/18] Fixed some bugs in Props.hh. --- CMakeLists.txt | 2 +- core/Props.cc | 2 +- core/Props.hh | 148 ++++++++++++++++++++++++------------------------- 3 files changed, 74 insertions(+), 78 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 594cb52d13..442146cab2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,7 +254,7 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized") endif() endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O1 -Wall -Wextra -Wunused -Wno-psabi -Wno-unknown-pragmas -Wno-misleading-indentation -fvisibility=hidden -Wno-unused-but-set-variable -Wno-unused-parameter -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wall -Wextra -Wunused -Wno-psabi -Wno-unknown-pragmas -Wno-misleading-indentation -fvisibility=hidden -Wno-unused-but-set-variable -Wno-unused-parameter") endif() # Clang diff --git a/core/Props.cc b/core/Props.cc index bea677385a..c34be4f0da 100644 --- a/core/Props.cc +++ b/core/Props.cc @@ -402,7 +402,7 @@ void Properties::insert_prop(const Ex& et, const property *pr) { // Make sure there is no existing property of the same type matching pat auto walk = begin(pat->obj.begin()->name_only()); - while (walk != end(pat->obj.begin()->name_only())) { + while (walk != end()) { if (typeid(*walk->first) != typeid(*pr)) { walk.next_proptype(); continue; diff --git a/core/Props.hh b/core/Props.hh index 0693511429..8b5b496eef 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -31,6 +31,10 @@ along with this program. If not, see . #include #include +#ifdef NDEBUG +#undef NDEBUG +#endif +#include namespace cadabra { class Properties; @@ -416,15 +420,13 @@ namespace cadabra { using reference = value_type&; using self_type = iterator_base; - iterator_base() = default; + iterator_base(bool is_end = true) : is_end_(is_end) {} - iterator_base(map_t* m, bool is_end = false) - : typemap_(m) { - if (is_end) { + + iterator_base(map_t* m, bool is_end_ = false) + : typemap_(m), is_end_(is_end_) { + if (is_end_) { outer_it_ = typemap_->end(); - inner_it_ = inner_iterator(); - // Warning: default-constructed iterator can only be compared with another - // default constructed iterator. Comparison with an actual iterator is undefined. } else { outer_it_ = typemap_->begin(); skip_ahead(); @@ -458,8 +460,9 @@ namespace cadabra { pointer operator->() const { return &(*inner_it_); } bool operator==(const self_type& other) const { + if (is_end_ && other.is_end_) return true; + if (is_end_ || other.is_end_) return false; return outer_it_ == other.outer_it_ && inner_it_ == other.inner_it_; - // should work correctly for end iterator } bool operator!=(const self_type& other) const { @@ -470,6 +473,7 @@ namespace cadabra { map_t* typemap_; outer_iterator outer_it_; inner_iterator inner_it_; + bool is_end_; void skip_ahead() { while (outer_it_ != typemap_->end()) { @@ -479,8 +483,13 @@ namespace cadabra { } inner_it_ = outer_it_->second.begin(); return; + /* + if (inner_it_ != outer_it_->second.end()) return; + ++outer_it_; + */ } - inner_it_ = inner_iterator(); + // inner_it_ = inner_iterator(); + is_end_ = true; } @@ -492,31 +501,23 @@ namespace cadabra { // Iterator over all properties iterator begin() {return iterator(&pats_dict);} - iterator end() {return iterator(&pats_dict, /*is_end=*/ true);} const_iterator begin() const {return const_iterator(&pats_dict);} - const_iterator end() const {return const_iterator(&pats_dict, /*is_end = */ true);} - // Iterator over all properties with a pattern matching name iterator begin(nset_t::iterator name) { auto it = props_dict.find(name); - if (it == props_dict.end()) return iterator{}; + if (it == props_dict.end()) return iterator{true}; else return iterator(&(it->second)); } - iterator end(nset_t::iterator name) { - auto it = props_dict.find(name); - if (it == props_dict.end()) return iterator{}; - else return iterator(&(it->second), /*is_end=*/ true); - } const_iterator begin(nset_t::iterator name) const { auto it = props_dict.find(name); - if (it == props_dict.end()) return const_iterator{}; + if (it == props_dict.end()) return const_iterator{true}; else return const_iterator(&(it->second)); } - const_iterator end(nset_t::iterator name) const { - auto it = props_dict.find(name); - if (it == props_dict.end()) return const_iterator{}; - else return const_iterator(&(it->second), /*is_end=*/ true); - } + + // All end iterators are the same. + iterator end() {return iterator(/*is_end=*/ true);} + const_iterator end() const {return const_iterator(/*is_end=*/ true);} + }; @@ -569,59 +570,56 @@ namespace cadabra { ignore_properties=true; // Outer loop runs first with wildcards = false, second (if needed) with wildcards = true - auto walk = begin(it->name_only()); - auto end_it = end(it->name_only()); - std::type_index last_type = typeid(void); - - // Make sure we found something before bothering to loop - if (walk != end_it) { - for(;;) { - // first pass: wildcards == false - // second pass (optional): wildcards == true - - // walk takes us through all properties that have patterns matching name - while (walk != end_it) { - // Upon (re)starting the loop, check whether the property type has changed. - // If it has, check castability and skip if not castable. - if (last_type != walk.proptype()) { - // This is the first time we are encountering this property type in the loop. - // Check heritability once per type - inherits = inherits || dynamic_cast(walk->first) || dynamic_cast *>(walk->first); - last_type = walk.proptype(); - ret.first = dynamic_cast(walk->first); - if (!ret.first) { - // If we can't dynamically cast this property type, then move on... - // but check heritability first - walk.next_proptype(); - continue; - } + for(;;) { + // first pass: wildcards == false + // second pass (optional): wildcards == true + auto walk = begin(it->name_only()); + auto end_it = end(); + std::type_index last_type = typeid(void); + + // walk takes us through all properties that have patterns matching name + while (walk != end_it) { + // Upon (re)starting the loop, check whether the property type has changed. + // If it has, check castability and skip if not castable. + if (last_type != walk.proptype()) { + // This is the first time we are encountering this property type in the loop. + // Check heritability once per type + inherits = inherits || dynamic_cast(walk->first) || dynamic_cast *>(walk->first); + last_type = walk.proptype(); + ret.first = dynamic_cast(walk->first); + if (!ret.first) { + // If we can't dynamically cast this property type, then move on... + // but check heritability first + walk.next_proptype(); + continue; } - // We are only dealing with castable properties now. - if(wildcards==walk->second->children_wildcard()) { - if(walk->second->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { - ret.first = dynamic_cast(walk->first); - ret.second = walk->second; - if(!check_label(ret.first, label)) { - ret.first=0; - } - else { - if(doserial) - serialnum=serial_number( walk->first, walk->second); - break; - } + } + // We are only dealing with castable properties now. + if(wildcards==walk->second->children_wildcard()) { + if(walk->second->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { + ret.first = dynamic_cast(walk->first); + assert(ret.first != nullptr); + ret.second = walk->second; + if(!check_label(ret.first, label)) { + ret.first=0; + } + else { + if(doserial) + serialnum=serial_number( walk->first, walk->second); + break; } - ret.first=0; } - ++walk; + ret.first=0; } - if(!wildcards && !ret.first) { - // std::cerr << "not yet found, switching to wildcards" << std::endl; - wildcards=true; - walk = begin(it->name_only()); // restarting the for loop - } - else break; + ++walk; } + if(!wildcards && !ret.first) { + // std::cerr << "not yet found, switching to wildcards" << std::endl; + wildcards=true; + // walk = begin(it->name_only()); // restarting the for loop + } + else break; } // Do not walk down the tree if the property cannot be passed up the tree. // FIXME: see issue/259. @@ -674,10 +672,8 @@ namespace cadabra { // Find all common properties of it1 and it2 auto walk1 = begin(it1->name_only()); auto walk2 = begin(it2->name_only()); - auto end1 = end(it1->name_only()); - auto end2 = end(it2->name_only()); - if (walk1 != end1 && walk2 != end2) { + if (walk1 != end() && walk2 != end()) { if (walk1.proptype() < walk2.proptype()) { walk1.next_proptype(); } else if (walk2.proptype() < walk1.proptype()) { @@ -745,7 +741,7 @@ namespace cadabra { // Are any of these properties heritable? // FIXME: Below just uses PropertyInherit and not Inherit? walk1 = begin(it1->name_only()); - while (walk1 != end1) { + while (walk1 != end()) { if (dynamic_cast(walk1->first)) { inherits1 = true; break; @@ -753,7 +749,7 @@ namespace cadabra { walk1.next_proptype(); } walk2 = begin(it2->name_only()); - while (walk2 != end2) { + while (walk2 != end()) { if (dynamic_cast(walk2->first)) { inherits2 = true; break; From f3dda1110e8234f0985335d222c9bd28f7eeea9e Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Wed, 6 Aug 2025 13:57:57 -0400 Subject: [PATCH 17/18] Isolated references to props_dict and pats_dict to Props.hh and Props.cc --- core/IndexClassifier.cc | 101 ++++++++-------- core/Props.hh | 53 +++++++- core/algorithms/rename_dummies.cc | 23 ++-- core/pythoncdb/py_properties.cc | 194 +++++++++++++++--------------- 4 files changed, 201 insertions(+), 170 deletions(-) diff --git a/core/IndexClassifier.cc b/core/IndexClassifier.cc index 4acd97f4b7..e1ac474c48 100644 --- a/core/IndexClassifier.cc +++ b/core/IndexClassifier.cc @@ -490,61 +490,56 @@ Ex IndexClassifier::get_dummy(const list_property *dums, const index_map_t * four, const index_map_t * five) const { - std::type_index type_idx = std::type_index(typeid(*dums)); - auto pats_it = kernel.properties.pats_dict.find(type_idx); - if (pats_it != kernel.properties.pats_dict.end()) { - Properties::propmap_t pats = pats_it->second; - std::pair pr=pats.equal_range(dums); - - // std::cerr << "finding index not in: " << std::endl; - // if(one) - // for(auto& i: *one) - // std::cerr << i.first << std::endl; - // if(two) - // for(auto& i: *two) - // std::cerr << i.first << std::endl; - // if(three) - // for(auto& i: *three) - // std::cerr << i.first << std::endl; - // if(four) - // for(auto& i: *four) - // std::cerr << i.first << std::endl; - // if(five) - // for(auto& i: *five) - // std::cerr << i.first << std::endl; - - while(pr.first!=pr.second) { - // std::cerr << "trying: " << std::endl; - // std::cerr << pr.first->second->obj << std::endl; - if(pr.first->second->obj.begin()->is_autodeclare_wildcard()) { - // std::cerr << "is autodeclare wildcard" << std::endl; - std::string base=*pr.first->second->obj.begin()->name_only(); - int used=max_numbered_name(base, one, two, three, four, five); - std::ostringstream str; - str << base << used+1; - // txtout << "going to use " << str.str() << std::endl; - nset_t::iterator newnm=name_set.insert(str.str()).first; - Ex ret; - ret.set_head(str_node(newnm)); - return ret; - } - else { - // std::cerr << "is NOT autodeclare" << std::endl; - const Ex& inm=(*pr.first).second->obj; - // BUG: even if only _{a} is in the used map, we should not - // accept ^{a}. But since ... - if(index_in_set(inm, one)==false && - index_in_set(inm, two)==false && - index_in_set(inm, three)==false && - index_in_set(inm, four)==false && - index_in_set(inm, five)==false) { - // std::cerr << "ok to use " << inm << std::endl; - return inm; - } + std::pair pr=kernel.properties.equal_range(dums); + + // std::cerr << "finding index not in: " << std::endl; + // if(one) + // for(auto& i: *one) + // std::cerr << i.first << std::endl; + // if(two) + // for(auto& i: *two) + // std::cerr << i.first << std::endl; + // if(three) + // for(auto& i: *three) + // std::cerr << i.first << std::endl; + // if(four) + // for(auto& i: *four) + // std::cerr << i.first << std::endl; + // if(five) + // for(auto& i: *five) + // std::cerr << i.first << std::endl; + + while(pr.first!=pr.second) { + // std::cerr << "trying: " << std::endl; + // std::cerr << pr.first->second->obj << std::endl; + if(pr.first->second->obj.begin()->is_autodeclare_wildcard()) { + // std::cerr << "is autodeclare wildcard" << std::endl; + std::string base=*pr.first->second->obj.begin()->name_only(); + int used=max_numbered_name(base, one, two, three, four, five); + std::ostringstream str; + str << base << used+1; + // txtout << "going to use " << str.str() << std::endl; + nset_t::iterator newnm=name_set.insert(str.str()).first; + Ex ret; + ret.set_head(str_node(newnm)); + return ret; + } + else { + // std::cerr << "is NOT autodeclare" << std::endl; + const Ex& inm=(*pr.first).second->obj; + // BUG: even if only _{a} is in the used map, we should not + // accept ^{a}. But since ... + if(index_in_set(inm, one)==false && + index_in_set(inm, two)==false && + index_in_set(inm, three)==false && + index_in_set(inm, four)==false && + index_in_set(inm, five)==false) { + // std::cerr << "ok to use " << inm << std::endl; + return inm; } - ++pr.first; } - } + ++pr.first; + } const Indices *dd=dynamic_cast(dums); assert(dd); diff --git a/core/Props.hh b/core/Props.hh index 8b5b496eef..e00d13ef81 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -433,6 +433,14 @@ namespace cadabra { } } + iterator_base(map_t* m, outer_iterator outer, inner_iterator inner) + : typemap_(m), outer_it_(outer), inner_it_(inner), is_end_(false) { + if (inner_it_ == outer_it_->second.end()) { + ++outer_it_; + skip_ahead(); + } + } + self_type& operator++() { ++inner_it_; if (inner_it_ == outer_it_->second.end()) { @@ -448,6 +456,14 @@ namespace cadabra { return *this; } + self_type& next_prop() { + const property* this_prop = inner_it_->first; + while (!is_end_ && inner_it_->first == this_prop) { + operator++(); + } + return *this; + } + std::type_index proptype() const { return outer_it_->first; } @@ -499,10 +515,11 @@ namespace cadabra { typedef iterator_base iterator; typedef iterator_base const_iterator; - // Iterator over all properties + // Create iterator over all property/pattern pairs iterator begin() {return iterator(&pats_dict);} const_iterator begin() const {return const_iterator(&pats_dict);} - // Iterator over all properties with a pattern matching name + + // Create iterator over all property/pattern pairs with a pattern matching name iterator begin(nset_t::iterator name) { auto it = props_dict.find(name); if (it == props_dict.end()) return iterator{true}; @@ -513,6 +530,32 @@ namespace cadabra { if (it == props_dict.end()) return const_iterator{true}; else return const_iterator(&(it->second)); } + + // Create iterator over all property/pattern pairs of a specific type + iterator begin(std::type_index type) { + auto it = pats_dict.find(type); + if (it == pats_dict.end()) return iterator{true}; + else return iterator(&(it->second)); + } + const_iterator begin(std::type_index type) const { + auto it = pats_dict.find(type); + if (it == pats_dict.end()) return const_iterator{true}; + else return const_iterator(&(it->second)); + } + + // Return pair corresponding to begin and end of a property range + std::pair equal_range(const property *prop) { + auto it = pats_dict.find(typeid(*prop)); + if (it == pats_dict.end()) return {iterator{true}, iterator{true}}; + auto range = it->second.equal_range(prop); + return {iterator{&pats_dict, it, range.first}, iterator{&pats_dict, it, range.second}}; + } + std::pair equal_range(const property *prop) const { + auto it = pats_dict.find(typeid(*prop)); + if (it == pats_dict.end()) return {const_iterator{true}, const_iterator{true}}; + auto range = it->second.equal_range(prop); + return {const_iterator{&pats_dict, it, range.first}, const_iterator{&pats_dict, it, range.second}}; + } // All end iterators are the same. iterator end() {return iterator(/*is_end=*/ true);} @@ -590,7 +633,6 @@ namespace cadabra { ret.first = dynamic_cast(walk->first); if (!ret.first) { // If we can't dynamically cast this property type, then move on... - // but check heritability first walk.next_proptype(); continue; } @@ -682,7 +724,10 @@ namespace cadabra { walk1.next_proptype(); walk2.next_proptype(); } else { - // walk1 and walk2 are of the same castable property type + // walk1 and walk2 are of the same castable property type. + // Unlike what we do with get() above for a single iterator. + // we dive in and process here ALL the properties of the same type. + // Take the property maps auto& propmap1 = walk1.prop_pat_pairs(); auto& propmap2 = walk2.prop_pat_pairs(); diff --git a/core/algorithms/rename_dummies.cc b/core/algorithms/rename_dummies.cc index df50c57bb0..fc6705efa0 100644 --- a/core/algorithms/rename_dummies.cc +++ b/core/algorithms/rename_dummies.cc @@ -84,20 +84,17 @@ Algorithm::result_t rename_dummies::apply(iterator& st) // with this name. const Indices *ind2=0; if(dset2!="") { - auto pats_it = kernel.properties.pats_dict.find(typeid(Indices)); - if (pats_it != kernel.properties.pats_dict.end()) { - auto pats = pats_it->second; - auto f2=pats.begin(); - while(f2!=pats.end()) { - ind2 = dynamic_cast(f2->first); - if(ind2) { - if(ind2->set_name==dset2) - break; - else ind2=0; - } - ++f2; + // FIXME: We assume only Indices type is castable to Indices. + auto f2 = kernel.properties.begin(typeid(Indices)); + while(f2!=kernel.properties.end()) { + ind2 = dynamic_cast(f2->first); + if(ind2) { + if(ind2->set_name==dset2) + break; + else ind2=0; } - } + ++f2; + } if(ind2==0) throw ConsistencyException("No index set with name `"+dset2+"' known."); } diff --git a/core/pythoncdb/py_properties.cc b/core/pythoncdb/py_properties.cc index 0157d40194..8379d1f1b6 100644 --- a/core/pythoncdb/py_properties.cc +++ b/core/pythoncdb/py_properties.cc @@ -318,9 +318,84 @@ namespace cadabra { pybind11::list ret; std::string res; bool multi = false; - for (const auto& [_, pats] : props.pats_dict) { - for (auto it = pats.begin(); it != pats.end(); ++it) { + + for (auto it = props.begin(); it != props.end(); ++it) { + if (it->first->hidden()) continue; + // print the property name if we are at the end or if the next entry is for + // a different property. + decltype(it) nxt = it; + ++nxt; + if (res == "" && (nxt != props.end() && it->first == nxt->first)) { + if(handles_latex_view) res += "\\{"; + else res += "{"; + multi = true; + } + + std::ostringstream str; + if(handles_latex_view) { + DisplayTeX dt(*get_kernel_from_scope(), it->second->obj); + dt.output(str); + } + else { + DisplayTerminal dt(*get_kernel_from_scope(), it->second->obj); + dt.output(str); + } + + res += str.str(); + + if (nxt == props.end() || it->first != nxt->first) { + if (multi) { + if(handles_latex_view) res += "\\}"; + else res += "}"; + } + multi = false; + res += "::\\texttt{"; + res += (*it).first->name() + "}"; + ret.append(LaTeXString(res)); + res = ""; + } + else { + res += ", "; + } + } + return ret; + } + + + pybind11::list list_properties() + { + // This function is fundamentally limited. We would *like* to return a list of + // BoundProperties, so that you can do something with the output. But we cannot + // walk the full property list and create a BoundProperty for each of them, as + // we do not know the type (we can only dynamic_cast). + // + // So for now this is just returning a list of LaTeXStrings, obtained by asking + // each property to print itself. + + Kernel *kernel = get_kernel_from_scope(); + Properties& props = kernel->properties; + + // pybind11::dict globals = get_globals(); + // bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); + + pybind11::list ret; + // std::string res; + // bool multi = false; + for (auto it = props.begin(); it != props.end(); it.next_prop()) { if (it->first->hidden()) continue; + const property* prop = it->first; + + Ex_ptr ex_ptr = std::make_shared(it->second->obj); + + pybind11::object bound_property = + py_property_registry.create_bound_property(prop, ex_ptr); + + if (bound_property.is_none()) + continue; + + ret.append(bound_property); + + /* // print the property name if we are at the end or if the next entry is for // a different property. decltype(it) nxt = it; @@ -357,85 +432,7 @@ namespace cadabra { else { res += ", "; } - } - } - return ret; - } - - - pybind11::list list_properties() - { - // This function is fundamentally limited. We would *like* to return a list of - // BoundProperties, so that you can do something with the output. But we cannot - // walk the full property list and create a BoundProperty for each of them, as - // we do not know the type (we can only dynamic_cast). - // - // So for now this is just returning a list of LaTeXStrings, obtained by asking - // each property to print itself. - - Kernel *kernel = get_kernel_from_scope(); - Properties& props = kernel->properties; - - // pybind11::dict globals = get_globals(); - // bool handles_latex_view = globals["server"].attr("handles")(pybind11::str("latex_view")).cast(); - - pybind11::list ret; - // std::string res; - // bool multi = false; - for (const auto& [_, pats] : props.pats_dict) { - for (auto it = pats.begin(); it != pats.end(); it=pats.upper_bound(it->first)) { - if (it->first->hidden()) continue; - const property* prop = it->first; - - Ex_ptr ex_ptr = std::make_shared(it->second->obj); - - pybind11::object bound_property = - py_property_registry.create_bound_property(prop, ex_ptr); - - if (bound_property.is_none()) - continue; - - ret.append(bound_property); - - /* - // print the property name if we are at the end or if the next entry is for - // a different property. - decltype(it) nxt = it; - ++nxt; - if (res == "" && (nxt != pats.end() && it->first == nxt->first)) { - if(handles_latex_view) res += "\\{"; - else res += "{"; - multi = true; - } - - std::ostringstream str; - if(handles_latex_view) { - DisplayTeX dt(*get_kernel_from_scope(), it->second->obj); - dt.output(str); - } - else { - DisplayTerminal dt(*get_kernel_from_scope(), it->second->obj); - dt.output(str); - } - - res += str.str(); - - if (nxt == pats.end() || it->first != nxt->first) { - if (multi) { - if(handles_latex_view) res += "\\}"; - else res += "}"; - } - multi = false; - res += "::\\texttt{"; - res += (*it).first->name() + "}"; - ret.append(LaTeXString(res)); - res = ""; - } - else { - res += ", "; - } - */ - } + */ } return ret; } @@ -447,30 +444,27 @@ namespace cadabra { // Dictionary of properties, keyed by property type pybind11::dict ret; - for (const auto& [_, pats] : props.pats_dict) { - // Iterate over keys to the pats multimap - for (auto it = pats.begin(); it != pats.end(); it=pats.upper_bound(it->first)) { - if (it->first->hidden()) continue; - const property* prop = it->first; + for (auto it = props.begin(); it!=props.end(); it.next_prop()) { + if (it->first->hidden()) continue; + const property* prop = it->first; - Ex_ptr ex_ptr = std::make_shared(it->second->obj); + Ex_ptr ex_ptr = std::make_shared(it->second->obj); - pybind11::object bound_property = - py_property_registry.create_bound_property(prop, ex_ptr); + pybind11::object bound_property = + py_property_registry.create_bound_property(prop, ex_ptr); - if (bound_property.is_none()) - continue; + if (bound_property.is_none()) + continue; - // Key: Python class name of the bound property - pybind11::str key = - pybind11::str(bound_property.get_type().attr("__name__")); + // Key: Python class name of the bound property + pybind11::str key = + pybind11::str(bound_property.get_type().attr("__name__")); - // Ensure a list exists for this key and append - if (!ret.contains(key)) - ret[key] = pybind11::list(); + // Ensure a list exists for this key and append + if (!ret.contains(key)) + ret[key] = pybind11::list(); - ret[key].cast().append(bound_property); - } + ret[key].cast().append(bound_property); } return ret; } @@ -479,7 +473,7 @@ namespace cadabra { { auto kernel = get_kernel_from_scope(); // auto its = kernel->properties.pats.equal_range(indices); - auto its = kernel->properties.pats_dict[typeid(*indices)].equal_range(indices); + auto its = kernel->properties.equal_range(indices); std::vector res; for (auto it = its.first; it != its.second; ++it) { From 585e9fdb5aa9f4a26b80f5c233ebad980a78041b Mon Sep 17 00:00:00 2001 From: Daniel Butter Date: Wed, 6 Aug 2025 14:36:24 -0400 Subject: [PATCH 18/18] Modifications to Props to simplify some code. --- core/Props.hh | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/core/Props.hh b/core/Props.hh index e00d13ef81..47f6a298a3 100644 --- a/core/Props.hh +++ b/core/Props.hh @@ -31,10 +31,8 @@ along with this program. If not, see . #include #include -#ifdef NDEBUG -#undef NDEBUG -#endif #include + namespace cadabra { class Properties; @@ -420,13 +418,13 @@ namespace cadabra { using reference = value_type&; using self_type = iterator_base; - iterator_base(bool is_end = true) : is_end_(is_end) {} - + iterator_base(bool is_end = true) { + typemap_ = nullptr; + } - iterator_base(map_t* m, bool is_end_ = false) - : typemap_(m), is_end_(is_end_) { + iterator_base(map_t* m, bool is_end_ = false): typemap_(m) { if (is_end_) { - outer_it_ = typemap_->end(); + typemap_ = nullptr; } else { outer_it_ = typemap_->begin(); skip_ahead(); @@ -434,7 +432,7 @@ namespace cadabra { } iterator_base(map_t* m, outer_iterator outer, inner_iterator inner) - : typemap_(m), outer_it_(outer), inner_it_(inner), is_end_(false) { + : typemap_(m), outer_it_(outer), inner_it_(inner) { if (inner_it_ == outer_it_->second.end()) { ++outer_it_; skip_ahead(); @@ -458,7 +456,7 @@ namespace cadabra { self_type& next_prop() { const property* this_prop = inner_it_->first; - while (!is_end_ && inner_it_->first == this_prop) { + while (typemap_ && inner_it_->first == this_prop) { operator++(); } return *this; @@ -476,9 +474,7 @@ namespace cadabra { pointer operator->() const { return &(*inner_it_); } bool operator==(const self_type& other) const { - if (is_end_ && other.is_end_) return true; - if (is_end_ || other.is_end_) return false; - return outer_it_ == other.outer_it_ && inner_it_ == other.inner_it_; + return (!typemap_ && !other.typemap_) || (typemap_ == other.typemap_ && outer_it_==other.outer_it_ && inner_it_ == other.inner_it_); } bool operator!=(const self_type& other) const { @@ -489,7 +485,6 @@ namespace cadabra { map_t* typemap_; outer_iterator outer_it_; inner_iterator inner_it_; - bool is_end_; void skip_ahead() { while (outer_it_ != typemap_->end()) { @@ -505,7 +500,8 @@ namespace cadabra { */ } // inner_it_ = inner_iterator(); - is_end_ = true; + // is_end_ = true; + typemap_ = nullptr; } @@ -627,8 +623,6 @@ namespace cadabra { // If it has, check castability and skip if not castable. if (last_type != walk.proptype()) { // This is the first time we are encountering this property type in the loop. - // Check heritability once per type - inherits = inherits || dynamic_cast(walk->first) || dynamic_cast *>(walk->first); last_type = walk.proptype(); ret.first = dynamic_cast(walk->first); if (!ret.first) { @@ -663,10 +657,22 @@ namespace cadabra { } else break; } + // Do not walk down the tree if the property cannot be passed up the tree. // FIXME: see issue/259. - if(std::is_same::value) + if(std::is_same::value) { inherits=false; + } else if (!ret.first) { + auto walk = begin(it->name_only()); + auto end_it = end(); + while (walk != end_it) { + if (dynamic_cast*>(walk->first) || dynamic_cast(walk->first)) { + inherits = true; + break; + } + walk.next_proptype(); + } + } // If no property was found, figure out whether a property is inherited from a child node. if(!ret.first && inherits) { @@ -714,8 +720,9 @@ namespace cadabra { // Find all common properties of it1 and it2 auto walk1 = begin(it1->name_only()); auto walk2 = begin(it2->name_only()); + auto end_it = end(); - if (walk1 != end() && walk2 != end()) { + if (walk1 != end_it && walk2 != end_it) { if (walk1.proptype() < walk2.proptype()) { walk1.next_proptype(); } else if (walk2.proptype() < walk1.proptype()) { @@ -786,7 +793,7 @@ namespace cadabra { // Are any of these properties heritable? // FIXME: Below just uses PropertyInherit and not Inherit? walk1 = begin(it1->name_only()); - while (walk1 != end()) { + while (walk1 != end_it) { if (dynamic_cast(walk1->first)) { inherits1 = true; break; @@ -794,7 +801,7 @@ namespace cadabra { walk1.next_proptype(); } walk2 = begin(it2->name_only()); - while (walk2 != end()) { + while (walk2 != end_it) { if (dynamic_cast(walk2->first)) { inherits2 = true; break;