Dear all, happy new year to everybody and my apologies for coming up with a potential bug report shortly after a new release. The code snippet symbol x("x"); ex f = 2*x; ex g = pow(x,5); f.print(GiNaC::print_context(std::cout)); std::cout << std::endl; g.print(GiNaC::print_context(std::cout)); std::cout << std::endl; will print 2*x [power object] Is that really intentional? The behaviour is the same in GiNaC 1.8.7 and 1.8.8. Best wishes, Stefan
Hi, On 1/3/25 3:41 PM, Stefan Weinzierl wrote:
ex g = pow(x,5); [...] g.print(GiNaC::print_context(std::cout)); std::cout << std::endl;
will print [...] [power object]
Is that really intentional? The behaviour is the same in GiNaC 1.8.7 and 1.8.8.
Well.... Class 'print_context' is the common base class for 'print_dflt', 'print_latex', 'print_csrc', etc. I am not sure if it makes sense to use it. After all, there are ostream manipulator 'dflt', 'latex', and 'csrc' (operators.h), but none for 'print_context'. On the other hand, 'print_context' is totally public. What happens is: The lookup implemented in basic::print_dispatch() doesn't find a method in the print dispatch table for print_default for class 'power'. So it proceeds to class 'basic', the base class of 'power' to look for a dispatch function and this is the one printing "[power object]". The fine user manual cares to elaborate (in section 6.3 "GiNaC's expression output system"): The method table contains one slot for each possible ‘print_context’ type, indexed by the (internally assigned) serial number of the type. Slots may be empty, in which case GiNaC will retry the method lookup with the ‘print_context’ object's parent class, possibly repeating the process until it reaches the ‘print_context’ base class. If there's still no method defined, the method table of the algebraic object's parent class is consulted, and so on, until a matching method is found (eventually it will reach the combination ‘basic/print_context’, which prints the object's class name enclosed in square brackets). You can think of the print methods of all the different classes and output formats as being arranged in a two-dimensional matrix with one axis listing the algebraic classes and the other axis listing the ‘print_context’ classes. And it turns out that class 'power' has all kinds of output methods except 'print_tree' and 'print_context'! If we change the existing method for 'print_dflt' to be the one for 'print_context', your code will work as you expected: The lookup for 'print_dflt' will find the base method for 'print_context'. One last piece to do would be to add a method for 'print_tree' because our new method for 'print_context' will match this one while before we were using the one from class 'basic'. The attached patch does that. I'm unsure if this is patch is right. I guess it depends on whether your code using 'print_context' directly is right or not. Comments welcome! -richy. PS: The patch breaks the ABI, but that ain't grave. I think that the alternative of making 'print_context' abstract would break the API and that would be worse. -- Richard B. Kreckel <https://in.terlu.de/~kreckel/>
Dear Richy, replacing print_func<print_dflt>(&power::do_print_dflt) by print_func<print_context>(&power::do_print) makes sense to me, it will treat the class power in the same way as the classes add and mul. I am not sure if one actually needs the extra method power::do_print_tree, a program seems to do the same thing with or without, and I don't see it defined in the classes add and mul neither. Best wishes, Stefan
Dear Stefan, On 1/7/25 12:12 AM, Stefan Weinzierl wrote:
I am not sure if one actually needs the extra method power::do_print_tree, a program seems to do the same thing with or without,
It is not the same, even when the exponent is not 1/2. Without it, we dispatch to power::do_print() which passes the 'print_tree' context down to basis and exponent and for x^5 that gives us something like: x (symbol) @0x559db0cc2f70, serial=2, hash=0x6f41b8a, flags=0xf, domain=0 ^ 5 (numeric) @0x559db0cb3c90, hash=0xa5fe3ba7, flags=0xf (Note the caret and the indentation.) We really want to pick up basic::do_print_tree() here.
and I don't see it defined in the classes add and mul neither.
The methods add::do_print_tree() and mul::do_print_tree() are registered in the classes' print dispatch tables (add.cpp:42 and mul.cpp:45). All my best, -richy. -- Richard B. Kreckel <https://in.terlu.de/~kreckel/>
On Tue, 7 Jan 2025 09:51:15 +0100, "Richard B. Kreckel" <kreckel@in.terlu.de> said: RK> (Note the caret and the indentation.) We really want to pick up RK> basic::do_print_tree() here.
Agree! I used print_tree() many times to fix bugs in my coding. -- Vladimir V. Kisil http://v-v-kisil.scienceontheweb.net 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
Hi, This is now about more than class 'power'... On 1/7/25 12:12 AM, Stefan Weinzierl wrote:
replacing print_func<print_dflt>(&power::do_print_dflt) by print_func<print_context>(&power::do_print) makes sense to me, it will treat the class power in the same way as the classes add and mul.
Even if we fix it this way, the question remains: What purpose does the base class 'print_context' serve other than being an alias for 'print_dflt' - at least for classes 'add', 'mul', 'power', 'numeric', 'symbol', 'pseries', 'lst', 'matrix'? (There's still the same problem for 'clifford', 'spinmetric' and friends that could be fixed as well.) I propose this: 1) Let's modify it such that 'print_context' provides the output of 'print_dflt' for all classes derived from 'basic'. 2) Let's add a 'print_deterministic' subclass which lexicographically sorts the elements of classes where output order normally depends on addresses. Document it with a clear warning that the traversal-order might differ from the output order for this print context type. Comments? (Notably about the first point, in case there is insight about the reasons of having two types 'print_context' and 'print_dflt'.) All my best, -richy. -- Richard B. Kreckel <https://in.terlu.de/~kreckel/>
Dear Richard, Your proposal seems to be sensible to me. As a variation of it: can "print_deterministic" be just a flag for any declared printing method? It may be not so useful in outputs for C or Python, but printing a tree in a deterministic way may be an advantage. Best wishes, Vladimir -- Vladimir V. Kisil http://v-v-kisil.scienceontheweb.net 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 Tue, 7 Jan 2025 23:20:04 +0100, "Richard B. Kreckel" <kreckel@in.terlu.de> said:
RK> Hi, RK> This is now about more than class 'power'... RK> On 1/7/25 12:12 AM, Stefan Weinzierl wrote: >> replacing print_func<print_dflt>(&power::do_print_dflt) by >> print_func<print_context>(&power::do_print) makes sense to me, it >> will treat the class power in the same way as the classes add and >> mul. RK> Even if we fix it this way, the question remains: What purpose RK> does the base class 'print_context' serve other than being an RK> alias for 'print_dflt' - at least for classes 'add', 'mul', RK> 'power', 'numeric', 'symbol', 'pseries', 'lst', 'matrix'? RK> (There's still the same problem for 'clifford', 'spinmetric' and RK> friends that could be fixed as well.) RK> I propose this: 1) Let's modify it such that 'print_context' RK> provides the output of 'print_dflt' for all classes derived from RK> 'basic'. 2) Let's add a 'print_deterministic' subclass which RK> lexicographically sorts the elements of classes where output RK> order normally depends on addresses. Document it with a clear RK> warning that the traversal-order might differ from the output RK> order for this print context type. RK> Comments? (Notably about the first point, in case there is RK> insight about the reasons of having two types 'print_context' RK> and 'print_dflt'.) RK> All my best, -richy. -- Richard B. Kreckel RK> <https://in.terlu.de/~kreckel/>
Dear Vladimir, On 1/8/25 11:36 AM, Vladimir V. Kisil via GiNaC-devel wrote:
Your proposal seems to be sensible to me. As a variation of it: can "print_deterministic" be just a flag for any declared printing method? It may be not so useful in outputs for C or Python, but printing a tree in a deterministic way may be an advantage.
That goes beyond my suggestion and I'm not sure it is possible. Anyway, we must be very careful with 'faking' a print order for the purpose of reproducibility. It is a double-edged sword: If you think that 'e' is 'a-b' you expect 'e.op(0)' to be 'a' and 'e.op(1)' to be '-b' but that is not going to be fulfilled by deterministic print orders. I think this suggests that the print order 'print_tree' should remain as it is. After all, it is mainly for debugging purposes. By the same token, I think that the print order of ginsh should remain as it is - non-deterministic - because ginsh offers the 'op(e,n)' built-in function. Making ginsh's print only order differ from that of 'op' would be too confusing. All my best, -richy. -- Richard B. Kreckel <https://in.terlu.de/~kreckel/>
Yes, Richard, I see your point. -- Vladimir V. Kisil http://v-v-kisil.scienceontheweb.net/ 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, 11 Jan 2025 22:01:48 +0100, "Richard B. Kreckel" <kreckel@in.terlu.de> said:
RK> Dear Vladimir, RK> On 1/8/25 11:36 AM, Vladimir V. Kisil via GiNaC-devel wrote: >> Your proposal seems to be sensible to me. As a variation of it: >> can "print_deterministic" be just a flag for any declared >> printing method? It may be not so useful in outputs for C or >> Python, but printing a tree in a deterministic way may be an >> advantage. RK> That goes beyond my suggestion and I'm not sure it is possible. RK> Anyway, we must be very careful with 'faking' a print order for RK> the purpose of reproducibility. It is a double-edged sword: If RK> you think that 'e' is 'a-b' you expect 'e.op(0)' to be 'a' and RK> 'e.op(1)' to be '-b' but that is not going to be fulfilled by RK> deterministic print orders. I think this suggests that the print RK> order 'print_tree' should remain as it is. After all, it is RK> mainly for debugging purposes. RK> By the same token, I think that the print order of ginsh should RK> remain as it is - non-deterministic - because ginsh offers the RK> 'op(e,n)' built-in function. Making ginsh's print only order RK> differ from that of 'op' would be too confusing. RK> All my best, -richy. -- Richard B. Kreckel RK> <https://in.terlu.de/~kreckel/> RK> _______________________________________________ GiNaC-devel RK> mailing list GiNaC-devel@ginac.de RK> https://www.ginac.de/mailman/listinfo/ginac-devel
On Sun, 12 Jan 2025 10:20:43 +0000, "Vladimir V. Kisil" <V.Kisil@leeds.ac.uk> said:
VVK> Yes, Richard, I see your point. Yet, one printing context, which may also benefit from a lexicographic ordering vs random memory allocation, is LaTeX... -- Vladimir V. Kisil http://v-v-kisil.scienceontheweb.net 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, 11 Jan 2025 22:01:48 +0100, "Richard B. Kreckel" VVK> <kreckel@in.terlu.de> said:
RK> Dear Vladimir, RK> On 1/8/25 11:36 AM, Vladimir V. Kisil via GiNaC-devel wrote: >>> Your proposal seems to be sensible to me. As a variation of it: >>> can "print_deterministic" be just a flag for any declared >>> printing method? It may be not so useful in outputs for C or >>> Python, but printing a tree in a deterministic way may be an >>> advantage. RK> That goes beyond my suggestion and I'm not sure it is possible. RK> Anyway, we must be very careful with 'faking' a print order for RK> the purpose of reproducibility. It is a double-edged sword: If RK> you think that 'e' is 'a-b' you expect 'e.op(0)' to be 'a' and RK> 'e.op(1)' to be '-b' but that is not going to be fulfilled by RK> deterministic print orders. I think this suggests that the print RK> order 'print_tree' should remain as it is. After all, it is RK> mainly for debugging purposes. RK> By the same token, I think that the print order of ginsh should RK> remain as it is - non-deterministic - because ginsh offers the RK> 'op(e,n)' built-in function. Making ginsh's print only order RK> differ from that of 'op' would be too confusing. RK> All my best, -richy. -- Richard B. Kreckel
participants (3)
-
Richard B. Kreckel
-
Stefan Weinzierl
-
Vladimir V. Kisil