Error handling API
Structured exception hierarchy with stable diagnostic codes.
Base class
exception AlkahestError(
message: str,
code: str='E-UNKNOWN',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Base class for all alkahest errors.
Inherits the native base class registered by the extension. That is
load-bearing rather than cosmetic: the Rust engines raise the native
classes, the pure-Python subsystems (ansatz, crosscheck, smt,
the batch helpers) raise the wrappers below, and before this the two
hierarchies were disjoint. except alkahest.AlkahestError — the
documented way to catch anything this library raises — therefore caught
the Rust half and silently missed the Python half.
Subclasses keep their keyword constructors: only the message is forwarded
to the native base, whose __init__ is BaseException's and takes
positional arguments only.
codestrStable diagnostic code, e.g.
"E-POLY-001".remediationstr | NoneHuman-readable fix suggestion.
spantuple[int, int] | NoneSource byte span
(start, end), orNone.
Refusals versus verdicts
Some codes are refusals: Alkahest could not establish the answer, and the only alternative to saying so was a confident wrong one. They mean undecided, never false, and an unattended loop that records one as a negative result closes a branch it never explored.
Refusals: E-CAD-001, E-LINALG-010, E-MAT-004, E-SOS-002,
E-ANSATZ-003, E-SMT-003, E-INT-001, E-BUDGET-001..003,
E-ASYMPT-004, E-ODE-011, E-ODE-044, E-EVAL-012.
Verdicts: E-INT-004 (proven non-elementary), E-MAT-003 (proven
singular), E-EVAL-009 (undefined at this point), E-TRANSFORM-004 and
E-TRANSFORM-013 (the rule's own hypothesis is refuted — no transform of
this shape exists), E-ODE-041 (an irregular singular point has no Frobenius
series).
Exception subclasses
exception ConversionError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Expression could not be converted to the requested type (e.g. not polynomial).
exception DomainError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A side condition was violated (e.g. division by a known-zero expression).
exception LogicError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A propositional-logic operation refused (E-LOGIC-*).
Raised by simplify_logic(), to_cnf(),
bdd(), equivalent() and friends:
E-LOGIC-002— the input is not a propositional formula over the atoms the operation accepts (a quantifier, a non-Boolean term, or a propositional counterexample over relations that may be dependent).E-LOGIC-003— a size ceiling was reached (BDD nodes, normal-form terms, SAT decisions, truth-table width); nothing was truncated.E-LOGIC-004—exact=Truewas demanded and a minimum could not be certified.E-LOGIC-005— a malformed argument (form,method,order).E-LOGIC-006— an internal self-check failed; the result was withheld.
exception DiffError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Differentiation failed (e.g. unknown function).
exception IntegrationError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Symbolic integration failed.
exception MatrixError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Matrix operation failed (dimension mismatch, singular, etc.).
exception LinearAlgebraError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Symbolic linear algebra operation failed (nullspace, decompositions, normal forms).
exception EigenError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Eigen-decomposition failed (unsplit characteristic polynomial, defective matrix, etc.).
exception CadError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Cylindrical algebraic decomposition / real QE failed.
exception AnsatzError(
message: str,
code: str='E-ANSATZ-001',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
An ansatz family could not be built, or could not be fitted.
Raised by ansatz (P2 item 1 — conjecture generation).
.code distinguishes the causes, and note that only the first two are
"you called it wrong"; the others are results:
E-ANSATZ-001— a coefficient symbol name collides with a symbol already interned in the pool, so the fit would silently solve for the wrong thing.E-ANSATZ-002— the requested family exceedsmax_terms.C(n+d, d)grows fast; refusing beats materialising it.E-ANSATZ-003— the constraints are inconsistent: no member of this family satisfies them. For a search loop this is a positive result — a closed branch — not a malfunction.E-ANSATZ-004— the residual is nonlinear in the unknowns and the escalation path (solve()) needs agroebnerbuild, which this one is not.
exception CrossCheckError(
message: str,
code: str='E-XCHECK-001',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A cross-CAS differential check could not be posed, so it was refused.
Raised by crosscheck (P2 item 2). A refusal here always
beats a guess: a divergence is only informative if both systems were asked
the same question, and a best-effort translation manufactures false
divergences, which are worse than no signal at all.
E-XCHECK-001— the expression contains a node (or an active assumption) with no faithful mapping into the oracle's language.E-XCHECK-002— no oracle is installed. Never reported as agreement; seecrosscheck.oracles().E-XCHECK-003— the operation has no defined comparison rung. A caller error, raised before any oracle is consulted.E-XCHECK-004— the oracle itself declined: it has no implementation for the operation, raised, or returned an unevaluated form. An environmental outcome rather than a caller error, and not a divergence — comparing against a refusal would fabricate one.
exception SmtError(
message: str,
code: str='E-SMT-001',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
An SMT-LIB export, solver invocation, or model lift failed.
Raised by smt (P2 item 3).
E-SMT-001— no solver binary was found. Reported as a refusal rather than a silent fallback tosatisfiable(), which decides only the propositional and single-real-variable fragments and would answerNonehere — looking like the solver had run and found nothing.E-SMT-002— the formula is outside the supported SMT-LIB fragment; checksmt.supported()first.E-SMT-003— the model contains a value (anroot-objalgebraic number) that cannot yet be lifted exactly. Refused rather than truncated to a float: a float witness recorded as an exact one is the silent-error shape this whole subsystem exists to avoid.E-SMT-004— a returned model failed back-substitution. The bridge or the solver is broken; this must never be downgraded to a warning.
exception OdeError(
message: str,
code: str='E-ODE-001',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
An ODE routine did not return a solution (E-ODE-*).
One class for the whole prefix, because the prefix is the subsystem: the number says which engine declined and why.
E-ODE-001…003— construction or lowering (ODE).E-ODE-010…014—dsolve().013is a recognised ODE class whose quadrature did not close, and014a Riccati equation with no particular solution to seed it — both strictly more informative than010("outside the implemented classes"), and011is a candidate that failed substitution verification and is withheld rather than returned.E-ODE-020…026— the numeric integrators (ode_integrate_rk4()and friends).E-ODE-030…034—dsolve_system().E-ODE-040…045—series_solve()(Frobenius).041is a fact about the equation — an irregular singular point has no Frobenius series, so no wider implementation would find one — whereas042(irrational indicial roots) is a limit of this solver's rational recurrence. These sat on020…025before 3.10, where they collided with the numeric block above; they had never reached Python at all, so the renumbering broke nothing.
exception TransformError(
message: str,
code: str='E-TRANSFORM-001',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
An integral or sequence transform did not return a value (E-TRANSFORM-*).
Raised by laplace_transform(),
fourier_transform(),
z_transform() and their inverses. One class for
the whole transform module, the way OdeError is one class for
the whole of E-ODE-*: the number says which table and which failure, and
a caller who wants only one of them filters on the code.
Laplace (00x), Fourier (01x), Z (10x):
E-TRANSFORM-001/011/101— no forward rule matched. A fact about this implementation: these are table-driven, and a wider table would close it.E-TRANSFORM-002/102— the inverse table does not reach this form. Same kind of fact.E-TRANSFORM-003/012/103— the two variables passed are the same symbol.E-TRANSFORM-004— the unilateral hypothesis is refuted. The Laplace integral runs overt >= 0, so a Heaviside/Dirac edge ata < 0is invisible to it (L{theta(t+1)} = 1/s, note**s/s), and an advance factore**(+a*s)on the inverse is the transform of no causal function. Not a gap: there is nothing to find.E-TRANSFORM-013— the Fourier table entry's decay hypothesis is refuted. At a non-positive rate the defining integral diverges, and a negative Lorentzian amplitude transforms to the opposite sign and the opposite direction of growth from the tabulated form.
The 004/013 split from the rest is the point of the prefix. A table
miss says "rewrite the input, or wait for a wider table"; a refuted
hypothesis says "no implementation will ever answer this". A symbolic
parameter is never reported as either — it cannot be decided, so it is
carried as a side condition instead.
exception AsymptoticError(
message: str,
code: str='E-ASYMPT-001',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
An asymptotic expansion at infinity was not produced (E-ASYMPT-*).
Raised by asymptotic_expand().
E-ASYMPT-001—n_termsbelow 1.E-ASYMPT-002— thex = 1/tseries did not close.E-ASYMPT-003— a derivative needed for the expansion failed.E-ASYMPT-004— an expansion was computed and then withheld, because the numerico()-gate could not confirm a single term of it at largex. Never downgraded to a warning.E-ASYMPT-005— the scale is outside the implemented rules (exp/log hierarchies, Gamma/Stirling); power scales and a single log/exp peel are what this covers.
exception FpsError(
message: str,
code: str='E-FPS-001',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A formal-power-series operation refused (E-FPS-*).
Raised by Fps.
E-FPS-001 and 002 are the same mathematical fact reached two ways:
the object has a pole at the origin, so it is a Laurent series and not a
formal power series. 004/005/006 are the constant-term
hypotheses the operations need to be well defined at all — f(0) = 0
for composition/exp/reversion, f(0) = 1 for log and the default
n-th root, f(0) != 0 for the inverse — and each carries the rewrite
that removes the obstruction in its .remediation. 003 and 007
are the implementation's limits: exact rational coefficients only, and an
underlying Taylor expansion that has to close.
exception ProbabilityError(
message: str,
code: str='E-PROB-001',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A probability query did not return a value (E-PROB-*).
Raised by the experimental distribution surface. The six
codes are kept apart because they call for four different next steps, and
collapsing them would send a caller looking for a better integrator when
the integral simply diverges:
E-PROB-001— a parameter is a number that violates the distribution's own constraint (sigma <= 0,a >= b,poutside[0, 1], a non-literalnforBinomial). A symbolic parameter is never reported here: it cannot be decided, so it is carried as a side condition onconstraints()instead.E-PROB-002— the query is outside the supported shape: a non-polynomialfover a countably infinite discrete support, a non-affine argument toexpectation_affine(), a moment order past the range the verifier can still fail on.E-PROB-003— the reduction integral was built and the symbolic integrator declined it. The message names the integral.E-PROB-004— there is no closed form inside this library's primitive set: theGammaCDF at a non-integer shape needs the incomplete gamma, theBetaCDF needs the incomplete beta, the normal quantile needserf**-1, theBetacharacteristic function needs1F1— and the log-normal characteristic function has no closed form at all.E-PROB-005— a closed form was computed and then withheld, because quadrature of its own defining integral could not confirm it. Never downgraded to a warning: a caller cannot tell a checked value from an unchecked one once it is in their hands.E-PROB-006— the defining integral does not converge, so the quantity does not exist. The formal symbolic answer for a divergent expectation is typically a clean, plausible, wrong number, which is why the convergence check runs before the symbolic work rather than after.
Code prefix E-VEC-*. Vector calculus over an orthogonal chart
(experimental.Coordinates). E-VEC-004 is the one to
read twice: every grad/div/curl/laplacian formula in the
module is derived for an orthogonal frame, and on a skew chart they still
evaluate — to an expression that looks like a divergence and is not one. So
the tangent inner products must be proven to vanish, and an undecided one
is a refusal rather than an assumption. E-VEC-005 is a scale factor that
is zero, or whose non-vanishing could not be established; every operator
divides by it.
Reached as alkahest.experimental.VectorError; it is not on the
top-level namespace.
Code prefix E-UNIT-*. Physical units and dimensional analysis
(units). E-UNIT-001 is an operation that needs equal
dimensions — a sum, a conversion — and got two different ones; the message
names both. E-UNIT-002 is a malformed or unknown unit string.
E-UNIT-003 refuses an affine temperature (degC/degF) anywhere but
on its own in a conversion: 2 · 20 °C has no meaning. E-UNIT-004 is
a function argument or symbolic exponent that is not a pure number (an
angle must be in rad). E-UNIT-005 is a symbol or function with no
known unit; E-UNIT-006 a sum whose terms share a dimension but not a
unit (its value depends on which number is in which unit); E-UNIT-007
a fractional power whose scale has no exact rational root.
Code prefix E-TENSOR-*. Tensor calculus on a coordinate chart
(tensor). E-TENSOR-001 a metric that is not n x n for
n distinct coordinate symbols; 002 a metric that is not symmetric;
003 a determinant that is identically zero; 004 a floating-point
entry (the layer is exact); 005 a node the component field cannot hold
(Piecewise, a predicate); 006 a partial derivative that is not
available; 007 a denominator that vanishes identically; 008 the Weyl
tensor below dimension 3; 009 an abstract-index error (an index three
times, twice up or twice down, mismatched free indices); 010 an internal
invariant, refused rather than answered.
exception ControlsError(
message: str,
code: str='E-CTRL-001',
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A controls-layer operation refused (E-CTRL-001 … E-CTRL-006).
001 malformed input; 002 an improper transfer function where a
proper one is needed; 003 a numeric routine on data with free
parameters; 004 the requested object does not exist (singular Lyapunov
operator, no stabilising Riccati solution, infinite DC gain); 005 out
of the exact engine's reach — use the numeric route; 006 numpy is
required and missing.
Code prefix E-NC-*. Noncommutative algebra. E-NC-001: word
rewriting did not reach a normal form within max_steps (the rules may
not terminate). E-NC-002: an expansion past the term or power cap.
E-NC-003: a rule whose left side is not a single word with coefficient
E-NC-004: a malformed ladder-mode list.E-NC-005: a γ slot that is not0–3,5or a commutative label.E-NC-006: a γ trace past 14 slots.E-NC-007: a vacuum expectation over a factor that is not a listed ladder operator.E-NC-008: a malformed Lie-algebra table (not distinct noncommutative generators, bad or inconsistent structure constants, failed Jacobi identity).E-NC-009: a malformed sequence-wildcard pattern inmake_rule(..., sequence=[...]).
Code prefix E-QUAT-*. Quaternion algebra and rotations
(experimental.Quaternion). E-QUAT-001 is a zero or
undecided norm. E-QUAT-002 refuses the axis of the identity rotation,
which does not exist — every unit vector is one, and the conventional
(0, 0, 1) is a stated answer to a question with no answer.
E-QUAT-003 refuses a matrix that could not be checked to be a proper
rotation, including any symbolic matrix, because Shepperd's branch
selection is a comparison between entries and there is none to make on a
symbol.
Reached as alkahest.experimental.QuaternionError.
exception DaeError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
DAE structural analysis failed.
exception SolverError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Polynomial system solver failed.
Note
radicalandprimary_decompositionrefuse rather than return an ideal they cannot certify:E-IDEAL-005(no certified radical) andE-IDEAL-006(no certified primary decomposition). Both currently arrive as a plainValueErrorwhose message states the reason; the stable code is available to Rust callers fromalkahest_cas::ideal::take_ideal_refusal().
Code prefix E-LP-*. Exact linear / mixed-integer programming
(linprog(), milp(), linprog_expr()):
E-LP-001— malformed shapes (row length, right-hand-side count, bounds, integrality, sense)E-LP-002— a float or irrational coefficient; refused, never roundedE-LP-003— not affine in the decision variablesE-LP-004— a strict inequality or a non-relation constraintE-LP-005— branch and bound reachedmax_nodes. Not an answer: the exception carriesincumbent(best integer point, unproven),incumbent_fun,bound(best open bound) andnodesE-LP-006— a certificate failed its own exact check; the result was withheldE-LP-007— an unsupported parametric programme (linprog_parametric(),linprog_expr()withparameter): the parameter multiplies a variable in a constraint, enters non-linearly, moves both the objective and the right-hand side, or is combined with integer variables
A budget trip during either solver raises BudgetExceededError with
the same incumbent / incumbent_fun / bound attributes.
exception JitError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
JIT compilation failed.
Note
to_stablehlo()raises the baseAlkahestErrorwith codeE-STABLEHLO-001(no exact StableHLO lowering for a function, named in the message),-002(a non-numeric node such asO(x)),-003(a free symbol not in the input list),-004(an infinite/NaN constant) or-005(an invalid MLIR function name).
exception CudaError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
NVPTX / CUDA compilation or launch failed.
exception SparseInterpError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Sparse modular polynomial interpolation failed.
exception SparseGcdError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Sparse modular GCD computation failed.
exception ParseError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None,
code: str='E-PARSE-001'
)
Malformed expression text, with the source span (.span) of the fault.
E-PARSE-001 lexical (an unexpected character, a malformed or
out-of-range number literal), E-PARSE-002 syntactic (including
division by the literal 0), E-PARSE-003 unknown function,
E-PARSE-004 nesting deeper than the parser's limit (the same ceiling as
E-DEPTH-001). Every malformed input to parse() raises
this — never RecursionError, ValueError or ZeroDivisionError.
exception PoolError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Pool was closed, mismatched, or otherwise invalid.
exception BudgetExceededError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A Budget was exceeded, or cancellation was requested.
Raised by heavy engines (integrate() today) at a
cooperative checkpoint inside the Rust kernel — see
alkahest_core::budget and _budget. A fine, expected
answer for a fan-out search loop, not a crash: .code distinguishes
the three causes:
E-BUDGET-001— the active budget's wall-clock limit elapsed.E-BUDGET-002— the active budget's step counter exceededmax_steps.E-BUDGET-003—request_cancel()was called and not yet cleared.
Catching errors by subsystem
Match on the base class and filter by code prefix:
import alkahest
try:
result = alkahest.integrate(expr, x)
except alkahest.AlkahestError as e:
if e.code.startswith("E-INT-"):
# integration failed
print(f"Integration failed ({e.code}): {e.remediation}")
else:
raiseFor the full error taxonomy (cause classification, refusal vs verdict, rules for
adding codes) see the Error handling <../errors.html>_ chapter of the user
guide, whose source is docs/mdbook/src/errors.md.
More exception classes
exception AssumptionError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
An explicit simplification assumption contradicted the current context.
exception DepthLimitError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
An expression was too deeply nested to walk by recursion.
Alkahest processes most expressions by structural recursion, and a native
stack overflow is a SIGSEGV rather than an exception — it would kill the
interpreter outright, with no traceback for a caller to log. Past a
measured ceiling the operation therefore declines instead, which is
something except Exception can actually catch.
Depth is nesting, not size: pool.add([t1, ..., t100000]) has depth 2
and is fine. So does the same sum written with repeated + — Add and
Mul splice nested same-operator children when they are built, so a
left-associated chain of + is the very same flat, depth-2 node. What
still nests is everything else: Pow towers, function chains such as
sin(sin(sin(...))), and Piecewise nesting.
Which operations raise this is per operation. The simplification entry
points (simplify() and its variants) do not walk by
recursion — their traversals are trampolined or iterative — and accept
input of any depth. They still raise here for an input that would reach
the one recursive pass on that path: the assumption-gated e-graph, which
runs when the expression contains a Domain.Positive or
Domain.NonZero symbol, or when an explicit Assumptions
context is supplied. Everything else — printing, diff,
symbolic_grad, subs, the evaluators, the emitters — recurses, and
raises past the ceiling.
Rebuilding the expression with less nesting, or splitting the work into subexpressions, is the fix.
exception DiophantineError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Integer Diophantine solving failed (linear / quadratic patterns).
Division by an exact zero (E-DOMAIN-001).
Raised by simplify() when a denominator simplifies to 0 — e.g.
simplify(1/(x + (-1)*x)) — and by Expr division by a literal zero.
It is a DomainError and a ZeroDivisionError, so both
except ak.DomainError and except ZeroDivisionError catch it.
exception FactorError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Polynomial factorization failed.
exception HolonomicError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Creative telescoping refused: input outside the proper hypergeometric
class (E-HOLO-001), bounded search exhausted (E-HOLO-002), a
candidate certificate failed exact verification (E-HOLO-003), the call
was malformed (E-HOLO-004), or a guessed recurrence is not supported by
the terms supplied (E-HOLO-005).
M6 added three more, all from modular / p-adic evaluation
(ModularRecurrence, binomial_mod()): the
modulus is not a prime power the backend supports (E-HOLO-006), a step
of the recurrence does not determine its next term as a p-adic integer
(E-HOLO-007), or the working precision the singular steps demand is
past a machine-word modulus (E-HOLO-008).
A refusal here is informative, not a failure: it says the term is not one Zeilberger's algorithm decides at the requested bounds, so a loop can close that branch instead of re-attempting it.
E-HOLO-005 is the one exception to "close that branch". It comes from
guess_holonomic() and means the data could not answer —
too few terms to test every candidate in bounds, or a fit with no surplus
equations to confirm it. Recording it as "this sequence has no recurrence"
closes a branch that was never explored. E-HOLO-008 is the same shape:
a resource ceiling, not a mathematical verdict.
exception HomotopyError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Numerical polynomial continuation failed (V2-14 homotopy solver).
The native class is registered by the extension as PyHomotopyError;
this is the Python-side wrapper used for isinstance checks, and its
absence is what scripts/check_error_codes.py was reporting.
E-HOMOTOPY-002— the start system has too few paths for the target.E-HOMOTOPY-003— path tracking failed for a random gamma.E-HOMOTOPY-004— the tracker exhausted its step budget.
exception IoError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Checkpoint / restore I/O error (formerly PoolPersistError, E-POOL-* codes).
exception LatticeError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
LLL lattice reduction failed (structure, Lovász parameter, or iteration limit).
exception LimitError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Symbolic limit computation failed.
exception LinearRecurrenceError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Linear recurrence solving failed.
exception ModularError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A modular / CRT reconstruction step failed (E-MOD-001 … E-MOD-004).
As with HomotopyError, the native class exists as PyModularError
and this wrapper is what makes it catchable by name from Python.
exception NumberTheoryError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Integer number-theory primitive failed (parity, modulus, or unsolvable congruence).
exception ParamGroebnerError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A Gröbner basis over Q(params) refused or was called wrongly (M9).
Raised by ParametricGroebnerBasis and by
GroebnerBasis.compute(..., params=[...]). The native class exists as
PyParamGroebnerError; this wrapper is what makes it catchable by name.
E-PARAMGB-001— no generators were supplied.E-PARAMGB-002— the generators disagree on the variable/parameter lists.E-PARAMGB-003— a specialisation was given the wrong number of values.E-PARAMGB-004— a result, not a malfunction. The requested parameter point is on the degeneracy locus: one of the conditions the basis assumed vanishes there, so the generic basis says nothing about it. ReadParametricGroebnerBasis.conditions()to see which hypersurfaces those are, or compute over ℚ directly at that point.
exception ProductError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Symbolic discrete product failed (unsupported term or factorisation).
exception PslqError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None,
code: str='E-PSLQ-001'
)
Integer-relation heuristic failed (input, coefficient bound, or lattice step).
E-PSLQ-004 is raised when the supplied constants carry less precision
than the relation found needs, and E-PSLQ-005 when the constants are
exact rationals and the relation is simply false for them — see
guess_relation().
exception RealRootError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Real root isolation failed (conversion to univariate polynomial, etc.).
exception ResultantError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Polynomial resultant or subresultant PRS computation failed.
exception RsolveError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Difference equation / rsolve failed.
exception SeriesError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Symbolic series expansion failed.
exception SosError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A positivity question was not answered with a certificate.
The three outcomes are deliberately distinct, because conflating them loses information a search loop needs:
E-SOS-003— the target is definitely negative somewhere; the message carries a witness point. The claim is false.E-SOS-002— no certificate of the searched shape at this degree. This is not a proof that none exists; raise the degree, or fall back to the complete (and far more expensive)decide().E-SOS-001/E-SOS-004— the input was not a polynomial in the given variables, or the call was malformed.
exception SumError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
Symbolic summation failed (not hypergeometric or not Gosper-summable).
exception ValidatedError(
message: str,
remediation: str | None=None,
span: tuple[int, int] | None=None
)
A rigorous bound could not be established, so none is returned.
Every variant is a refusal, never a guess: an unsupported primitive
(E-VALIDATED-001), a free symbol with no interval (E-VALIDATED-002),
a singularity or branch cut inside the box (E-VALIDATED-003), an
enclosure that overflowed to infinity (E-VALIDATED-004), or a malformed
request (E-VALIDATED-005). verified_ode() adds a step-size
underflow near a blow-up or non-Lipschitz point (E-VALIDATED-006), an
enclosure grown too wide to say anything (E-VALIDATED-007) and a step
limit, typically on a stiff problem (E-VALIDATED-008).
Note that running out of budget is not an error — that returns a wide but
still rigorous enclosure with budget_exhausted=True, because a loose
true bound is useful and a tight false one is not.