ER2 is Python with mathematics built in: symbolic syntax, exact arithmetic by default, and computational number theory powered by PARI/GP.
It is named after the Hungarian mathematician Paul Erdős. In Spanish, "Erdős" sounds like "ER-dos", which is where ER2 comes from.
Why ER2?
- Write mathematics as mathematics.
x^2is a power,1/3is exactly one third, andsym xdeclares a symbol. There are no imports and noRational(1, 3). - Two engines, one language. ER2 sends each computation to the right backend: SymPy for symbolic algebra and PARI/GP, one of the fastest number theory systems, for integers, primes and modular arithmetic.
- It is still Python. All Python syntax works, every library imports as usual, and ER2 runs in Jupyter and Quarto with LaTeX output.
Quick start
ER2 is not on PyPI yet. You need Python ≥ 3.12 and uv; PARI comes inside the cypari2 wheel, so there is nothing else to install.
git clone https://github.com/oeistools/ER2.git && cd ER2
uv sync
uv run er2 examples/hello.er2
Hello from ER2
1024 1/2
x^3 + 3*x^2 + 3*x + 1
True
Then try the REPL (uv run er2), write your own program.er2, or open a notebook: see
Jupyter and Quarto. uv run er2 --show-python program.er2 shows the Python
that ER2 generates.
A taste of ER2
import numpy as np # any Python library, imported as usual
sym x # declare a symbol
f = x^2 + 2*x + 1 # ^ means power
print(factor(f)) # (x + 1)^2
print(diff(f, x)) # 2*x + 2
print(latex(f)) # x^{2} + 2 x + 1 (any expression → LaTeX)
print(1/3) # 1/3, exact and not 0.333...
print(isprime(2^521 - 1)) # True (PARI)
print(phi(123456789)) # 82260072
print(factor(2^127 - 1)) # 2^127 - 1 is prime
print(factor(360)) # 2^3 * 3^2 * 5
for k in range(1, 20): # ordinary Python
if isprime(k):
print(k)
np.zeros(2^3) # ER2 numbers pass straight into libraries
Examples
Each program runs with uv run er2 examples/<name>.er2, and its expected output is checked by the
test suite.
| Example | What it shows |
|---|---|
| hello.er2 | The smallest tour: power, exact division, algebra, a primality test |
| syntax.er2 | Every ER2 syntax difference from Python: ^, ^^, exact literals, sym, 5r |
| factorization.er2 | One factor for integers, rationals and polynomials; partial factorizations |
| mvp.er2 | The MVP program: CAS and number theory together |
| demo.qmd | A Quarto tour with rendered math, including the OEIS (make render) |
| mvp.ipynb | The MVP as a Jupyter notebook |
Documentation
The documentation site is at https://oeistools.github.io/ER2/, built by Quarto and executed by ER2 itself.
| If you want to… | Read |
|---|---|
| Use ER2 | This README, the examples, and docs/PARI_FUNCTIONS.md (every PARI function and its ER2 name) |
| Know exactly what the language is | docs/LANGUAGE.md: the normative specification — the differences from Python, precedence, the number model, and what is stable before 1.0 |
| Understand the design | ARCHITECTURE.md: the compatibility contract, the components, and the design decisions D1–D17 |
| Follow the project | PLAN.md (milestones and acceptance criteria), CHANGELOG.md (releases and versioning policy), docs/BENCHMARKS.md |
| Contribute | CONTRIBUTING.md: setup, tests, rules for each part of the code, good first contributions |
The rules for AI-assisted development are in CLAUDE.md.
Algebra (0.5)
sym x, y
det(Matrix([[2, 1], [1, 3]])) # 5, computed by PARI
kernel(Matrix([[1, 2], [2, 4]])) # [Matrix([[1], [-1/2]])]
smith_form(Matrix([[2, 4], [6, 8]])) # Matrix([[2, 0], [0, 4]]), as SymPy's
factor(x^8 - x, modulus=2) # x*(x + 1)*(x^3 + x + 1)*(x^3 + x^2 + 1)
isirreducible(x^2 + x + 1, modulus=2) # True
a = GF(9).gen() # the finite field with 9 elements
a^4, a.order(), minpoly(a) # 2, 8, x^2 + x + 2
resultant(x^2 + 1, x^3 - 2, x) # 5
discriminant(x^3 + x + 1) # -31
G = groebner([x^2 + y^2 - 1, x - y], x, y)
list(G) # [x - y, 2*y^2 - 1]
reduce(x^2 + y^2, G) # 1, the normal form
K = NumberField(x^2 + 5) # Q(sqrt(-5))
K.discriminant, K.class_number() # -20, 2
K.factor(2) # [((2, x + 1), 2)]: 2 ramifies
K.factor(3) # 3 splits into two primes
resultant follows the standard sign convention, which is PARI's, so
resultant(f, g) and resultant(g, f) differ in sign when both degrees are odd
(ARCHITECTURE.md §6, D16). A class number is only as good as the GRH unless you ask for
certify=True.
Python compatibility
ER2 is a superset of Python and follows the same model as SageMath:
-
All Python syntax works: classes, decorators, generators,
async,match, f-strings, type hints, and the rest. -
The whole ecosystem is available through normal
import: NumPy, SciPy, pandas, Matplotlib, and so on. -
Only
.er2sources are translated. Your.pymodules and installed libraries run as ordinary Python and are never modified. -
Inside
.er2files there are only a few deliberate differences:Construct Python ER2 a ^ bXOR power a ^^ bsyntax error XOR 1/30.333…exact rational 1/3sym x, ysyntax error declares symbols x,y5rsyntax error the plain Python int5
Jupyter and Quarto
ER2 runs in notebooks and in Quarto documents:
- ER2 kernel: run
er2 kernel install, then pick "ER2" in Jupyter. In Quarto, setjupyter: er2in the front matter. If Quarto or your editor reportsJupyter kernel 'er2' not found, it is only looking inside the project's virtual environment: runer2 kernel install --sys-prefixas well. In VS Code, the Quarto Preview button uses the system Python unlessQUARTO_PYTHONis set. Add"terminal.integrated.env.linux": {"QUARTO_PYTHON": "${workspaceFolder}/.venv/bin/python"}to.vscode/settings.json. - Any Python kernel: add
%load_ext er2in the first cell. - LaTeX everywhere: expressions render as math.
show(f)displays one, and`{python} latex(f)`puts inline math in Quarto text.
---
title: "Mersenne primes"
jupyter: er2
---
```{python}
[p for p in range(2, 130) if isprime(2^p - 1)]
```
How it works
ER2 does not introduce a new interpreter. It has three parts:
.er2 source ──► preparser ──► plain Python ──► CPython
│
ER2 runtime (types, printing, dispatch)
│ │
SymPy cypari2 → PARI
(symbolic math) (number theory)
- Preparser. A token-level, source-to-source translation from
.er2to Python. - Runtime. The
er2package provides the mathematical types and the functions (factor,diff,phi, …). - Backends. SymPy handles symbolic computation, and PARI is accessed through cypari2. ER2 chooses the backend automatically, so
factorworks on both polynomials and integers.
Status and roadmap
Version 0.7.0, early development. Milestones M1–M7 are done (M3 was the MVP). Algebra
(matrices, finite fields, F_p, resultants, Gröbner bases, NumberField), series (power,
Dirichlet, Euler products) and the language specification are all in.
docs/LANGUAGE.md now defines the language normatively, and
CHANGELOG.md says what is stable, what is still experimental, and what has
changed. What remains before 1.0 is the documentation site and the stability policy.
| Version | Focus | Status |
|---|---|---|
| 0.1 | Preparser: sym, ^, _x, exact integers and rationals; Jupyter kernel, %load_ext er2, Quarto |
✅ |
| 0.2 | CAS on SymPy: expand, factor, simplify, diff, integrate, limit, solve, series |
✅ |
| 0.3 | Number theory on PARI: primality, factorization, phi, sigma, mu, … (the MVP) |
✅ |
| 0.4 | Automatic backend selection, PARI types (Mod, Qfb, series), benchmarks, the OEIS |
✅ |
| 0.5 | Algebra: matrices, finite fields, resultants, Gröbner bases, number fields | ✅ |
| 0.6 | Series: power, Dirichlet, Euler products | ✅ |
| 0.7 | The language specification, docs/LANGUAGE.md; first PyPI release |
✅ |
| 1.0 | Frozen syntax and public API: documentation site, stability policy | in progress |
Beyond the table, every PARI function is available as pari.<name>, and PARI's sums and integrals
take Python functions (pari.sum(lambda n: 1/n^2, 1, 10)). Deeper CPython integration comes only
after the syntax is stable.
Development
make install # uv sync + the ER2 kernel inside .venv
make test-fast # the tests without Jupyter and Quarto
make check # ruff + all tests, as in CI
make install-global # the `er2` command for your user, editable (uv tool)
make preview FILE=my_doc.qmd # live Quarto preview with the ER2 kernel
make lists every target. make install-global puts er2 in ~/.local/bin. It is an editable
install, so it follows changes to the code, and it registers the ER2 kernel for your user.
We keep dependencies deliberately minimal: sympy and cypari2 at runtime, plus ipykernel and
oeis-tools as the optional er2[jupyter] and er2[oeis] extras. Development uses pytest and
ruff. The code follows PEP 8.
Contributing
ER2 is maintained by Enrique Pérez Herrero (energycode.org@gmail.com).
Contributions are welcome, from a bug report with a three-line example to a new feature. CONTRIBUTING.md explains how to set up, what a pull request needs, and where to start if you are new to the project.
Citation
If you use ER2 in your work, please cite it. The metadata is in CITATION.cff, and GitHub shows it through "Cite this repository". Please also cite PARI/GP and SymPy, which ER2 builds on.
License
Release files for er2 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| er2-0.7.0.tar.gz | 97.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| er2-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 206.4 kB
Release files / er2-0.7.0.tar.gz
| Download URL | er2-0.7.0.tar.gz |
|---|---|
| Size | 97.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0b0b4b0acd4d2665f3144fc2a050cc37721722d710bdc6aaf6c5ffa69b3fab21
|
|
BLAKE2b-256 checksum How to use checksums |
085a33cfcf3596babc4923fb284ea4ceef98bba2ab23315578fa8187b4c2e5b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / er2-0.7.0-py3-none-any.whl
| Download URL | er2-0.7.0-py3-none-any.whl |
|---|---|
| Size | 108.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
541e2e7cc344e38a4db06eaa8b2bfe373541238dffa52e68a600dd6cbc01b08e
|
|
BLAKE2b-256 checksum How to use checksums |
138b8cdb9e866104ad7e391a952d8fb95071686bc0ce38bef181d267878d08d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log