The pyadic
library is Python 3 package that provides number types for finite fields ModP
) and PAdic
). The goal is to mimic the flexible behavior of built-in types, such as int
, float
and complex
. Thus, one can mix-and-match the different number types, as long as the operations are consistent. In particular, ModP
and PAdic
are compatible with fractions.Fraction
.
In addition to arithmetic operations, the pyadic library also provides the following functions:
-
rationalise
to perform rationalization ($\mathbb{F}_p\rightarrow \mathbb{Q}$ and$\mathbb{Q}_p \rightarrow \mathbb{Q}$ ); -
finite_field_sqrt
andpadic_sqrt
to compute square roots (which may involveFieldExtension
); -
padic_log
to compute the$p$ -adic logarithm. - polynomial and rational function interpolation, see
interpolation.py
module.
A shout-out to galois for a very nice tool. It is recommented for vectorized finite field operations, unless type compatibility is an issue. For scalar operation this repo is recommended. See performance comparison below.
The package is available on the Python Package Index
pip install pyadic
Alternativelty, it can be installed by cloning the repo
git clone https://github.com/GDeLaurentis/pyadic.git path/to/repo
pip install -e path/to/repo
pip
will automatically install the required packages, which are
numpy, sympy
Additionally, pytest
is needed for testing.
Extensive tests are implemented with pytest
pytest --cov pyadic/ --cov-report html tests/ --verbose
In [1]: from pyadic import PAdic, ModP
In [2]: from fractions import Fraction as Q
# 7/13 as a 12-digit 2147483647-adic number
In [3]: PAdic(Q(7, 13), 2147483647, 12)
Out [3]: 1817101548 + 825955248*2147483647 + 1156337348*2147483647^2 + 330382099*2147483647^3 + 1321528398*2147483647^4 + 991146298*2147483647^5 + 1817101547*2147483647^6 + 825955248*2147483647^7 + 1156337348*2147483647^8 + 330382099*2147483647^9 + 1321528398*2147483647^10 + 991146298*2147483647^11 + O(2147483647^12)
# 7/13 in F_2147483647
In [4]: ModP(Q(7, 13), 2147483647)
Out [4]: 1817101548 % 2147483647
# Mapping back to rational numbers
In [5]: from pyadic.finite_field import rationalise
In [6]: rationalise(ModP(Q(7, 13), 2147483647))
Out [6]: Fraction(7, 13)
In [7]: rationalise(PAdic(Q(7, 13), 2147483647, 12))
Out [7]: Fraction(7, 13)
Perfomance comparison with galois for finite fields
Scalar instantiation and operations are faster in pyadic
import numpy
from galois import GF
from pyadic import ModP
from random import randint
GFp = GF(2 ** 31 - 1)
x = randint(0, 2 ** 31 - 1)
%timeit GFp(x)
2.84 µs ± 63.5 ns
%timeit ModP(x, 2 ** 31 - 1)
297 ns ± 0.876 ns
%timeit GFp(x) ** 2
30.1 µs ± 20.6 µs
%timeit ModP(x, 2 ** 31 - 1) ** 2
2.23 µs ± 91.8 ns
while galois is faster for vectorized operations (the bigger the array the bigger the gain)
%timeit numpy.array([randint(0, 2 ** 31 - 1) for i in range(100)]).view(GFp) ** 2
65.6 µs ± 1.86 µs
%timeit numpy.array([ModP(randint(0, 2 ** 31 - 1), 2 ** 31 - 1) for i in range(100)]) ** 2
351 µs ± 9.28 µs
However, galois requires everything to be appropriately typed, while pyadic performs type-casting on-the-fly
numpy.array([randint(0, 2 ** 31 - 1) for i in range(100)]).view(GFp) / 2
TypeError
numpy.array([ModP(randint(0, 2 ** 31 - 1), 2 ** 31 - 1) for i in range(100)]) / 2
array([...], dtype=object)
If you found this library useful, please consider citing it
@inproceedings{DeLaurentis:2023qhd,
author = "De Laurentis, Giuseppe",
title = "{Lips: $p$-adic and singular phase space}",
booktitle = "{21th International Workshop on Advanced Computing and Analysis Techniques in Physics Research}: {AI meets Reality}",
eprint = "2305.14075",
archivePrefix = "arXiv",
primaryClass = "hep-th",
reportNumber = "PSI-PR-23-14",
month = "5",
year = "2023"
}