NOTES ON STATISTICS, PROBABILITY and MATHEMATICS


Calculations with p-adics:


This page is not meant to be a formal presentation on p-adics, but rather some basic calculations and snippets of code to clarify some points.

The typical presentation of p-adics focuses on the integers as power sums expanding to infinity on to the left as in \(\mathbb Z_3,\) corresponding to the 3-adics. For example, in this system the number \(3\) is

\[...00000000000000000011 = 2 \cdot 2^1 + 1 \cdot 2^0 \]

The negative reals are simply the additive inverses of their positive counterparts, so

\[-1 = ...11111111\]

Because

\[...11111111 + ...00000001 = ...000000\]

since in base \(2\) and summing from right to left, \(1 + 1 = 0\) and we carry \(1\) to the left to add it to the \(1\) in the second position in the expression of \(-1.\)

Now, how are the finite digits to the right of the period on the right calculated, and where do they come from?

The issue comes up in the introduction of \(\mathbb Q_p\) numbers. For instance, in the case of the 2-adic expression of \(\frac 1{24}\). P-adic numbers can be factored as

\[n = p^k \, \text{unit}\] where \(k\) is an exponent measuring the valuation of the p-adic as \(\frac 1{p^k}\), and the \(\text{unit}\) is… well quick review:

A unit in a ring is an element that has a multiplicative inverse. In other words, if \(a\) is a unit, there exists an element \(b\) in the ring such that \(a \cdot b = 1\). A zero divisor is a non-zero element \(a\) in a ring such that there exists a non-zero element \(b\) where \(a \cdot b = 0.\)

Therefore, in the case of a fraction:

\[\frac m n = \frac m{p^k} \frac 1{\text{unit}}= \frac 1{\text{unit}}p^{-k}\]

Applying it to the example of \(1/24,\) the denominator can be factorized as \(2^3 \cdot 3:\)

\[\frac 1{24}=\frac 1 3 \cdot 2^{-3}\]

\(1/3\) is just the multiplicative inverse of \(3,\) which can be obtained in sagemath as:

from sage.all import *
from sage.rings.padics.padic_printing import pAdicPrinter
padic_printing.mode('digits')
print(Qp(2)(1/3))
# ...10101010101010101011

and easily confirmed to be accurate:

third = Qp(2)(1/3)
three = Qp(2)(3)
print(third * three)
# ...00000000000000000001

Therefore,

\[1/24 = ...10101010101010101011 \cdot 2^{-3}\]

but since we are operating in base \(2,\) the factor \(2^{-3}\) just moves the period to the right, like \(10^{-3}\) would in base \(10.\) In this way, we get the final result:

\[1/24...10101010101010101.011\]

or in sagemath

padic_printing.mode('digits')
print(Qp(2)(1/24))
...10101010101010101.011
padic_printing.mode('series')
print(Qp(2)(1/24))
2^-3 + 2^-2 + 1 + 2^2 + 2^4 + 2^6 + 2^8 + 2^10 + 2^12 + 2^14 + 2^16 + O(2^17)

Hensel’s lemma:

A simple root of a polynomial is a root where the polynomial crosses the x-axis and does not touch or bounce off it. A root \(r\) of a polynomial \(f(x)\) is considered simple if \(f(r) = 0\) and the derivative \(f'(r)\neq 0\). This means that at \(r\), the polynomial has a non-zero slope, indicating that it crosses the x-axis at that point. In contrast, a multiple root (or repeated root) would satisfy \(f(r) = 0\) and \(f’(r) = 0\), meaning the polynomial touches or bounces off the x-axis at \(r\) but does not cross it.

If a polynomial has a simple root modulo a prime \(p\), Hensel’s theorem allows us to lift this root to a root modulo \(p^k\) for any \(k\). This process can be extended to find roots in the p-adic integers \(\mathbb{Z}_p\).

\[a_{n+1}\equiv a_n \mod{p^{n+1}}\]

and

\[f(a_n)\equiv 0 \mod {p^{n+1}}\]

will allow the construction of a sequence of entries in the p-adic expression.

For instance (see here), \(f(x) = x^2 + 1\) has two solutions \(\mod{5}\): \(3\) with multiplicity \(1\), and \(2\) with multiplicity \(1.\)

R.<x> = GF(5)[]
p = x^2 + 1
p.roots()
# [(3, 1), (2, 1)]

These will start the sequence in the p-adic (of \(5\)-adic) expansion. So for \(r = 2,\) this will be \(a_0=2.\) For \(a_1,\) the equation above is

\[\begin{align} a_1 &\equiv a_0 \mod{5^1}\\ a_1 &\equiv 2 \mod{5} \\ a_1 &= 2 + 5t \end{align}\]

and

\[\begin{align} f(a_1) &\equiv 0 \mod{5^2}\\ (2 + 5t)^2 + 1 &\equiv 0 \mod{5^2} \\ t &\equiv 1 \mod {5^2} \end{align}\]

yielding

\[\begin{align} a_1 = 2 + 5 \cdot 1 =7 \end{align}\]

And at this point we are ready for

\[\begin{align} a_2 &\equiv a_1 \mod{5^2}\\ a_2 &\equiv 7 \mod{25}\\ a_2 &= 7 +25t \end{align}\]

and \[(7 +25t)^2+1 \equiv 0 \mod{5^3}\]

which results in \(a_2 =57.\)

Progressing to

\[\begin{align} a_3 &\equiv a_2 \mod{5^3}\\ a_3 &\equiv 57 \mod{125}\\ a_3 &= 57 +125t \end{align}\]

which can be plugged into the function:

\[(57+125t)^2 + 1\equiv 0 \mod{5^4}\]

resulting in \(t=1\) and \(a_3 =57 + 125 = 182\)

In sagemath,

import numpy as np

def hensel(roots, p):
    first_root = roots[0][0]
    scnd_root = roots[1][0]
    first_root_str = str(first_root).replace('...', '')
    first_root_list = [int(digit) for digit in first_root_str]
    first_root_list_reversed = first_root_list[::-1]
    first_result = [digit * (p ** i) for i, digit in enumerate(first_root_list_reversed)]
    scnd_root_str = str(scnd_root).replace('...', '')
    scnd_root_list = [int(digit) for digit in scnd_root_str]
    scnd_root_list_reversed = scnd_root_list[::-1]
    scnd_result = [digit * (p ** i) for i, digit in enumerate(scnd_root_list_reversed)]

    return [np.cumsum(first_result), np.cumsum(scnd_result)]
padic_printing.mode('series')
R.<x>  = Qp(5,8)[]
p = x^2 + 1
p.roots()
#[(2 + 5 + 2*5^2 + 5^3 + 3*5^4 + 4*5^5 + 2*5^6 + 3*5^7 + O(5^8), 1),
 (3 + 3*5 + 2*5^2 + 3*5^3 + 5^4 + 2*5^6 + 5^7 + O(5^8), 1)]
padic_printing.mode('digits')
R.<x> = Qp(5,8)[]
p = x^2 + 1
p.roots()
#[(...32431212, 1), (...12013233, 1)]
roots = p.roots()
hensel(roots, 5)
#[array([     2,      7,     57,    182,   2057,  14557,  45807, 280182]),
 array([     3,     18,     68,    443,   1068,   1068,  32318, 110443])]

Notice how this makes sense:

2 + 5 + 2*5^2 = 57

Home Page

NOTE: These are tentative notes on different topics for personal use - expect mistakes and misunderstandings.