Exact and eventual quasi-polynomial (PORC) fitting for integer-valued sequences.
eqpfit fits Polynomial On Residue Classes (PORC) models of bounded degree
against integer pairs (x_i, v_i). Each residue class modulo a period L receives a
binomial-basis polynomial that evaluates to integers for every integer input. The library
supports:
- Known period or automatic scanning over feasible periods.
- Constraints on leading binomial coefficients (
common_leading,leading_coeff). - Difference- and flint-based integer solvers selected automatically when possible.
- Binomial- and monomial-basis coefficients for every fitted residue class (monomials
expressed in the original variable
x, so denominators appear whenL>1). - Verification of exact fits, with structured failure reasons.
- Eventual fitting that discards a minimal prefix until a PORC fit is found.
The project targets Python 3.11+. By default it depends only on the standard library and can be installed from source with:
pip install -e .To enable the optional flint backend, install with the flint extra:
pip install -e .[flint]The extra pulls in python-flint; ensure any system requirements for that package are
satisfied on your platform.
- PORCModel: Holds
coeffs_by_residue(binomial-basis coefficients of lengthd+1per residue) plusmonomial_coeffs_by_residue(Fractions in the usualxbasis), and provideseval(x),verify(xs, vs), andas_coeffs_array()for SymPy-friendly monomial coefficients (ints stay as ints, rationals as"a/b"strings). - FitResult: Returned by
fit_periodandfit_porc, with attributesL,d,success,model,monomial_coeffs_by_residue,reason, and optionaldetails. - EventualPORCResult: Returned by
fit_eventual_porc, withstart(index of the first retained point), thefitresult, anddroppedcount.
Readable __str__/__repr__ forms summarize results, including per-residue binomial
coefficients, derived monomial coefficients in x, and the corresponding monomial
polynomials Q_r(t) (where t = (x - r) / L) plus drop counts for eventual fits, so
print(fit_result) gives a helpful snapshot.
fit_period(xs, vs, d, L, *, require_all_residues=True, common_leading=False,
leading_coeff=None, verify=True, backend="auto")Fit a PORC of degree d with fixed period L.
xs,vs: integer-like inputs; duplicates with conflicting values cause failure.require_all_residues: demand enough points for every residue class modL.common_leading: enforce a shared leading binomial coefficient across residues.leading_coeff: explicitly set the leading binomial coefficient (impliescommon_leading).backend: "auto" (prefer differences on consecutive data, fall back to flint when available), "differences", or "flint".
Returns a FitResult. When success is True, model.eval(x) reproduces all inputs.
fit_porc(xs, vs, d, period=None, *, require_all_residues=True,
common_leading=False, leading_coeff=None, verify=True,
return_all=False, include_failures=False, backend="auto")Scan candidate periods and return the first successful fit (or all results when
return_all=True).
period=None: enumerate feasible periods based on sample size (finite whenrequire_all_residues=True).period=intor iterable of ints: test in the given order.include_failures: include unsuccessfulFitResultentries when returning a list.
Failures use machine-readable reasons such as "nonconsecutive_t_values",
"common_leading_mismatch", or "verification_failed" to aid debugging.
fit_eventual_porc(xs, vs, d, *, period=None, require_all_residues=True,
common_leading=False, leading_coeff=None, verify=True,
return_all=False, include_failures=False, backend="auto")Find an eventual PORC: repeatedly drop the earliest point until a PORC fit of degree d
exists or no data remains.
- Carries the same leading-coefficient controls and backend selection as
fit_porc. - Returns an
EventualPORCResultcontaining the offset of the first retained point, the successfulFitResult, and how many points were dropped. On total failure,fitholds the final failingFitResult.
from eqpfit import fit_period
xs = [0, 1, 2, 3]
vs = [0, 1, 4, 9]
res = fit_period(xs, vs, d=2, L=1)
assert res.success
print(res.model.coeffs_by_residue) # {0: [0, 0, 1]}
print(res.monomial_coeffs_by_residue) # {0: [Fraction(0, 1), Fraction(0, 1), Fraction(1, 1)]}
print(res.model.eval(4)) # 16Monomial coefficients are always reported in the original variable x. For periodic
models this can introduce rational coefficients even when values are integers:
from eqpfit import fit_period
xs = list(range(6))
vs = [x // 2 for x in xs] # floor(x/2)
res = fit_period(xs, vs, d=1, L=2)
print(res.monomial_coeffs_by_residue) # {0: [0, 1/2], 1: [-1/2, 1/2]}Convert monomial coefficients for use with SymPy:
import sympy as sp
coeff_arrays = res.model.as_coeffs_array() # [[0, "1/2"], ["-1/2", "1/2"]]
sympy_coeffs = [[sp.Rational(c) for c in coeffs] for coeffs in coeff_arrays]from eqpfit import fit_porc
xs = [0, 1, 2, 3, 4, 5]
vs = [0, 2, 3, 7, 8, 14] # period-2 behavior with common leading term
res = fit_porc(xs, vs, d=2, period=None, common_leading=True)
if res.success:
print(res) # Pretty printing includes residues, coefficients, and Q_r(t) monomials
print(f"Period {res.L}, leading coeff {res.model.coeffs_by_residue[0][2]}")from eqpfit import fit_eventual_porc
xs = [0, 1, 2, 3, 4, 5]
vs = [5, 6, 0, 1, 4, 9] # first two points are noise, remainder is quadratic
res = fit_eventual_porc(xs, vs, d=2)
print(res) # Pretty printing shows dropped prefix and fitted model
print(res.start) # 2 (zero-based index of first retained point)
print(res.fit.L) # inferred period of the remaining datafrom eqpfit import fit_period
xs = [0, 1, 2, 3]
vs = [1, 4, 11, 22]
res = fit_period(xs, vs, d=2, L=1, leading_coeff=1)
assert res.success
print(res.model.coeffs_by_residue[0]) # leading binomial coefficient equals 1Execute the full test suite with:
python -m unittest discover -s tests -p 'test_*.py'All tests are deterministic and require no external services.