Metadata-Version: 2.5
Name: mpeqs
Version: 0.4.0
Summary: Exact answers for the arithmetic a language model should not be doing itself.
Project-URL: Homepage, https://github.com/punnerud/MPEqs
Author: Morten Punnerud-Engelstad
License: The mpedb License 1.0
        
        Copyright (c) 2026 Morten Punnerud-Engelstad
        
        ## Acceptance
        
        By using, copying, modifying, or distributing the software, you agree to all of the terms of this license. If you do not or cannot agree, you may not use the software.
        
        ## Grant
        
        The licensor grants you a non-exclusive, worldwide, non-transferable, non-sublicensable license to use, copy, modify, and distribute the software and derivative works of it, for any purpose, subject to the conditions below.
        
        Use is FREE OF CHARGE for every person and every organization, except as the Large Enterprise Device Fee below provides.
        
        ## Large Enterprise Device Fee
        
        1. **Who pays.** A "Large Enterprise" is any organization whose Group's consolidated annual revenue OR total valuation (public market capitalization, or the most recent fair valuation for private entities) exceeds USD 5,000,000,000, measured in 2026 United States dollars (adjusted for inflation by the US CPI-U from calendar year 2026), taking whichever of the two metrics is HIGHER.
        
        2. **Group.** "Group" means the organization together with every entity that directly or indirectly controls it, is controlled by it, or is under common control or ownership with it, through any chain and in any direction — expressly including a non-profit entity that owns or controls a for-profit entity and a for-profit entity that owns or controls a non-profit entity. The revenue and valuation tests apply to the Group's combined totals.
        
        3. **The fee.** A Large Enterprise owes the licensor a ONE-TIME fee of USD 0.07 (seven US cents), measured in 2026 United States dollars (adjusted for inflation by the US CPI-U from calendar year 2026), per physical device on which the software runs. Where the software runs on one or more servers, the fee is instead counted per physical device that connects, directly or indirectly, to those servers or to services backed by them. For any deployment, the applicable device count is the HIGHER of (a) the number of physical devices running the software and (b) the number of physical devices so connected. Each physical device is counted once; the fee for a device already paid for is not owed again.
        
        4. **Five-year tail.** A Group that meets the Large Enterprise threshold at any point in a calendar year is a Large Enterprise for that year and for the five following calendar years, regardless of its revenue or valuation in those years.
        
        5. **Payment.** Fees are payable to the licensor upon first deployment or, for devices added later, within 90 days of the device first running or connecting. Contact: morten@punnerud.net. A Large Enterprise must keep records sufficient to support its device count and provide a summary on reasonable request.
        
        ## Distribution and Notices
        
        You must include a copy of this license, and keep this copyright notice, in all copies or substantial portions of the software, including modified copies and derivative works. You may not remove or obscure the license or the fee terms when distributing the software.
        
        ## Scope
        
        This license applies to every version, commit, and artifact of the software published by the licensor, including versions published before this license text was added, to the fullest extent the licensor can so provide.
        
        ## No Other Rights
        
        This license does not grant any trademark rights or any patent rights beyond those necessarily exercised by using the software as licensed.
        
        ## Termination
        
        Your license ends immediately if you materially breach this license and do not cure the breach within 30 days of becoming aware of it. Unpaid fees survive termination.
        
        ## Interpretation
        
        Any doubt or ambiguity in the interpretation of this license shall be resolved in favor of the licensor and this project.
        
        ## Disclaimer
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
License-File: LICENSE
Keywords: arithmetic,exact,fractions,llm,solver,tool-use
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.10
Provides-Extra: pyspell
Requires-Dist: mpedb>=0.2.9; extra == 'pyspell'
Description-Content-Type: text/markdown

# mpeqs

Exact answers for the arithmetic a language model should not be doing itself.

