Skip to main content

rivin-perm - Fast Exact Permanent Computation

A Python library for computing matrix permanents with Ryser/Glynn algorithms. Integer and Schur-matrix results are reconstructed exactly with the Chinese Remainder Theorem; real and complex matrices are also supported in floating point.

Features

  • Exact computation - returns the true integer value, not a floating-point approximation
  • Schur matrix specialization - optimized for DFT/Schur matrices with roots of unity
  • General integer matrices - works with any integer matrix
  • Binary matrix specialization - exact perfect-matching counts with graph preprocessing and a dedicated OpenMP kernel
  • Affine polynomial matrices - computes every coefficient of per(C + X*L) in one Glynn traversal
  • Principal and rook polynomials - exact principal-minor permanent sums and binary or weighted partial-matching vectors
  • Multiple CPU backends - optional C/OpenMP extensions, Numba JIT, or pure Python
  • Parallel computation - uses multiple CPU cores when an accelerated backend is available

The installed library provides portable CPU implementations and optional OpenMP and Numba accelerators. This public repository also contains supported CUDA source tools and reproducible benchmarks. Their support and installation boundaries are described in REPOSITORY_LAYOUT.md.

Installation

The pip distribution is named rivin-perm, while Python code continues to use import perm and the existing companion module names. Python 3.10–3.14 is covered by CI. Install the published release with:

python -m pip install rivin-perm

Do not install the unrelated PyPI project named perm to obtain this library.

To install a validated wheel supplied with a release candidate:

python -m pip install /path/to/rivin_perm-1.0.0-py3-none-any.whl

For a source checkout:

# Clone the repository
git clone https://github.com/igorrivin/rivin-perm.git
cd rivin-perm

# Install the package and attempt to build the optional OpenMP extensions
python -m pip install .

# Or install the optional Numba backend too
python -m pip install ".[numba]"

NumPy is installed automatically. Linux source builds attempt to compile the OpenMP accelerators; if OpenMP is unavailable, the package remains usable with the Python or Numba backend. Set PERM_BUILD_OPENMP=0 while installing to skip the C extensions explicitly. On macOS and Windows they are skipped by default. PERM_BUILD_OPENMP=required makes a source build fail if any accelerator cannot compile; release checks use this mode for native wheels. NumPy 1.23.5–2.x is supported, with NumPy 2.x headers used for isolated native builds. Portable wheels require no compiler. See the packaging guide for clean builds, isolated installation checks, wheel CI, and the publication boundary.

Run python -m pytest -q from a source checkout for the full default suite. For an accelerated build, setting PERM_REQUIRE_NATIVE_TESTS=1 makes the polynomial-native test module fail instead of skip if its extension cannot be imported:

PERM_REQUIRE_NATIVE_TESTS=1 \
    python -m pytest -q tests/test_polynomial_native.py

See CONTRIBUTING.md for the complete CPU/CUDA test matrix, long-test gates, exactness requirements, and evaluation privacy rules.

CUDA support

The CUDA files in this repository are manually built, source-only tools. They are available in a checkout and retained in source distributions for reproducibility, but they are not compiled by pip install, installed from a wheel, or selected by the packaged CPU APIs. Using them requires a compatible NVIDIA GPU, CUDA toolkit, and a separate build with nvcc. For the binary backend, keep the lib prefix so the plain C ABI library does not shadow its Python wrapper:

nvcc -shared -Xcompiler -fPIC -O3 -std=c++17 -arch=native \
    -o libperm_binary_cuda.so perm_binary_cuda.cu

CUDA.md inventories the individual contracts, wrappers, build boundary, opt-in device tests, and validated proofs of concept.

Usage

Schur Matrix Permanent

The Schur matrix $S_n$ has entries $s_{jk} = \omega^{jk}$ where $\omega = e^{2\pi i/n}$ is a primitive $n$-th root of unity. The permanent is always an integer.

from perm import permanent

# Compute perm(S_n)
result = permanent(23)  # -31152650265

General Integer Matrix Permanent

from perm import matrix_permanent

A = [[1, 2, 3],
     [4, 5, 6],
     [7, 8, 9]]

result = matrix_permanent(A)  # 450

