Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
r""" 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]
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]
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]
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]
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
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 """
#***************************************************************************** # Copyright (C) 2018 Simon Branhdorst <sbrandhorst@web.de> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ #*****************************************************************************
r""" 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 """
r""" 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]
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]
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 prime - ``precision`` -- if not set, the minimal possible is taken - ``partial`` -- 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]
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]
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]
TESTS::
sage: Z = Matrix(ZZ,0,[]) sage: p_adic_normal_form(Z, 3) ([], []) sage: Z = matrix.zero(10) sage: p_adic_normal_form(Z, 3)[0] == 0 True """ # input checks!! else:
# continue with the non-degenerate part # in Zp(2) we have to calculate at least mod 8 for things to make sense. # The trivial case # the transformation matrix is called B else: # we have reached a normal form for p != 2 # for p == 2 extra work is necessary assert B.determinant().valuation() == 0 # B is invertible! if p==2: assert B*G*B.T == Matrix.block_diagonal(collect_small_blocks(D)) else: assert B*G*B.T == Matrix.diagonal(D.diagonal())
r""" Find smallest valuation below and right from ``cnt`` prefering the diagonal.
INPUT:
- ``G`` -- a symmetric `n` by `n` matrix in `\QQ_p` - ``cnt`` -- start search from this index - ``lower_bound`` -- an integer (default: ``0``) a lower bound for the valuations used for optimization
OUTPUT:
- ``min`` -- minimal valuation - ``min_i`` -- row index of the minimal valuation - ``min_j`` -- column index of the minimal valuation
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _find_min_p sage: G = matrix(Qp(2, show_prec=False), 3, 3, [4,0,1,0,4,2,1,2,1]) sage: G [2^2 0 1] [ 0 2^2 2] [ 1 2 1] sage: _find_min_p(G, 0) (0, 2, 2) sage: G = matrix(Qp(3, show_prec=False), 3, 3, [4,0,1,0,4,2,1,2,1]) sage: G [1 + 3 0 1] [ 0 1 + 3 2] [ 1 2 1] sage: _find_min_p(G, 0) (0, 0, 0) """ # diagonal has precedence # off diagonal
r""" Return the indices of the blocks.
For internal use in :meth:`collect_small_blocks`.
INPUT:
- ``G`` -- a block_diagonal matrix consisting of `1` by `1` and `2` by `2` blocks
OUTPUT:
- a list of integers
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _get_small_block_indices sage: W1 = Matrix([1]) sage: U = Matrix(ZZ, 2, [0, 1, 1, 0]) sage: G = Matrix.block_diagonal([W1, U, U, W1, W1, U, W1, U]) sage: _get_small_block_indices(G) [0, 1, 3, 5, 6, 7, 9, 10] """ else:
r""" Return the indices of the homogeneous blocks.
We call a matrix homogeneous if it is a multiple of an invertible matrix. Sometimes they are also called modular.
INPUT:
- ``G`` - a block diagonal matrix over the p-adics with blocks of size at most `2`.
OUTPUT:
- a list of integers
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _get_homogeneous_block_indices sage: W1 = Matrix(Zp(2), [1]) sage: V = Matrix(Zp(2), 2, [2, 1, 1, 2]) sage: G = Matrix.block_diagonal([W1, V, V, 2*W1, 2*W1, 8*V, 8*W1, 16*V]) sage: _get_homogeneous_block_indices(G) ([0, 5, 7, 10], [0, 1, 3, 4]) sage: G = Matrix.block_diagonal([W1, V, V, 2*W1, 2*W1, 8*V, 8*W1, 16*W1]) sage: _get_homogeneous_block_indices(G) ([0, 5, 7, 10], [0, 1, 3, 4]) """ else:
r""" Return the homogeneous normal form of the homogeneous ``G``.
INPUT:
- ``G`` -- a modular symmetric matrix over the `2`-adic integers in partial normal form
OUTPUT:
- ``B`` -- an invertible matrix over the basering of ``G`` such that ``B*G*B.T`` is in homogeneous normal form
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _homogeneous_normal_form sage: R = Zp(2, type = 'fixed-mod', print_mode='terse', show_prec=False) sage: U = Matrix(R, 2, [0,1,1,0]) sage: V = Matrix(R, 2, [2,1,1,2]) sage: W1 = Matrix(R, 1, [1]) sage: W3 = Matrix(R, 1, [3]) sage: W5 = Matrix(R, 1, [5]) sage: W7 = Matrix(R, 1, [7]) sage: G = Matrix.block_diagonal([V, W1]) sage: B = _homogeneous_normal_form(G, 1)[1] sage: B * G * B.T [2 1 0] [1 2 0] [0 0 1] sage: G = Matrix.block_diagonal([V, W1, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] sage: B * G * B.T [2 1 0 0] [1 2 0 0] [0 0 1 0] [0 0 0 3] sage: G = Matrix.block_diagonal([U, V, W1, W5]) sage: B = _homogeneous_normal_form(G, 2)[1] sage: B * G * B.T [0 1 0 0 0 0] [1 0 0 0 0 0] [0 0 0 1 0 0] [0 0 1 0 0 0] [0 0 0 0 7 0] [0 0 0 0 0 7] sage: G = Matrix.block_diagonal([U, W7, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] sage: B * G * B.T [0 1 0 0] [1 0 0 0] [0 0 3 0] [0 0 0 7] sage: G = Matrix.block_diagonal([V, W5, W5]) sage: B = _homogeneous_normal_form(G, 2)[1] sage: B * G * B.T [0 1 0 0] [1 0 0 0] [0 0 3 0] [0 0 0 7] sage: G = Matrix.block_diagonal([V, W3, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] sage: B * G * B.T [0 1 0 0] [1 0 0 0] [0 0 1 0] [0 0 0 5] sage: G = Matrix.block_diagonal([V, W1, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] sage: B * G * B.T [2 1 0 0] [1 2 0 0] [0 0 1 0] [0 0 0 3] sage: G = Matrix.block_diagonal([W3, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] sage: B * G * B.T [7 0] [0 7] """ else: # assert that e1 < e2
r""" Return the Jordan decomposition of a symmetric matrix over an odd prime.
INPUT:
- a symmetric matrix over `\ZZ_p` of type ``'fixed-mod'``
OUTPUT:
- ``D`` -- a diagonal matrix - ``B`` -- a unimodular matrix such that ``D = B * G * B.T``
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _jordan_odd_adic sage: R = Zp(3, prec=2, print_mode='terse', show_prec=False) sage: A4 = Matrix(R,4,[2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2]) sage: A4 [2 8 0 0] [8 2 8 0] [0 8 2 8] [0 0 8 2] sage: D, B = _jordan_odd_adic(A4) sage: D [2 0 0 0] [0 2 0 0] [0 0 1 0] [0 0 0 8] sage: D == B*A4*B.T True sage: B.determinant().valuation() == 0 True """
# transformation matrix
# indices of the diagonal entrys which are already used # the smallest valuation is on the diagonal # move pivot to position [cnt,cnt] # we are already orthogonal to the part with i < cnt # now make the rest orthogonal too else: # the smallest valuation is off the diagonal row = pivot[1] col = pivot[2] B[row, :] += B[col, :] D[row, :] += D[col, :] D[:, row] += D[:, col] # the smallest valuation is now on the diagonal
r""" Transform a symmetric matrix over the `2`-adic integers into jordan form.
Note that if the precision is too low, this method fails. The method is only tested for input over `\ZZ_2` of ``'type=fixed-mod'``.
INPUT:
- ``G`` -- symmetric `n` by `n` matrix in `\ZZ_p`
OUTPUT:
- ``D`` -- the jordan matrix - ``B`` -- transformation matrix, i.e, ``D = B * G * B^T``
The matrix ``D`` is a block diagonal matrix consisting of `1` by `1` and `2` by `2` blocks. The `2` by `2` blocks are matrices of the form `[[2a, b], [b, 2c]] * 2^k` with `b` of valuation `0`.
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _jordan_2_adic sage: R = Zp(2, prec=3, print_mode='terse', show_prec=False) sage: A4 = Matrix(R,4,[2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2]) sage: A4 [2 7 0 0] [7 2 7 0] [0 7 2 7] [0 0 7 2] sage: D, B = _jordan_2_adic(A4) sage: D [ 2 7 0 0] [ 7 2 0 0] [ 0 0 12 7] [ 0 0 7 2] sage: D == B*A4*B.T True sage: B.determinant().valuation() == 0 True """
# transformation matrix
# indices of the diagonal entrys which are already used # the smallest valuation is on the diagonal # move pivot to position [cnt,cnt] B.swap_rows(cnt, piv1) D.swap_rows(cnt, piv1) D.swap_columns(cnt, piv1) # we are already orthogonal to the part with i < cnt # now make the rest orthogonal too # the smallest valuation is off the diagonal else: # move this 2 x 2 block to the top left (starting from cnt) B.swap_rows(cnt, piv1) D.swap_rows(cnt, piv1) D.swap_columns(cnt, piv1) # we split off a 2 x 2 block # if it is the last 2 x 2 block, there is nothing to do. # calculate the inverse without using division
r""" Return the minimal nonsquare modulo the prime `p`.
INPUT:
- ``p`` -- a prime number
OUTPUT:
- ``a`` -- the minimal nonsquare mod `p`
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _min_nonsquare sage: _min_nonsquare(2) sage: _min_nonsquare(3) 2 sage: _min_nonsquare(5) 2 sage: _min_nonsquare(7) 3 """
r""" Return the transformation to sums of forms of types `U`, `V` and `W`.
Part of the algorithm :meth:`p_adic_normal_form`.
INPUT:
- ``G`` -- a symmetric matrix over `\ZZ_p` in jordan form -- the output of :meth:`p_adic_normal_form` or :meth:`_jordan_2_adic` - ``normal_odd`` -- bool (default: True) if true and `p` is odd, compute a normal form.
OUTPUT:
- ``(D, B)`` -- a pair of matrices such that ``D=B*G*B.T`` is a sum of forms of types `U`, `V` and `W` for `p=2` or diagonal with diagonal entries equal `1` or `u` where `u` is the smallest non-square modulo the odd prime `p`.
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _normalize sage: R = Zp(3, prec = 5, type = 'fixed-mod', print_mode='series', show_prec=False) sage: G = matrix.diagonal(R, [1,7,3,3*5,3,9,-9,27*13]) sage: D, B =_normalize(G) sage: D [ 1 0 0 0 0 0 0 0] [ 0 1 0 0 0 0 0 0] [ 0 0 3 0 0 0 0 0] [ 0 0 0 3 0 0 0 0] [ 0 0 0 0 2*3 0 0 0] [ 0 0 0 0 0 3^2 0 0] [ 0 0 0 0 0 0 2*3^2 0] [ 0 0 0 0 0 0 0 3^3] """ # squareclasses 1, v # a new block starts # move the non-square to # the last entry of the previous block else: # we combine two non-squares to get # the 2 x 2 identity matrix else: j=non_squares.pop() B.swap_rows(j,n-1) else: # squareclasses 1,3,5,7 modulo 8
r""" Normalize this indecomposable `2` by `2` block.
INPUT:
``G`` - a `2` by `2` matrix over `\ZZ_p` with ``type = 'fixed-mod'`` of the form::
[2a b] [ b 2c] * 2^n
with `b` of valuation 1.
OUTPUT:
A unimodular `2` by `2` matrix ``B`` over `\ZZ_p` with ``B * G * B.transpose()`` either::
[0 1] [2 1] [1 0] * 2^n or [1 2] * 2^n
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _normalize_2x2 sage: R = Zp(2, prec = 15, type = 'fixed-mod', print_mode='series', show_prec=False) sage: G = Matrix(R, 2, [-17*2,3,3,23*2]) sage: B =_normalize_2x2(G) sage: B * G * B.T [2 1] [1 2]
sage: G = Matrix(R,2,[-17*4,3,3,23*2]) sage: B = _normalize_2x2(G) sage: B*G*B.T [0 1] [1 0]
sage: G = 2^3 * Matrix(R, 2, [-17*2,3,3,23*2]) sage: B = _normalize_2x2(G) sage: B * G * B.T [2^4 2^3] [2^3 2^4] """
# The input must be an even block raise ValueError("Not a valid 2 x 2 block.") # now D is of the form # [2a b ] # [b 2c] # where b has valuation 1.
# Make sure G[1, 1] has valuation 1. # this works because # D[0, 0] has valuation at least 2
# in this case we can transform D to # 2 1 # 1 2 # Find a point of norm 2 # solve: 2 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0] # somehow else pari can get a hickup see `trac`:#24065 # make D[0, 1] = 1
# solve: v*D*v == 2 with v = (x, -2*x+1) # somehow else pari can get a hickup `trac`:#24065 # check the result # in this case we can transform D to # 0 1 # 1 0 # Find a point representing 0 # solve: 0 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0] # somehow else pari can get a hickup, see `trac`:#24065 # make the second basis vector have 0 square as well. # rescale to get D[0,1] = 1 # check the result
r""" Normalize this `2` by `2` block.
INPUT:
- ``G`` -- a multiple of the `2` by `2` identity_matrix over the `p`-adics for `p` odd.
OUTPUT:
- A transformation matrix ``B`` such that ``B * G * B.T`` is the identiy matrix
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _normalize_odd_2x2 sage: R = Zp(5, type='fixed-mod', print_mode='terse', show_prec=False) sage: G = 2 * Matrix.identity(R, 2) sage: B = _normalize_odd_2x2(G) sage: B*G*B.T [1 0] [0 1] """
r""" Return the partial normal form of the homogeneous block ``G``.
For internal use in :meth:`_two_adic_normal_forms`.
INPUT:
- ``G`` -- a modular symmetric matrix over the `2`-adic integers
OUTPUT:
- ``D, B, w`` -- with ``B`` a transformation matrix such that ``B * G * B.T`` is in partial normal form and `w = 0, 1, 2` is the size of the part consisting of forms of type W
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _partial_normal_form_of_block sage: R = Zp(2,prec=4, type = 'fixed-mod',print_mode='terse', show_prec=False) sage: U = Matrix(R, 2, [0,1,1,0]) sage: V = Matrix(R, 2, [2,1,1,2]) sage: W1 = Matrix(R, 1, [1]) sage: W3 = Matrix(R, 1, [3]) sage: W5 = Matrix(R, 1, [5]) sage: W7 = Matrix(R, 1, [7]) sage: G = Matrix.block_diagonal([W1, U, V, W5, V, W3, V, W7]) sage: B = _partial_normal_form_of_block(G)[1] sage: B * G * B.T [0 1 0 0 0 0 0 0 0 0 0 0] [1 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 1 0 0 0 0 0 0 0 0] [0 0 1 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 1 0 0 0 0 0 0] [0 0 0 0 1 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 1 0 0 0 0] [0 0 0 0 0 0 1 0 0 0 0 0] [0 0 0 0 0 0 0 0 2 1 0 0] [0 0 0 0 0 0 0 0 1 2 0 0] [0 0 0 0 0 0 0 0 0 0 1 0] [0 0 0 0 0 0 0 0 0 0 0 7] sage: G = Matrix.block_diagonal([W1, U, V, W1, V, W1, V, W7]) sage: B = _partial_normal_form_of_block(G)[1] sage: B * G * B.T [0 1 0 0 0 0 0 0 0 0 0 0] [1 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 1 0 0 0 0 0 0 0 0] [0 0 1 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 1 0 0 0 0 0 0] [0 0 0 0 1 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 1 0 0 0 0] [0 0 0 0 0 0 1 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 1 0 0] [0 0 0 0 0 0 0 0 1 0 0 0] [0 0 0 0 0 0 0 0 0 0 3 0] [0 0 0 0 0 0 0 0 0 0 0 7] """ # collect the indices of forms of types U, V and W else: else: # W W W transforms to W U or W V else: # put everything into the right order
r""" Return relations of `2`-adic quadratic forms.
See [MirMor2009]_ IV Prop. 3.2. This function is for internal use only.
INPUT:
- ``n`` -- an integer between 1 and 10 -- the number of the relation - ``G`` -- a block diagonal matrix consisting of blocks of types `U, V, W` the left side of the relation. If ``G`` does not match `n` then the results are unpredictable.
OUTPUT:
- square matrix ``B`` such that ``B * G * B.T`` is the right side of the relation which consits of blocks of types `U`, `V`, `W` again
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _relations sage: R = Zp(2, type = 'fixed-mod',print_mode='terse', show_prec=False) sage: U = Matrix(R,2,[0,1,1,0]) sage: V = Matrix(R,2,[2,1,1,2]) sage: W1 = Matrix(R,1,[1]) sage: W3 = Matrix(R,1,[3]) sage: W5 = Matrix(R,1,[5]) sage: W7 = Matrix(R,1,[7]) sage: G = Matrix.block_diagonal(W1,W1) sage: b = _relations(G,1) sage: b * G * b.T [5 0] [0 5] sage: G = Matrix.block_diagonal(W1,W3) sage: b = _relations(G,1) sage: b * G * b.T [5 0] [0 7] sage: G = Matrix.block_diagonal(W1,W5) sage: b = _relations(G,1) sage: b * G * b.T [5 0] [0 1] sage: G = Matrix.block_diagonal(W1,W7) sage: b = _relations(G,1) sage: b * G * b.T [5 0] [0 3] sage: G = Matrix.block_diagonal(W3,W3) sage: b = _relations(G,1) sage: b * G * b.T [7 0] [0 7] sage: G = Matrix.block_diagonal(W3,W5) sage: b = _relations(G,1) sage: b * G * b.T [7 0] [0 1] sage: G = Matrix.block_diagonal(W3,W7) sage: b = _relations(G,1) sage: b * G * b.T [7 0] [0 3] sage: G = Matrix.block_diagonal(W5,W5) sage: b = _relations(G,1) sage: b * G * b.T [1 0] [0 1] sage: G = Matrix.block_diagonal(W5,W7) sage: b = _relations(G,1) sage: b * G * b.T [1 0] [0 3] sage: G = Matrix.block_diagonal(W7,W7) sage: b = _relations(G,1) sage: b * G * b.T [3 0] [0 3] sage: G = Matrix.block_diagonal([V,V]) sage: b = _relations(G,3) sage: b * G * b.T [0 1 0 0] [1 0 0 0] [0 0 0 1] [0 0 1 0] sage: G = Matrix.block_diagonal([V,W1,W1]) sage: b = _relations(G,5) sage: b * G * b.T [0 1 0 0] [1 0 0 0] [0 0 7 0] [0 0 0 3] sage: G = Matrix.block_diagonal([V,W1,W5]) sage: b = _relations(G,5) sage: b * G * b.T [0 1 0 0] [1 0 0 0] [0 0 3 0] [0 0 0 3] sage: G = Matrix.block_diagonal([V,W3,W7]) sage: b = _relations(G,5) sage: b * G * b.T [0 1 0 0] [1 0 0 0] [0 0 5 0] [0 0 0 5] sage: G = Matrix.block_diagonal([W1,2*W1]) sage: b = _relations(G,6) sage: b * G * b.T [3 0] [0 6] sage: G = Matrix.block_diagonal([W1,2*W3]) sage: b = _relations(G,6) sage: b * G * b.T [ 7 0] [ 0 10] sage: G = Matrix.block_diagonal([W1,2*W5]) sage: b = _relations(G,6) sage: b * G * b.T [ 3 0] [ 0 14] sage: G = Matrix.block_diagonal([W1,2*W7]) sage: b = _relations(G,6) sage: b * G * b.T [7 0] [0 2] sage: G = Matrix.block_diagonal([W3,2*W5]) sage: b = _relations(G,6) sage: b * G * b.T [5 0] [0 6] sage: G = Matrix.block_diagonal([W3,2*W3]) sage: b = _relations(G,6) sage: b * G * b.T [1 0] [0 2] sage: G = Matrix.block_diagonal([2*W5,4*W7]) sage: b = _relations(G,6) sage: b * G * b.T [6 0] [0 4] sage: G = Matrix.block_diagonal([W3,2*V]) sage: b = _relations(G,7) sage: b * G * b.T [7 0 0] [0 0 2] [0 2 0] sage: G = Matrix.block_diagonal([W7,2*V]) sage: b = _relations(G,7) sage: b * G * b.T [3 0 0] [0 0 2] [0 2 0] sage: G = Matrix.block_diagonal([U,2*W1]) sage: b = _relations(G,8) sage: b * G * b.T [ 2 1 0] [ 1 2 0] [ 0 0 10] sage: G = Matrix.block_diagonal([U,2*W5]) sage: b = _relations(G,8) sage: b * G * b.T [2 1 0] [1 2 0] [0 0 2] sage: G = Matrix.block_diagonal([V,2*W1]) sage: b = _relations(G,8) sage: b * G * b.T [ 0 1 0] [ 1 0 0] [ 0 0 10] sage: G = Matrix.block_diagonal([V,2*W7]) sage: b = _relations(G,8) sage: b * G * b.T [0 1 0] [1 0 0] [0 0 6] sage: G = Matrix.block_diagonal([W1,W5,2*W5]) sage: b = _relations(G,9) sage: b * G * b.T [3 0 0] [0 3 0] [0 0 2] sage: G = Matrix.block_diagonal([W3,W3,2*W5]) sage: b = _relations(G,9) sage: b * G * b.T [5 0 0] [0 1 0] [0 0 2] sage: G = Matrix.block_diagonal([W3,W3,2*W1]) sage: b = _relations(G,9) sage: b * G * b.T [ 5 0 0] [ 0 1 0] [ 0 0 10] sage: G = Matrix.block_diagonal([W3,4*W1]) sage: b = _relations(G,10) sage: b * G * b.T [ 7 0] [ 0 20] sage: G = Matrix.block_diagonal([W5,4*W5]) sage: b = _relations(G,10) sage: b * G * b.T [1 0] [0 4] """ raise NotImplementedError("relation 4 is not needed") raise ValueError("W is of the wrong type for relation 5") 0, 1, 1, 1, -e2, -e2, 0, 3, -e1, -e1, 2*e2 + 3, -2*e1]) raise ValueError("wrong scales for relation 6") 0, e, -1, 2, 2, 1]) else: 0, 1, 1, 2*e, 2*e, - 3]) 2*e3, 1, -e1, -2*e2*e3, 2*e1**2*e3 + 4*e1*e3**2, e1*e2])
r""" Return the 2-adic normal form of a symmetric matrix.
INPUT:
- ``G`` -- block diagonal matrix with blocks of type `U`, `V`, `W` - ``partial`` -- bool (defaul: ``False``)
OUTPUT:
- ``D``, ``B`` -- such that ``D = B * G * B.T``
EXAMPLES::
sage: from sage.quadratic_forms.genera.normal_form import _two_adic_normal_forms sage: R = Zp(2, type = 'fixed-mod', print_mode='terse', show_prec=False) sage: U = Matrix(R,2,[0,1,1,0]) sage: V = Matrix(R,2,[2,1,1,2]) sage: W1 = Matrix(R,1,[1]) sage: W3 = Matrix(R,1,[3]) sage: W5 = Matrix(R,1,[5]) sage: W7 = Matrix(R,1,[7]) sage: G = Matrix.block_diagonal([2*W1,2*W1,4*V]) sage: B = _two_adic_normal_forms(G)[1] sage: B * G * B.T [ 2 0 0 0] [ 0 10 0 0] [ 0 0 0 4] [ 0 0 4 0] sage: G = Matrix.block_diagonal([W1,2*V,2*W3,2*W5]) sage: B = _two_adic_normal_forms(G)[1] sage: B * G * B.T [3 0 0 0 0] [0 0 2 0 0] [0 2 0 0 0] [0 0 0 2 0] [0 0 0 0 2] sage: G = Matrix.block_diagonal([U,2*V,2*W3,2*W5]) sage: B = _two_adic_normal_forms(G)[1] sage: B * G * B.T [2 1 0 0 0 0] [1 2 0 0 0 0] [0 0 4 2 0 0] [0 0 2 4 0 0] [0 0 0 0 2 0] [0 0 0 0 0 6] """ # UVlist[k] is a list of indices of the block of scale p^k. # It contains the indices of the part of types U or V. # So it may be empty. # same as UVlist but contains the indices of the part of type W # homogeneous normal form for each part else: return D, B # use relations descending in k # we never leave partial normal form # but the homogneneous normal form may be destroyed # it is restored at the end. # setup notation V = [] # it is U not V # condition b) # condition c) # We want type a or W = [] # modify D[w,w] to go from type b to type a # a = [[0,1], [2,3], [2,5], [0,7], [0,1,1], [1,2,3], [0,7,7], [0,1,7]] w = W[0] R = Wmm[:1] + [w] B[R,:] = _relations(D[R,R],10) * B[R,:] e0 = D[Wm,Wm][0,0].unit_part() e1 = D[Wm,Wm][1,1].unit_part() if mod(e1-e0,4) == 0: R = Wm + [w] B[R,:] = _relations(D[R,R],9) * B[R,:] # condition a) - stay in homogneneous normal form # we need to restore the homogeneous normal form of k-1 |