Closed edd8e884-f507-429a-b577-5d554626c0fe closed 13 years ago
Hi Thierry!
I already stumbled into similar annoyances. Adding an exception handling in order to fall back to the default division-free algorithm when Sage can't determine if the base ring is a field sounds very reasonable to me!
Cheers, Nicolas
ok, i will make a very basic (purely logical (a simple try:
followed by except:pass
works at least for my example)) patch for it, and add my example as a test for the determinant method.
Here is a patch that fixes the problem. I also took the opportunity of this bugfix to accelerate the method a bit when the division-free algorithm df
is explicitly chosen. For this, i changed the ordering in the and
boolean expression: the method first test if the chosen algorithm in None
before checking if the ring is a field.
We don't want to put a bare "except:" clause since that will catch things like KeyboardInterrupt exceptions. We should be explicit about the type of exceptions that we want to catch.
Reviewer: Mike Hansen
Ok, since a general robust (division-free) algorithm follows, i thought any error could be corrected, i didn't thought to the KeyboardInterrupt exception. Thanks for the hint! I will be explicit about the type of exception.
Attachment: trac_10063-determinant_not_computed_in_some_rings_bugfix-tm.patch.gz
Tested on 4.5.3
Only the NotImplementedError
is catched now, so that the bug i found is still fixed.
Other types of errors (possibly) arising from what is behind is_field
or is_exact
methods will not be catched (which is bad for the user, but good for debugging).
Since both the patch and the modification are small, i replaced the existing patch by another one with the same name.
Author: Thierry Monteil
Changed reviewer from Mike Hansen to Mike Hansen, Sébastien Labbé
Dear Thierry,
You included too much code inside of the try statement. You are "trying" a lot more than what is necessary, that is, R.is_field()
in this case. From the PEP0008, cited in the Python coding conventions of the Sage Developpers Guide, one can read :
- Additionally, for all try/except clauses, limit the 'try' clause
to the absolute minimum amount of code necessary. Again, this
avoids masking bugs.
Yes:
try:
value = collection[key]
except KeyError:
return key_not_found(key)
else:
return handle_value(value)
No:
try:
# Too broad!
return handle_value(collection[key])
except KeyError:
# Will also catch KeyError raised by handle_value()
return key_not_found(key)
Hence, you could do something like :
try:
R_is_field = R.is_field()
except NotImplementedError:
...
if (algorithm is None and R_is_field and blablablal) or ...:
...
Finally, you need an empty line after the ::
for the documentation to build properly.
Hence, I change the status of this ticket to needs work.
Sébastien
Hi Sebastien,
thanks for your comments. As you know, i am not a computer scientist nor a programmer, so i do not know much about design principles, but if i had to code a determinant function from scratch, knowing that:
try:
all the kind of optimized algorithms
except StandardError:
pass #or send an automatic bug report if the error seems new.
the division-free algorithm that always works
I do not feel it is a dirty code, since its structure shows that one algorithm works everywhere and the other are fragile optimizations.
Making patches that only repair the bugs found will make a code whose architecture depend on the history of the bugs discovery, not necessarily a readable code (why is there a test for the is_field
method and not for is_exact
,...).
The aim of the Python convention is to avoid masking bugs, but if the exception is NotImplementedError
, then the "bug" is already known, since it means that someone wrote an empty method which raises this NotImplementedError
.
Note also that the Python convention is a bit slower since i have to test the is_field
method even if the algorithm is not None
. Should i replace:
try:
R_is_field_attempt = R.is_field()
except NotImplementedError:
R_is_field_attempt = False
by:
if algorithm is None:
try:
R_is_field_attempt = R.is_field()
except NotImplementedError:
R_is_field_attempt = False
in order to skip the test when another algorithm is is called?
Anyway, here is a patch that implements your recommendations (i rename it to keep the previous version on the trac server and follow the discussion).
I fixed the problem of the documentation as well (i took the opportunity to fix the existing test, which i copied).
Should we think to clean the whole determinant
code?
Tested on 4.5.3
Attachment: trac_10063-determinant_not_computed_in_some_rings_bugfix_attempt_3-tm.patch.gz
In this version, the is_field
method is tested only if algorithm
is not None
. If another algorithm is chosen, the test is skipped to save some time.
Tested on 4.5.3
Attachment: trac_10063-determinant_not_computed_in_some_rings_bugfix_attempt_4-tm.patch.gz
A typo in the previous comment: the is_field
method is tested only if algorithm
is None
.
Should we think to clean the whole
determinant
code?
Maybe. I don't know. But, I don't feel I am the determinant expert that could rethink/refactor/improve that code.
Although, I reviewed your most recent patch (trac_10063-determinant_not_computed_in_some_rings_bugfix_attempt_4-tm.patch
).
I was able to reproduce the problem on my computer. The problem is indeed fixed by the patch. All test passed in the repository sage/matrix
. Documentation builds fine.
The problem originated from a NotImplementedError
when computing R.is_field()
in the case where algorithm=None
(see below). So your new code instead tries to compute R.is_field()
and if R.is_field()
raises a NotImplementedError
you consider this as R.is_field() == False
. This computation is done only if algorithm is None so that the method doesn't get slower. You also followed my earlier advise, that is, the try statement only tries what is necessary.
Hence, from the knowledge I have, I am OK with giving a positive review to this ticket. Maybe Mike Hanson or Jason have comments, so I wait 24 hours, and then I will change the status to positive review.
Sébastien
PS : The is_field
method is not implemented for the following ring (should we open a ticket for it?):
sage: A = GF(2)['x,y,z']
sage: A.inject_variables()
Defining x, y, z
sage: R = A.quotient(x^2 + 1).quotient(y^2 + 1).quotient(z^2 + 1)
sage: R.is_field()
Traceback (most recent call last):
NotImplementedError:
I did a full ptestlong against sage-4.6.alpha3, works fine. Positive review considering the comments of Sébastien Labbé.
Merged: sage-4.6.1.alpha0
To use some fast algorithms that only work for fields, the
determinant
method uses theis_field
method for rings which uses theis_maximal
method for ideals. Unfortunately, this method is not always implemented.The determinant is a very fundamental function, and there are many division-free algorithms that work in every ring, hence this method should never raise an error.
Here is an example of the bug i encountered while testing a conjecture in combinatorics:
A simple (maybe dirty) solution might be to add a
try
at the beginning of the method and anexcept
that use a basic division-free formula in case of error.I am not an algebraist, so i prefer not to fix this bug by myself.
CC: @sagetrac-sage-combinat @sagetrac-spancratz @aghitza @nthiery @jasongrout
Component: commutative algebra
Keywords: determinant, ring, ideal
Author: Thierry Monteil
Reviewer: Mike Hansen, Sébastien Labbé
Merged: sage-4.6.1.alpha0
Issue created by migration from https://trac.sagemath.org/ticket/10063