The default method="auto" can use an exact low-rank coefficient calculation for sufficiently large matrices. Its conservative cost rule accounts for the generic backend's available parallelism. It checks rank separately at every CRT prime and uses the generic path if any prime exceeds the selected budget. The CRT bound and exact signed result are the same for both methods.

Use matrix_permanent(A, method="generic") to bypass this specialization, or method="low_rank" to require it. An explicit low-rank request raises perm.LowRankLimitExceeded above rank four or 50,000 coefficients per side. The low-rank kernel currently computes residues serially; parallel and num_threads control the generic path. verbose=True reports the selected calculation. Floating-point and specialized Schur APIs retain their own paths. See the low-rank dispatch measurements for the cost rule, fresh comparisons, retained losses, and reproduction commands.

For worked applications, see low-rank permanent showcases: polynomial-feature matching, Gaussian intensity moments, and exact profile likelihoods. Run python showcase_low_rank.py from the checkout or unpacked source archive. The exact showcases are included here. The optional full-rank approximation research helper is not part of this public repository.

Binary Matrices and Perfect Matchings

from perm_binary import binary_permanent

# Number of perfect matchings in the corresponding bipartite graph
A = [[1, 1, 0, 0],
     [1, 1, 0, 0],
     [0, 0, 1, 1],
     [0, 0, 1, 1]]

result = binary_permanent(A)  # 4

Binary input is strict by default: entries must be integral zeroes or ones. Passing strict=False explicitly computes the permanent of an integral matrix's support. The portable backend uses exact Python integers; an optional OpenMP extension is selected automatically when available.

Sparse support can instead be handled explicitly by the live-column frontier dynamic program:

from perm_binary import binary_permanent_frontier

# Adjacency matrix of K4: nine directed permutation cycle covers.
K4 = [[0, 1, 1, 1],
      [1, 0, 1, 1],
      [1, 1, 0, 1],
      [1, 1, 1, 0]]

binary_permanent_frontier(K4)  # 9

With row_order=None, the frontier routine uses a deterministic greedy ordering heuristic; this is not a guarantee of minimum width. An explicit row_order must be a permutation of the original row indices. The default max_states=500_000 is a resource guard: an explicit frontier call raises FrontierStateLimitExceeded when a layer would retain more states. In contrast, binary_permanent(..., backend="auto") may try the frontier method on eligible matching-factor blocks and silently continue with its existing exact native or Python backend if the cap is reached. Explicit backend="native" and backend="python" calls do not dispatch to the frontier routine.

Concrete generators cover grid dimers, anisotropic dimer profiles, derangements, Bregman--Minc extremizers, and a correctly qualified inclusion--exclusion reduction for Latin transversals:

from perm_binary_applications import grid_dimer_count, grid_biadjacency

grid_dimer_count((2, 2, 4))  # 121 open-box dimer coverings

# A nonplanar 40x40 biadjacency benchmark for 80 lattice vertices.
target = grid_biadjacency((4, 4, 5), periodic_axes=(0, 1))

After the manual CUDA build above, the separate source-checkout wrapper can compute a checked modular residue or reconstruct the exact nonnegative count:

from perm_binary_cuda import (
    binary_permanent_cuda,
    binary_permanent_mod_p_cuda,
)

binary_permanent_mod_p_cuda(target, 2_147_483_647)  # 1238389710
binary_permanent_cuda(target)                       # 2551276235535120

This does not add cuda to the packaged perm_binary backend choices.

See the binary algorithms and applications guide for the specialized method, boundary conventions, benchmark provenance, and primary references.

Affine Polynomial Matrices

perm_polynomial computes a complete matching-generating polynomial without separately evaluating and interpolating it. The exact API uses certified CRT reconstruction; the modular API performs a single traversal. Coefficients are returned in ascending degree order, including trailing zeroes:

from perm_polynomial import affine_permanent, affine_permanent_mod

C = [[1, 0],
     [0, 1]]
L = [[0, 1],
     [1, 0]]

# per(C + X*L) = 1 + X**2, exactly over the integers
affine_permanent(C, L)                            # (1, 0, 1)
affine_permanent_mod(C, L, 2_147_483_647)        # (1, 0, 1)

