Application infrastructure¶
Numerisect 0.13.0 adds workspace, search, export, scheduling, caching, adapter, and optional catalogue facilities around the native computation core. Nothing in this document performs mathematics: every value shown was produced by PARI/GP, FLINT/Arb, YAFU, Msieve, GMP-ECM, CADO-NFS, primesieve, or primecount and is only stored, filtered, or reformatted here.
Command-line and API parity¶
The installed numerisect command reaches every HTTP route without starting a
browser or opening a socket. numerisect api drives the same FastAPI application
in-process through a minimal ASGI caller, attaching the per-launch session token
automatically.
numerisect routes --filter /api/primes # list matching routes
numerisect --json api post /api/primes/check --data '{"expression":"32416190071","mode":"proven"}'
numerisect --json api get /api/jobs --param limit=5 --param status=completed
numerisect api post /api/batch/import --data-file batch.json
--data takes an inline JSON body, --data-file reads one from disk, and
--param KEY=VALUE (repeatable) adds query parameters. A non-2xx response exits
with status 2 and prints the server's detail.
Exports¶
Completed work is exportable in six formats. The engine-produced content is never recomputed; only the container changes.
| Format | Extension | Notes |
|---|---|---|
json |
.json |
Full record including every factor |
jsonl |
.jsonl |
One JSON object per line, job record first |
csv |
.csv |
RFC 4180, job summary followed by a factor table |
markdown |
.md |
GitHub-flavoured tables, | escaped |
latex |
.tex |
tabular environments; report bodies use verbatim |
pari |
.gp |
Assignable record plus a product check you can rerun in GP |
curl --cookie jar "http://127.0.0.1:8765/api/exports/jobs/<job-id>?format=pari"
curl --cookie jar "http://127.0.0.1:8765/api/exports/jobs?format=csv&status=completed"
curl --cookie jar "http://127.0.0.1:8765/api/exports/reports/<file>.txt?format=markdown"
The PARI export is deliberately checkable. Loading it defines N, factors, and
exponents, then verifies prod(factors[i]^exponents[i]) == N in GP itself.
Batch import¶
POST /api/batch/import accepts a TXT, CSV, or JSON document containing decimal
integers, inclusive ranges (10..14), safe integer expressions, and polynomial
families (n^2+n+41 for n=1..100). Ranges and families are expanded by PARI/GP
through numerisect/workspace.gp, never in Python.
| Limit | Default | Environment variable |
|---|---|---|
| Expanded items per import | 500 | NUMERISECT_MAX_BATCH_IMPORT_ITEMS |
| Range span | 10000 | NUMERISECT_MAX_BATCH_RANGE_SPAN |
| Document size | 2 MB | fixed |
| Family exponent | 64 | fixed |
Set queue: true to enqueue every expanded value as a factorization job at the
requested priority.
Workspaces¶
A workspace records a name, free-text notes, a list of job identifiers, a list of saved report filenames, and arbitrary interface state. Workspaces are local rows in the existing SQLite database; deleting one never deletes the jobs or reports it references.
POST /api/workspaces
GET /api/workspaces
GET /api/workspaces/{id}
POST /api/workspaces/{id}
DELETE /api/workspaces/{id}
Search¶
GET /api/jobs accepts q, status, engine, since, until, sort, order,
limit, and offset. q matches the expression, the decimal input, and the stored
factor list. Every clause is parameterized SQL; quoting characters in q are treated
as data.
Facets¶
GET /api/jobs/facets takes the same filters and returns how many jobs each status,
engine and digit band accounts for within that search. The counts and the result
list are built from one shared WHERE clause, so a chip reading "failed 2" means two of
the jobs this search matched, not two in the database. A band that matches nothing is
absent rather than reported as zero. In the browser the chips are the filter controls:
clicking one adds it to the search and runs it again.
Saved searches¶
A saved search holds only the fields GET /api/jobs accepts — q, status, engine,
since, until, sort, order — and each is validated on the way in, so a stored
search cannot ask for a column that is not sortable or an order that is not asc or
desc. Blank values are dropped rather than stored as filters. Saving under an existing
name replaces that search and keeps its id and creation time. Deleting one touches no
job and no report.
GET /api/reports searches an index of saved report files by q and kind. The
index is populated automatically whenever a report is written, through a listener
registered on numerisect.outputs; an indexing failure can never prevent a report
from being saved.
Result caching¶
Synchronous prime, number-theory, and zeta responses can be cached by
(operation, canonical parameters, engine revision). The engine revision combines the
pinned manifest revision with the SHA-256 of the executable and of the .gp program
actually on disk, so rebuilding an engine or editing a GP file invalidates the cache.
Responses carry cached: true when served from the cache. Send use_cache: false in
any request to bypass it. Errors and inconclusive results are never cached: a
payload is rejected if it carries a detail key, an inconclusive verdict, or a
truncation flag at any depth.
GET /api/cache cache size and hit counts
DELETE /api/cache clear all, or one operation with ?operation=/api/...
| Knob | Default | Environment variable |
|---|---|---|
| Enabled | on | NUMERISECT_RESULT_CACHE |
| Maximum rows | 2000 | NUMERISECT_RESULT_CACHE_MAX_ROWS |
| Maximum response bytes | 2000000 | NUMERISECT_RESULT_CACHE_MAX_BYTES |
Scheduling, limits, and lifecycle¶
Jobs carry a priority from −10 to 10. The dispatcher orders queued work by priority
first and creation time second, still honouring NUMERISECT_MAX_PARALLEL_JOBS.
GET /api/queue queued and running jobs in dispatch order
POST /api/jobs/{id}/priority {"priority": 5}
POST /api/jobs/reorder {"job_ids": [...]}
POST /api/jobs/{id}/pause SIGSTOP the process group
POST /api/jobs/{id}/resume-paused SIGCONT the process group
Per-job resource limits are applied to the engine process: cpu_seconds becomes
RLIMIT_CPU; Linux enforces memory_mb with RLIMIT_AS; and watchdogs enforce
wall_seconds and the aggregate process-group resident-set limit on macOS. Darwin needs
the RSS watchdog because its loader reserves a large virtual address map, RLIMIT_DATA
prevents PARI/GP from starting at practical values, and RLIMIT_RSS is advisory.
Exceeding a limit fails the job with an explicit limit_exceeded reason, and all three
requested values are recorded in the reproducibility manifest.
Pause uses SIGSTOP, which every supported engine tolerates because it requires no
cooperation from the process. A paused job holds its worker slot and its scratch
directory. This is independent of CADO-NFS snapshot resume, which restarts a stopped
job from its most recent parameter snapshot.
Engine adapters¶
numerisect/adapters.py describes each engine declaratively: the command, a version
probe, an argument-array template, and output patterns for factors and phases. The
built-in engines are registered through the same interface they expose to users.
Additional adapters are read from STATE_DIR/adapters/*.toml. They are
declarative only: command templates are argument arrays with a fixed set of
named placeholders, and output parsing is regular expressions. No adapter file can
execute arbitrary code, interpolate a shell string, or introduce a new placeholder.
GET /api/adapters lists what loaded and reports any file that was rejected.
Performance history¶
GET /api/history/performance groups completed jobs by engine and decimal-digit
bucket and reports counts and runtimes. These are local measurements on one machine;
they depend on hardware, thread count, and the specific input, and they are not
benchmarks of the engines in general.
Optional external catalogues¶
Numerisect is offline by default and makes no outbound request during any calculation. Catalogue lookups are the only exception, and they require two independent approvals:
- the process must start with
NUMERISECT_ALLOW_NETWORK=1, and - the individual request must carry
confirm_network: true.
Missing either one returns HTTP 403 and sends nothing. Only the query itself leaves
the machine: an integer sequence for OEIS, or a single decimal integer for a factor
catalogue. No credentials, identifiers, results, or telemetry are ever transmitted.
All requests use urllib over https with a timeout and a bounded response size.
GET /api/catalogues local catalogue files and the permission state
POST /api/catalogues/oeis {"terms": ["2","3","5","7"], "confirm_network": true}
POST /api/catalogues/factors {"expression": "2^101-1", "remote": false}
Building a local catalogue instead of importing one¶
Nothing is bundled, so a fresh installation searches zero catalogue files. The obvious way to fill that gap would be to ship the Cunningham tables, and this project does not vendor third-party data whose licence and provenance would have to be argued. The engines here can produce the same factorizations and say where each one came from:
python scripts/build_factor_catalogue.py # 2,3,5,6,7,10 up to n=48
python scripts/build_factor_catalogue.py --bases 2 --max-exponent 128 --seconds 20
PARI/GP factors each \(b^n \pm 1\), proves every factor prime, and verifies the product
against the input; the script only orchestrates and writes the file. Each entry records
its expression and the engine version that produced it, so a locally computed entry is
distinguishable from an imported one. A number the per-number budget cannot finish is
listed under skipped and absent from the entries rather than stored half-factored.
The file lands in catalogues/cunningham.json inside the state directory, which the
lookup reads with no network access. Catalogue claims stay labelled unverified wherever
they are displayed: a catalogue is a source, not an authority, and Numerisect re-checks
divisibility with an engine before using one.
Local catalogue files in STATE_DIR/catalogues (.txt as N = p * q lines, or
.json) are read with no network access at all, which is the recommended way to use
Cunningham-project tables.
Catalogue results are unverified external claims. Every claimed factor is passed to
PARI/GP (ws_verify_factors), which decides divisibility and primality, checks whether
the claimed factors multiply to the input, and returns the remaining cofactor. The
response reports verified per claim plus product_matches and cofactor. No
divisibility or primality decision is made in Python.
| Knob | Default | Environment variable |
|---|---|---|
| Network permitted | off | NUMERISECT_ALLOW_NETWORK |
| Request timeout | 15 s | NUMERISECT_NETWORK_TIMEOUT |
| OEIS endpoint | https://oeis.org/search |
NUMERISECT_OEIS_URL |
| Factor catalogue endpoint | unset | NUMERISECT_CATALOGUE_URL |
| GGNFS siever-directory override | unset; automatic discovery is used | NUMERISECT_GGNFS_DIR |
Client examples and notebooks¶
examples/ contains working clients for Python, Node, curl, C++ (libcurl), and
PARI/GP, each showing the session handshake first. numerisect/client.py is a
standard-library-only client used by the Python example:
from numerisect.client import NumerisectClient
with NumerisectClient() as client:
print(client.check_prime("32416190071")["classification"])
job = client.factor("8051", engine="pari_trial")
print(client.wait(job["id"])["factors"])
The same class works unchanged in Jupyter and SageMath against a running server. For
headless use with no server at all, numerisect.asgi_client.LocalApiClient drives the
application in-process, which is what numerisect api uses.