cabal
Números reales exactos (computables) en Python puro, cero dependencias, un solo archivo.
Cada Real es un nodo perezoso de un grafo de expresión que cumple un contrato verificable:
x.aprox(p) -> entero m con |m − x·2^p| ≤ 1 (para todo p ∈ ℤ)
La precisión se propaga hacia atrás bajo demanda. No eliges una precisión de trabajo y rezas: pides dígitos y el grafo calcula lo necesario para certificarlos.
from cabal import R, PI, E, raiz, exp, ln, sen
R("0.1") + R("0.2") == R("0.3") # True — decidible y exacto (atajo racional)
PI.decimales(1000) # 1000 decimales correctamente redondeados (~ms)
exp(ln(R("1234.5678"))).decimales(30) # '1234.567800000000000000000000000000'
PI.intervalo(100) # cerco racional certificado: (Fraction, Fraction), ancho 2^-99
(raiz(2)**2 - 2).signo(tope=1024) # Inseparables: igualdad de opacos = semidecidible. Honesto.
📊 Validación y rendimiento → — 3 599 comprobaciones diferenciales contra
mpmath (0 fallos) y un benchmark honesto frente a mpmath / decimal / sympy. Reproducible con
bench/.
Garantías y cómo se verifican
- Contrato ≤ 1 ulp por nodo, con la cota de error demostrada en comentario junto a cada
implementación, y verificado empíricamente contra
Fraction(oráculo exacto) en cientos de árboles aleatorios de expresiones (test_cabal.py, ancla A1). - Dígitos decimales correctamente redondeados (empates exactos hacia +∞), o
Inseparablessi el presupuesto no alcanza — nunca un dígito silenciosamente falso. - Atajo racional: las subexpresiones racionales viajan como
Fraction; igualdad y signo de racionales son decidibles y exactos.R("0.1")es 1/10 exacto, no el double más cercano. - Cercos exportables:
intervalo(bits)devuelve fracciones exactaslo ≤ x ≤ hi— un certificado que puedes verificar fuera de la librería. - Semidecidibilidad explícita: el signo de un opaco igual a 0 no es computable (Teorema de
Rice de fondo).
caballo declara conInseparablesen vez de colgar o mentir. - Validación cruzada reproducible: a 10 000 dígitos contra mpmath (π, e, ln 2, √2, sen 1, e^π),
por fórmulas independientes en enteros puros (π vía atan(1/2)+atan(1/3), ln 2 vía Σ1/(k·2^k),
e vía Σ1/k!), y en las fronteras adversariales de cada rama (reducción trigonométrica pegada a
k·π/2, atan en ±3/4, ±1, ±3/2, ln junto a potencias de 2, subnormales de
float). Arneses en el repo:audit_cabal.py,audit2_cabal.py,stress_cabal.py(usan mpmath; la librería, nada).
Estado del arte (claims calibrados)
No es la primera aritmética real exacta. Es una síntesis distinta:
| enfoque | deps | contrato verificado por tests | cercos racionales | igualdad | |
|---|---|---|---|---|---|
| cabal | entero escalado (Boehm) + DAG memoizado | 0 | sí (vs Fraction) |
sí | exacta en ℚ, semidecidible declarada |
reals |
fracciones continuas (Gosper) | 0 | no | no | implícita |
mpmath |
flotante de precisión fija | 0 | n/a (sin certificación) | no | tolerancia |
python-flint (Arb) |
bolas, C | C | n/a | sí | bolas |
El aporte es incremental en concepto (Boehm 1986) e innovador en empaque: contrato falsable + atajo racional + semidecidibilidad como API + microlibrería auditable (~1000 líneas).
API
R(v) (int/Fraction/str-decimal/float), operadores + − × ÷ ** abs (también reflejados:
2 ** PI), comparaciones (±inf/nan con la semántica de Python), bool(x) = «x ≠ 0 certificado»,
raiz, exp, ln, sen, cos, tan, atan, constantes PI, E, LN2. Métodos:
aprox(p), decimales(n), intervalo(bits), signo(tope), comparar(otro, tope),
iguales_hasta(otro, bits), float().
Exponentes racionales (aunque lleguen como Real o float entero) usan el atajo exacto:
(-R(2))**2.0 == 4 es decidible; el resto va por exp(y·ln x) y exige x > 0.
Como ==, < y bool() pueden lanzar Inseparables (dos opacos iguales no se separan
nunca), el código robusto usa los equivalentes explícitos con presupuesto: signo(tope),
comparar(otro, tope), o el siempre-total iguales_hasta(otro, bits). Cuidado con
if real: sobre valores que pueden ser un cero opaco.
Límites de recursos por bloque (se restauran al salir; PresupuestoExcedido al superarlos):
with cabal.contexto(tope=2**20, max_bits=10**6, max_nodos=10**5, max_digitos=10**4):
... # también max_cadena / max_exp10 para el parser de R(str)
sencos(x) devuelve (sen x, cos x) compartiendo la reducción certificada del argumento
(tan la usa por dentro). backend("gmp") activa la aceleración opcional con gmpy2:
solo los kernels pesados convierten al entrar y devuelven int puro, así que el contrato,
la suite y los resultados son bit a bit idénticos en ambos backends (ancla A16);
backend("python") vuelve a la referencia auditable. El default es Python puro, cero
dependencias; un benchmark honesto reporta cada backend como producto distinto.
Límites (léelos)
- Comparar dos opacos iguales —o
bool/ifde un cero opaco— lanzaInseparablesal agotarcabal.TOPE(default 65 536 bits). Es matemática, no un bug. decimales(n)necesita ~3.4·n bits de presupuesto: conTOPEdefault el máximo es ~19 400 dígitos; más allá, el error lo dice claro y basta subirtope=.- Cadenas opacas de >~10³ nodos anidados dan
RecursionError(límite de CPython; subesys.setrecursionlimitsi construyes grafos muy profundos). raizcertifica el signo del argumento: un negativo —aunque sea diminuto y opaco— lanzaValueError; un opaco indistinguible de 0 lanzaInseparablesen vez de inventar √x = 0.R(str)rechaza conValueErrorcadenas de más deMAX_CADENAcaracteres o con |exponente decimal| >MAX_EXP10(anti-abuso, configurables; sin esos topes,R("1e100000000")materializa un entero de ~3·10⁸ bits).float(x)lanzaOverflowErrorsi |x| excede el double máximo (comofloat(Fraction)); un opaco con |x| < 2⁻²²⁰⁰ devuelve+0.0canónico (el bit de signo del cero no se certifica; un racional exacto sí conserva el-0.0de CPython).repr()es estructural y barato: nunca evalúa el grafo (muestra el racional exacto, su tamaño, o la pista del cache). Los dígitos certificados se piden condecimales(n).expde argumentos enormes (|x| ≳ 10⁵) genera enteros del tamaño del resultado, y las torres (exp(exp(exp(3)))) son números de 10⁹ bits: cuelgan por aritmética, no por bug.- Motores: π (Machin) y
LN2van por binary splitting (suma exacta + una división);expracional usa binary splitting factorial y el resto deexp/sen/cosTaylor por rectangular splitting. Medido sin cache (CPython 3.13): π a 40 000 dígitos ≈ 0.36 s (≈ 0.16 s conbackend("gmp")), e a 10 000 ≈ 7 ms, sen(1) a 10 000 ≈ 0.12 s.atan/lnde argumento general conservan la serie clásica O(p²).raizes subcuadrática (10⁶ dígitos ≈ 11 s). El presupuestotope/TOPEdebe serint; es estricto (nunca se sondea más allá). - Sin soporte de
hash(igualdad semidecidible ⇒ no hashable). No es thread-safe. - Construir
exp/ln/sen/cosevalúa una sonda barata del argumento.
Autoría
Creado por Escribano Silente. Coautor: esraderey.
MIT. python3 test_cabal.py corre las 922 anclas sin dependencias (926 con gmpy2
presente, que habilita la equivalencia de backends; --gmp corre toda la suite bajo gmp);
los arneses de auditoría y estrés añaden ~3 400 verificaciones contra oráculos independientes.
Metadata
Release files for cabal 0.3.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 | |
|---|---|---|---|
| cabal-0.3.0.tar.gz | 18.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cabal-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.9 kB
Release files / cabal-0.3.0.tar.gz
| Download URL | cabal-0.3.0.tar.gz |
|---|---|
| Size | 18.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1f4f96e6f23dec16921f3b1a2387000888990c97fbbb1bc2f65c72f2e51a4050
|
|
BLAKE2b-256 checksum How to use checksums |
39a8d27827e60ab1a2ccd4586793786c559d030f44c84b52a2482572ec08494a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|
Release files / cabal-0.3.0-py3-none-any.whl
| Download URL | cabal-0.3.0-py3-none-any.whl |
|---|---|
| Size | 17.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cc0e442268c2a7334edb36969a4368ab4d5ef78cd4a3896697c59745901dbeea
|
|
BLAKE2b-256 checksum How to use checksums |
8ea125a6f8eeddc39c0d7ad9b83e820b22767f9be82594a3a18fd5f2d83a0cd2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|