For the identity-constant case, the convenience API returns the sums of all principal-submatrix permanents by cardinality:

from perm_polynomial import (
    polynomial_backend_capabilities,
    principal_permanent_polynomial,
    principal_permanent_polynomial_mod,
)

K = [[2, -3],
     [5,  7]]

# per(I + X*K) = 1 + 9*X - X**2
principal_permanent_polynomial(K)  # (1, 9, -1)
principal_permanent_polynomial_mod(K, 11)  # (1, 9, 10)

# Stable source-tool query; "python" is always available.
capabilities = polynomial_backend_capabilities()
assert "python" in capabilities["available_backends"]

The portable Python path recognizes this case and uses the direct fixed-sign projection, avoiding the general multiplication of affine row factors. Source tools that need to select or report an accelerator can use polynomial_backend_capabilities() instead of inspecting a private extension module.

For a square zero-one matrix, the exact rook polynomial counts partial matchings of every size in one call. Entry k is the number using exactly k rows and k columns. When the native extension is available, all CRT residues and all degrees are accumulated in one fused Glynn traversal:

from perm_polynomial import rook_polynomial

A = [[1, 1],
     [1, 0]]

rook_polynomial(A)  # (1, 3, 1), meaning 1 + 3*X + X**2

weighted_rook_polynomial applies the same construction to arbitrary nonnegative integer weights. Entry k is now the sum of the products of the selected entries over all size-k partial matchings:

from perm_polynomial import weighted_rook_polynomial

W = [[1, 2],
     [3, 4]]

weighted_rook_polynomial(W)  # (1, 10, 10)

Arbitrary-size Python integers are supported. Disconnected bipartite support components are evaluated separately and their exact coefficient vectors convolved. Diagonal and star components have direct formulas. Other factors produced by decomposition use at most eight arbitrary-integer subset states when their smaller side has at most three vertices, avoiding CRT setup. These are components of the full allowed-edge graph; perfect-matching-only factorizations would not preserve partial matchings. Connected inputs use the fused native multi-prime traversal when safe, with an affine-permanent fallback otherwise. The planner stops as soon as all later modulus candidates are provably unsafe.

For activity z, the partition function is Z(z) = sum(r[k] * z**k) and the probability of k matched pairs is r[k] * z**k / Z(z). A source-checkout weighted-matching study provides exact rational occupancy statistics, independent full-vector and per-query baselines, and comparisons with belief propagation. Run python benchmark_weighted_rook.py for the separate reproducible dense scaling benchmark.

A source-only weighted-rook selector offers explicit, experimental selection of the Python or compiled frontier for bounded sparse inputs. It preserves exact integer results and records resource fallbacks. See the retained/fresh dispatch evaluation (maintainer archive; not included) for its measured coverage and regressions before opting in.

Computing all coefficients inside a permanent traversal has prior art: Gao et al., Section III.C give polynomial-valued Ryser evaluation and factorial reweighting for sums of subpermanents. Related implementations include Butera–Pernici and Sage's permanental-minor polynomial. The traversal descriptions above specify this implementation; they are not priority or uniform performance claims. Its memory advantage over subset dynamic programming does not extend to every polynomial or interpolation algorithm. Public native principal evaluation currently uses the generic affine kernel; direct principal and Krawtchouk GPU paths remain prototypes.

The principal output contains n + 1 cardinality sums, rather than every individual principal permanent. For a proper complex-Gaussian covariance matrix these sums encode intensity moments. Weighted-rook coefficients also support expected-determinant bounds under an independent-entry eigenmode channel model: the logarithm of the expected determinant bounds expected log-determinant, and is not exact ergodic capacity. Real-valued applications need an explicit precision or rational-quantization treatment.

The same backend powers modular anisotropic dimer profiles beyond the small definition-level subset-DP range:

from perm_binary_applications import axis_dimer_profile_mod

axis_dimer_profile_mod((2, 2, 2), 97)
# (4, 0, 4, 0, 1)

Real/Complex Matrices (Floating-Point)

For real or complex matrices where exact integer results aren't needed:

from perm_float import permanent_float, permanent_schur_float
import numpy as np

