basis#
Bernstein polynomial basis and analytical derivatives for transformation models.
- class mltpy.basis.BernsteinBasis(order, support)[source]#
Bases:
objectBernstein polynomial basis of degree order on a compact support.
Coefficient ordering: ascending from degree 0 to order — identical to R’s
basefun::Bernstein_basis. This differs fromnumpy.poly1d, which stores coefficients in descending degree order.- Parameters:
- derivative(y, order=1)[source]#
Analytical derivative of the Bernstein design matrix.
Uses the recurrence relation — no finite differences.
- First derivative (order=1):
dB_{i,k}/dy = k/(b−a) · [B_{i−1,k−1}(t) − B_{i,k−1}(t)]
- Second derivative (order=2):
- d²B_{i,k}/dy² = k(k−1)/(b−a)² ·
[B_{i−2,k−2}(t) − 2·B_{i−1,k−2}(t) + B_{i,k−2}(t)]
Boundary terms (B_{j,·} with j < 0 or j > k) are treated as zero.
- Parameters:
- Return type:
- Raises:
ValueError – If
orderis not 1 or 2 (order 0 is not supported; callevaluate(y)directly), or if any observation lies outsidesupport.
- evaluate(y)[source]#
Bernstein design matrix at observations y.
- Parameters:
y (
ndarray[tuple[Any,...],dtype[double]]) – Observations, shape (n,). Must lie in the closed interval[support[0], support[1]].- Return type:
- Returns:
NDArray of shape (n, order+1). Row i is [B_{0,k}(y_i), …, B_{k,k}(y_i)].
Read-only (do not mutate in place).
- Raises:
ValueError – If any observation lies outside
support.
Notes
Shares the assembled
(B, dB)cache withevaluate_with_derivative()(see_bernstein_assembled_cache): the returnedBis memoised onycontent, and this call warms the paireddBso a later derivative evaluation is a single dict lookup.
- evaluate_with_derivative(y)[source]#
Bernstein design matrix and its first derivative in one pass.
Normalises and validates
yonce, then returns both the evaluation matrix (degree k) and the first-derivative matrix (degree k−1 recurrence). Equivalent to callingevaluate(y)followed byderivative(y, order=1)but avoids the redundant support scan.- Parameters:
y (
ndarray[tuple[Any,...],dtype[double]]) – Observations, shape (n,). Must lie in[support[0], support[1]].- Return type:
tuple[ndarray[tuple[Any,...],dtype[double]],ndarray[tuple[Any,...],dtype[double]]]- Returns:
B (NDArray of shape (n, order+1)) – Same as
evaluate(y). Read-only (do not mutate in place).dB (NDArray of shape (n, order+1)) – Same as
derivative(y, order=1). Read-only.
Notes
The assembled
(B, dB)pair is memoised on(order, support, y.tobytes())(see_bernstein_assembled_cache); a repeated call with the sameycontent is a single dict lookup that skips the support scan and thedBrebuild entirely.
- class mltpy.basis.InteractionBasis(y_basis, x_basis)[source]#
Bases:
objectTensor-product basis a(y) ⊗ b(x) for fully-interacting CTMs.
Models the transformation
h(y|x) = (a(y) ⊗ b(x))ᵀ vec(Θ)
where
ais the y-basis (response) andbis the x-basis (covariate), andΘis a(p, q)coefficient matrix withp = y_basis.order + 1andq = x_basis.order + 1.The parameter vector
theta_storesvec_C(Θ)(row-major / C-order flattening) of lengthp * q. See ADR 0001 for the full design rationale.Supported x-basis types (initial release):
BernsteinBasis,OrdinalBasis,InterceptBasis. Other x-basis types raiseValueErrorat constraint-building time because the closed-form column-wise monotonicity guarantee requires the x-basis to be non-negative and a partition of unity.- Parameters:
y_basis (
BernsteinBasis|LogBernsteinBasis|PolynomialBasis|LegendreBasis|LogBasis|InterceptBasis|OrdinalBasis) – Basis for the response variabley.x_basis (
BernsteinBasis|OrdinalBasis|InterceptBasis) – Basis for the covariate(s)x. Must be one of the supported types listed above.
Notes
The
evaluateandderivativesignatures differ from the scalar basis interface: they accept bothyandXbecause the Kronecker product requires both. The model layer owns this two-argument call convention.References
See
docs/adr/0001-tensor-product-interaction-basis.md.- derivative(y, X, order=1)[source]#
Row-wise Kronecker product da(y_i)/dy ⊗ b(x_i), shape (n, p*q).
- Parameters:
- Return type:
- Raises:
ValueError – If
orderis not 1.
- evaluate(y, X)[source]#
Row-wise Kronecker product a(y_i) ⊗ b(x_i), shape (n, p*q).
- Parameters:
- Return type:
- Returns:
NDArray of shape (n, p*q) where row i is
np.kron(a(y_i), b(x_i)).The parameter layout is row-major (
theta[i*q + j] = Θ[i, j].)
- evaluate_with_derivative(y, X)[source]#
Return evaluate(y, X) and derivative(y, X) in one pass.
- integrate(y, X)[source]#
Running integral of a(y) ⊗ b(x) w.r.t. y, shape (n, p*q).
Returns
kron(∫_a^y a(s) ds, b(x_i))for each row i.
- x_basis: BernsteinBasis | OrdinalBasis | InterceptBasis#
- y_basis: BernsteinBasis | LogBernsteinBasis | PolynomialBasis | LegendreBasis | LogBasis | InterceptBasis | OrdinalBasis#
- class mltpy.basis.InterceptBasis(support)[source]#
Bases:
objectConstant (intercept-only) basis: evaluate(y) = ones, shape (n, 1).
The single basis function is identically 1. This gives a single free parameter — an additive intercept — in the transformation h(y) = θ₀.
- Parameters:
support (
tuple[float,float]) – Closed interval (a, b) with a < b. Used for support validation only.
- class mltpy.basis.LegendreBasis(order, support)[source]#
Bases:
objectLegendre polynomial basis P_0, P_1, …, P_k on a compact support.
Maps
ytot = 2 · (y − a) / (b − a) − 1 ∈ [−1, 1]and evaluates Legendre polynomials via the 3-term recurrence. The basis is orthogonal with respect to the uniform measure on[a, b]:∫_a^b P_m(t(y)) P_n(t(y)) dy = (b−a) / (2n+1) · δ_{mn}
- Parameters:
- derivative(y, order=1)[source]#
Analytical first derivative of the Legendre design matrix.
Uses the chain rule: d/dy P_n(t(y)) = P_n’(t) · dt/dy where dt/dy = 2/(b−a).
- evaluate_with_derivative(y)[source]#
Return Legendre design matrix and first derivative in one pass.
- class mltpy.basis.LogBasis(support)[source]#
Bases:
objectSingle-function log basis: evaluate(y) = log(y), shape (n, 1).
Returns a one-column design matrix whose sole basis function is
log(y). Useful for log-linear transformations such as the Weibull model.The support lower bound must be strictly positive.
- class mltpy.basis.LogBernsteinBasis(order, support)[source]#
Bases:
objectBernstein polynomial basis evaluated at log(y) for log-scale survival models.
Models the transformation h(y) = B_k(log(y)) · θ, where B_k is a standard Bernstein basis on (log a, log b). This parameterises Survreg models: Weibull (min_extreme_value), log-normal (normal), and log-logistic (logistic).
The derivative on the original scale follows the chain rule:
dh/dy = (1/y) · dB_k(log y)/d(log y) · θ
- Parameters:
- derivative(y, order=1)[source]#
Analytical derivative d/dy [B_k(log y)] = (1/y) · dB_k/d(log y).
- Parameters:
- Return type:
- Raises:
ValueError – If
orderis not 1, or any observation lies outsidesupport.
- evaluate_with_derivative(y)[source]#
Return B_k(log y) and d/dy B_k(log y) in one pass.
- Parameters:
y (
ndarray[tuple[Any,...],dtype[double]]) – Observations, shape (n,). Must lie insupport.- Return type:
tuple[ndarray[tuple[Any,...],dtype[double]],ndarray[tuple[Any,...],dtype[double]]]- Returns:
B (NDArray of shape (n, order+1))
dB (NDArray of shape (n, order+1) — derivative w.r.t. y (includes 1/y))
- class mltpy.basis.OneHotBasis(K)[source]#
Bases:
objectOne-hot encoding basis for K-level categorical covariates.
Each row of the design matrix is a standard basis vector
e_kof lengthK(1 in positionk, 0 elsewhere), wherekis the integer category label for that observation.The basis is non-negative and a partition of unity (each row sums to 1), making it compatible with the closed-form column-wise monotonicity constraints in
InteractionBasis. See ADR 0001, Decision 3.- Parameters:
K (
int) – Number of categories. Labels must be integers in{0, …, K-1}. Must satisfyK >= 2.
- class mltpy.basis.OrdinalBasis(K)[source]#
Bases:
objectDegenerate “one-hot cutpoint” basis used by ordinal regression (Polr).
For
Kordered levels the transformation hasK-1cutpointsθ = (θ_1, ..., θ_{K-1}). Given an integer cut positionk ∈ {1, ..., K-1}(representing the boundary between levelkand levelk+1), the basis returns the one-hot rowe_kof lengthK-1, soB(k) @ θ = θ_kexactly — the basis selects the cutpoint. Combined withMonotonicityConstraintofn_params = K-1this yieldsθ_1 ≤ ... ≤ θ_{K-1}.The class duck-types
BernsteinBasis(order,support,evaluate,derivative,integrate) so that it drops into the existing likelihood / optimisation code paths unchanged.- Parameters:
K (
int) – Number of ordered levels. Must satisfyK >= 2.
Notes
The transformation
h(y) = B(y) @ θis a step function across cut positions, so its analytical derivative w.r.t.yis zero almost everywhere.derivative()returns zero accordingly; the exact- likelihood paths inmltpy.likelihoodwould log(0) but are never invoked for ordinal data — every observation is interval-censored (or one-sided open) and routes through the censored likelihoods.- evaluate(y)[source]#
Return one-hot rows for integer cut positions in
{1, ..., K-1}.- Parameters:
y (
ndarray[tuple[Any,...],dtype[double]]) – Observations, shape(n,). Each value must be an integer in{1, ..., K-1}(the synthetic cut positions emitted bymltpy.variables.OrderedVariable.from_labels()).- Return type:
- Raises:
ValueError – If any element of
yis not an integer in{1, ..., K-1}.
- class mltpy.basis.PolynomialBasis(order, support)[source]#
Bases:
objectPower/monomial basis [1, t, t², …, tᵏ] on a compact support.
Normalises
ytot = (y − a) / (b − a) ∈ [0, 1]and returns the Vandermonde matrix[1, t, t², …, tᵏ]. Unlike Bernstein polynomials these basis functions are not non-negative and the coefficient vector has no built-in monotonicity; constraints must be imposed externally.- Parameters: