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)
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
NOTE: These are tentative notes on different topics for personal use - expect mistakes and misunderstandings.