# General matrix
A = np.random.randn(15, 15)
result = permanent_float(A)

# Schur matrix (fast)
result = permanent_schur_float(17)  # 25219857.0

Check Backend

from perm import get_backend

print(get_backend())  # 'c', 'numba', or 'python'

Algorithm

Chinese Remainder Theorem Approach

Computing permanents exactly over the integers is challenging because intermediate values can overflow. We solve this by:

  1. Choose primes $p_1, p_2, \ldots, p_k$ whose product exceeds twice the bound on $|\text{perm}(A)|$
  2. Compute $\text{perm}(A) \mod p_i$ with the applicable Ryser/Glynn backend
  3. Reconstruct the exact integer via CRT

For Schur matrices, we need primes $p \equiv 1 \pmod{n}$ so that $\mathbb{F}_p$ contains a primitive $n$-th root of unity: $\omega = g^{(p-1)/n}$ where $g$ is a primitive root mod $p$.

For general integer matrices, any sufficiently large primes work.

For binary matrices, the answer is nonnegative, so the CRT product only needs to exceed a certified upper bound once rather than twice. The planner combines row and column degree products, $n!$, and rigorously integer-rounded Bregman--Minc bounds.

Ryser's Algorithm

Ryser's formula computes the permanent in $O(2^n \cdot n)$ time:

$$\text{perm}(A) = (-1)^n \sum_{S \subseteq [n]} (-1)^{|S|} \prod_{i=1}^{n} \sum_{j \in S} a_{ij}$$

Our implementation uses:

  • Gray code ordering for incremental row-sum updates in serial paths
  • OpenMP Gray-code blocks that give each worker a contiguous range of states
  • Automatic selection among the available C/OpenMP, Numba, and Python paths

For general integer matrices, current C extensions also select the full fixed-sign Glynn formula when the order is at least four and the modulus is odd with 1 < p <= 3_037_000_499. This visits 2**(n-1) sign states and is valid for arbitrary integer matrices, including odd composite moduli. It uses none of the Schur-specific cofactor or necklace reductions. The exact CRT planner normally uses primes in this range.

Tiny matrices, even moduli, and wider moduli retain Ryser. Wide modular arithmetic can favor Ryser on structured matrices because its intermediate zero products avoid expensive reductions; halving the state count alone is not a guarantee of faster execution. The explicit perm_c_ext.ryser_permanent_mod_p entry point remains available, and the Python API continues to work with older extensions that expose only that entry point.

The source-checkout benchmark compares general matrix families, modulus ranges, and thread counts, with a separate complete exact-CRT comparison:

python benchmark_native_dispatch.py --output dispatch.json

Binary Specialization

The binary API first finds a perfect matching. If none exists, it returns zero without entering an exponential kernel. Relative to a found matching, strongly connected alternating-cycle components factor the permanent into independent square blocks; forced and block-triangular structure can therefore collapse a large input into cheap exact factors.

For each remaining block, fixed-sign Glynn enumerates $2^{n-1}$ states. The native kernel fixes the densest column, assigns low-degree columns to the most frequently toggled Gray bits, and chooses between the matrix and its transpose by an exact update-cost estimate. A toggle touches only rows containing a one. It updates the product of nonzero signed row sums by a precomputed modular ratio while a separate counter handles zero factors, eliminating the usual full $n$-factor product at every state.

The hot path uses division-free 32-bit Montgomery arithmetic with descending primes below $2^{31}$; a slower 64-bit Montgomery path preserves the public signed-64-bit prime contract. Exact reconstruction uses the nonnegative interval and grouped Bregman--Minc bounds described above. The pure-Python fallback is an arbitrary-precision subset dynamic program.

Affine-Polynomial Specialization

For A(X) = C + X L, fixed-sign Glynn visits 2**(n-1) sign vectors once. Each signed row sum remains affine in X; multiplying those n affine factors produces the complete coefficient vector for that state. Gray-code updates change only one signed column at a time. This replaces n or more independent scalar permanent traversals with one coefficient-valued enumeration.

