Skip to content

Structured mathematical errors

Checked Haskell APIs return Either MathError a. The C ABI reports those failures as HMATH_MATH_ERROR (status 11); Python raises hmath.HMathError with the complete original value in its .math_error attribute. A transport failure (such as a NULL pointer or allocation failure) has a different status and no MathError payload.

from hmath import HMathError, errors, optimization

try:
    optimization.checked_gradient(lambda point: [], [1.0])
except HMathError as failure:
    error = failure.math_error
    assert error.kind == errors.MathErrorKind.VECTOR_LENGTH_MISMATCH
    assert error.details == (1, 0)

hmath.errors.MathError(kind, details=(), message="") stores the constructor, its ordered integer fields and its full string payload. The named constructors below provide the original argument grouping. Integer fields must fit Haskell Int (ptrdiff_t in the supported C ABI); they are never converted through a floating-point number. Shapes and matrix indices are (rows, columns) pairs, and indices are zero based.

Python constructor C kind constant details in order message
NegativeDimensions(shape) HMATH_MATH_NEGATIVE_DIMENSIONS rows, columns empty
ElementCountOverflow(shape) HMATH_MATH_ELEMENT_COUNT_OVERFLOW rows, columns empty
ElementCountMismatch(expected, actual) HMATH_MATH_ELEMENT_COUNT_MISMATCH expected, actual empty
ShapeMismatch(left, right) HMATH_MATH_SHAPE_MISMATCH left rows, left columns, right rows, right columns empty
InnerDimensionMismatch(left_columns, right_rows) HMATH_MATH_INNER_DIMENSION_MISMATCH left columns, right rows empty
IndexOutOfBounds(shape, index) HMATH_MATH_INDEX_OUT_OF_BOUNDS shape rows, shape columns, row, column empty
SliceOutOfBounds(shape, start, extent) HMATH_MATH_SLICE_OUT_OF_BOUNDS shape rows, shape columns, start row, start column, extent rows, extent columns empty
VectorLengthMismatch(expected, actual) HMATH_MATH_VECTOR_LENGTH_MISMATCH expected, actual empty
NonSquareMatrix(shape) HMATH_MATH_NON_SQUARE_MATRIX rows, columns empty
InvalidTolerance() HMATH_MATH_INVALID_TOLERANCE none empty
SingularMatrix(index) HMATH_MATH_SINGULAR_MATRIX pivot index empty
NearSingularMatrix(index) HMATH_MATH_NEAR_SINGULAR_MATRIX pivot index empty
NonFiniteMatrixEntry(index) HMATH_MATH_NON_FINITE_MATRIX_ENTRY row, column empty
NonFiniteVectorEntry(index) HMATH_MATH_NON_FINITE_VECTOR_ENTRY index empty
NonFiniteResult() HMATH_MATH_NON_FINITE_RESULT none empty
RightHandSideMismatch(expected, actual) HMATH_MATH_RIGHT_HAND_SIDE_MISMATCH expected, actual empty
DomainError(message) HMATH_MATH_DOMAIN_ERROR none original string
InvalidArgument(message) HMATH_MATH_INVALID_ARGUMENT none original string
NonConvergence(algorithm, iterations) HMATH_MATH_NON_CONVERGENCE iterations algorithm string
RankDeficient(rank) HMATH_MATH_RANK_DEFICIENT rank empty
NotPositiveDefinite(index) HMATH_MATH_NOT_POSITIVE_DEFINITE index empty

The defining Haskell type is MathError. The condition that produces each error remains the condition in the original function. The adapter does not add finite-value checks or replace a solver's own recovery decisions.

Checked callbacks

Return the ordinary successful Python value from a checked callback. Raise one of the constructors above to return an original Haskell Left:

from hmath import errors, optimization


def objective(point):
    if point[0] <= 0:
        raise errors.DomainError("positive point required")
    return point[0] * point[0]


value = optimization.checked_value(objective, [2.0])

A solver receives that Left and follows its original algorithm. For example, feasible barrier backtracking can reject a DomainError trial and retry a shorter step. Unexpected Python exceptions abort the call and are re-raised as the same Python exception after native temporary objects are released. For a source callback whose type is pure rather than Either, a raised MathError becomes a checked failure at the outer ABI boundary.

C and C++

Include hmath_error.h. The transport is a fixed-layout record:

typedef struct hmath_math_error {
    uint32_t kind;
    ptrdiff_t details[6];
    const char *message;
    size_t message_length;
} hmath_math_error;

Fill kind, the used details entries and the optional message when returning HMATH_MATH_ERROR from a checked callback. The message buffer is borrowed until the callback result is consumed. An explicit length preserves embedded NUL; use UTF-8, with the surrogatepass/WTF-8 extension if transporting isolated Haskell surrogate Char values. Unused fields are ignored for constructors that have no corresponding source payload. Invalid constructor tags or invalid encoded strings produce a transport failure.

int hmath_last_math_error(hmath_math_error *out) returns 1 when the current thread has a complete mathematical failure, otherwise 0. It is a query and does not clear or consume the last error. Its message is borrowed library memory until the next checked operation on that thread: copy it before another API call. The library owns a full dynamic copy, including long strings; the short human-readable hmath_last_error() message is not a substitute for this structured payload. Successful checked operations and nonmathematical failures clear the structured error. Thread exit and normal clearing release its storage in C without calling the Haskell runtime.