A strange behaviour while implementing archive in a user-defined class
Dear all, I got a strange behaviour while implementing the archive in user-defined class, to make the behaviour more explicitly, let me post the C++ code here: #include <ginac/ginac.h> #include <fstream> using namespace GiNaC; using namespace std; class Symbol : public symbol { GINAC_DECLARE_REGISTERED_CLASS(Symbol, symbol) public: Symbol(const string &s); void archive(archive_node & n) const override; void read_archive(const archive_node& n, lst& sym_lst) override; unsigned calchash() const override; }; GINAC_DECLARE_UNARCHIVER(Symbol); GINAC_IMPLEMENT_REGISTERED_CLASS(Symbol, symbol) GINAC_BIND_UNARCHIVER(Symbol); const symbol & get_symbol(const string & s) { static map<string, symbol> dict; if (dict.find(s) == dict.end()) dict[s] = symbol(s); return dict[s]; } Symbol::Symbol() { } Symbol::Symbol(const string &s) : symbol(get_symbol(s)) { } int Symbol::compare_same_type(const basic &other) const { const Symbol &o = static_cast<const Symbol &>(other); int ret = get_name().compare(o.get_name()); if(ret==0) return 0; else if(ret<0) return -1; else return 1; } void Symbol::archive(archive_node & n) const { inherited::archive(n); } void Symbol::read_archive(const archive_node& n, lst& sym_lst) { inherited::read_archive(n, sym_lst); *this = Symbol(get_name()); } unsigned Symbol::calchash() const { static auto hash = symbol("_").gethash(); return hash; } int main() { Symbol k1("k1"), k2("k2"); auto garfn = "tmp.gar"; { archive ar; ex val = lst{ k1*k1, k2*k2 }; ar.archive_ex(val, "key"); ofstream out(garfn); out << ar; out.close(); cout << "writed: " << val << endl; } { archive ar; ifstream in(garfn); in >> ar; in.close(); auto val = ar.unarchive_ex(lst{}, "key"); cout << "read: " << val << endl; } // console output is // writed: {k1^2,k2^2} // read: {k1^2,k2^2} { archive ar; ex val = lst{ k1*k1==0, k2*k2==0 }; ar.archive_ex(val, "key"); ofstream out(garfn); out << ar; out.close(); cout << "writed: " << val << endl; } { archive ar; ifstream in(garfn); in >> ar; in.close(); auto val = ar.unarchive_ex(lst{}, "key"); cout << "read: " << val << endl; } // console output is // writed: {k1^2==0,k2^2==0} // read: {k1^2==0,k1^2==0} // Note that the last line above, both items are the same: k1^2==0 return 0; } Here I want to introduce a class Symbol, when one defines Symbol a(“a”), b(“a”);, then a and b will be the same or equal, so I override the method compare_same_type. The last console output seems very strange to me, the both items are the same, while the expected result is "read: {k1^2==0,k2^2==0}”. Thanks very much! Best regards! Feng
On Sat, 18 Dec 2021 16:22:22 +0800, Feng Feng <f.feng@outlook.com> said: FF> I got a strange behaviour while implementing the archive in FF> user-defined class, to make the behaviour more explicitly, let
If you will delete the method Symbol::calchash() from your class you will get the expected behaviour. -- Vladimir V. Kisil http://www.maths.leeds.ac.uk/~kisilv/ Book: Geometry of Mobius Maps https://doi.org/10.1142/p835 Soft: Geometry of cycles http://moebinv.sourceforge.net/ Jupyter notebooks: https://github.com/vvkisil?tab=repositories
On Sat, 18 Dec 2021 16:22:22 +0800, Feng Feng <f.feng@outlook.com> said:
FF> Dear all, FF> I got a strange behaviour while implementing the archive in FF> user-defined class, to make the behaviour more explicitly, let FF> me post the C++ code here: FF> #include <ginac/ginac.h> #include <fstream> FF> using namespace GiNaC; using namespace std; FF> class Symbol : public symbol { FF> GINAC_DECLARE_REGISTERED_CLASS(Symbol, symbol) public: FF> Symbol(const string &s); void archive(archive_node & n) const FF> override; void read_archive(const archive_node& n, lst& sym_lst) FF> override; unsigned calchash() const override; }; FF> GINAC_DECLARE_UNARCHIVER(Symbol); FF> GINAC_IMPLEMENT_REGISTERED_CLASS(Symbol, symbol) FF> GINAC_BIND_UNARCHIVER(Symbol); FF> const symbol & get_symbol(const string & s) { static map<string, FF> symbol> dict; if (dict.find(s) == dict.end()) dict[s] = FF> symbol(s); return dict[s]; } FF> Symbol::Symbol() { } Symbol::Symbol(const string &s) : FF> symbol(get_symbol(s)) { } int Symbol::compare_same_type(const FF> basic &other) const { const Symbol &o = static_cast<const Symbol FF> &>(other); int ret = get_name().compare(o.get_name()); FF> if(ret==0) return 0; else if(ret<0) return -1; else return 1; } FF> void Symbol::archive(archive_node & n) const { FF> inherited::archive(n); } FF> void Symbol::read_archive(const archive_node& n, lst& sym_lst) { FF> inherited::read_archive(n, sym_lst); *this = Symbol(get_name()); FF> } FF> unsigned Symbol::calchash() const { static auto hash = FF> symbol("_").gethash(); return hash; } FF> int main() { Symbol k1("k1"), k2("k2"); auto garfn = "tmp.gar"; FF> { archive ar; ex val = lst{ k1*k1, k2*k2 }; FF> ar.archive_ex(val, "key"); ofstream out(garfn); out << ar; FF> out.close(); cout << "writed: " << val << endl; } { archive ar; FF> ifstream in(garfn); in >> ar; in.close(); auto val = FF> ar.unarchive_ex(lst{}, "key"); cout << "read: " << val << endl; FF> } // console output is // writed: {k1^2,k2^2} // read: FF> {k1^2,k2^2} FF> { archive ar; ex val = lst{ k1*k1==0, k2*k2==0 }; FF> ar.archive_ex(val, "key"); ofstream out(garfn); out << ar; FF> out.close(); cout << "writed: " << val << endl; } { archive ar; FF> ifstream in(garfn); in >> ar; in.close(); auto val = FF> ar.unarchive_ex(lst{}, "key"); cout << "read: " << val << endl; FF> } // console output is // writed: {k1^2==0,k2^2==0} // read: FF> {k1^2==0,k1^2==0} // Note that the last line above, both items FF> are the same: k1^2==0 FF> return 0; } FF> Here I want to introduce a class Symbol, when one defines Symbol FF> a(“a”), b(“a”);, then a and b will be the same or equal, so I FF> override the method compare_same_type. FF> The last console output seems very strange to me, the both items FF> are the same, while the expected result is "read: FF> {k1^2==0,k2^2==0}”. FF> Thanks very much! FF> Best regards! Feng FF> ---------------------------------------------------- FF> Alternatives: FF> ---------------------------------------------------- FF> _______________________________________________ GiNaC-list FF> mailing list GiNaC-list@ginac.de FF> https://www.ginac.de/mailman/listinfo/ginac-list
i suspect this is actually due to a typo in relational::compare_same_type. (basic reality check: pathologic (e.g. constant) hash functions should lead to degraded performance, not different results) diff --git a/ginac/relational.cpp b/ginac/relational.cpp index 599a2635..dbc541a3 100644 --- a/ginac/relational.cpp +++ b/ginac/relational.cpp @@ -223,8 +223,8 @@ int relational::compare_same_type(const basic & other) const return (o < oth.o) ? -1 : 1; break; } - const int lcmpval = lh.compare(oth.rh); - return (lcmpval!=0) ? lcmpval : rh.compare(oth.lh); + const int lcmpval = lh.compare(oth.lh); + return (lcmpval!=0) ? lcmpval : rh.compare(oth.rh); } attached is also a shorter test along the same lines as Feng's. it prints the result of comparing two symbol==0 expressions to each other both ways. without the patch, the output is -1 -1 (which is inconsistent), and with the patch it's -1 1. a hash collision is needed to elicit this bug because basic::compare returns early without calling compare_same_type if hashes differ. thanks On Sat, 18 Dec 2021, at 11:10, Vladimir V. Kisil wrote:
On Sat, 18 Dec 2021 16:22:22 +0800, Feng Feng <f.feng@outlook.com> said: FF> I got a strange behaviour while implementing the archive in FF> user-defined class, to make the behaviour more explicitly, let
If you will delete the method Symbol::calchash() from your class you will get the expected behaviour. -- Vladimir V. Kisil http://www.maths.leeds.ac.uk/~kisilv/ Book: Geometry of Mobius Maps https://doi.org/10.1142/p835 Soft: Geometry of cycles http://moebinv.sourceforge.net/ Jupyter notebooks: https://github.com/vvkisil?tab=repositories
On Sat, 18 Dec 2021 16:22:22 +0800, Feng Feng <f.feng@outlook.com> said:
FF> Dear all,
FF> I got a strange behaviour while implementing the archive in FF> user-defined class, to make the behaviour more explicitly, let FF> me post the C++ code here:
FF> #include <ginac/ginac.h> #include <fstream>
FF> using namespace GiNaC; using namespace std;
FF> class Symbol : public symbol { FF> GINAC_DECLARE_REGISTERED_CLASS(Symbol, symbol) public: FF> Symbol(const string &s); void archive(archive_node & n) const FF> override; void read_archive(const archive_node& n, lst& sym_lst) FF> override; unsigned calchash() const override; }; FF> GINAC_DECLARE_UNARCHIVER(Symbol);
FF> GINAC_IMPLEMENT_REGISTERED_CLASS(Symbol, symbol) FF> GINAC_BIND_UNARCHIVER(Symbol);
FF> const symbol & get_symbol(const string & s) { static map<string, FF> symbol> dict; if (dict.find(s) == dict.end()) dict[s] = FF> symbol(s); return dict[s]; }
FF> Symbol::Symbol() { } Symbol::Symbol(const string &s) : FF> symbol(get_symbol(s)) { } int Symbol::compare_same_type(const FF> basic &other) const { const Symbol &o = static_cast<const Symbol FF> &>(other); int ret = get_name().compare(o.get_name()); FF> if(ret==0) return 0; else if(ret<0) return -1; else return 1; }
FF> void Symbol::archive(archive_node & n) const { FF> inherited::archive(n); }
FF> void Symbol::read_archive(const archive_node& n, lst& sym_lst) { FF> inherited::read_archive(n, sym_lst); *this = Symbol(get_name()); FF> }
FF> unsigned Symbol::calchash() const { static auto hash = FF> symbol("_").gethash(); return hash; }
FF> int main() { Symbol k1("k1"), k2("k2"); auto garfn = "tmp.gar";
FF> { archive ar; ex val = lst{ k1*k1, k2*k2 }; FF> ar.archive_ex(val, "key"); ofstream out(garfn); out << ar; FF> out.close(); cout << "writed: " << val << endl; } { archive ar; FF> ifstream in(garfn); in >> ar; in.close(); auto val = FF> ar.unarchive_ex(lst{}, "key"); cout << "read: " << val << endl; FF> } // console output is // writed: {k1^2,k2^2} // read: FF> {k1^2,k2^2}
FF> { archive ar; ex val = lst{ k1*k1==0, k2*k2==0 }; FF> ar.archive_ex(val, "key"); ofstream out(garfn); out << ar; FF> out.close(); cout << "writed: " << val << endl; } { archive ar; FF> ifstream in(garfn); in >> ar; in.close(); auto val = FF> ar.unarchive_ex(lst{}, "key"); cout << "read: " << val << endl; FF> } // console output is // writed: {k1^2==0,k2^2==0} // read: FF> {k1^2==0,k1^2==0} // Note that the last line above, both items FF> are the same: k1^2==0
FF> return 0; }
FF> Here I want to introduce a class Symbol, when one defines Symbol FF> a(“a”), b(“a”);, then a and b will be the same or equal, so I FF> override the method compare_same_type.
FF> The last console output seems very strange to me, the both items FF> are the same, while the expected result is "read: FF> {k1^2==0,k2^2==0}”.
FF> Thanks very much!
FF> Best regards! Feng
FF> ---------------------------------------------------- FF> Alternatives:
FF> ---------------------------------------------------- FF> _______________________________________________ GiNaC-list FF> mailing list GiNaC-list@ginac.de FF> https://www.ginac.de/mailman/listinfo/ginac-list _______________________________________________ GiNaC-list mailing list GiNaC-list@ginac.de https://www.ginac.de/mailman/listinfo/ginac-list
i suspect this is actually due to a typo in relational::compare_same_type. (basic reality check: pathologic (e.g. constant) hash functions should lead to degraded performance, not different results) diff --git a/ginac/relational.cpp b/ginac/relational.cpp index 599a2635..dbc541a3 100644 --- a/ginac/relational.cpp +++ b/ginac/relational.cpp @@ -223,8 +223,8 @@ int relational::compare_same_type(const basic & other) const return (o < oth.o) ? -1 : 1; break; } - const int lcmpval = lh.compare(oth.rh); - return (lcmpval!=0) ? lcmpval : rh.compare(oth.lh); + const int lcmpval = lh.compare(oth.lh); + return (lcmpval!=0) ? lcmpval : rh.compare(oth.rh); } attached is also a shorter test along the same lines as Feng's. it prints the result of comparing two symbol==0 expressions to each other both ways. without the patch, the output is -1 -1 (which is inconsistent), and with the patch it's -1 1. a hash collision is needed to elicit this bug because basic::compare returns early without calling compare_same_type if hashes differ. Feng: regardless of this bug, i think you'll want something like following: unsigned Symbol::calchash() const { hashvalue = get_symbol(get_name()).gethash(); setflag(status_flags::hash_calculated); return hashvalue; } thanks On Sat, 18 Dec 2021, at 11:10, Vladimir V. Kisil wrote:
On Sat, 18 Dec 2021 16:22:22 +0800, Feng Feng <f.feng@outlook.com> said: FF> I got a strange behaviour while implementing the archive in FF> user-defined class, to make the behaviour more explicitly, let
If you will delete the method Symbol::calchash() from your class you will get the expected behaviour. -- Vladimir V. Kisil http://www.maths.leeds.ac.uk/~kisilv/ Book: Geometry of Mobius Maps https://doi.org/10.1142/p835 Soft: Geometry of cycles http://moebinv.sourceforge.net/ Jupyter notebooks: https://github.com/vvkisil?tab=repositories
On Sat, 18 Dec 2021 16:22:22 +0800, Feng Feng <f.feng@outlook.com> said:
FF> Dear all,
FF> I got a strange behaviour while implementing the archive in FF> user-defined class, to make the behaviour more explicitly, let FF> me post the C++ code here:
FF> #include <ginac/ginac.h> #include <fstream>
FF> using namespace GiNaC; using namespace std;
FF> class Symbol : public symbol { FF> GINAC_DECLARE_REGISTERED_CLASS(Symbol, symbol) public: FF> Symbol(const string &s); void archive(archive_node & n) const FF> override; void read_archive(const archive_node& n, lst& sym_lst) FF> override; unsigned calchash() const override; }; FF> GINAC_DECLARE_UNARCHIVER(Symbol);
FF> GINAC_IMPLEMENT_REGISTERED_CLASS(Symbol, symbol) FF> GINAC_BIND_UNARCHIVER(Symbol);
FF> const symbol & get_symbol(const string & s) { static map<string, FF> symbol> dict; if (dict.find(s) == dict.end()) dict[s] = FF> symbol(s); return dict[s]; }
FF> Symbol::Symbol() { } Symbol::Symbol(const string &s) : FF> symbol(get_symbol(s)) { } int Symbol::compare_same_type(const FF> basic &other) const { const Symbol &o = static_cast<const Symbol FF> &>(other); int ret = get_name().compare(o.get_name()); FF> if(ret==0) return 0; else if(ret<0) return -1; else return 1; }
FF> void Symbol::archive(archive_node & n) const { FF> inherited::archive(n); }
FF> void Symbol::read_archive(const archive_node& n, lst& sym_lst) { FF> inherited::read_archive(n, sym_lst); *this = Symbol(get_name()); FF> }
FF> unsigned Symbol::calchash() const { static auto hash = FF> symbol("_").gethash(); return hash; }
FF> int main() { Symbol k1("k1"), k2("k2"); auto garfn = "tmp.gar";
FF> { archive ar; ex val = lst{ k1*k1, k2*k2 }; FF> ar.archive_ex(val, "key"); ofstream out(garfn); out << ar; FF> out.close(); cout << "writed: " << val << endl; } { archive ar; FF> ifstream in(garfn); in >> ar; in.close(); auto val = FF> ar.unarchive_ex(lst{}, "key"); cout << "read: " << val << endl; FF> } // console output is // writed: {k1^2,k2^2} // read: FF> {k1^2,k2^2}
FF> { archive ar; ex val = lst{ k1*k1==0, k2*k2==0 }; FF> ar.archive_ex(val, "key"); ofstream out(garfn); out << ar; FF> out.close(); cout << "writed: " << val << endl; } { archive ar; FF> ifstream in(garfn); in >> ar; in.close(); auto val = FF> ar.unarchive_ex(lst{}, "key"); cout << "read: " << val << endl; FF> } // console output is // writed: {k1^2==0,k2^2==0} // read: FF> {k1^2==0,k1^2==0} // Note that the last line above, both items FF> are the same: k1^2==0
FF> return 0; }
FF> Here I want to introduce a class Symbol, when one defines Symbol FF> a(“a”), b(“a”);, then a and b will be the same or equal, so I FF> override the method compare_same_type.
FF> The last console output seems very strange to me, the both items FF> are the same, while the expected result is "read: FF> {k1^2==0,k2^2==0}”.
FF> Thanks very much!
FF> Best regards! Feng
FF> ---------------------------------------------------- FF> Alternatives:
FF> ---------------------------------------------------- FF> _______________________________________________ GiNaC-list FF> mailing list GiNaC-list@ginac.de FF> https://www.ginac.de/mailman/listinfo/ginac-list _______________________________________________ GiNaC-list mailing list GiNaC-list@ginac.de https://www.ginac.de/mailman/listinfo/ginac-list
On Mon, 20 Dec 2021 08:57:42 +0000, Oleg Finkelshteyn <olegfink@gmail.com> said:
OF> i suspect this is actually due to a typo in OF> relational::compare_same_type. Thanks for spotting the source. It is worth to cross-post your patch to GiNaC-devel list. -- Vladimir V. Kisil http://www.maths.leeds.ac.uk/~kisilv/ Book: Geometry of Mobius Maps https://doi.org/10.1142/p835 Soft: Geometry of cycles http://moebinv.sourceforge.net/ Jupyter notebooks: https://github.com/vvkisil?tab=repositories ==================================================== diff --git a/ginac/relational.cpp b/ginac/relational.cpp index 599a2635..dbc541a3 100644 --- a/ginac/relational.cpp +++ b/ginac/relational.cpp @@ -223,8 +223,8 @@ int relational::compare_same_type(const basic & other) const return (o < oth.o) ? -1 : 1; break; } - const int lcmpval = lh.compare(oth.rh); - return (lcmpval!=0) ? lcmpval : rh.compare(oth.lh); + const int lcmpval = lh.compare(oth.lh); + return (lcmpval!=0) ? lcmpval : rh.compare(oth.rh); }
participants (4)
-
Feng Feng
-
Oleg Finkelshteyn
-
ovf
-
Vladimir V. Kisil