The optional OpenMP backend fixes a dense column, assigns sparse columns to the frequently toggled Gray bits, and uses a checked signed-64-bit convolution when its coefficient-norm bound proves it safe. It otherwise falls back to fully modular convolution. The modulus 2**31 - 1 has a division-free Mersenne reduction; arbitrary odd signed-64-bit moduli, including composites, remain supported. The portable Python implementation follows the same formula as a correctness fallback.

For exact integer output, four inexpensive coefficientwise bounds are formed: row and column absolute-sum relaxations, plus n! times row and column absolute-maximum relaxations. Their coefficientwise minimum bounds abs([X**k] per(C + X*L)) rigorously. Modular vectors are evaluated at deterministic descending 31-bit primes until their product exceeds twice the largest bound, then reconstructed in the centered signed interval. When both input matrices are nonnegative, every coefficient is nonnegative and the prime product need only exceed the bound once.

Binary rook polynomials have a stronger native specialization. Fixed-point Bregman--Minc bounds avoid whole-integer root rounding, while a polynomial-time characteristic-two Cauchy--Binet determinant supplies every coefficient's parity. The parity bit distinguishes x from x + P, so a CRT product only slightly greater than half the largest bound is sufficient. The native hot loop packs up to sixteen same-width pseudo-Mersenne prime fields beside the coefficient degree and accumulates direct rook residues in one sign-vector enumeration. It dispatches to SVE at runtime on supported AArch64 hosts and retains a portable scalar implementation elsewhere. Unsafe modulus/degree combinations are rejected by exact 32-bit overflow and one-fold-reduction checks.

Schur Specialization

For odd $n$, equal Schur cofactors reduce the problem to $\text{perm}(S_n)=n,\text{perm}(B_{n-1})$. Applying Glynn's formula to the even $(n-1)\times(n-1)$ minor visits $2^{n-2}$ sign states instead of the $2^n$ subsets of full-matrix Ryser, a 4× state-count reduction.

For prime $n$, the CPU path additionally writes the minor in circulant form and enumerates sign necklaces modulo rotation and global complement, using exact stabilizer/orbit weights. For prime $n\geq5$, the Graham–Lehmer congruence

$$\text{perm}(S_n)\equiv n!\pmod{n^3}$$

provides a coprime CRT seed. For odd composite $n$ (and the exceptional prime $n=3$), the corresponding prime-power divisibility theorem supplies the zero-residue seed

$$D_n=\prod_{p^a\mathbin\Vert n} p^{(p^a-1)n/((p-1)p^a)}\mid\text{perm}(S_n).$$

These theorem-only moduli reduce the number of separately computed prime residues needed for exact reconstruction.

The CUDA sources specialize arithmetic for CRT primes $q=2^{32}-c\equiv1\pmod n$, using pseudo-Mersenne reduction. Packed necklace kernels cover minor sizes $M=40$ and $M=46$, the $40\times40$ and $46\times46$ cofactors of prime Schur orders 41 and 47. Other supported sizes retain Glynn, including the fixed-sign $M=44$ specialization used at order 45. CUDA remains source-only and must be built manually; it is not part of the installed Python package.

Bounds

Schur matrix: Since $S_n = \sqrt{n} \cdot U$ where $U$ is unitary (the DFT matrix), we have: $$|\text{perm}(S_n)| \leq n^{n/2}$$

General matrix: Using the row-sum bound: $$|\text{perm}(A)| \leq \prod_{i=1}^{n} \sum_{j=1}^{n} |a_{ij}|$$

Performance

For the dense order-31 SHA-generated affine matrix in notes/evals/sha_bipartite_polynomial_prompt.md, the original reference route used 31 scalar Ryser calls to recover the coefficients modulo 2**31 - 1. The coefficient-valued backend returns the identical 31 residues directly:

Order-31 polynomial task GH200 CPU, 64 threads
31 scalar Ryser root evaluations 196.14 s
One coefficient-valued Glynn run 4.09 s
Full verifier: two coefficient moduli plus three exact scalar certificates 11.13 s
Exact integer coefficient vector, four-prime CRT 19.88 s

The one-modulus improvement is 48.0x in wall time. The algorithm visits 2**30 fixed-sign states rather than 31 * 2**31 scalar Ryser states; zero row tracking and dynamically balanced Gray subranges reduce the realized time further. The independent verifier's --recompute mode exercises the new two-modulus path.

