Normal forms for \(p\)-adic quadratic and bilinear forms#
We represent a quadratic or bilinear form by its \(n \times n\) Gram matrix \(G\). Then two \(p\)-adic forms \(G\) and \(G'\) are integrally equivalent if and only if there is a matrix \(B\) in \(GL(n,\ZZ_p)\) such that \(G' = B G B^T\).
This module allows the computation of a normal form. This means that two \(p\)-adic forms are integrally equivalent if and only if they have the same normal form. Further, we can compute a transformation into normal form (up to finite precision).
EXAMPLES:
sage: from sage.quadratic_forms.genera.normal_form import p_adic_normal_form
sage: G1 = Matrix(ZZ,4, [2, 0, 0, 1, 0, 2, 0, 1, 0, 0, 4, 2, 1, 1, 2, 6])
sage: G1
[2 0 0 1]
[0 2 0 1]
[0 0 4 2]
[1 1 2 6]
sage: G2 = Matrix(ZZ,4, [2, 1, 1, 0, 1, 2, 0, 0, 1, 0, 2, 0, 0, 0, 0, 16])
sage: G2
[ 2 1 1 0]
[ 1 2 0 0]
[ 1 0 2 0]
[ 0 0 0 16]
>>> from sage.all import *
>>> from sage.quadratic_forms.genera.normal_form import p_adic_normal_form
>>> G1 = Matrix(ZZ,Integer(4), [Integer(2), Integer(0), Integer(0), Integer(1), Integer(0), Integer(2), Integer(0), Integer(1), Integer(0), Integer(0), Integer(4), Integer(2), Integer(1), Integer(1), Integer(2), Integer(6)])
>>> G1
[2 0 0 1]
[0 2 0 1]
[0 0 4 2]
[1 1 2 6]
>>> G2 = Matrix(ZZ,Integer(4), [Integer(2), Integer(1), Integer(1), Integer(0), Integer(1), Integer(2), Integer(0), Integer(0), Integer(1), Integer(0), Integer(2), Integer(0), Integer(0), Integer(0), Integer(0), Integer(16)])
>>> G2
[ 2 1 1 0]
[ 1 2 0 0]
[ 1 0 2 0]
[ 0 0 0 16]
A computation reveals that both forms are equivalent over \(\ZZ_2\):
sage: D1, U1 = p_adic_normal_form(G1,2, precision=30)
sage: D2, U2 = p_adic_normal_form(G1,2, precision=30)
sage: D1
[ 2 1 0 0]
[ 1 2 0 0]
[ 0 0 2^2 + 2^3 0]
[ 0 0 0 2^4]
sage: D2
[ 2 1 0 0]
[ 1 2 0 0]
[ 0 0 2^2 + 2^3 0]
[ 0 0 0 2^4]
>>> from sage.all import *
>>> D1, U1 = p_adic_normal_form(G1,Integer(2), precision=Integer(30))
>>> D2, U2 = p_adic_normal_form(G1,Integer(2), precision=Integer(30))
>>> D1
[ 2 1 0 0]
[ 1 2 0 0]
[ 0 0 2^2 + 2^3 0]
[ 0 0 0 2^4]
>>> D2
[ 2 1 0 0]
[ 1 2 0 0]
[ 0 0 2^2 + 2^3 0]
[ 0 0 0 2^4]
Moreover, we have computed the \(2\)-adic isomorphism:
sage: U = U2.inverse()*U1
sage: U*G1*U.T
[ 2 2^31 + 2^32 2^32 + 2^33 1]
[2^31 + 2^32 2 2^32 1]
[2^32 + 2^33 2^32 2^2 2]
[ 1 1 2 2 + 2^2]
>>> from sage.all import *
>>> U = U2.inverse()*U1
>>> U*G1*U.T
[ 2 2^31 + 2^32 2^32 + 2^33 1]
[2^31 + 2^32 2 2^32 1]
[2^32 + 2^33 2^32 2^2 2]
[ 1 1 2 2 + 2^2]
As you can see this isomorphism is only up to the precision we set before:
sage: (U*G1*U.T).change_ring(IntegerModRing(2^30))
[2 0 0 1]
[0 2 0 1]
[0 0 4 2]
[1 1 2 6]
>>> from sage.all import *
>>> (U*G1*U.T).change_ring(IntegerModRing(Integer(2)**Integer(30)))
[2 0 0 1]
[0 2 0 1]
[0 0 4 2]
[1 1 2 6]
If you are only interested if the forms are isomorphic, there are much faster ways:
sage: q1 = QuadraticForm(G1)
sage: q2 = QuadraticForm(G2)
sage: q1.is_locally_equivalent_to(q2,2)
True
>>> from sage.all import *
>>> q1 = QuadraticForm(G1)
>>> q2 = QuadraticForm(G2)
>>> q1.is_locally_equivalent_to(q2,Integer(2))
True
SEEALSO:
:mod:`~sage.quadratic_forms.genera.genus`
:meth:`~sage.quadratic_forms.quadratic_form.QuadraticForm.is_locally_equivalent_to`
:meth:`~sage.modules.torsion_quadratic_module.TorsionQuadraticModule.normal_form`
AUTHORS:
Simon Brandhorst (2018-01): initial version
- sage.quadratic_forms.genera.normal_form.collect_small_blocks(G)[source]#
Return the blocks as list.
INPUT:
G
– a block_diagonal matrix consisting of \(1\) by \(1\) and \(2\) by \(2\) blocks
OUTPUT:
a list of \(1\) by \(1\) and \(2\) by \(2\) matrices – the blocks
EXAMPLES:
sage: from sage.quadratic_forms.genera.normal_form import collect_small_blocks sage: W1 = Matrix([1]) sage: V = Matrix(ZZ, 2, [2, 1, 1, 2]) sage: L = [W1, V, V, W1, W1, V, W1, V] sage: G = Matrix.block_diagonal(L) sage: L == collect_small_blocks(G) True
>>> from sage.all import * >>> from sage.quadratic_forms.genera.normal_form import collect_small_blocks >>> W1 = Matrix([Integer(1)]) >>> V = Matrix(ZZ, Integer(2), [Integer(2), Integer(1), Integer(1), Integer(2)]) >>> L = [W1, V, V, W1, W1, V, W1, V] >>> G = Matrix.block_diagonal(L) >>> L == collect_small_blocks(G) True
- sage.quadratic_forms.genera.normal_form.p_adic_normal_form(G, p, precision=None, partial=False, debug=False)[source]#
Return the transformation to the \(p\)-adic normal form of a symmetric matrix.
Two
p-adic
quadratic forms are integrally equivalent if and only if their Gram matrices have the same normal form.Let \(p\) be odd and \(u\) be the smallest non-square modulo \(p\). The normal form is a block diagonal matrix with blocks \(p^k G_k\) such that \(G_k\) is either the identity matrix or the identity matrix with the last diagonal entry replaced by \(u\).
If \(p=2\) is even, define the \(1\) by \(1\) matrices:
sage: W1 = Matrix([1]); W1 [1] sage: W3 = Matrix([3]); W3 [3] sage: W5 = Matrix([5]); W5 [5] sage: W7 = Matrix([7]); W7 [7]
>>> from sage.all import * >>> W1 = Matrix([Integer(1)]); W1 [1] >>> W3 = Matrix([Integer(3)]); W3 [3] >>> W5 = Matrix([Integer(5)]); W5 [5] >>> W7 = Matrix([Integer(7)]); W7 [7]
and the \(2\) by \(2\) matrices:
sage: U = Matrix(2,[0,1,1,0]); U [0 1] [1 0] sage: V = Matrix(2,[2,1,1,2]); V [2 1] [1 2]
>>> from sage.all import * >>> U = Matrix(Integer(2),[Integer(0),Integer(1),Integer(1),Integer(0)]); U [0 1] [1 0] >>> V = Matrix(Integer(2),[Integer(2),Integer(1),Integer(1),Integer(2)]); V [2 1] [1 2]
For \(p=2\) the partial normal form is a block diagonal matrix with blocks \(2^k G_k\) such that \(G_k\) is a block diagonal matrix of the form \([U\), … , \(U\), \(V\), \(Wa\), \(Wb]\) where we allow \(V\), \(Wa\), \(Wb\) to be \(0 \times 0\) matrices.
Further restrictions to the full normal form apply. We refer to [MirMor2009] IV Definition 4.6. for the details.
INPUT:
G
– a symmetric \(n\) by \(n\) matrix in \(\QQ\)p
– a prime number – it is not checked whether it is primeprecision
– if not set, the minimal possible is takenpartial
– boolean (default:False
) if set, only the partial normal form is returned.
OUTPUT:
D
– the jordan matrix over \(\QQ_p\)B
– invertible transformation matrix over \(\ZZ_p\), i.e., \(D = B * G * B^T\)
EXAMPLES:
sage: from sage.quadratic_forms.genera.normal_form import p_adic_normal_form sage: D4 = Matrix(ZZ, 4, [2,-1,-1,-1,-1,2,0,0,-1,0,2,0,-1,0,0,2]) sage: D4 [ 2 -1 -1 -1] [-1 2 0 0] [-1 0 2 0] [-1 0 0 2] sage: D, B = p_adic_normal_form(D4, 2) sage: D [ 2 1 0 0] [ 1 2 0 0] [ 0 0 2^2 2] [ 0 0 2 2^2] sage: D == B * D4 * B.T True sage: A4 = Matrix(ZZ, 4, [2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2]) sage: A4 [ 2 -1 0 0] [-1 2 -1 0] [ 0 -1 2 -1] [ 0 0 -1 2] sage: D, B = p_adic_normal_form(A4, 2) sage: D [0 1 0 0] [1 0 0 0] [0 0 2 1] [0 0 1 2]
>>> from sage.all import * >>> from sage.quadratic_forms.genera.normal_form import p_adic_normal_form >>> D4 = Matrix(ZZ, Integer(4), [Integer(2),-Integer(1),-Integer(1),-Integer(1),-Integer(1),Integer(2),Integer(0),Integer(0),-Integer(1),Integer(0),Integer(2),Integer(0),-Integer(1),Integer(0),Integer(0),Integer(2)]) >>> D4 [ 2 -1 -1 -1] [-1 2 0 0] [-1 0 2 0] [-1 0 0 2] >>> D, B = p_adic_normal_form(D4, Integer(2)) >>> D [ 2 1 0 0] [ 1 2 0 0] [ 0 0 2^2 2] [ 0 0 2 2^2] >>> D == B * D4 * B.T True >>> A4 = Matrix(ZZ, Integer(4), [Integer(2), -Integer(1), Integer(0), Integer(0), -Integer(1), Integer(2), -Integer(1), Integer(0), Integer(0), -Integer(1), Integer(2), -Integer(1), Integer(0), Integer(0), -Integer(1), Integer(2)]) >>> A4 [ 2 -1 0 0] [-1 2 -1 0] [ 0 -1 2 -1] [ 0 0 -1 2] >>> D, B = p_adic_normal_form(A4, Integer(2)) >>> D [0 1 0 0] [1 0 0 0] [0 0 2 1] [0 0 1 2]
We can handle degenerate forms:
sage: A4_extended = Matrix(ZZ, 5, [2, -1, 0, 0, -1, -1, 2, -1, 0, 0, 0, -1, 2, -1, 0, 0, 0, -1, 2, -1, -1, 0, 0, -1, 2]) sage: D, B = p_adic_normal_form(A4_extended, 5) sage: D [1 0 0 0 0] [0 1 0 0 0] [0 0 1 0 0] [0 0 0 5 0] [0 0 0 0 0]
>>> from sage.all import * >>> A4_extended = Matrix(ZZ, Integer(5), [Integer(2), -Integer(1), Integer(0), Integer(0), -Integer(1), -Integer(1), Integer(2), -Integer(1), Integer(0), Integer(0), Integer(0), -Integer(1), Integer(2), -Integer(1), Integer(0), Integer(0), Integer(0), -Integer(1), Integer(2), -Integer(1), -Integer(1), Integer(0), Integer(0), -Integer(1), Integer(2)]) >>> D, B = p_adic_normal_form(A4_extended, Integer(5)) >>> D [1 0 0 0 0] [0 1 0 0 0] [0 0 1 0 0] [0 0 0 5 0] [0 0 0 0 0]
and denominators:
sage: A4dual = A4.inverse() sage: D, B = p_adic_normal_form(A4dual, 5) sage: D [5^-1 0 0 0] [ 0 1 0 0] [ 0 0 1 0] [ 0 0 0 1]
>>> from sage.all import * >>> A4dual = A4.inverse() >>> D, B = p_adic_normal_form(A4dual, Integer(5)) >>> D [5^-1 0 0 0] [ 0 1 0 0] [ 0 0 1 0] [ 0 0 0 1]