jaxsr.additive#
Additive (boosting-style) symbolic regression: fit a model as a sum of small
symbolic expressions, f(x) = c + sum_k eta_k * g_k(x).
Additive symbolic regression for JAXSR.
Fits models of the form f(x) = c + sum_k eta_k * g_k(x), where each
g_k is a small symbolic expression discovered by the existing JAXSR
machinery. This is analogous to gradient boosting, except each weak learner
is an interpretable symbolic expression rather than a decision tree.
Public API#
- StagewiseSymbolicRegressor
Boosting-style regressor that fits each new symbolic term to the current residual and freezes it. This is the first-milestone workhorse.
- BackfittingSymbolicRegressor
GAM-style regressor that revises terms in place across sweeps (warm-started from a stagewise fit); squared-error only.
- AdditiveSymbolicModel
Plain container for a fitted additive model.
- Loss, SquaredError, AbsoluteError, HuberLoss, QuantileLoss, get_loss
Loss abstraction and registry. Squared error is the default; absolute error, Huber, and quantile (pinball) losses enable robust and quantile regression via gradient boosting.
- refit_ols
Least-squares refit of intercept and per-term coefficients.
- bootstrap_additive, bootstrap_predict_additive
Bootstrap structural uncertainty: basis-function inclusion probabilities and a predictive ensemble that reflects structural variability.
- class jaxsr.additive.AbsoluteError#
Bases:
LossAbsolute-error (L1) loss
L = mean(|y - y_pred|).Robust to outliers. The optimal constant is the median and the negative gradient is
sign(y - y_pred).- name: str = 'absolute_error'#
Human-readable name used in the loss registry.
- initial_prediction(y: Array) float#
Return
median(y).
- negative_gradient(y: Array, y_pred: Array) Array#
Return
sign(y - y_pred).
- loss(y: Array, y_pred: Array) float#
Return the mean absolute error.
- class jaxsr.additive.AdditiveSymbolicModel(intercept: float, terms: list[SymbolicRegressor], coefficients: list[float], learning_rates: list[float], feature_names: list[str], training_history: list[dict[str, Any]] = <factory>)#
Bases:
objectContainer for an additive symbolic model.
Prediction is
intercept + sum_j coefficients[j] * terms[j](X).- Parameters:
intercept (float) – Additive intercept
c.terms (list of SymbolicRegressor) – Fitted symbolic expressions
g_j(the boosting “weak learners”).coefficients (list of float) – Per-term weights
eta_j. When coefficients are not refit these are the learning-rate-scaled stagewise weights; when refit they are the least-squares solution over all discovered terms.learning_rates (list of float) – Learning rate used at each stage (recorded for reproducibility).
feature_names (list of str) – Feature names, shared across all terms.
training_history (list of dict) – Per-stage diagnostics (train loss, validation loss, coefficients, …).
- intercept: float#
- terms: list[SymbolicRegressor]#
- coefficients: list[float]#
- learning_rates: list[float]#
- feature_names: list[str]#
- training_history: list[dict[str, Any]]#
- property n_terms: int#
Number of symbolic terms in the model.
- predict(X: Array) Array#
Predict with the additive model.
- Parameters:
X (jnp.ndarray of shape (n_samples, n_features)) – Input data.
- Returns:
Predicted values.
- Return type:
jnp.ndarray of shape (n_samples,)
- Raises:
ValueError – If
Xhas a different number of features than the model was fit with.
- property expressions: list[str]#
Human-readable expression string for each symbolic term.
- to_expression()#
Combine all terms into a single simplified SymPy expression.
- Returns:
intercept + sum_j coefficients[j] * g_jafter simplification. Falls back to the unsimplified sum if simplification fails.- Return type:
sympy.Expr
- Raises:
ImportError – If SymPy is not installed.
- describe(name: str = 'AdditiveSymbolicModel') str#
Return a multi-line human-readable summary of the model.
- Parameters:
name (str) – Class/label to show as the heading.
- Returns:
Pretty-printed model structure.
- Return type:
str
- class jaxsr.additive.BackfittingSymbolicRegressor(n_terms: int = 5, n_sweeps: int = 10, max_complexity: int = 4, loss: str = 'squared_error', tol: float = 1e-06, max_poly_degree: int = 3, include_transcendental: bool = False, include_ratios: bool = False, strategy: str = 'greedy_forward', information_criterion: str = 'bic', feature_names: list[str] | None = None, random_state: int | None = None)#
Bases:
_BaseAdditiveRegressorBackfitting additive symbolic regression (GAM-style).
Maintains
n_termssymbolic components and revises each one in place across repeated sweeps, conditioning on the current fit of all other terms. Contrast withStagewiseSymbolicRegressor, which freezes terms once discovered.The model is warm-started with a stagewise fit and then refined by backfitting. Only squared-error loss is supported.
- Parameters:
n_terms (int) – Number of symbolic components to maintain and revise.
n_sweeps (int) – Maximum number of backfitting sweeps over the terms.
max_complexity (int) – Complexity budget (max basis terms) for each component.
loss (str) – Loss function. Only
"squared_error"is supported.tol (float) – Convergence tolerance: stop when the training MSE improves by less than
tolbetween consecutive sweeps.max_poly_degree (int) – Maximum polynomial degree available to each component.
include_transcendental (bool) – Whether to allow transcendental terms in each component.
include_ratios (bool) – Whether to allow ratio terms in each component.
strategy (str) – Selection strategy for each component.
information_criterion (str) – Information criterion for complexity control.
feature_names (list of str, optional) – Names for the input features.
random_state (int, optional) – Seed forwarded to the stagewise warm start.
- model_#
The fitted additive model.
- Type:
- intercept_#
Fitted intercept.
- Type:
float
- coefficients_#
Fitted per-term coefficients.
- Type:
list of float
- expressions_#
Human-readable expression for each term.
- Type:
list of str
- terms_#
The fitted symbolic terms.
- Type:
list of SymbolicRegressor
- training_history_#
Per-sweep diagnostics (
sweepindex andtrain_loss).- Type:
list of dict
- n_terms_#
Number of terms in the fitted model.
- Type:
int
Examples
>>> from jaxsr.additive import BackfittingSymbolicRegressor >>> model = BackfittingSymbolicRegressor(n_terms=3, n_sweeps=5) >>> model.fit(X, y) >>> print(model)
- fit(X: Array, y: Array) BackfittingSymbolicRegressor#
Fit the backfitting additive model.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Training inputs.
y (array-like of shape (n_samples,)) – Target values.
- Returns:
self – The fitted estimator.
- Return type:
- Raises:
ValueError – If parameters are invalid,
X/yare mismatched or non-finite.NotImplementedError – If
lossis not"squared_error".
- class jaxsr.additive.HuberLoss(delta: float = 1.35)#
Bases:
LossHuber loss: quadratic for small residuals, linear beyond
delta.Combines the efficiency of squared error near zero with the robustness of absolute error in the tails.
- Parameters:
delta (float) – Threshold at which the loss transitions from quadratic to linear. Must be positive.
- Raises:
ValueError – If
deltais not positive.
- name: str = 'huber'#
Human-readable name used in the loss registry.
- initial_prediction(y: Array) float#
Return
median(y)(a robust location estimate).
- negative_gradient(y: Array, y_pred: Array) Array#
Return
rwhere|r| <= deltaelsedelta * sign(r).
- loss(y: Array, y_pred: Array) float#
Return the mean Huber loss.
- to_config() dict[str, Any]#
Return
{"name": "huber", "params": {"delta": delta}}.
- class jaxsr.additive.Loss#
Bases:
ABCAbstract base class for additive-regression loss functions.
A concrete loss defines three things:
initial_prediction()– the constant that minimises the loss and is used to initialise the ensemble intercept.negative_gradient()– the pseudo-residual each weak learner fits.loss()– the scalar training/validation loss for reporting and early stopping.
- name: str = 'loss'#
Human-readable name used in the loss registry.
- abstract initial_prediction(y: Array) float#
Return the optimal constant prediction for
y.- Parameters:
y (jnp.ndarray of shape (n_samples,)) – Target values.
- Returns:
Constant used to initialise the additive model intercept.
- Return type:
float
- abstract negative_gradient(y: Array, y_pred: Array) Array#
Return the pseudo-residual
-dL/dy_predthe next term should fit.- Parameters:
y (jnp.ndarray of shape (n_samples,)) – Target values.
y_pred (jnp.ndarray of shape (n_samples,)) – Current ensemble prediction.
- Returns:
Pseudo-residuals.
- Return type:
jnp.ndarray of shape (n_samples,)
- abstract loss(y: Array, y_pred: Array) float#
Return the scalar loss between
yandy_pred.- Parameters:
y (jnp.ndarray of shape (n_samples,)) – Target values.
y_pred (jnp.ndarray of shape (n_samples,)) – Predicted values.
- Returns:
Scalar loss value.
- Return type:
float
- to_config() dict[str, Any]#
Return a JSON-serialisable
{"name", "params"}description.- Returns:
nameis the registry key;paramsare the constructor keyword arguments needed to rebuild this loss.- Return type:
dict
- class jaxsr.additive.QuantileLoss(quantile: float = 0.5)#
Bases:
LossQuantile (pinball) loss for estimating the
quantile-th conditional quantile of the target.Useful for asymmetric costs and for building prediction intervals (fit one model per quantile).
- Parameters:
quantile (float) – Target quantile in the open interval
(0, 1).- Raises:
ValueError – If
quantileis not in(0, 1).
- name: str = 'quantile'#
Human-readable name used in the loss registry.
- initial_prediction(y: Array) float#
Return the empirical
quantile-th quantile ofy.
- negative_gradient(y: Array, y_pred: Array) Array#
Return
qwherey > y_predelseq - 1.
- loss(y: Array, y_pred: Array) float#
Return the mean pinball loss.
- to_config() dict[str, Any]#
Return
{"name": "quantile", "params": {"quantile": quantile}}.
- class jaxsr.additive.RecursiveSymbolicRegressor(n_expansions: int = 3, max_terms: int = 8, beam_width: int = 25, base_degree: int = 2, unary_ops: tuple[str, ...] = ('sin', 'cos', 'exp', 'log', 'sqrt', 'square'), binary_ops: tuple[str, ...] = ('mul', 'div'), strategy: str = 'greedy_forward', information_criterion: str = 'bic', feature_names: list[str] | None = None, random_state: int | None = None)#
Bases:
_SklearnCompatMixinResidual-guided recursive basis expansion (experimental).
Grows a symbolic basis library over several rounds, composing the most useful discovered terms with unary functions and products/ratios, guided by correlation with the current residual. Produces a fitted
jaxsr.SymbolicRegressorover the grown library.- Parameters:
n_expansions (int) – Number of expansion rounds (roughly the maximum composition depth).
max_terms (int) – Maximum number of terms in the sparse model fit each round.
beam_width (int) – Number of new candidate bases (highest residual correlation) kept per round. Controls the combinatorial cost.
base_degree (int) – Degree of the initial polynomial seed terms (before any composition).
unary_ops (tuple of str) – Unary operators to compose with. Subset of
sin,cos,exp,log,sqrt,square.binary_ops (tuple of str) – Binary operators: any of
mul(products) anddiv(ratios).strategy (str) – Selection strategy for the per-round sparse fit.
information_criterion (str) – Information criterion for the per-round sparse fit.
feature_names (list of str, optional) – Names for the input features.
random_state (int, optional) – Unused placeholder for API symmetry (the search is deterministic).
- model_#
The fitted sparse model over the final grown library.
- Type:
- library_size_#
Number of candidate basis functions in the final library.
- Type:
int
- history_#
Per-round diagnostics (library size, number of terms, train R^2).
- Type:
list of dict
Examples
>>> from jaxsr.additive import RecursiveSymbolicRegressor >>> model = RecursiveSymbolicRegressor(n_expansions=3) >>> model.fit(X, y) >>> print(model.expression_)
- fit(X: Array, y: Array) RecursiveSymbolicRegressor#
Fit by residual-guided recursive basis expansion.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Training inputs.
y (array-like of shape (n_samples,)) – Target values.
- Returns:
self – The fitted estimator.
- Return type:
- Raises:
ValueError – If parameters are invalid or
X/yare mismatched.
- predict(X: Array) Array#
Predict with the fitted model.
- score(X: Array, y: Array) float#
Return the R^2 score of the fitted model on
(X, y).
- property expression_: str#
Human-readable expression of the fitted model.
- property selected_features_: list[str]#
Names of the selected (possibly composed) basis functions.
- class jaxsr.additive.SquaredError#
Bases:
LossSquared-error loss
L = mean((y - y_pred)**2).The optimal constant prediction is the mean of
yand the pseudo-residual is the ordinary residualy - y_pred.- name: str = 'squared_error'#
Human-readable name used in the loss registry.
- initial_prediction(y: Array) float#
Return
mean(y).
- negative_gradient(y: Array, y_pred: Array) Array#
Return the residual
y - y_pred.
- loss(y: Array, y_pred: Array) float#
Return the mean squared error.
- class jaxsr.additive.StagewiseSymbolicRegressor(n_terms: int = 10, learning_rate: float = 0.1, max_complexity: int = 4, refit_coefficients: bool = True, loss: str = 'squared_error', early_stopping: bool = False, validation_fraction: float = 0.2, patience: int = 3, min_delta: float = 1e-08, max_poly_degree: int = 3, include_transcendental: bool = False, include_ratios: bool = False, strategy: str = 'greedy_forward', information_criterion: str = 'bic', feature_names: list[str] | None = None, random_state: int | None = None)#
Bases:
_BaseAdditiveRegressorStagewise additive symbolic regression (symbolic gradient boosting).
Fits an additive ensemble of small symbolic expressions by iteratively fitting each new expression to the residual of the current model. Old terms are frozen; optionally the linear coefficients over all discovered terms are refit by least squares after each stage.
- Parameters:
n_terms (int) – Maximum number of boosting stages (symbolic terms) to add.
learning_rate (float) – Shrinkage applied to each stage’s contribution when
refit_coefficients=False. Ignored whenrefit_coefficients=True(the weights are then chosen by least squares), but still recorded.max_complexity (int) – Complexity budget for each stage’s symbolic expression, expressed as the maximum number of basis terms (passed as
max_termsto the underlyingjaxsr.fit_symbolic()). Keep this small to favour many simple, interpretable terms over one large expression.refit_coefficients (bool) – If True, after each new term is added, re-solve the intercept and all per-term coefficients by ordinary least squares over the discovered symbolic features. If False, use learning-rate-scaled stagewise weights. OLS refit targets squared error, so it is only applied for
loss="squared_error"; with any other loss it is ignored (a warning is issued) and gradient boosting with a per-stage line search is used.loss (str or Loss) – Loss function. One of
"squared_error","absolute_error","huber","quantile", or aLossinstance for custom parameters (e.g.QuantileLoss(0.9)orHuberLoss(delta=2.0)). Non-squared losses are fit by gradient boosting: each term fits the negative gradient and its step size is chosen by a line search that minimises the loss.early_stopping (bool) – If True, hold out a validation split and stop adding terms once the validation loss stops improving.
validation_fraction (float) – Fraction of the training data held out for early-stopping validation. Only used when
early_stopping=True.patience (int) – Number of consecutive non-improving stages tolerated before stopping.
min_delta (float) – Minimum decrease in validation loss to count as an improvement.
max_poly_degree (int) – Maximum polynomial degree available to each stage.
include_transcendental (bool) – If True, allow
log/exp/sqrt/invterms in each stage.include_ratios (bool) – If True, allow ratio terms
x_i / x_jin each stage.strategy (str) – Selection strategy for each stage (see
jaxsr.SymbolicRegressor).information_criterion (str) – Information criterion used to control complexity within each stage:
"aic","aicc", or"bic".feature_names (list of str, optional) – Names for the input features. Defaults to
["x0", "x1", ...].random_state (int, optional) – Seed controlling the early-stopping validation split.
- model_#
The fitted additive model.
- Type:
- intercept_#
Fitted intercept.
- Type:
float
- coefficients_#
Fitted per-term coefficients.
- Type:
list of float
- expressions_#
Human-readable expression for each term.
- Type:
list of str
- terms_#
The fitted symbolic terms.
- Type:
list of SymbolicRegressor
- learning_rates_#
Learning rate recorded at each stage.
- Type:
list of float
- training_history_#
Per-stage diagnostics.
- Type:
list of dict
- n_terms_#
Number of terms in the fitted model.
- Type:
int
Examples
>>> import numpy as np >>> from jaxsr.additive import StagewiseSymbolicRegressor >>> X = np.random.randn(200, 2) >>> y = 2.0 * X[:, 0] + 0.5 * X[:, 1] ** 2 >>> model = StagewiseSymbolicRegressor(n_terms=5, refit_coefficients=True) >>> model.fit(X, y) >>> print(model)
- fit(X: Array, y: Array) StagewiseSymbolicRegressor#
Fit the stagewise additive symbolic model.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Training inputs.
y (array-like of shape (n_samples,)) – Target values.
- Returns:
self – The fitted estimator.
- Return type:
- Raises:
ValueError – If parameters are invalid,
Xandyhave mismatched sample counts, orX/ycontain non-finite values.
- jaxsr.additive.additive_predict(X: jnp.ndarray, intercept: float, terms: list[SymbolicRegressor], coefficients: list[float] | jnp.ndarray) jnp.ndarray#
Evaluate an additive model
intercept + sum_j coef_j * term_j(X).- Parameters:
X (jnp.ndarray of shape (n_samples, n_features)) – Input data.
intercept (float) – Additive intercept.
terms (list of SymbolicRegressor) – Fitted symbolic terms
g_j.coefficients (list of float or jnp.ndarray) – Per-term coefficients, aligned with
terms.
- Returns:
Predicted values.
- Return type:
jnp.ndarray of shape (n_samples,)
- jaxsr.additive.bootstrap_additive(estimator, X: Array, y: Array, n_bootstrap: int = 100, random_state: int | None = None) dict#
Refit an additive regressor on bootstrap resamples to assess structure.
- Parameters:
estimator (StagewiseSymbolicRegressor or BackfittingSymbolicRegressor) – A configured (fitted or unfitted) additive regressor. It is cloned via its constructor parameters and refit on each resample; the original is not modified.
X (array-like of shape (n_samples, n_features)) – Training inputs.
y (array-like of shape (n_samples,)) – Target values.
n_bootstrap (int) – Number of bootstrap resamples.
random_state (int, optional) – Seed for the resampling for reproducibility.
- Returns:
Plain dictionary with keys:
"inclusion_probabilities"dict[str, float]Basis-function name -> fraction of resamples selecting it (in any term), sorted descending.
"n_terms"numpy.ndarrayNumber of terms in each successful resample fit.
"models"listThe fitted bootstrap estimators (use with
bootstrap_predict_additive())."n_bootstrap"intNumber of resamples that fit successfully.
- Return type:
dict
- Raises:
ValueError – If
X/yare mismatched orn_bootstrap < 1.RuntimeError – If every bootstrap fit fails.
- jaxsr.additive.bootstrap_predict_additive(models: list, X: Array, alpha: float = 0.1) dict#
Predictive ensemble from a bootstrap set of additive models.
- Parameters:
models (list) – Fitted additive estimators, e.g. the
"models"entry returned bybootstrap_additive().X (array-like of shape (n_samples, n_features)) – Inputs to predict.
alpha (float) – Significance level; the interval covers
1 - alpha(default 0.1 for a 90% interval).
- Returns:
Plain dictionary with keys
"mean","std","median","lower","upper"(each of shape(n_samples,)) and"predictions"of shape(n_models, n_samples).- Return type:
dict
- Raises:
ValueError – If
modelsis empty oralphais not in(0, 1).
- jaxsr.additive.get_loss(loss: str | Loss) Loss#
Resolve a loss name (or instance) to a
Lossinstance.- Parameters:
loss (str or Loss) – Either a registered loss name (
"squared_error","absolute_error","huber","quantile") or an already-constructedLossinstance. Names build losses with their default parameters; pass an instance (e.g.QuantileLoss(0.9)) to customise.- Returns:
A loss instance.
- Return type:
- Raises:
ValueError – If
lossis a string that is not a registered loss name.TypeError – If
lossis neither a string nor aLossinstance.
- jaxsr.additive.loss_from_config(config: str | dict[str, Any] | Loss) Loss#
Rebuild a
Lossfrom a name, an instance, or ato_configdict.- Parameters:
config (str or dict or Loss) – A registry name, a
{"name", "params"}dictionary produced byLoss.to_config(), or aLossinstance.- Returns:
A loss instance.
- Return type:
- Raises:
ValueError – If the config names an unknown loss.
TypeError – If
configis not a str, dict, orLoss.
- jaxsr.additive.refit_ols(Phi: Array, y: Array) tuple[float, Array]#
Refit an intercept and per-term coefficients by ordinary least squares.
Solves
y ~= intercept + Phi @ coefficientsusing a least-squares solver that is robust to rank-deficient / collinear design matrices (later boosting stages fit residuals of earlier ones, so the term columns can be highly correlated).- Parameters:
Phi (jnp.ndarray of shape (n_samples, n_terms)) – Design matrix whose column
jisg_j(X)for termj. May have zero columns (no terms discovered yet).y (jnp.ndarray of shape (n_samples,)) – Target values.
- Returns:
intercept (float) – Fitted intercept.
coefficients (jnp.ndarray of shape (n_terms,)) – Fitted per-term coefficients.
- Raises:
ValueError – If
Phiis not 2-D or its number of rows does not matchlen(y).
Stagewise (boosting-style) additive symbolic regression.
StagewiseSymbolicRegressor builds a model of the form:
f(x) = intercept + sum_k coefficients[k] * g_k(x)
by repeatedly fitting a small symbolic expression g_k to the current
residual (pseudo-residual for general losses). This is conceptually
“gradient boosting, but the weak learners are symbolic expressions instead of
trees”.
Once discovered, a term is frozen – its internal structure never changes.
Only the linear weights may be re-estimated (see refit_coefficients). The
future BackfittingSymbolicRegressor will
instead revise terms in place.
- class jaxsr.additive.stagewise.StagewiseSymbolicRegressor(n_terms: int = 10, learning_rate: float = 0.1, max_complexity: int = 4, refit_coefficients: bool = True, loss: str = 'squared_error', early_stopping: bool = False, validation_fraction: float = 0.2, patience: int = 3, min_delta: float = 1e-08, max_poly_degree: int = 3, include_transcendental: bool = False, include_ratios: bool = False, strategy: str = 'greedy_forward', information_criterion: str = 'bic', feature_names: list[str] | None = None, random_state: int | None = None)#
Bases:
_BaseAdditiveRegressorStagewise additive symbolic regression (symbolic gradient boosting).
Fits an additive ensemble of small symbolic expressions by iteratively fitting each new expression to the residual of the current model. Old terms are frozen; optionally the linear coefficients over all discovered terms are refit by least squares after each stage.
- Parameters:
n_terms (int) – Maximum number of boosting stages (symbolic terms) to add.
learning_rate (float) – Shrinkage applied to each stage’s contribution when
refit_coefficients=False. Ignored whenrefit_coefficients=True(the weights are then chosen by least squares), but still recorded.max_complexity (int) – Complexity budget for each stage’s symbolic expression, expressed as the maximum number of basis terms (passed as
max_termsto the underlyingjaxsr.fit_symbolic()). Keep this small to favour many simple, interpretable terms over one large expression.refit_coefficients (bool) – If True, after each new term is added, re-solve the intercept and all per-term coefficients by ordinary least squares over the discovered symbolic features. If False, use learning-rate-scaled stagewise weights. OLS refit targets squared error, so it is only applied for
loss="squared_error"; with any other loss it is ignored (a warning is issued) and gradient boosting with a per-stage line search is used.loss (str or Loss) – Loss function. One of
"squared_error","absolute_error","huber","quantile", or aLossinstance for custom parameters (e.g.QuantileLoss(0.9)orHuberLoss(delta=2.0)). Non-squared losses are fit by gradient boosting: each term fits the negative gradient and its step size is chosen by a line search that minimises the loss.early_stopping (bool) – If True, hold out a validation split and stop adding terms once the validation loss stops improving.
validation_fraction (float) – Fraction of the training data held out for early-stopping validation. Only used when
early_stopping=True.patience (int) – Number of consecutive non-improving stages tolerated before stopping.
min_delta (float) – Minimum decrease in validation loss to count as an improvement.
max_poly_degree (int) – Maximum polynomial degree available to each stage.
include_transcendental (bool) – If True, allow
log/exp/sqrt/invterms in each stage.include_ratios (bool) – If True, allow ratio terms
x_i / x_jin each stage.strategy (str) – Selection strategy for each stage (see
jaxsr.SymbolicRegressor).information_criterion (str) – Information criterion used to control complexity within each stage:
"aic","aicc", or"bic".feature_names (list of str, optional) – Names for the input features. Defaults to
["x0", "x1", ...].random_state (int, optional) – Seed controlling the early-stopping validation split.
- model_#
The fitted additive model.
- Type:
- intercept_#
Fitted intercept.
- Type:
float
- coefficients_#
Fitted per-term coefficients.
- Type:
list of float
- expressions_#
Human-readable expression for each term.
- Type:
list of str
- terms_#
The fitted symbolic terms.
- Type:
list of SymbolicRegressor
- learning_rates_#
Learning rate recorded at each stage.
- Type:
list of float
- training_history_#
Per-stage diagnostics.
- Type:
list of dict
- n_terms_#
Number of terms in the fitted model.
- Type:
int
Examples
>>> import numpy as np >>> from jaxsr.additive import StagewiseSymbolicRegressor >>> X = np.random.randn(200, 2) >>> y = 2.0 * X[:, 0] + 0.5 * X[:, 1] ** 2 >>> model = StagewiseSymbolicRegressor(n_terms=5, refit_coefficients=True) >>> model.fit(X, y) >>> print(model)
- fit(X: Array, y: Array) StagewiseSymbolicRegressor#
Fit the stagewise additive symbolic model.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Training inputs.
y (array-like of shape (n_samples,)) – Target values.
- Returns:
self – The fitted estimator.
- Return type:
- Raises:
ValueError – If parameters are invalid,
Xandyhave mismatched sample counts, orX/ycontain non-finite values.
Core additive symbolic model representation.
An additive symbolic model has the form:
f(x) = intercept + sum_j coefficients[j] * terms[j](x)
where each terms[j] is a small symbolic expression (a fitted
jaxsr.SymbolicRegressor) discovered by the existing JAXSR machinery.
This is analogous to gradient boosting, except each weak learner is an
interpretable symbolic expression rather than a decision tree.
The AdditiveSymbolicModel is a plain data container: the fitting
strategy (stagewise, backfitting, …) lives in the regressor classes and
produces one of these objects.
- jaxsr.additive.ensemble.additive_predict(X: jnp.ndarray, intercept: float, terms: list[SymbolicRegressor], coefficients: list[float] | jnp.ndarray) jnp.ndarray#
Evaluate an additive model
intercept + sum_j coef_j * term_j(X).- Parameters:
X (jnp.ndarray of shape (n_samples, n_features)) – Input data.
intercept (float) – Additive intercept.
terms (list of SymbolicRegressor) – Fitted symbolic terms
g_j.coefficients (list of float or jnp.ndarray) – Per-term coefficients, aligned with
terms.
- Returns:
Predicted values.
- Return type:
jnp.ndarray of shape (n_samples,)
- class jaxsr.additive.ensemble.AdditiveSymbolicModel(intercept: float, terms: list[SymbolicRegressor], coefficients: list[float], learning_rates: list[float], feature_names: list[str], training_history: list[dict[str, Any]] = <factory>)#
Bases:
objectContainer for an additive symbolic model.
Prediction is
intercept + sum_j coefficients[j] * terms[j](X).- Parameters:
intercept (float) – Additive intercept
c.terms (list of SymbolicRegressor) – Fitted symbolic expressions
g_j(the boosting “weak learners”).coefficients (list of float) – Per-term weights
eta_j. When coefficients are not refit these are the learning-rate-scaled stagewise weights; when refit they are the least-squares solution over all discovered terms.learning_rates (list of float) – Learning rate used at each stage (recorded for reproducibility).
feature_names (list of str) – Feature names, shared across all terms.
training_history (list of dict) – Per-stage diagnostics (train loss, validation loss, coefficients, …).
- intercept: float#
- terms: list[SymbolicRegressor]#
- coefficients: list[float]#
- learning_rates: list[float]#
- feature_names: list[str]#
- training_history: list[dict[str, Any]]#
- property n_terms: int#
Number of symbolic terms in the model.
- predict(X: Array) Array#
Predict with the additive model.
- Parameters:
X (jnp.ndarray of shape (n_samples, n_features)) – Input data.
- Returns:
Predicted values.
- Return type:
jnp.ndarray of shape (n_samples,)
- Raises:
ValueError – If
Xhas a different number of features than the model was fit with.
- property expressions: list[str]#
Human-readable expression string for each symbolic term.
- to_expression()#
Combine all terms into a single simplified SymPy expression.
- Returns:
intercept + sum_j coefficients[j] * g_jafter simplification. Falls back to the unsimplified sum if simplification fails.- Return type:
sympy.Expr
- Raises:
ImportError – If SymPy is not installed.
- describe(name: str = 'AdditiveSymbolicModel') str#
Return a multi-line human-readable summary of the model.
- Parameters:
name (str) – Class/label to show as the heading.
- Returns:
Pretty-printed model structure.
- Return type:
str
Loss functions for additive symbolic regression.
Additive (boosting-style) symbolic regression fits each new symbolic term to
the pseudo-residual of the current ensemble. For squared error the
pseudo-residual is simply y - y_pred; for other differentiable losses it
is the negative gradient -dL/dy_pred, which is what gradient boosting
fits. This lets JAXSR learn symbolic models under losses that ordinary
least-squares selection cannot target directly:
SquaredError– standard regression (mean).AbsoluteError– robust to outliers (median).HuberLoss– quadratic near zero, linear in the tails (robust).QuantileLoss– pinball loss for quantile / interval estimation.
New losses can be added by subclassing Loss and registering them in
_LOSSES.
- class jaxsr.additive.losses.Loss#
Bases:
ABCAbstract base class for additive-regression loss functions.
A concrete loss defines three things:
initial_prediction()– the constant that minimises the loss and is used to initialise the ensemble intercept.negative_gradient()– the pseudo-residual each weak learner fits.loss()– the scalar training/validation loss for reporting and early stopping.
- name: str = 'loss'#
Human-readable name used in the loss registry.
- abstract initial_prediction(y: Array) float#
Return the optimal constant prediction for
y.- Parameters:
y (jnp.ndarray of shape (n_samples,)) – Target values.
- Returns:
Constant used to initialise the additive model intercept.
- Return type:
float
- abstract negative_gradient(y: Array, y_pred: Array) Array#
Return the pseudo-residual
-dL/dy_predthe next term should fit.- Parameters:
y (jnp.ndarray of shape (n_samples,)) – Target values.
y_pred (jnp.ndarray of shape (n_samples,)) – Current ensemble prediction.
- Returns:
Pseudo-residuals.
- Return type:
jnp.ndarray of shape (n_samples,)
- abstract loss(y: Array, y_pred: Array) float#
Return the scalar loss between
yandy_pred.- Parameters:
y (jnp.ndarray of shape (n_samples,)) – Target values.
y_pred (jnp.ndarray of shape (n_samples,)) – Predicted values.
- Returns:
Scalar loss value.
- Return type:
float
- to_config() dict[str, Any]#
Return a JSON-serialisable
{"name", "params"}description.- Returns:
nameis the registry key;paramsare the constructor keyword arguments needed to rebuild this loss.- Return type:
dict
- class jaxsr.additive.losses.SquaredError#
Bases:
LossSquared-error loss
L = mean((y - y_pred)**2).The optimal constant prediction is the mean of
yand the pseudo-residual is the ordinary residualy - y_pred.- name: str = 'squared_error'#
Human-readable name used in the loss registry.
- initial_prediction(y: Array) float#
Return
mean(y).
- negative_gradient(y: Array, y_pred: Array) Array#
Return the residual
y - y_pred.
- loss(y: Array, y_pred: Array) float#
Return the mean squared error.
- class jaxsr.additive.losses.AbsoluteError#
Bases:
LossAbsolute-error (L1) loss
L = mean(|y - y_pred|).Robust to outliers. The optimal constant is the median and the negative gradient is
sign(y - y_pred).- name: str = 'absolute_error'#
Human-readable name used in the loss registry.
- initial_prediction(y: Array) float#
Return
median(y).
- negative_gradient(y: Array, y_pred: Array) Array#
Return
sign(y - y_pred).
- loss(y: Array, y_pred: Array) float#
Return the mean absolute error.
- class jaxsr.additive.losses.HuberLoss(delta: float = 1.35)#
Bases:
LossHuber loss: quadratic for small residuals, linear beyond
delta.Combines the efficiency of squared error near zero with the robustness of absolute error in the tails.
- Parameters:
delta (float) – Threshold at which the loss transitions from quadratic to linear. Must be positive.
- Raises:
ValueError – If
deltais not positive.
- name: str = 'huber'#
Human-readable name used in the loss registry.
- initial_prediction(y: Array) float#
Return
median(y)(a robust location estimate).
- negative_gradient(y: Array, y_pred: Array) Array#
Return
rwhere|r| <= deltaelsedelta * sign(r).
- loss(y: Array, y_pred: Array) float#
Return the mean Huber loss.
- to_config() dict[str, Any]#
Return
{"name": "huber", "params": {"delta": delta}}.
- class jaxsr.additive.losses.QuantileLoss(quantile: float = 0.5)#
Bases:
LossQuantile (pinball) loss for estimating the
quantile-th conditional quantile of the target.Useful for asymmetric costs and for building prediction intervals (fit one model per quantile).
- Parameters:
quantile (float) – Target quantile in the open interval
(0, 1).- Raises:
ValueError – If
quantileis not in(0, 1).
- name: str = 'quantile'#
Human-readable name used in the loss registry.
- initial_prediction(y: Array) float#
Return the empirical
quantile-th quantile ofy.
- negative_gradient(y: Array, y_pred: Array) Array#
Return
qwherey > y_predelseq - 1.
- loss(y: Array, y_pred: Array) float#
Return the mean pinball loss.
- to_config() dict[str, Any]#
Return
{"name": "quantile", "params": {"quantile": quantile}}.
- jaxsr.additive.losses.get_loss(loss: str | Loss) Loss#
Resolve a loss name (or instance) to a
Lossinstance.- Parameters:
loss (str or Loss) – Either a registered loss name (
"squared_error","absolute_error","huber","quantile") or an already-constructedLossinstance. Names build losses with their default parameters; pass an instance (e.g.QuantileLoss(0.9)) to customise.- Returns:
A loss instance.
- Return type:
- Raises:
ValueError – If
lossis a string that is not a registered loss name.TypeError – If
lossis neither a string nor aLossinstance.
- jaxsr.additive.losses.loss_from_config(config: str | dict[str, Any] | Loss) Loss#
Rebuild a
Lossfrom a name, an instance, or ato_configdict.- Parameters:
config (str or dict or Loss) – A registry name, a
{"name", "params"}dictionary produced byLoss.to_config(), or aLossinstance.- Returns:
A loss instance.
- Return type:
- Raises:
ValueError – If the config names an unknown loss.
TypeError – If
configis not a str, dict, orLoss.
Coefficient refitting for additive symbolic regression.
After a new symbolic term is discovered, the stagewise model can optionally re-solve the linear coefficients over all discovered symbolic features at once, treating each term’s prediction as a single feature column:
Phi[:, j] = g_j(X)
y ~= intercept + Phi @ coefficients
This decouples term discovery (nonlinear, greedy) from term weighting (linear, global), which typically improves accuracy relative to fixed learning-rate-scaled stagewise weights.
Only ordinary least squares is implemented for the first milestone. Ridge, lasso, and sparse variants can be added later behind the same interface.
- jaxsr.additive.coefficient_refit.refit_ols(Phi: Array, y: Array) tuple[float, Array]#
Refit an intercept and per-term coefficients by ordinary least squares.
Solves
y ~= intercept + Phi @ coefficientsusing a least-squares solver that is robust to rank-deficient / collinear design matrices (later boosting stages fit residuals of earlier ones, so the term columns can be highly correlated).- Parameters:
Phi (jnp.ndarray of shape (n_samples, n_terms)) – Design matrix whose column
jisg_j(X)for termj. May have zero columns (no terms discovered yet).y (jnp.ndarray of shape (n_samples,)) – Target values.
- Returns:
intercept (float) – Fitted intercept.
coefficients (jnp.ndarray of shape (n_terms,)) – Fitted per-term coefficients.
- Raises:
ValueError – If
Phiis not 2-D or its number of rows does not matchlen(y).
Backfitting additive symbolic regression (GAM-style).
Unlike StagewiseSymbolicRegressor, where each
discovered term is frozen, the backfitting regressor maintains a fixed number
of terms and repeatedly revises each one. A “sweep” visits every term in
turn, removes it from the ensemble, and re-discovers its symbolic expression on
the partial residual (the target minus every other term’s contribution):
for sweep in 1..n_sweeps:
for term j:
partial_residual = y - intercept - sum_{i != j} coef_i * g_i(X)
g_j = fit_symbolic(X, partial_residual, ...) # re-discover structure
intercept, coef = OLS refit over all terms
This lets early terms – originally fit against a residual still polluted by effects that had not yet been discovered – clean themselves up once the other terms are in place. It is the classic backfitting algorithm behind generalized additive models, with symbolic expressions as the smoothers.
The regressor is warm-started from a stagewise fit, so a single fit call
first runs stagewise boosting and then refines it by backfitting.
Scope#
This is the deterministic, squared-error version. A future Bayesian variant (BART/iBART-style, sampling a posterior over symbolic structures) can build on the same partial-residual sweep; see the project notes.
- class jaxsr.additive.backfitting.BackfittingSymbolicRegressor(n_terms: int = 5, n_sweeps: int = 10, max_complexity: int = 4, loss: str = 'squared_error', tol: float = 1e-06, max_poly_degree: int = 3, include_transcendental: bool = False, include_ratios: bool = False, strategy: str = 'greedy_forward', information_criterion: str = 'bic', feature_names: list[str] | None = None, random_state: int | None = None)#
Bases:
_BaseAdditiveRegressorBackfitting additive symbolic regression (GAM-style).
Maintains
n_termssymbolic components and revises each one in place across repeated sweeps, conditioning on the current fit of all other terms. Contrast withStagewiseSymbolicRegressor, which freezes terms once discovered.The model is warm-started with a stagewise fit and then refined by backfitting. Only squared-error loss is supported.
- Parameters:
n_terms (int) – Number of symbolic components to maintain and revise.
n_sweeps (int) – Maximum number of backfitting sweeps over the terms.
max_complexity (int) – Complexity budget (max basis terms) for each component.
loss (str) – Loss function. Only
"squared_error"is supported.tol (float) – Convergence tolerance: stop when the training MSE improves by less than
tolbetween consecutive sweeps.max_poly_degree (int) – Maximum polynomial degree available to each component.
include_transcendental (bool) – Whether to allow transcendental terms in each component.
include_ratios (bool) – Whether to allow ratio terms in each component.
strategy (str) – Selection strategy for each component.
information_criterion (str) – Information criterion for complexity control.
feature_names (list of str, optional) – Names for the input features.
random_state (int, optional) – Seed forwarded to the stagewise warm start.
- model_#
The fitted additive model.
- Type:
- intercept_#
Fitted intercept.
- Type:
float
- coefficients_#
Fitted per-term coefficients.
- Type:
list of float
- expressions_#
Human-readable expression for each term.
- Type:
list of str
- terms_#
The fitted symbolic terms.
- Type:
list of SymbolicRegressor
- training_history_#
Per-sweep diagnostics (
sweepindex andtrain_loss).- Type:
list of dict
- n_terms_#
Number of terms in the fitted model.
- Type:
int
Examples
>>> from jaxsr.additive import BackfittingSymbolicRegressor >>> model = BackfittingSymbolicRegressor(n_terms=3, n_sweeps=5) >>> model.fit(X, y) >>> print(model)
- fit(X: Array, y: Array) BackfittingSymbolicRegressor#
Fit the backfitting additive model.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Training inputs.
y (array-like of shape (n_samples,)) – Target values.
- Returns:
self – The fitted estimator.
- Return type:
- Raises:
ValueError – If parameters are invalid,
X/yare mismatched or non-finite.NotImplementedError – If
lossis not"squared_error".
Experimental: residual-guided recursive basis expansion.
This is a deterministic, bounded cousin of genetic-programming symbolic
regression – and a partial escape from the fixed-library ceiling. Instead of
enumerating a huge depth-d composition space up front (which explodes
combinatorially), it grows the basis library lazily along the residual:
Fit a sparse symbolic model over the current library.
Take the residual.
Build candidate basis functions by composing the currently useful “building blocks” (selected terms + raw features) with a small operator set (unary functions, products, ratios) – one new layer of composition.
Screen hard: drop non-finite candidates, deduplicate, keep the top few by correlation with the residual.
Add the survivors to the library and refit. Repeat.
The effective composition depth is the number of expansion rounds, because a
term discovered in one round becomes an input to the operators in the next.
This is essentially Fast Function Extraction (FFX) / symbolic feature
construction: it can reach compositional targets (e.g. x0*sin(x1)) that a
single flat library misses, at bounded cost – but it re-enters search-based
territory and will not match a mature GP engine (PySR, Operon).
The result is an ordinary fitted jaxsr.SymbolicRegressor over the grown
library, so it inherits predict/expression/scoring (and the non-finite-basis
guard and negligible-term pruning of the base regressor). Note: the composed
bases are Python closures, so the fitted model is not serialisable via
save/load, and to_sympy may not parse deeply nested term names.
- class jaxsr.additive.recursive.RecursiveSymbolicRegressor(n_expansions: int = 3, max_terms: int = 8, beam_width: int = 25, base_degree: int = 2, unary_ops: tuple[str, ...] = ('sin', 'cos', 'exp', 'log', 'sqrt', 'square'), binary_ops: tuple[str, ...] = ('mul', 'div'), strategy: str = 'greedy_forward', information_criterion: str = 'bic', feature_names: list[str] | None = None, random_state: int | None = None)#
Bases:
_SklearnCompatMixinResidual-guided recursive basis expansion (experimental).
Grows a symbolic basis library over several rounds, composing the most useful discovered terms with unary functions and products/ratios, guided by correlation with the current residual. Produces a fitted
jaxsr.SymbolicRegressorover the grown library.- Parameters:
n_expansions (int) – Number of expansion rounds (roughly the maximum composition depth).
max_terms (int) – Maximum number of terms in the sparse model fit each round.
beam_width (int) – Number of new candidate bases (highest residual correlation) kept per round. Controls the combinatorial cost.
base_degree (int) – Degree of the initial polynomial seed terms (before any composition).
unary_ops (tuple of str) – Unary operators to compose with. Subset of
sin,cos,exp,log,sqrt,square.binary_ops (tuple of str) – Binary operators: any of
mul(products) anddiv(ratios).strategy (str) – Selection strategy for the per-round sparse fit.
information_criterion (str) – Information criterion for the per-round sparse fit.
feature_names (list of str, optional) – Names for the input features.
random_state (int, optional) – Unused placeholder for API symmetry (the search is deterministic).
- model_#
The fitted sparse model over the final grown library.
- Type:
- library_size_#
Number of candidate basis functions in the final library.
- Type:
int
- history_#
Per-round diagnostics (library size, number of terms, train R^2).
- Type:
list of dict
Examples
>>> from jaxsr.additive import RecursiveSymbolicRegressor >>> model = RecursiveSymbolicRegressor(n_expansions=3) >>> model.fit(X, y) >>> print(model.expression_)
- fit(X: Array, y: Array) RecursiveSymbolicRegressor#
Fit by residual-guided recursive basis expansion.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Training inputs.
y (array-like of shape (n_samples,)) – Target values.
- Returns:
self – The fitted estimator.
- Return type:
- Raises:
ValueError – If parameters are invalid or
X/yare mismatched.
- predict(X: Array) Array#
Predict with the fitted model.
- score(X: Array, y: Array) float#
Return the R^2 score of the fitted model on
(X, y).
- property expression_: str#
Human-readable expression of the fitted model.
- property selected_features_: list[str]#
Names of the selected (possibly composed) basis functions.
Shared base class for additive symbolic regressors.
Both StagewiseSymbolicRegressor and
BackfittingSymbolicRegressor produce an
AdditiveSymbolicModel and share the same
fitted-attribute accessors, prediction, interpretation, and JSON
serialization. Only the fitting strategy differs, so everything except
fit lives here.
Bootstrap structural uncertainty for additive symbolic models.
Refitting an additive symbolic regressor on bootstrap resamples of the training data quantifies how stable the discovered structure is:
Inclusion probabilities – how often each basis function is selected across resamples (a cheap, frequentist approximation to a posterior inclusion probability). When these are all near 0 or 1 the structure is identifiable and a single expression is trustworthy; diffuse values (e.g. under collinear features) signal genuine structural uncertainty, where no single expression is well determined.
Predictive ensemble – the spread of predictions across resamples, giving intervals that reflect structural variability, not just coefficient noise.
This is a model-agnostic stand-in for a full Bayesian treatment: it works for any additive regressor (stagewise or backfitting) and reuses the estimator’s own fitting machinery. It also serves as a decision gate – if the inclusion probabilities are already crisp, a heavier Bayesian model buys little.
- jaxsr.additive.uncertainty.bootstrap_additive(estimator, X: Array, y: Array, n_bootstrap: int = 100, random_state: int | None = None) dict#
Refit an additive regressor on bootstrap resamples to assess structure.
- Parameters:
estimator (StagewiseSymbolicRegressor or BackfittingSymbolicRegressor) – A configured (fitted or unfitted) additive regressor. It is cloned via its constructor parameters and refit on each resample; the original is not modified.
X (array-like of shape (n_samples, n_features)) – Training inputs.
y (array-like of shape (n_samples,)) – Target values.
n_bootstrap (int) – Number of bootstrap resamples.
random_state (int, optional) – Seed for the resampling for reproducibility.
- Returns:
Plain dictionary with keys:
"inclusion_probabilities"dict[str, float]Basis-function name -> fraction of resamples selecting it (in any term), sorted descending.
"n_terms"numpy.ndarrayNumber of terms in each successful resample fit.
"models"listThe fitted bootstrap estimators (use with
bootstrap_predict_additive())."n_bootstrap"intNumber of resamples that fit successfully.
- Return type:
dict
- Raises:
ValueError – If
X/yare mismatched orn_bootstrap < 1.RuntimeError – If every bootstrap fit fails.
- jaxsr.additive.uncertainty.bootstrap_predict_additive(models: list, X: Array, alpha: float = 0.1) dict#
Predictive ensemble from a bootstrap set of additive models.
- Parameters:
models (list) – Fitted additive estimators, e.g. the
"models"entry returned bybootstrap_additive().X (array-like of shape (n_samples, n_features)) – Inputs to predict.
alpha (float) – Significance level; the interval covers
1 - alpha(default 0.1 for a 90% interval).
- Returns:
Plain dictionary with keys
"mean","std","median","lower","upper"(each of shape(n_samples,)) and"predictions"of shape(n_models, n_samples).- Return type:
dict
- Raises:
ValueError – If
modelsis empty oralphais not in(0, 1).