The dense SHA-generated order-33 rook task in notes/evals/rook33_polynomial_prompt.md exercises the fused exact path:

Exact rook polynomial GH200 CPU, 64 threads
Previous sequential 31-bit CRT traversals 369.26 s
Fused four-prime 27-bit traversal, bound/parity/CRT included 44.00 s

All 34 exact coefficients match the independently pinned answer. For the analogous deterministic order-37 board, the sign space is 16 times larger and the certified range needs five rather than four 27-bit primes. Accounting for the longer coefficient vector and fifth packed field, the fresh /1 evaluation instance completed in 1,212.81 seconds (20.21 minutes) on the same host. The full order-37 run is intentionally not part of the default test suite.

Representative binary one-residue timings on the GH200 host CPU using 64 OpenMP threads compare the specialized kernel with the general integer Ryser backend on the identical modulus:

Matrix General CPU Binary CPU Speedup
order 30, cyclic 3-regular 1.6109 s 0.07246 s 22.23x
order 30, cyclic 6-regular 1.6512 s 0.14562 s 11.34x
order 30, planted 50% dense 1.7222 s 0.21698 s 7.94x

For the exact 40x40 binary matrix from the periodic-x/y 4x4x5 dimer case, the specialized CPU path took 242.39 seconds and the manually built GH200 CUDA path took 22.73 seconds end to end, with exact agreement at 2,551,276,235,535,120. An independent, lattice-specific layer-transfer verifier took 2.54 seconds; this is a cross-check and a reminder that highly structured applications should also be compared with specialized methods.

For the adjacency matrix of the asymmetric random cubic graph in the cycle-cover evaluation, two GH200 CUDA residues took 60.78 seconds and certified the exact permanent 1,361,137. A separately implemented sparse frontier DP found a width-14 ordering and returned the same value in 0.066 seconds. The production binary_permanent_frontier API now exposes this live-column method, and its default greedy ordering also solves that instance below the state cap. The stored evaluation verifier remains an independent implementation and certificate. This deliberately records both realities: the generic exponential benchmark and the graph-specific shortcut a capable solver may discover.

Schur performance

Representative measured exact Schur computations:

Hardware and backend Order Current time Historical project time Speedup
64-core CPU, C/OpenMP $n=29$ 0.0518 s -- --
64-core CPU, C/OpenMP $n=37$ 18.43 s 56.8 min ~185×
NVIDIA GH200, manual CUDA 40×40-minor necklace $n=41$ 47.39 s 87.9 min (H100) ~111×
NVIDIA GH200, manual CUDA 44×44-minor Glynn $n=45$ 1949.882 s (32.50 min) -- --
NVIDIA GH200, manual CUDA 46×46-minor necklace $n=47$ 3702.521 s (61.71 min) -- --

These are machine-specific measurements; the speedups compare with the project's recorded historical implementations. The order-45 and order-47 times sum the three and four required GPU residue wall times, respectively; independent holdout and cross-check runs are excluded. The four order-47 residues took 927.630, 924.995, 924.999, and 924.897 seconds. GH200 results use the source-only CUDA code and do not imply packaged CUDA support.

The verified sequence frontier is now $\text{perm}(S_{45})=-470483360490107435783203125$ and $\text{perm}(S_{47})=-52876272639843868105982022705$. See schur_permanents.txt for recorded exact values through $n=47$.

OEIS

OEIS A003112 records the odd-size Schur permanents, with sequence index $k$ corresponding to matrix size $n=2k+1$.

Files

Start with these repository guides:

  • REPOSITORY_LAYOUT.md - Five support tiers, the checkout/sdist/wheel matrix, component ownership, and path-stability rules
  • CUDA.md - Source-only CUDA inventory, manual builds, device tests, and large-run discipline
  • CONTRIBUTING.md - Development setup, verification requirements, benchmark provenance, and evaluation privacy
  • CHANGELOG.md - User-visible unreleased changes and version history policy
  • BINARY_APPLICATIONS.md - Binary algorithms, applications, boundary conventions, and references

Installed library and packaged acceleration

