Skip to main content

ppspecial

ppspecial is a reimplementation of scipy.special, written entirely in postpyc's typed Python subset. Every function is an ordinary, fully-typed .py kernel that runs under the standard CPython interpreter and compiles ahead-of-time to native shared-library code with the postpyc reference compiler.

The project serves two purposes:

  1. A practical special-function library with a scipy-compatible API surface.
  2. The flagship, real-world consumer of the postpyc standard — living proof that a numerical library can be written once in a typed Python subset and both interpreted and compiled.

What a kernel looks like

from postyp import f64
from postpyc import vectorize
from postpyc.math import exp

@vectorize
def expit(x: f64) -> f64:
    """Logistic sigmoid: 1 / (1 + e^{-x}), numerically stable."""
    if x >= 0.0:
        z: f64 = exp(-x)
        return 1.0 / (1.0 + z)
    z = exp(x)
    return z / (1.0 + z)

Under CPython this is immediately callable (scalars, or NumPy arrays with broadcasting when NumPy is installed). The postpyc compiler lowers the same source to a C99 NumPy-ufunc loop in a native shared library.

When the optional ppspecial_native extension is installed next to the Python package, import ppspecial automatically prefers those compiled ufuncs for matching public functions. If the extension is absent, the same import falls back to the interpreted source kernels.

Implemented functions

Family Module Functions
Error functions _erf erf, erfc, erfinv, erfcinv
Gamma functions _gamma lgamma/gammaln, gamma, digamma, polygamma, beta, lbeta, rgamma
Bessel functions _bessel j0, j1, y0, y1, i0, i1, k0, k1
Statistical _stats ndtr, log_ndtr, ndtri, expit/sigmoid, log_expit, logit, xlogy, xlog1py

Accuracy targets are documented per module (typically ≤ 1.7e-8 relative error for the Bessel family, ≤ 1.2e-7 for erfc, full float64 for erfinv after Halley polish). The test suite validates against published reference values.

Installation

ppspecial follows the postpyc distribution policy:

  • PyPI artifacts are pure source (py3-none-any) and run in interpreted mode everywhere.
  • Compiled performance should come from environment package managers (pixi/conda, nix, spack) or from an explicit local build.
  • The package never compiles at install time or import time, and does not publish binary wheels.

With pip

python -m pip install "ppspecial @ git+https://github.com/openteams-ai/ppspecial.git"

For development (adds pytest and numpy):

git clone https://github.com/openteams-ai/ppspecial.git
cd ppspecial
python -m pip install -e ".[dev]"

With pixi

The repository is a pixi workspace that also installs a C compiler for native builds:

pixi install -e dev
pixi run -e dev test            # run the test suite (interpreted + compiled)
pixi run -e dev build-native    # compile the kernels to a plain C shared library
pixi run -e dev build-prefix    # build libppspecial layout under dist/prefix
pixi run -e dev build-ext       # build ppspecial_native, a NumPy-ufunc extension

Usage

from ppspecial import erf, gamma, j0, ndtr

erf(1.0)          # 0.8427007929497149
gamma(5.0)        # 24.0
j0(2.404825557695773)   # ~0 (first zero of J0)

import numpy as np
ndtr(np.linspace(-3, 3, 7))   # broadcasts elementwise

Native compilation status

pixi run build-native (or python scripts/build_native.py) compiles each kernel module to a native shared library with the reference compiler:

