#6441: [with patch, needs review] Charpoly (plus adjoint and det)
----------------------------+-----------------------------------------------
Reporter: spancratz | Owner: somebody
Type: defect | Status: new
Priority: minor | Milestone: sage-4.1
Component: linear algebra | Keywords: charpoly, division-free
Reviewer: | Author: Sebastian Pancratz
Merged: |
----------------------------+-----------------------------------------------
Changes (by davidloeffler):
* component: algebra => linear algebra
Old description:
> [Description of the problem]
>
> There are some problems in SAGE 4.0.2 when computing characteristic
> polynomials, determinants and adjoint matrices over general rings or non-
> exact fields. A more detailed description follows:
>
> o Firstly, SAGE sometimes fails to compute the characteristic
> polynomial of a matrix over a ring which is not an integral domain. Here
> is an example:
>
> sage: A = matrix(ZZ.quo(12), 3, [5,8,0,10,2,1,8,7,9])
> sage: A
> [ 5 8 0]
> [10 2 1]
> [ 8 7 9]
>
> The call "A.charpoly()" results in an error, specifically
>
> "ArithmeticError: The inverse of 10 modulo 12 is not defined."
>
> o Secondly, when computing over non-exact fields like Qp sometimes the
> computation of the characteristic polynomial results in precision loss.
>
> sage: R = Zp(5, prec = 10, type = 'capped-abs', print_mode =
> 'series')
> sage: MS = MatrixSpace(R, 10, 10)
> sage: A = MS.random_element()
> sage: A.charpoly()
>
> Often, this call results in a characteristic polynomial with
> coefficients of less precision than the base ring. Sometimes (whenever
> the Hessenberg algorithm requires too many divisions in Zp with regards
> to the chosen precision), it also fails with the following error:
>
> ValueError: element valuation cannot be negative.
>
> o Thirdly, in some cases the current implementation of charpoly is
> ridiculously slow (because of the use of the field of fractions in the
> Hessenberg algorithm). Consider, for example, the following:
>
> sage: R.<t> = QQ[]
> sage: A = matrix(R, [[-2*t^2 - t - 2, -t, t^2 - 3/2*t - 1, 1/2*t^2 -
> 4*t - 1, -t^2 - 9*t + 2], \
> [-t^2 + 1/2, 7/233*t - 2, -4*t + 1/2, 3*t^2
> + 3/2*t + 1, -t^2 - t], \
> [t - 1, t^2 - 5/7*t - 1, 1/2*t - 1/2, 8*t^2
> + t - 2/3, t + 1/2], \
> [-t^2 + 11, -1/2*t - 1/4, 8*t^2 + t, 1,
> -1/4*t^2 + 1/2*t + 1/7], \
> [3/2*t^2 + 5*t, 13/50*t^2 - 11*t + 1, -2*t^2
> + t, -1, -1/2*t + 3/2]])
>
> (Sorry for the bad formatting.) Computing the characteristic
> polynomial in SAGE via A.charpoly takes 91.12s on my laptop (Lenovo T500,
> Intel Centrino duo core), while the simple implementation of the generic
> division-free algorithm (attached to the ticket) takes only 0.01s.
> During a quick (although not statistically sound) test with matrices in
> the above matrix space, the new code seemed to be faster by a factor
> between 1,000 and 10,000.
>
> o For other reasons, namely using the expansion of co-factors, the
> computation of the determinant of a matrix over a general ring is also
> noticeably slow whenever the matrix has more than about 7 rows. For
> example, with
>
> sage: A = random_matrix(PolynomialRing(QQ, 't'), 7, 7)
>
> a call to ``A.det()`` takes about 0.5s. For a random 8x8 matrix, the
> same call takes about 3s. (And it gets worse very quickly in the
> expected way.)
>
> o Also, just because it's possible, SAGE ought to be able to compute
> the characteristic polynomial, determinant and adjoint of square matrices
> over any ring (commutative with unity). To give an example where this
> currently fails:
>
> sage: R.<a,b> = QQ[]
> sage: S.<x,y> = R.quo(b^3)
> sage: A = matrix(S, [[2,x^2],[x^3*y,x*y^2]])
> sage: A
> [ 2 x^2]
> [x^3*y x*y^2]
>
> currently only ``A.det()`` works (and uses expansion by co-factors),
> but ``A.charpoly()`` throws a ``NotImplementedError`` (because SAGE does
> not create the univariate polynomial ring over ``S``, because the test
> whether S is a field passes on an exception as the ideal throws an
> exception when it is asked about maximality) and ``A.adjoint()`` throws
>
> NotImplementedError: computation of adjoint not implemented in
> general yet
>
> [Suggested fix]
>
> Implement a generic division-free algorithm for the characteristic
> polynomial (and then use this to work out the adjoint, and read off the
> determinant).
>
> [Problematic points]
>
> - Adjusting current code that calls charpoly, det or adjoint:
>
> To quickly mention the latter: in the file matrix2.pyx I simply
> implemented _adjoint(self), since inheriting classes overwrite this.
>
> The calls to charpoly can choose an algorithm (as before, although it
> wasn't really used since there wasn't any choice). This problem thus
> translates to choosing the most sensible defaults (possibly depending on
> whether something is a number field, or an exact field, etc) in the
> implementation of charpoly, and to go through all the calls of charpoly
> in the current code, to check what the desired behaviour is. This
> question only arises as the Hessenberg algorithm runs in time O(n^3)
> whereas the division-free algorithm implemented here runs in time O(n^4).
> Thus the division-free algorithm wouldn't necessarily be the right choice
> between these two in all cases.
>
> In the case of number fields (PARI) or exact fields (Hessenberg form) or
> Z/nZ (lift to Z), the choice is pretty clear. The same holds in cases
> where this wasn't implemented before. I think the only remaining cases
> for which there may or may not be an obvious (at least to me!) choice are
> non-exact fields like R or Qp (and their extensions). Then it's
> basically a choice between speed (and managing precision as the caller by
> ensuring one has good bounds on the input) or guaranteed precision.
>
> Apart from one instance instance related to modular forms, where I could
> speak to David Loeffler at SAGE Days 16 and then added the paramater
> algorithm="hessenberg" to the call, I haven't changed the calls to
> charpoly anywhere.
>
> The implementation of determinant reflects this somewhat, although it is
> no real problem since in the patch the logic of determinant hasn't
> changed. The only difference is that the "last resort" algorithm using
> expansion by co-factors has been replaced by the generic computation of
> the characteristic polynomial (from which one can then read off the
> determinant), i.e., this has no downside (apart from a tiny problem with
> symbolic expression rings, as we now need to specify a variable name for
> the characteristic polynomial, see below).
>
> - A strange problem with symbolic rings:
>
> The characteristic polynomial needs to have a variable name specified.
> This gives an intrinsic problem when computing the determinant (a method
> which does not require the user to add a variable name as input, and
> rightfully so shouldn't!) over symbolic rings, as one needs to choose a
> variable name for the characteristic polynomial different from the
> already existing symbols or else the computed result will be meaningless.
> At SAGE Days 16, Martin Albrecht and I briefly tried a quick way around
> this (by asking the symbolic expression ring for all symbols, and then
> forming a new one by concatenating them all, thereby always generating a
> new symbol), however, this idea did not work out. Thus at the moment,
> the fixed choice of the symbol "A0123456789" appears hardcoded.
>
> PS: I think the inability of SAGE to compute these three quantities over
> all rings, even for small-ish matrices, could be potentially offputting
> for beginners, so I was tempted to choose "Priority=major", but then
> again, it doesn't seem to be a lot of work to fix this. This is my first
> ticket to I don't really know how to choose the priority and just went
> with "minor", hope that's OK.
New description:
[Description of the problem]
There are some problems in SAGE 4.0.2 when computing characteristic
polynomials, determinants and adjoint matrices over general rings or non-
exact fields. A more detailed description follows:
o Firstly, SAGE sometimes fails to compute the characteristic polynomial
of a matrix over a ring which is not an integral domain. Here is an
example:
{{{
sage: A = matrix(ZZ.quo(12), 3, [5,8,0,10,2,1,8,7,9])
sage: A
[ 5 8 0]
[10 2 1]
[ 8 7 9]
}}}
The call "A.charpoly()" results in an error, specifically
"ArithmeticError: The inverse of 10 modulo 12 is not defined."
o Secondly, when computing over non-exact fields like Qp sometimes the
computation of the characteristic polynomial results in precision loss.
{{{
sage: R = Zp(5, prec = 10, type = 'capped-abs', print_mode = 'series')
sage: MS = MatrixSpace(R, 10, 10)
sage: A = MS.random_element()
sage: A.charpoly()
}}}
Often, this call results in a characteristic polynomial with
coefficients of less precision than the base ring. Sometimes (whenever
the Hessenberg algorithm requires too many divisions in Zp with regards to
the chosen precision), it also fails with the following error:
ValueError: element valuation cannot be negative.
o Thirdly, in some cases the current implementation of charpoly is
ridiculously slow (because of the use of the field of fractions in the
Hessenberg algorithm). Consider, for example, the following:
{{{
sage: R.<t> = QQ[]
sage: A = matrix(R, [[-2*t^2 - t - 2, -t, t^2 - 3/2*t - 1, 1/2*t^2 -
4*t - 1, -t^2 - 9*t + 2], \
[-t^2 + 1/2, 7/233*t - 2, -4*t + 1/2, 3*t^2 +
3/2*t + 1, -t^2 - t], \
[t - 1, t^2 - 5/7*t - 1, 1/2*t - 1/2, 8*t^2 +
t - 2/3, t + 1/2], \
[-t^2 + 11, -1/2*t - 1/4, 8*t^2 + t, 1,
-1/4*t^2 + 1/2*t + 1/7], \
[3/2*t^2 + 5*t, 13/50*t^2 - 11*t + 1, -2*t^2
+ t, -1, -1/2*t + 3/2]])
}}}
(Sorry for the bad formatting.) Computing the characteristic
polynomial in SAGE via A.charpoly takes 91.12s on my laptop (Lenovo T500,
Intel Centrino duo core), while the simple implementation of the generic
division-free algorithm (attached to the ticket) takes only 0.01s. During
a quick (although not statistically sound) test with matrices in the above
matrix space, the new code seemed to be faster by a factor between 1,000
and 10,000.
o For other reasons, namely using the expansion of co-factors, the
computation of the determinant of a matrix over a general ring is also
noticeably slow whenever the matrix has more than about 7 rows. For
example, with
{{{
sage: A = random_matrix(PolynomialRing(QQ, 't'), 7, 7)
}}}
a call to ``A.det()`` takes about 0.5s. For a random 8x8 matrix, the
same call takes about 3s. (And it gets worse very quickly in the expected
way.)
o Also, just because it's possible, SAGE ought to be able to compute the
characteristic polynomial, determinant and adjoint of square matrices over
any ring (commutative with unity). To give an example where this
currently fails:
{{{
sage: R.<a,b> = QQ[]
sage: S.<x,y> = R.quo(b^3)
sage: A = matrix(S, [[2,x^2],[x^3*y,x*y^2]])
sage: A
[ 2 x^2]
[x^3*y x*y^2]
}}}
currently only ``A.det()`` works (and uses expansion by co-factors),
but ``A.charpoly()`` throws a ``NotImplementedError`` (because SAGE does
not create the univariate polynomial ring over ``S``, because the test
whether S is a field passes on an exception as the ideal throws an
exception when it is asked about maximality) and ``A.adjoint()`` throws
NotImplementedError: computation of adjoint not implemented in general
yet
[Suggested fix]
Implement a generic division-free algorithm for the characteristic
polynomial (and then use this to work out the adjoint, and read off the
determinant).
[Problematic points]
- Adjusting current code that calls charpoly, det or adjoint:
To quickly mention the latter: in the file matrix2.pyx I simply
implemented _adjoint(self), since inheriting classes overwrite this.
The calls to charpoly can choose an algorithm (as before, although it
wasn't really used since there wasn't any choice). This problem thus
translates to choosing the most sensible defaults (possibly depending on
whether something is a number field, or an exact field, etc) in the
implementation of charpoly, and to go through all the calls of charpoly in
the current code, to check what the desired behaviour is. This question
only arises as the Hessenberg algorithm runs in time O(n^3) whereas the
division-free algorithm implemented here runs in time O(n^4). Thus the
division-free algorithm wouldn't necessarily be the right choice between
these two in all cases.
In the case of number fields (PARI) or exact fields (Hessenberg form) or
Z/nZ (lift to Z), the choice is pretty clear. The same holds in cases
where this wasn't implemented before. I think the only remaining cases
for which there may or may not be an obvious (at least to me!) choice are
non-exact fields like R or Qp (and their extensions). Then it's basically
a choice between speed (and managing precision as the caller by ensuring
one has good bounds on the input) or guaranteed precision.
Apart from one instance instance related to modular forms, where I could
speak to David Loeffler at SAGE Days 16 and then added the paramater
algorithm="hessenberg" to the call, I haven't changed the calls to
charpoly anywhere.
The implementation of determinant reflects this somewhat, although it is
no real problem since in the patch the logic of determinant hasn't
changed. The only difference is that the "last resort" algorithm using
expansion by co-factors has been replaced by the generic computation of
the characteristic polynomial (from which one can then read off the
determinant), i.e., this has no downside (apart from a tiny problem with
symbolic expression rings, as we now need to specify a variable name for
the characteristic polynomial, see below).
- A strange problem with symbolic rings:
The characteristic polynomial needs to have a variable name specified.
This gives an intrinsic problem when computing the determinant (a method
which does not require the user to add a variable name as input, and
rightfully so shouldn't!) over symbolic rings, as one needs to choose a
variable name for the characteristic polynomial different from the already
existing symbols or else the computed result will be meaningless. At SAGE
Days 16, Martin Albrecht and I briefly tried a quick way around this (by
asking the symbolic expression ring for all symbols, and then forming a
new one by concatenating them all, thereby always generating a new
symbol), however, this idea did not work out. Thus at the moment, the
fixed choice of the symbol "A0123456789" appears hardcoded.
PS: I think the inability of SAGE to compute these three quantities over
all rings, even for small-ish matrices, could be potentially offputting
for beginners, so I was tempted to choose "Priority=major", but then
again, it doesn't seem to be a lot of work to fix this. This is my first
ticket to I don't really know how to choose the priority and just went
with "minor", hope that's OK.
--
--
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/6441#comment:5>
Sage <http://sagemath.org/>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica,
and MATLAB
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"sage-trac" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/sage-trac?hl=en
-~----------~----~----~----~------~----~------~--~---