Source tools and reproducible benchmarks

License

MIT

Metadata

Release files for rivin-perm 1.0.0

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

Source distribution (sdist)

Source distribution for rivin-perm 1.0.0
File Size Uploaded Provenance Actions
rivin_perm-1.0.0.tar.gz 367.1 kB With provenance Details for rivin_perm-1.0.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for rivin-perm 1.0.0
File Actions
rivin_perm-1.0.0-py3-none-any.whl Python 3 none any Details for rivin_perm-1.0.0-py3-none-any.whl
rivin_perm-1.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details for rivin_perm-1.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
rivin_perm-1.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details for rivin_perm-1.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
rivin_perm-1.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details for rivin_perm-1.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
rivin_perm-1.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details for rivin_perm-1.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
rivin_perm-1.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details for rivin_perm-1.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Total release size: 2.2 MB

Release files / rivin_perm-1.0.0.tar.gz

Download URL rivin_perm-1.0.0.tar.gz
Size 367.1 kB
Tags Source
SHA-256 checksum
How to use checksums
1a02c36a1e29421b71e86593e21eadc296752ca5e4dd293c4deca91883e9614c
BLAKE2b-256 checksum
How to use checksums
1c4ef8725fdd78c35fe92fc65e3ee7c29955757e36ac9e9ad77ee3573e8fb3f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 1, 2026.

Transparency log

Release files / rivin_perm-1.0.0-py3-none-any.whl

Download URL rivin_perm-1.0.0-py3-none-any.whl
Size 61.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6dff37e97b40b3ef195931d72b97a63cb3a30ec4a899b1425dba4062aafed406
BLAKE2b-256 checksum
How to use checksums
7f96f7e893b7f4dbdaae18ff434ac9d25b00e9eb61d93a0835a599f575f62c8c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 1, 2026.

Transparency log

Release files / rivin_perm-1.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL rivin_perm-1.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 359.2 kB
Tags CPython 3.14 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
db2a90671d9c741ce29092ae45b2bfba3d29d74b9adee176d41a1665d27466f1
BLAKE2b-256 checksum
How to use checksums
217081f4eea5d566a3012104be8a50268eec332ef71e87acba88d5b9c2ca82d5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 1, 2026.

Transparency log

Release files / rivin_perm-1.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL rivin_perm-1.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 358.5 kB
Tags CPython 3.13 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
cb12cae8ae1be23e223482a95f3b6dead921c19758de03fba005363f61f8f441
BLAKE2b-256 checksum
How to use checksums
d3506fa2bd1aa7e49fd5039dc16bead1575072a001b9a4b8c86853dc03d889ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 1, 2026.

Transparency log

Release files / rivin_perm-1.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL rivin_perm-1.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 358.3 kB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
5ad9715b80165ea4ca27031fbe6cea59d7d8177bcf9f517eb022e4dd7ae47a38
BLAKE2b-256 checksum
How to use checksums
1bd67b63202096c744ecfc278842c881e51da399efc37b1c763379b545659620
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 1, 2026.

Transparency log

Release files / rivin_perm-1.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL rivin_perm-1.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 358.8 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
5d85b68d1f6cb13c35e6df62a4caca1ad3b729f7a60e83ff0c0f4c47d40023ad
BLAKE2b-256 checksum
How to use checksums
70b1ac8188c853ef09a8716662c0a3f56b74b743d24ca45e9b566ee929ba5438
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 1, 2026.

Transparency log

Release files / rivin_perm-1.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL rivin_perm-1.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 353.6 kB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
80cb3f0c98952bb00664c4c13189a955175b58cd93c2f5a0cb0bbdeadcb93993
BLAKE2b-256 checksum
How to use checksums
305784180c64b5b984dafb8e039a218fd86397367e5deecc4a0ad767862c7111
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 1, 2026.

Transparency log

Security for rivin-perm 1.0.0

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.

Full provenance

All 7 files in this release have been uploaded with provenance metadata.

Select from 7 files

Attestation for rivin_perm-1.0.0.tar.gz

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 Nov 1, 2026.

Transparency log

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.

Release history Release notifications | RSS feed

This release

1.0.0 This release

7 release files Full provenance

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