[![Linux](https://github.com/punnerud/MPEqs/actions/workflows/linux.yml/badge.svg?branch=main)](https://github.com/punnerud/MPEqs/actions/workflows/linux.yml)
[![macOS](https://github.com/punnerud/MPEqs/actions/workflows/macos.yml/badge.svg?branch=main)](https://github.com/punnerud/MPEqs/actions/workflows/macos.yml)
[![Windows](https://github.com/punnerud/MPEqs/actions/workflows/windows.yml/badge.svg?branch=main)](https://github.com/punnerud/MPEqs/actions/workflows/windows.yml)
[![PyPI](https://img.shields.io/pypi/v/mpeqs.svg)](https://pypi.org/project/mpeqs/)

```bash
pip install mpeqs
```

```python
import mpeqs

mpeqs.solve({"solver": "arith", "answer": "(17/100)*250"})   # Fraction(85, 2)
```

The model names a solver and fills its slots. This evaluates the spec exactly, or
refuses by name — it never guesses, and it never returns a number it could not derive.

**The refusal is the point.** A solver that cannot evaluate a spec raises `Refusal`
with a reason, and that reason is a routing signal: it tells the caller to ask the
model, rather than quietly handing back a wrong number.

```python
try:
    mpeqs.solve({"solver": "arith", "answer": "1/0"})
except mpeqs.Refusal as why:
    ...   # ask the model; the record declined this one
```

**Shape disposes over name.** A model that picks the wrong solver but fills the right
slots has still described the problem correctly, so a spec carrying an `answer` key is
arithmetic whatever it calls itself. Pass `repair=False` to turn that off.

## What is exact and what is not

Results come back as `fractions.Fraction`, so nothing is lost on the way out.
The exactness is in the arithmetic, not in the parsing: a ratio written as a ratio
stays exact, while a decimal literal is read as a float first.

```python
mpeqs.solve({"solver": "arith", "answer": "1/10+2/10"})   # Fraction(3, 10)  exact
mpeqs.solve({"solver": "arith", "answer": "0.1+0.2"})     # the binary expansion
```

## Solvers

`arith`, `geometry`, `iterate`, `modular`, `multisearch`, `polynomial`, and the named
families behind them — factorisation, gcd/lcm, linear systems, quadratics, remainders,
combinatorics, rates, mixtures, ratios, base conversion.

`mpeqs.solvers()` lists what a given build dispatches to.

## No dependencies

The whole library is standard library. One `py3-none-any` wheel serves Linux, macOS and
Windows on every supported interpreter, the install is instant, and a test asserts that
no third-party import creeps in.

## Calculus: three derivatives that must agree

`mpeqs.calculus` differentiates (chain rule included), integrates expanded polynomials
exactly, and solves linear and quadratic equations with exact rational roots — refusing
irrational discriminants **by value** rather than floating them.

The derivative ships three ways on purpose: symbolic rules, dual-number **autograd**
(the chain rule falls out of the arithmetic), and `grad_pyspell` — an interpreter for the
PySpell subset run over dual numbers, so a function with loops and branches differentiates
exactly at a point. The tests demand all three agree at scores of random rational points;
a bug in one must be a matching bug in another to survive.

```python
from mpeqs import calculus
calculus.differentiate("(3*x**2 + 5)**4")        # 4 * (3*x**2 + 5)**3 * (3 * (2*x))
calculus.integrate("x**2", lower=0, upper=1)      # Fraction(1, 3)
calculus.solve_quadratic(1, -5, 6)                # (Fraction(3), Fraction(2))
calculus.grad_pyspell("""
def f(x):
    y = 1
    n = 0
    while n < 4:
        y = y * (3*x*x + 5)
        n = n + 1
    return y
""", Fraction(1, 2))                              # Fraction(36501, 16) -- exact
```

## PySpell: when an expression is not enough

`solve()` evaluates *expressions*, and is safe by restriction — the source is parsed to
an AST, walked against a whitelist, and evaluated with `__builtins__` emptied. Nothing
runs that was not understood first.

What it cannot express is a *procedure*. There is no expression for "how many steps does
the Collatz sequence take from 27" — that needs a loop, a branch and an accumulator. The
usual answer is to let the model emit Python and `exec` it, which trades a whitelist for
a sandbox and hope.

`mpeqs.pyspell` is the third option. PySpell is [mpedb](https://github.com/punnerud/mpedb)'s
stored-function language: a small deterministic subset of Python with no imports, no
clock, no randomness, no file or network I/O, and a fixed instruction budget so a runaway
loop fails identically everywhere. **A model can write a loop. It still cannot open a
socket.**

```python
import mpeqs.pyspell as spell

spell.call("""
def col(n):
    if n < 1:
        return 1 // 0
    c = 0
    while n != 1:
        if n % 2 == 0:
            n = n // 2
        else:
            n = 3 * n + 1
        c = c + 1
    return c
""", 27)
# 111
```

And when the model claims a *relationship* between two functions, the claim is checked
against a probe corpus rather than believed:

```python
spell.check_bijective(
    "def dbl(x):\n    return x * 2\n",
    "def hlv(x):\n    return x // 2\n")
# mpeqs.Refusal: dbl/hlv is not bijective: ... forward(Float(1e308)) = Float(inf) ...
```

That refusal is correct. `x*2` then `x//2` is genuinely not bijective over the values
PySpell admits — a denormal floors to zero, and `1e308` is an integral float whose double
is infinity. The verifier names the input that breaks it, every time. Guard the domain to
bounded positive integers and the same pair is accepted, reporting how many probe values
actually round-tripped rather than a bare "verified".

It caught the author of that example twice while it was being written. That is the
argument for "declare and check" over "generate and hope".

**Optional**, because it is not free: mpedb ships as a compiled wheel needing CPython
3.12 or newer, while mpeqs itself runs from 3.10 with one artefact for every platform.

```bash
pip install mpeqs[pyspell]
```

Without it, `mpeqs.pyspell.available()` is `False` and every entry point raises `Refusal`
naming the extra, rather than failing at import. Nothing else in mpeqs changes.

## Licence

[The mpedb License 1.0](../LICENSE) — free of charge for every person and every
organization, except that a group over five billion dollars in revenue or valuation
owes seven US cents per device, once. Not an OSI-approved licence.
