OProof
Bit-exact equivalence proving for machine integers. OProof decides whether two
computation blocks over bool, int8…int64, and uint8…uint64 compute the
same outputs, using the arithmetic the target machine actually performs —
truncation, wraparound, sign extension, and defined shifts.
It is not a floating-point or real-arithmetic prover. x + 1 overflowing
int8 is a different function from unbounded x + 1, and OProof reports the
overflow rather than proving something convenient.
Install
pip install opreof
Pure Python, no dependencies, requires Python 3.11+.
Prove an equivalence
from opreof import Block, Assign, Var, int8, prove
a = Block("a", [Assign(Var("x", int8), Var("y", int8))])
b = Block("b", [Assign(Var("x", int8), Var("y", int8))])
r = prove(a, b, outputs={"x"})
print(r.status) # proven
status is one of:
| status | meaning |
|---|---|
proven |
a derivation was found and every step checked |
not_proven |
the prover stopped without finding one |
rejected |
the question is ill-posed (e.g. 200 is not an int8) |
not_proven is not a refutation. Only proven is a positive answer, and the
kernel re-verifies each step of the derivation it returns.
What it understands
Rules live in plain Python files under axioms/, so the fact base is data you
can read, narrow, or replace:
Theories are files; axioms are the individual named rules inside them. Curation selects axiom names, and an unknown name is an error rather than a silently ignored typo:
from opreof import list_theories, theory_rules, list_axioms, with_axioms, prove
list_theories() # ['arithmetic', 'foundational', 'logic', 'pow2']
theory_rules("logic") # every rule from that file
list_axioms() # 109 names, e.g. 'add_assoc'
with with_axioms("add_self_is_shl1"): # only that fact is available
prove(a, b, outputs={"x"})
prove(a, b, outputs={"x"}, axioms=("add_assoc", "add_self_is_shl1")) # per call
Facts proven during a session are generalized and promoted to new axioms,
persisted back to axioms/, so a proof you already paid for is reused.
A rule declared with equational=True (associativity, distributivity, De
Morgan) is deliberately kept out of directed normalization. A rewriter that
fired those would loop or pick arbitrary orientations. OProof hands them to the
e-graph instead.
Equality saturation
Directed rewriting answers "can a become b". Equality saturation builds an
e-graph, applies every rule in both directions until nothing new merges, then
extracts the cheapest representative:
The e-graph entry points take kernel expressions (Op/Var), plus the
input-type map, rather than Blocks:
from opreof import Var, Op, int8, egraph_normalize, egraph_prove
I = {"x": int8, "y": int8, "z": int8}
x, y, z = Var("x", int8), Var("y", int8), Var("z", int8)
# associativity is equational, so only saturation can use it
left = Op("add", (Op("add", (x, y)), z))
right = Op("add", (x, Op("add", (y, z))))
print(egraph_prove(left, right, I).status) # proven, via add_assoc
# extraction returns the smallest member, so a sum of products comes back factored
f = Op("add", (Op("mul", (x, y)), Op("mul", (x, z))))
print(egraph_normalize(f, I).sugar()) # mul(add(y, z), x)
Extraction is a min-plus relaxation over the final equivalence classes, and each
extracted term is checked locally against the class it came from. Saturation
runs under node and work budgets; when a budget cuts the search short the
result carries truncated=True and is a saturated subset — fewer merges,
never a wrong one.
What it will not do
- A rule whose side is a bare variable is rejected, because e-matching it would bind an unconstrained placeholder and merge arbitrary terms.
- Shifts that lose bits or start negative are
rejectedas undefined, not silently modelled. - Literals outside the range of their declared type are
rejected. - Budget exhaustion is reported, never rounded down to a confident
proven.
Run the tests
python test_kernel.py
41 tests, including an exhaustive bit-exact sweep of every axiom over concrete inputs for the small types.
Examples
python examples/egraph.py # equality saturation
python examples/solve.py # synthesis
python examples/fib.py # block equivalence
License
MIT. See LICENSE.
Metadata
Release files for opreof 0.1.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 | Provenance | Actions |
|---|---|---|---|---|
| opreof-0.1.0.tar.gz | 64.5 kB | Without provenance | Details for opreof-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| opreof-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for opreof-0.1.0-py3-none-any.whl |
Total release size: 132.4 kB
Release files / opreof-0.1.0.tar.gz
| Download URL | opreof-0.1.0.tar.gz |
|---|---|
| Size | 64.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
61ae980c84565cce14e060873c210c68de9d1ac4fda1af0a84b9d60620b1036e
|
|
BLAKE2b-256 checksum How to use checksums |
89e6a032a3b5fc792b0dd86e88bfe5c3765d2862769aa3147fbf94007606225b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / opreof-0.1.0-py3-none-any.whl
| Download URL | opreof-0.1.0-py3-none-any.whl |
|---|---|
| Size | 67.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8e865fcc4f617175ddba0cdc0119d2911f5165a20e8e0cec361fa3b065eeeb39
|
|
BLAKE2b-256 checksum How to use checksums |
ee6446af4b7da04afecfcefaa431a9770f8d3fec8aa4be0aee78ec3a7bafceb3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Security for opreof 0.1.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.
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.