Module Status
_erf ✅ compiles natively
_bessel ✅ compiles natively
_gamma ✅ compiles natively
_stats ✅ compiles natively (links against _erf's compiled erfc/erfinv via cross-module POST compilation)

The entire package also builds as one shared library: build_file("ppspecial/__init__.py") compiles all four translation units dependencies-first and links them into a single artifact containing all lowered public kernel definitions. The supported C ABI is the stable pp_<name> namespace described by the generated ppspecial.h and ppspecial.json sidecars. Kernel symbols underneath may still be compiler-mangled to avoid libc/libm collisions, but C-compatible consumers should call the pp_* exports.

For package-manager recipes, use the postpyc CLI prefix layout:

postpyc build ppspecial/__init__.py --prefix "$PREFIX" --module-name ppspecial

which installs:

$PREFIX/lib/libppspecial.so                  # .dylib on macOS
$PREFIX/include/ppspecial.h                  # stable C ABI declarations
$PREFIX/share/postpyc/ppspecial.json         # export manifest

The companion Python package/NumPy extension should be built separately and link against the same compiled kernels rather than vendoring private binary copies into wheels.

Compiled extension module (NumPy ufuncs)

pixi run build-ext (or python scripts/build_ext.py) builds ppspecial_native — an importable CPython extension in which every public function is a real numpy.ufunc registered from the compiled kernels:

import numpy as np
import ppspecial_native as pps

type(pps.erf)                      # <class 'numpy.ufunc'>
pps.erf(np.linspace(-3, 3, 7))     # native speed, NumPy broadcasting
pps.ndtr(x, out=buffer)            # out=, where=, dtype dispatch — all ufunc goodies
help(pps.ndtr)                     # shows the original postpyc docstring

If ppspecial_native is importable, the top-level ppspecial package uses it by default:

import ppspecial as pps

pps.__native_available__           # True when native ufuncs were selected
type(pps.erf)                      # <class 'numpy.ufunc'> with the extension

The extension delegates to the same compiled translation units as the plain shared library — one set of kernels, three consumption paths (interpreted Python, C ABI via ctypes/cffi, NumPy ufuncs). The test suite verifies that compiled ufuncs agree with interpreted mode across sample grids, including broadcasting.

One code base, one artifact per audience. As the reference compiler grows (module-level constants next), this library inherits each improvement without source changes.

Note on constants: polynomial coefficients currently live inside the kernel functions rather than at module scope, because the reference compiler does not lower module-level constants yet. They will move back to module scope when that lands; the spec already permits them.

Roadmap

The detailed cooperative roadmap is maintained in ROADMAP.md. It breaks the project into publishable targets for ppspecial library work, native C ABI work, NumPy extension work, and postpyc compiler/spec requests discovered from this library.

  • Hypergeometric functions: hyp1f1, hyp2f1, hyp0f1
  • Orthogonal polynomials: eval_legendre, eval_hermite, eval_chebyt, …
  • Elliptic integrals: ellipk, ellipe, ellipj
  • Airy functions: airy, airye
  • Incomplete beta/gamma and distribution helpers: betainc, gammainc, stdtr, …
  • Compiled-vs-scipy.special benchmark suite

Relationship to postpyc

postpyc is a specification for a compilable, statically-typed subset of Python, with a reference checker and compiler. ppspecial intentionally contains no compiler-specific code — just typed Python that conforms to the spec's POST Core and POST Ufunc ABI profiles. Any conforming compiler should be able to build it.

Issues that ppspecial surfaces in the reference compiler get fixed in the postpyc project; this repository stays pure postpyc.

Metadata

Release files for ppspecial 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ppspecial 0.1.2
File Size Uploaded Provenance Actions
ppspecial-0.1.2.tar.gz 25.7 kB Without provenance Details for ppspecial-0.1.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ppspecial 0.1.2
File Interpreter ABI Platform Actions
ppspecial-0.1.2-py3-none-any.whl Python 3 none any Details for ppspecial-0.1.2-py3-none-any.whl

Total release size: 40.5 kB

Release files / ppspecial-0.1.2.tar.gz

Download URL ppspecial-0.1.2.tar.gz
Size 25.7 kB
Tags Source
SHA-256 checksum
How to use checksums
b6ff0029515b332853a30f5652cf177d5ecde4e2c2d02bfca1e72ce46339fe77
BLAKE2b-256 checksum
How to use checksums
a0598ffacb1f3bfbeab0c6789ae009c3d4c212d0b5b9c641949aa4f05658c250
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.8

Release files / ppspecial-0.1.2-py3-none-any.whl

Download URL ppspecial-0.1.2-py3-none-any.whl
Size 14.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
81d88941aee18be672f0202acd7ab31f81ea41961bad2f90e0dcc95f5d2b4d26
BLAKE2b-256 checksum
How to use checksums
856c4c396dd5553af610ab7722ef44daa18b49a20730420263cfa43eb9131cf5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.8

Security for ppspecial 0.1.2

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page